mirror of
https://gitlab.com/ryandwyer/perfect-dark
synced 2026-07-04 20:15:28 -04:00
529 lines
13 KiB
C
529 lines
13 KiB
C
#include <ultra64.h>
|
|
#include "constants.h"
|
|
#include "game/smoke.h"
|
|
#include "game/bg.h"
|
|
#include "game/propobj.h"
|
|
#include "game/splat.h"
|
|
#include "game/utils.h"
|
|
#include "game/wallhit.h"
|
|
#include "bss.h"
|
|
#include "lib/lib_17ce0.h"
|
|
#include "lib/model.h"
|
|
#include "lib/rng.h"
|
|
#include "lib/mtx.h"
|
|
#include "data.h"
|
|
#include "types.h"
|
|
|
|
#define SPLATTYPE_PUDDLE 1
|
|
#define SPLATTYPE_DROP 2
|
|
|
|
struct splatdata {
|
|
struct coord relpos;
|
|
struct coord unk0c;
|
|
struct coord unk18;
|
|
struct coord gunpos;
|
|
s32 splattype;
|
|
struct prop *objprop;
|
|
struct prop *chrprop;
|
|
struct chrdata *chr;
|
|
s32 mtxindex;
|
|
s32 room;
|
|
s32 isskedar;
|
|
s32 translucent;
|
|
f32 unk50;
|
|
s32 timermax;
|
|
s32 timerspeed;
|
|
};
|
|
|
|
#if VERSION < VERSION_NTSC_1_0
|
|
u32 var80082100nb = 0;
|
|
#endif
|
|
|
|
f32 var8007f8a0 = 0.15;
|
|
f32 var8007f8a4 = 3;
|
|
f32 var8007f8a8 = 12;
|
|
s32 var8007f8ac = 8;
|
|
f32 g_SplatMaxDistance = 180;
|
|
f32 var8007f8b4 = 5;
|
|
f32 var8007f8b8 = 50;
|
|
|
|
#if VERSION == VERSION_JPN_FINAL
|
|
#else
|
|
bool splat0f149274(f32 arg0, struct prop *prop, struct shotdata *shotdata, f32 arg3, bool isskedar, s32 arg5, s32 arg6, struct chrdata *chr, s32 arg8);
|
|
void splat0f14986c(struct splatdata *splatdata);
|
|
#endif
|
|
|
|
void splatTickChr(struct prop *prop)
|
|
{
|
|
struct chrdata *chr = prop->chr;
|
|
struct chrdata *attacker = chr->lastattacker;
|
|
s32 race;
|
|
|
|
if (chr->noblood || (chr->chrflags & CHRCFLAG_HIDDEN) || chr->bulletstaken == 0) {
|
|
return;
|
|
}
|
|
|
|
race = CHRRACE(chr);
|
|
|
|
if (race != RACE_DRCAROLL && race != RACE_ROBOT) {
|
|
u8 isskedar = false;
|
|
|
|
if (race == RACE_SKEDAR || chr->bodynum == BODY_MRBLONDE) {
|
|
isskedar = true;
|
|
}
|
|
|
|
osSyncPrintf("Splat Tick - P=%x, B=%d, T=%d, S=%d, W=%d, D=%d, H=%d\n");
|
|
|
|
if (chr->actiontype == ACT_DEAD || chr->actiontype == ACT_DIE) {
|
|
u32 stack;
|
|
f32 thudframe = -1.0f;
|
|
|
|
if (chr->actiontype == ACT_DIE) {
|
|
if (chr->act_die.thudframe2 != -1) {
|
|
thudframe = chr->act_die.thudframe2;
|
|
} else if (chr->act_die.thudframe1 != -1) {
|
|
thudframe = chr->act_die.thudframe1;
|
|
}
|
|
}
|
|
|
|
if (thudframe != -1.0f && modelGetCurAnimFrame(chr->model) < thudframe) {
|
|
osSyncPrintf("SPLAT : Not Dead Enough %s%s%f", "", "", modelGetCurAnimFrame(chr->model));
|
|
} else if (chr->tickssincesplat > TICKS(30) && chr->deaddropsplatsadded < 6) {
|
|
chr->deaddropsplatsadded += splatsCreate(1, 1.1f, prop, NULL, 0, 0, isskedar, SPLATTYPE_PUDDLE, TICKS(150), attacker, random() & 8);
|
|
}
|
|
} else {
|
|
// Consider creating a wounded drop
|
|
u32 value = chr->bulletstaken * chr->tickssincesplat;
|
|
|
|
if (value > TICKS(240)) {
|
|
f32 dist = coordsGetDistance(&chr->lastdroppos, &prop->pos);
|
|
s32 addmore = false;
|
|
|
|
if (dist > 40) {
|
|
addmore = true;
|
|
chr->splatsdroppedhere = 0;
|
|
} else if (chr->splatsdroppedhere < 8) {
|
|
addmore = true;
|
|
chr->splatsdroppedhere++;
|
|
}
|
|
|
|
if (addmore) {
|
|
chr->woundedsplatsadded += splatsCreate(1, 0.3f, prop, NULL, 0, 0, isskedar, SPLATTYPE_DROP, TICKS(80), attacker, 0);
|
|
}
|
|
}
|
|
|
|
if (chr->woundedsplatsadded >= 40) {
|
|
wallhitRemoveOldestWoundedSplatByChr(prop);
|
|
chr->woundedsplatsadded--;
|
|
}
|
|
|
|
chr->deaddropsplatsadded = 0;
|
|
}
|
|
}
|
|
|
|
chr->tickssincesplat += g_Vars.lvupdate60;
|
|
}
|
|
|
|
void splatsCreateForChrHit(struct prop *prop, struct shotdata *shotdata, struct coord *arg2, struct coord *arg3, bool isskedar, s32 splattype, struct chrdata *chr2)
|
|
{
|
|
#if VERSION != VERSION_JPN_FINAL
|
|
struct chrdata *chr = prop->chr;
|
|
|
|
if (chr->bulletstaken < 7) {
|
|
chr->bulletstaken++;
|
|
}
|
|
|
|
if (splattype == 0) {
|
|
u32 qty = random() % 3;
|
|
|
|
if (qty) {
|
|
chr->stdsplatsadded += splatsCreate(qty, 0.8f, prop, shotdata, arg2, arg3, isskedar, splattype, TICKS(50), chr2, 0);
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
s32 splatsCreate(s32 qty, f32 arg1, struct prop *prop, struct shotdata *shotdataarg,
|
|
struct coord *arg4, struct coord *arg5, bool isskedar, s32 splattype,
|
|
s32 timermax, struct chrdata *chr, s32 timerspeed)
|
|
{
|
|
#if VERSION == VERSION_JPN_FINAL
|
|
return 0;
|
|
#else
|
|
struct shotdata stackshotdata;
|
|
struct shotdata *shotdata = splattype == 0 ? shotdataarg : &stackshotdata;
|
|
struct coord spfc;
|
|
struct coord spf0;
|
|
struct coord spe4;
|
|
Mtxf spa4;
|
|
s32 numdropped = 0;
|
|
f32 dist;
|
|
s32 i;
|
|
s32 j;
|
|
|
|
if (splattype == 0) {
|
|
dist = coordsGetDistance(&shotdata->gunpos, arg5);
|
|
|
|
for (i = 0; i < 3; i++) {
|
|
spfc.f[i] = shotdata->dir.f[i];
|
|
spf0.f[i] = shotdata->unk0c.f[i];
|
|
shotdata->gunpos.f[i] = arg5->f[i];
|
|
shotdata->unk00.f[i] = arg4->f[i];
|
|
}
|
|
} else {
|
|
f32 extraheight;
|
|
|
|
if (prop->type == PROPTYPE_CHR) {
|
|
extraheight = 50;
|
|
} else {
|
|
extraheight = 0;
|
|
}
|
|
|
|
dist = 0.7f;
|
|
|
|
spfc.x = shotdata->dir.x = 0;
|
|
spfc.y = shotdata->dir.y = -1;
|
|
spfc.z = shotdata->dir.z = 0;
|
|
|
|
spf0.x = shotdata->unk0c.x = 0;
|
|
spf0.y = shotdata->unk0c.y = -1;
|
|
spf0.z = shotdata->unk0c.z = 0;
|
|
|
|
shotdata->gunpos.x = prop->pos.x;
|
|
shotdata->gunpos.y = prop->pos.y + extraheight;
|
|
shotdata->gunpos.z = prop->pos.z;
|
|
|
|
shotdata->unk00.x = prop->pos.x;
|
|
shotdata->unk00.y = prop->pos.y + extraheight;
|
|
shotdata->unk00.z = prop->pos.z;
|
|
}
|
|
|
|
for (i = 0; i < qty; i++) {
|
|
for (j = 0; j < 3; j++) {
|
|
spe4.f[j] = (RANDOMFRAC() * var8007f8a8 * 2.0f - var8007f8a8) * 0.017453292384744f;
|
|
}
|
|
|
|
mtx4LoadRotation(&spe4, &spa4);
|
|
mtx4RotateVec(&spa4, &spfc, &shotdata->dir);
|
|
mtx4RotateVec(&spa4, &spf0, &shotdata->unk0c);
|
|
|
|
#if VERSION >= VERSION_NTSC_1_0
|
|
func0f177164(&shotdata->dir, &shotdata->dir, 403, "splat.c");
|
|
func0f177164(&shotdata->unk0c, &shotdata->unk0c, 404, "splat.c");
|
|
#else
|
|
func0f177164(&shotdata->dir, &shotdata->dir, 405, "splat.c");
|
|
func0f177164(&shotdata->unk0c, &shotdata->unk0c, 406, "splat.c");
|
|
#endif
|
|
|
|
if (splat0f149274(arg1, prop, shotdata, /*reused var*/ dist, isskedar, splattype, timermax, chr, timerspeed)) {
|
|
numdropped++;
|
|
}
|
|
}
|
|
|
|
if (numdropped) {
|
|
struct chrdata *chr = prop->chr;
|
|
|
|
chr->tickssincesplat = 0;
|
|
chr->lastdroppos.x = prop->pos.x;
|
|
chr->lastdroppos.y = prop->pos.y;
|
|
chr->lastdroppos.z = prop->pos.z;
|
|
}
|
|
|
|
return numdropped;
|
|
#endif
|
|
}
|
|
|
|
#if VERSION == VERSION_JPN_FINAL
|
|
void splat0f149274(void)
|
|
{
|
|
osSyncPrintf("Splat : Out of range\n");
|
|
}
|
|
#else
|
|
bool splat0f149274(f32 arg0, struct prop *chrprop, struct shotdata *shotdata, f32 arg3, bool isskedar, s32 splattype, s32 timermax, struct chrdata *chr, s32 timerspeed)
|
|
{
|
|
struct prop **propptr;
|
|
struct prop *objprop;
|
|
struct hitthing hitthing;
|
|
struct hitthing besthitthing;
|
|
s16 rooms[32];
|
|
s16 gunrooms[8];
|
|
s16 endrooms[8];
|
|
struct coord endpos;
|
|
s32 i;
|
|
struct coord *sp50c;
|
|
struct coord *hitpos;
|
|
struct coord *sp504;
|
|
s32 bestroom = 0;
|
|
s32 mtxindex;
|
|
s32 room;
|
|
f32 spraydistance;
|
|
bool translucent;
|
|
bool hasresult = false;
|
|
struct shotdata stackshotdata;
|
|
|
|
for (i = 0; i < 3; i++) {
|
|
stackshotdata.unk00.f[i] = shotdata->unk00.f[i];
|
|
stackshotdata.unk0c.f[i] = shotdata->unk0c.f[i];
|
|
stackshotdata.gunpos.f[i] = shotdata->gunpos.f[i];
|
|
stackshotdata.dir.f[i] = shotdata->dir.f[i];
|
|
}
|
|
|
|
stackshotdata.penetration = 1;
|
|
|
|
for (i = 0; i < 3; i++) {
|
|
endpos.f[i] = stackshotdata.gunpos.f[i] + stackshotdata.dir.f[i] * g_SplatMaxDistance;
|
|
}
|
|
|
|
portal00018148(&chrprop->pos, &stackshotdata.gunpos, chrprop->rooms, gunrooms, NULL, 0);
|
|
portal00018148(&stackshotdata.gunpos, &endpos, gunrooms, endrooms, rooms, ARRAYCOUNT(rooms) - 1);
|
|
|
|
for (i = 0; rooms[i] != -1; i++) {
|
|
if (bgTestHitInRoom(&stackshotdata.gunpos, &endpos, rooms[i], &hitthing)
|
|
&& ((stackshotdata.gunpos.x <= endpos.x && hitthing.unk00.x <= endpos.x && stackshotdata.gunpos.x <= hitthing.unk00.x) || (endpos.x <= stackshotdata.gunpos.x && endpos.x <= hitthing.unk00.x && hitthing.unk00.x <= stackshotdata.gunpos.x))
|
|
&& ((stackshotdata.gunpos.y <= endpos.y && hitthing.unk00.y <= endpos.y && stackshotdata.gunpos.y <= hitthing.unk00.y) || (endpos.y <= stackshotdata.gunpos.y && endpos.y <= hitthing.unk00.y && hitthing.unk00.y <= stackshotdata.gunpos.y))
|
|
&& ((stackshotdata.gunpos.z <= endpos.z && hitthing.unk00.z <= endpos.z && stackshotdata.gunpos.z <= hitthing.unk00.z) || (endpos.z <= stackshotdata.gunpos.z && endpos.z <= hitthing.unk00.z && hitthing.unk00.z <= stackshotdata.gunpos.z))) {
|
|
if (stackshotdata.gunpos.x != hitthing.unk00.x || stackshotdata.gunpos.y != hitthing.unk00.y || stackshotdata.gunpos.z != hitthing.unk00.z) {
|
|
bestroom = rooms[i];
|
|
besthitthing = hitthing;
|
|
|
|
endpos.x = hitthing.unk00.x;
|
|
endpos.y = hitthing.unk00.y;
|
|
endpos.z = hitthing.unk00.z;
|
|
|
|
hasresult = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (hasresult) {
|
|
spraydistance = coordsGetDistance(&stackshotdata.gunpos, &besthitthing.unk00);
|
|
|
|
if (spraydistance < g_SplatMaxDistance) {
|
|
sp50c = &hitthing.unk00;
|
|
hitpos = &hitthing.unk00;
|
|
sp504 = &hitthing.unk0c;
|
|
objprop = NULL;
|
|
mtxindex = 0;
|
|
room = bestroom;
|
|
translucent = hitthing.unk2c == 2;
|
|
} else {
|
|
osSyncPrintf("Splat : Out of range\n");
|
|
hasresult = false;
|
|
}
|
|
} else {
|
|
spraydistance = 999999.0f;
|
|
}
|
|
|
|
if (splattype == 0) {
|
|
stackshotdata.unk34 = (spraydistance < g_SplatMaxDistance ? spraydistance : g_SplatMaxDistance);
|
|
stackshotdata.unk34 += arg3;
|
|
|
|
for (i = 0; i < ARRAYCOUNT(stackshotdata.hits); i++) {
|
|
stackshotdata.hits[i].prop = NULL;
|
|
stackshotdata.hits[i].hitpart = 0;
|
|
stackshotdata.hits[i].node = NULL;
|
|
}
|
|
|
|
propptr = g_Vars.endonscreenprops - 1;
|
|
|
|
while (propptr >= g_Vars.onscreenprops) {
|
|
struct prop *prop = *propptr;
|
|
|
|
if (prop) {
|
|
if (prop->type == PROPTYPE_OBJ || prop->type == PROPTYPE_DOOR || prop->type == PROPTYPE_WEAPON) {
|
|
func0f085e00(prop, &stackshotdata);
|
|
if (1);
|
|
}
|
|
}
|
|
|
|
propptr--;
|
|
}
|
|
|
|
for (i = 0; i < ARRAYCOUNT(stackshotdata.hits); i++) {
|
|
struct hit *hit = &stackshotdata.hits[i];
|
|
|
|
if (hit->prop && (hit->hitthing.texturenum < 0
|
|
|| hit->hitthing.texturenum >= NUM_TEXTURES
|
|
|| g_SurfaceTypes[g_Textures[hit->hitthing.texturenum].surfacetype]->numwallhittexes != 0)) {
|
|
sp50c = &hit->hitthing.unk00;
|
|
hitpos = &hit->pos;
|
|
sp504 = &hit->hitthing.unk0c;
|
|
objprop = hit->prop;
|
|
mtxindex = (s8)hit->mtxindex;
|
|
room = 1;
|
|
translucent = false;
|
|
hasresult = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (hasresult) {
|
|
struct splatdata splatdata;
|
|
u32 stack;
|
|
|
|
for (i = 0; i < 3; i++) {
|
|
splatdata.relpos.f[i] = sp50c->f[i];
|
|
splatdata.unk0c.f[i] = hitpos->f[i];
|
|
splatdata.gunpos.f[i] = stackshotdata.gunpos.f[i];
|
|
splatdata.unk18.f[i] = sp504->f[i];
|
|
}
|
|
|
|
splatdata.chrprop = chrprop;
|
|
splatdata.objprop = objprop;
|
|
splatdata.chr = chr;
|
|
splatdata.mtxindex = mtxindex;
|
|
splatdata.room = room;
|
|
splatdata.isskedar = isskedar;
|
|
splatdata.timermax = timermax;
|
|
splatdata.unk50 = arg0;
|
|
splatdata.splattype = splattype;
|
|
splatdata.timerspeed = timerspeed;
|
|
splatdata.translucent = translucent;
|
|
|
|
splat0f14986c(&splatdata);
|
|
|
|
return true;
|
|
}
|
|
|
|
if (hitthing.unk00.x);
|
|
if (hitthing.unk00.y);
|
|
if (hitthing.unk00.z);
|
|
|
|
return false;
|
|
}
|
|
#endif
|
|
|
|
void splatsTick(void)
|
|
{
|
|
// empty
|
|
}
|
|
|
|
#if VERSION == VERSION_JPN_FINAL
|
|
void splat0f14986c(void)
|
|
{
|
|
// empty
|
|
}
|
|
#else
|
|
void splat0f14986c(struct splatdata *splat)
|
|
{
|
|
f32 spac;
|
|
f32 spa8;
|
|
struct defaultobj *obj;
|
|
f32 spa0;
|
|
f32 sp9c;
|
|
f32 height;
|
|
f32 width;
|
|
u8 maxalpha = 0xff;
|
|
u8 minalpha = 0xc0;
|
|
s32 texnum;
|
|
bool sp88 = splat->isskedar & 1;
|
|
bool sp84 = splat->isskedar & 1;
|
|
bool translucent = splat->translucent;
|
|
f32 distance;
|
|
s16 smokerooms[2];
|
|
|
|
texnum = WALLHITTEX_BLOOD1 + (random() % 3);
|
|
|
|
if (splat->objprop != NULL && splat->objprop->type == PROPTYPE_OBJ) {
|
|
obj = splat->objprop->obj;
|
|
|
|
if (obj && (obj->type == OBJTYPE_GLASS || obj->type == OBJTYPE_TINTEDGLASS)) {
|
|
minalpha = 0x40;
|
|
translucent = true;
|
|
}
|
|
}
|
|
|
|
switch (splat->splattype) {
|
|
case SPLATTYPE_PUDDLE:
|
|
texnum = WALLHITTEX_BLOOD1 + (random() % 3);
|
|
break;
|
|
case SPLATTYPE_DROP:
|
|
texnum = WALLHITTEX_BLOOD4 + (random() % 1);
|
|
break;
|
|
}
|
|
|
|
switch (random() % 6) {
|
|
case 0:
|
|
case 1:
|
|
case 2:
|
|
sp9c = 1.5f;
|
|
break;
|
|
case 3:
|
|
case 4:
|
|
sp9c = 5.0f;
|
|
break;
|
|
case 5:
|
|
sp9c = 3.0f;
|
|
break;
|
|
}
|
|
|
|
distance = coordsGetDistance(&splat->gunpos, &splat->unk0c);
|
|
spa0 = var8007f8a0 * distance * sp9c;
|
|
|
|
if (var8007f8b8 < spa0) {
|
|
spa0 = var8007f8b8;
|
|
}
|
|
|
|
if (var8007f8b4 > spa0) {
|
|
spa0 = var8007f8b4;
|
|
}
|
|
|
|
spac = 0.5f * spa0;
|
|
spa8 = 0.5f * spa0;
|
|
|
|
if (spac < 1.0f) {
|
|
spac = 1.0f;
|
|
}
|
|
|
|
if (spa8 < 1.0f) {
|
|
spa8 = 1.0f;
|
|
}
|
|
|
|
width = RANDOMFRAC() * spac * 2.0f - spac + spa0;
|
|
height = RANDOMFRAC() * spa8 * 2.0f - spa8 + spa0;
|
|
|
|
if (width > var8007f8b8) {
|
|
width = var8007f8b8;
|
|
}
|
|
|
|
if (height > var8007f8b8) {
|
|
height = var8007f8b8;
|
|
}
|
|
|
|
width *= splat->unk50;
|
|
height *= splat->unk50;
|
|
|
|
wallhitChooseBloodColour(splat->chrprop);
|
|
|
|
wallhitCreateWith20Args(&splat->relpos, &splat->unk18, splat->chr ? &splat->chr->prop->pos : NULL, NULL,
|
|
NULL, texnum, splat->room, splat->objprop,
|
|
splat->chrprop, splat->mtxindex, 0, splat->chr,
|
|
width, height, minalpha, maxalpha,
|
|
random() % 360, (u16)splat->timermax, splat->timerspeed, translucent);
|
|
|
|
if (sp88 || sp84) {
|
|
smokerooms[0] = splat->room;
|
|
smokerooms[1] = -1;
|
|
|
|
smokeCreateSimple(&splat->unk0c, smokerooms, sp88 ? SMOKETYPE_SKCORPSE : SMOKETYPE_14);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
void splatResetChr(struct chrdata *chr)
|
|
{
|
|
osSyncPrintf("Splat_ResetChr : Reset One Char : chrdata = %x\n", (u32)chr);
|
|
|
|
chr->bulletstaken = 0;
|
|
chr->tickssincesplat = 0;
|
|
chr->stdsplatsadded = 0;
|
|
chr->woundedsplatsadded = 0;
|
|
chr->deaddropsplatsadded = 0;
|
|
chr->splatsdroppedhere = 0;
|
|
chr->lastdroppos.x = 0;
|
|
chr->lastdroppos.y = 0;
|
|
chr->lastdroppos.z = 0;
|
|
}
|