1552 lines
40 KiB
C
1552 lines
40 KiB
C
#include <ultra64.h>
|
|
#include "constants.h"
|
|
#include "game/chraction.h"
|
|
#include "game/dlights.h"
|
|
#include "game/chr.h"
|
|
#include "game/prop.h"
|
|
#include "game/setuputils.h"
|
|
#include "game/propsnd.h"
|
|
#include "game/tex.h"
|
|
#include "game/camera.h"
|
|
#include "game/explosions.h"
|
|
#include "game/smoke.h"
|
|
#include "game/bg.h"
|
|
#include "game/room.h"
|
|
#include "game/file.h"
|
|
#include "game/gfxmemory.h"
|
|
#include "game/mplayer/mplayer.h"
|
|
#include "game/propobj.h"
|
|
#include "game/utils.h"
|
|
#include "game/wallhit.h"
|
|
#include "bss.h"
|
|
#include "lib/collision.h"
|
|
#include "lib/vi.h"
|
|
#include "lib/main.h"
|
|
#include "lib/rng.h"
|
|
#include "lib/mtx.h"
|
|
#include "lib/anim.h"
|
|
#include "data.h"
|
|
#include "types.h"
|
|
|
|
struct explosion *g_Explosions;
|
|
s32 g_MaxExplosions;
|
|
|
|
s32 g_ExplosionShakeTotalTimer = 0;
|
|
s32 g_ExplosionShakeIntensityTimer = 0;
|
|
f32 g_ExplosionDamageTxScale = 1;
|
|
u32 var8007e4ac = 0x0000004b;
|
|
u32 var8007e4b0 = 0x000001e0;
|
|
u32 var8007e4b4 = 0x000000a8;
|
|
|
|
struct explosiontype g_ExplosionTypes[] = {
|
|
// rangeh
|
|
// | rangev
|
|
// | | changerateh
|
|
// | | | changeratev
|
|
// | | | | innersize
|
|
// | | | | | blastradius
|
|
// | | | | | | damageradius
|
|
// | | | | | | | duration
|
|
// | | | | | | | | propagationrate
|
|
// | | | | | | | | | flarespeed
|
|
// | | | | | | | | | | smoketype
|
|
// | | | | | | | | | | | sound
|
|
// | | | | | | | | | | | | damage
|
|
// | | | | | | | | | | | | |
|
|
/*00*/ { 0.1, 0.1, 0, 0, 0.1, 0, 0, 1, 1, 1, SMOKETYPE_NONE, 0x0000, 0 },
|
|
/*01*/ { 1, 1, 0, 0, 1, 0, 0, 30, 1, 1, SMOKETYPE_BULLETIMPACT, 0x0000, 0 },
|
|
/*02*/ { 20, 20, 0, 0, 30, 50, 50, 40, 1, 3, SMOKETYPE_MINI, 0x8099, 0.125 },
|
|
/*03*/ { 50, 50, 0, 0, 50, 100, 100, 45, 1, 4, SMOKETYPE_MINI, 0x809a, 0.5 },
|
|
/*04*/ { 60, 80, 2, 0.6, 100, 130, 240, 60, 2, 5, SMOKETYPE_ELECTRICAL, 0x809e, 1 },
|
|
/*05*/ { 60, 120, 2, 0.6, 150, 160, 280, 60, 2, 5, SMOKETYPE_ELECTRICAL, 0x809e, 2 },
|
|
/*06*/ { 20, 20, 0, 0, 22, 40, 40, 60, 1, 3, SMOKETYPE_MINI, 0x8099, 0.5 },
|
|
/*07*/ { 35, 40, 0, 0, 35, 70, 70, 60, 1, 4, SMOKETYPE_MINI, 0x809a, 1 },
|
|
/*08*/ { 50, 80, 2, 0.6, 50, 100, 160, 60, 2, 5, SMOKETYPE_ELECTRICAL, 0x809e, 2 },
|
|
/*09*/ { 60, 120, 2, 0.6, 50, 130, 180, 60, 2, 5, SMOKETYPE_ELECTRICAL, 0x809e, 2 },
|
|
/*10*/ { 40, 40, 0.8, 0.5, 70, 80, 160, 80, 4, 5, SMOKETYPE_SMALL, 0x80a0, 1 },
|
|
/*11*/ { 50, 50, 1.2, 0.8, 100, 100, 200, 90, 1, 4, SMOKETYPE_SMALL, 0x809e, 2 },
|
|
/*12*/ { 70, 60, 2, 1.2, 150, 140, 280, 90, 2, 5, SMOKETYPE_MEDIUM, 0x809e, 4 },
|
|
/*13*/ { 80, 60, 4, 1.4, 200, 200, 400, 90, 2, 5, SMOKETYPE_LARGE, 0x809f, 4 },
|
|
/*14*/ { 50, 50, 0, 0, 120, 150, 300, 150, 4, 4, SMOKETYPE_SMALL, 0x809f, 4 },
|
|
/*15*/ { 1, 1, 0, 0, 1, 0, 0, 1, 1, 1, SMOKETYPE_BULLETIMPACT, 0x809c, 0 },
|
|
/*16*/ { 1, 1, 0, 0, 1, 0, 0, 1, 1, 1, SMOKETYPE_BULLETIMPACT, 0x809c, 0 },
|
|
/*17*/ { 80, 60, 10, 5, 1500, 2200, 3600, 500, 1, 2, SMOKETYPE_NONE, 0x80a5, 4 },
|
|
/*18*/ { 80, 60, 3, 1, 300, 450, 640, 60, 1, 2, SMOKETYPE_NONE, 0x809f, 4 },
|
|
/*19*/ { 90, 75, 2.5, 0.87, 250, 375, 600, 180, 2, 5, SMOKETYPE_LARGE, 0x809f, 4 },
|
|
/*20*/ { 160, 120, 6, 2, 600, 450, 640, 60, 1, 2, SMOKETYPE_NONE, 0x809f, 4 },
|
|
/*21*/ { 40, 30, 2, 0.7, 100, 140, 270, 45, 2, 5, SMOKETYPE_SMALL, 0x809f, 3.5 },
|
|
/*22*/ { 20, 20, 0, 0, 30, 100, 200, 40, 1, 3, SMOKETYPE_MINI, 0x8099, 0.25 },
|
|
/*23*/ { 100, 80, 4, 1.4, 210, 220, 500, 90, 2, 5, SMOKETYPE_LARGE, 0x809f, 4 },
|
|
/*24*/ { 80, 60, 4, 1.4, 500, 200, 400, 90, 2, 5, SMOKETYPE_LARGE, 0x809f, 4 },
|
|
/*25*/ { 640, 480, 32, 11.2, 1600, 1000, 1000, 180, 2, 5, SMOKETYPE_NONE, 0x80a4, 4 },
|
|
};
|
|
|
|
bool explosionCreateSimple(struct prop *prop, struct coord *pos, RoomNum *rooms, s16 type, s32 playernum)
|
|
{
|
|
return explosionCreate(prop, pos, rooms, type, playernum, false, NULL, 0, NULL);
|
|
}
|
|
|
|
bool explosionCreateComplex(struct prop *prop, struct coord *pos, RoomNum *rooms, s16 type, s32 playernum)
|
|
{
|
|
struct coord sp100;
|
|
struct coord sp88;
|
|
struct explosiontype *etype;
|
|
bool makescorch = true;
|
|
RoomNum room;
|
|
f32 y;
|
|
struct prop *collisionprop;
|
|
|
|
if (type == EXPLOSIONTYPE_NONE) {
|
|
return false;
|
|
}
|
|
|
|
if (prop) {
|
|
room = cdFindFloorRoomYColourNormalPropAtPos(&prop->pos, prop->rooms, &y, NULL, &sp88, &collisionprop);
|
|
sp100.x = prop->pos.x;
|
|
sp100.y = y;
|
|
sp100.z = prop->pos.z;
|
|
} else {
|
|
room = cdFindFloorRoomYColourNormalPropAtPos(pos, rooms, &y, NULL, &sp88, &collisionprop);
|
|
sp100.x = pos->x;
|
|
sp100.y = y;
|
|
sp100.z = pos->z;
|
|
}
|
|
|
|
etype = &g_ExplosionTypes[type];
|
|
|
|
if (collisionprop || room <= 0
|
|
|| !(pos->y - y <= (etype->rangev + etype->changeratev * etype->duration + etype->innersize) * 0.5f || pos->y - y <= 75)) {
|
|
makescorch = false;
|
|
}
|
|
|
|
return explosionCreate(prop, pos, rooms, type, playernum, makescorch, &sp100, room, &sp88);
|
|
}
|
|
|
|
f32 explosionGetHorizontalRangeAtFrame(struct explosion *exp, s32 frame)
|
|
{
|
|
struct explosiontype *type = &g_ExplosionTypes[exp->type];
|
|
f32 changerate = PALUPF(type->changerateh);
|
|
f32 result;
|
|
|
|
if (exp->type == EXPLOSIONTYPE_GASBARREL && frame > TICKS(32)) {
|
|
result = frame * PALUPF(3.0f) + 40.0f;
|
|
|
|
if (result > 300) {
|
|
result = 300;
|
|
}
|
|
} else {
|
|
result = type->rangeh + changerate * frame;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
f32 explosionGetVerticalRangeAtFrame(struct explosion *exp, s32 frame)
|
|
{
|
|
struct explosiontype *type = &g_ExplosionTypes[exp->type];
|
|
f32 changerate = PALUPF(type->changeratev);
|
|
f32 result;
|
|
|
|
if (exp->type == EXPLOSIONTYPE_GASBARREL && frame > TICKS(32)) {
|
|
result = 20;
|
|
} else {
|
|
result = type->rangev + changerate * frame;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
void explosionGetBboxAtFrame(struct coord *lower, struct coord *upper, s32 frame, struct prop *prop)
|
|
{
|
|
struct explosion *exp = prop->explosion;
|
|
struct explosiontype *type = &g_ExplosionTypes[exp->type];
|
|
|
|
f32 rangeh = explosionGetHorizontalRangeAtFrame(exp, frame);
|
|
f32 rangev = explosionGetVerticalRangeAtFrame(exp, frame);
|
|
|
|
rangeh = rangeh * 0.5f + type->innersize * 1.5f;
|
|
rangev = rangev * 0.5f + type->innersize * 1.5f;
|
|
|
|
lower->x = prop->pos.x - rangeh;
|
|
lower->y = prop->pos.y - rangev;
|
|
lower->z = prop->pos.z - rangeh;
|
|
|
|
upper->x = prop->pos.x + rangeh;
|
|
upper->y = prop->pos.y + rangev;
|
|
upper->z = prop->pos.z + rangeh;
|
|
}
|
|
|
|
void explosionAlertChrs(f32 *radius, struct coord *noisepos)
|
|
{
|
|
u32 stack[2];
|
|
s32 *end = (s32 *)&doorDestroyGlass;
|
|
s32 i;
|
|
|
|
for (i = 0; i < g_NumChrSlots; i++) {
|
|
if (g_ChrSlots[i].model
|
|
&& chrGetTargetProp(&g_ChrSlots[i]) == g_Vars.currentplayer->prop
|
|
&& g_ChrSlots[i].prop
|
|
&& g_ChrSlots[i].prop->type == PROPTYPE_CHR
|
|
&& (g_ChrSlots[i].prop->flags & PROPFLAG_ENABLED)) {
|
|
f32 distance = chrGetDistanceToCoord(&g_ChrSlots[i], noisepos);
|
|
|
|
if (distance == 0) {
|
|
distance = 2;
|
|
} else {
|
|
distance = (10.0f * *radius * g_ChrSlots[i].hearingscale) / distance;
|
|
}
|
|
|
|
if (distance > 1) {
|
|
chrRecordLastHearTargetTime(&g_ChrSlots[i]);
|
|
}
|
|
}
|
|
}
|
|
|
|
#if PIRACYCHECKS
|
|
{
|
|
u32 checksum = 0;
|
|
s32 *ptr = (s32 *)&glassDestroy;
|
|
|
|
while (ptr < end) {
|
|
checksum ^= *ptr;
|
|
checksum <<= 1;
|
|
ptr++;
|
|
}
|
|
|
|
if (checksum != CHECKSUM_PLACEHOLDER) {
|
|
struct explosiontype *type = &g_ExplosionTypes[0];
|
|
s32 i;
|
|
|
|
for (i = 0; i != ARRAYCOUNT(g_ExplosionTypes) - 1; i++) {
|
|
type->rangeh = 80;
|
|
type->rangev = 60;
|
|
type->changerateh = 15;
|
|
type->changeratev = 5;
|
|
type->innersize = 1500;
|
|
type->blastradius = 200;
|
|
type->damageradius = 3600;
|
|
type++;
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
bool explosionCreate(struct prop *sourceprop, struct coord *exppos, RoomNum *exprooms,
|
|
s16 type, s32 playernum, bool makescorch, struct coord *arg6, RoomNum room, struct coord *arg8)
|
|
{
|
|
u32 stack;
|
|
struct explosion *exp = NULL;
|
|
s32 i;
|
|
|
|
if (type == EXPLOSIONTYPE_NONE || exprooms[0] == -1) {
|
|
return false;
|
|
}
|
|
|
|
// Bullet holes: only crate the flame (explosion) if within 4 metres
|
|
if (type == EXPLOSIONTYPE_BULLETHOLE) {
|
|
f32 lodscale = camGetLodScaleZ();
|
|
struct coord *campos = &g_Vars.currentplayer->cam_pos;
|
|
f32 xdist = exppos->x - campos->x;
|
|
f32 ydist = exppos->y - campos->y;
|
|
f32 zdist = exppos->z - campos->z;
|
|
f32 sum = xdist * xdist + ydist * ydist + zdist * zdist;
|
|
|
|
if (sum * lodscale * lodscale > 400 * 400) {
|
|
if (random() % 2 == 0) {
|
|
if (sourceprop) {
|
|
smokeCreateSimple(&sourceprop->pos, sourceprop->rooms, g_ExplosionTypes[type].smoketype);
|
|
} else {
|
|
smokeCreateSimple(exppos, exprooms, g_ExplosionTypes[type].smoketype);
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
}
|
|
|
|
// Try to find an unused slot
|
|
for (i = 0; i < g_MaxExplosions; i++) {
|
|
if (g_Explosions[i].prop == NULL) {
|
|
exp = &g_Explosions[i];
|
|
break;
|
|
}
|
|
}
|
|
|
|
// If there's no unused slots, find the oldest bullethole flame and replace it
|
|
if (exp == NULL) {
|
|
s32 maxage = -1;
|
|
s32 index = -1;
|
|
|
|
for (i = 0; i < g_MaxExplosions; i++) {
|
|
if (g_Explosions[i].type == EXPLOSIONTYPE_BULLETHOLE && g_Explosions[i].age > maxage) {
|
|
maxage = g_Explosions[i].age;
|
|
index = i;
|
|
}
|
|
}
|
|
|
|
if (index >= 0) {
|
|
propExecuteTickOperation(g_Explosions[index].prop, TICKOP_FREE);
|
|
g_Explosions[index].prop = NULL;
|
|
exp = &g_Explosions[index];
|
|
}
|
|
}
|
|
|
|
if (exp) {
|
|
struct prop *expprop = propAllocate();
|
|
|
|
if (type != EXPLOSIONTYPE_16 && type != EXPLOSIONTYPE_BULLETHOLE) {
|
|
g_ExplosionShakeTotalTimer = 6;
|
|
}
|
|
|
|
if (expprop) {
|
|
s32 exproom;
|
|
f32 value1;
|
|
f32 value2;
|
|
s32 k;
|
|
s32 index;
|
|
s32 portalnum;
|
|
s32 indexplus1;
|
|
s32 indexplus2;
|
|
struct coord portalbbmin;
|
|
struct coord portalbbmax;
|
|
struct coord portal2bbmin;
|
|
struct coord portal2bbmax;
|
|
struct coord spd4;
|
|
struct coord spc8;
|
|
RoomNum otherroom;
|
|
RoomNum otherroom2;
|
|
f32 mult = 1;
|
|
s32 stack4;
|
|
s32 portalnum2;
|
|
struct coord spac;
|
|
u32 stack2;
|
|
u32 stack3;
|
|
s32 j;
|
|
|
|
expprop->type = PROPTYPE_EXPLOSION;
|
|
expprop->explosion = exp;
|
|
expprop->pos.x = exppos->x;
|
|
expprop->pos.y = exppos->y;
|
|
expprop->pos.z = exppos->z;
|
|
|
|
for (i = 0; exprooms[i] != -1 && i < ARRAYCOUNT(expprop->rooms) - 1; i++) {
|
|
expprop->rooms[i] = exprooms[i];
|
|
|
|
roomFlashLighting(exprooms[i], g_ExplosionTypes[type].rangeh, 255);
|
|
}
|
|
|
|
expprop->rooms[i] = -1;
|
|
|
|
propActivateThisFrame(expprop);
|
|
propEnable(expprop);
|
|
|
|
exp->type = type;
|
|
exp->prop = expprop;
|
|
exp->source = sourceprop;
|
|
exp->age = 0;
|
|
exp->makescorch = makescorch;
|
|
exp->owner = playernum;
|
|
|
|
if (type != EXPLOSIONTYPE_BULLETHOLE && type != EXPLOSIONTYPE_PHOENIX) {
|
|
propSetDangerous(expprop);
|
|
}
|
|
|
|
exproom = expprop->rooms[0];
|
|
|
|
explosionGetBboxAtFrame(&spd4, &spc8, g_ExplosionTypes[type].duration, expprop);
|
|
|
|
spd4.x *= mult;
|
|
spd4.y *= mult;
|
|
spd4.z *= mult;
|
|
|
|
spc8.x *= mult;
|
|
spc8.y *= mult;
|
|
spc8.z *= mult;
|
|
|
|
exp->bbs[0].bbmin.x = g_Rooms[exproom].bbmin[0];
|
|
exp->bbs[0].bbmin.y = g_Rooms[exproom].bbmin[1];
|
|
exp->bbs[0].bbmin.z = g_Rooms[exproom].bbmin[2];
|
|
exp->bbs[0].bbmax.x = g_Rooms[exproom].bbmax[0];
|
|
exp->bbs[0].bbmax.y = g_Rooms[exproom].bbmax[1];
|
|
exp->bbs[0].bbmax.z = g_Rooms[exproom].bbmax[2];
|
|
exp->bbs[0].room = exproom;
|
|
exp->bbs[0].room2 = -1;
|
|
exp->numbb = 1;
|
|
|
|
if (exp->type == EXPLOSIONTYPE_HUGE25) {
|
|
exp->numbb = 0;
|
|
} else {
|
|
exp->bbs[0].bbmin.x = g_Rooms[exproom].bbmin[0];
|
|
exp->bbs[0].bbmin.y = g_Rooms[exproom].bbmin[1];
|
|
exp->bbs[0].bbmin.z = g_Rooms[exproom].bbmin[2];
|
|
exp->bbs[0].bbmax.x = g_Rooms[exproom].bbmax[0];
|
|
exp->bbs[0].bbmax.y = g_Rooms[exproom].bbmax[1];
|
|
exp->bbs[0].bbmax.z = g_Rooms[exproom].bbmax[2];
|
|
exp->bbs[0].room = exproom;
|
|
exp->bbs[0].room2 = -1;
|
|
exp->numbb = 1;
|
|
|
|
for (k = 0; k < g_Rooms[exproom].numportals; k++) {
|
|
portalnum = g_RoomPortals[g_Rooms[exproom].roomportallistoffset + k];
|
|
|
|
bgCalculatePortalBbox(portalnum, &portalbbmin, &portalbbmax);
|
|
|
|
if (bgIsBboxOverlapping(&portalbbmin, &portalbbmax, &spd4, &spc8)) {
|
|
otherroom2 = -1;
|
|
index = 0;
|
|
|
|
if (exproom == g_BgPortals[portalnum].roomnum1) {
|
|
otherroom = g_BgPortals[portalnum].roomnum2;
|
|
} else {
|
|
otherroom = g_BgPortals[portalnum].roomnum1;
|
|
}
|
|
|
|
spac.f[0] = (g_PortalMetrics + portalnum)->normal.f[0];
|
|
spac.f[1] = (g_PortalMetrics + portalnum)->normal.f[1];
|
|
spac.f[2] = (g_PortalMetrics + portalnum)->normal.f[2];
|
|
|
|
if (spac.f[0] < 0.0f) {
|
|
spac.f[0] = -spac.f[0];
|
|
}
|
|
|
|
if (spac.f[1] < 0.0f) {
|
|
spac.f[1] = -spac.f[1];
|
|
}
|
|
|
|
if (spac.f[2] < 0.0f) {
|
|
spac.f[2] = -spac.f[2];
|
|
}
|
|
|
|
if (spac.f[0] < spac.f[1]) {
|
|
index = 1;
|
|
}
|
|
|
|
if (spac.f[index] < spac.f[2]) {
|
|
index = 2;
|
|
}
|
|
|
|
indexplus1 = (index + 1) % 3;
|
|
indexplus2 = (index + 2) % 3;
|
|
|
|
value1 = portalbbmax.f[indexplus1] - portalbbmin.f[indexplus1];
|
|
value2 = portalbbmax.f[indexplus2] - portalbbmin.f[indexplus2];
|
|
|
|
if (value2 < value1) {
|
|
value1 = value2;
|
|
}
|
|
|
|
portalbbmin.f[index] -= value1;
|
|
portalbbmax.f[index] += value1;
|
|
|
|
if (portalbbmin.f[index] < g_Rooms[exproom].bbmin[index]) {
|
|
portalbbmin.f[index] = g_Rooms[exproom].bbmin[index];
|
|
}
|
|
|
|
if (portalbbmax.f[index] > g_Rooms[exproom].bbmax[index]) {
|
|
portalbbmax.f[index] = g_Rooms[exproom].bbmax[index];
|
|
}
|
|
|
|
if (portalbbmin.f[index] > g_Rooms[otherroom].bbmin[index]) {
|
|
portalbbmin.f[index] = g_Rooms[otherroom].bbmin[index];
|
|
}
|
|
|
|
if (portalbbmax.f[index] < g_Rooms[otherroom].bbmax[index]) {
|
|
portalbbmax.f[index] = g_Rooms[otherroom].bbmax[index];
|
|
}
|
|
|
|
for (j = 0; j < g_Rooms[otherroom].numportals; j++) {
|
|
portalnum2 = g_RoomPortals[g_Rooms[otherroom].roomportallistoffset + j];
|
|
|
|
if (portalnum2 != portalnum) {
|
|
bgCalculatePortalBbox(portalnum2, &portal2bbmin, &portal2bbmax);
|
|
|
|
if (portal2bbmin.f[indexplus1] <= portalbbmin.f[indexplus1] + 10.0f * mult
|
|
&& portal2bbmin.f[indexplus2] <= portalbbmin.f[indexplus2] + 10.0f * mult
|
|
&& portal2bbmax.f[indexplus1] >= portalbbmax.f[indexplus1] - 10.0f * mult
|
|
&& portal2bbmax.f[indexplus2] >= portalbbmax.f[indexplus2] - 10.0f * mult) {
|
|
if (otherroom == g_BgPortals[portalnum2].roomnum1) {
|
|
otherroom2 = g_BgPortals[portalnum2].roomnum2;
|
|
} else {
|
|
otherroom2 = g_BgPortals[portalnum2].roomnum1;
|
|
}
|
|
|
|
if (portalbbmin.f[index] > g_Rooms[otherroom2].bbmin[index]) {
|
|
portalbbmin.f[index] = g_Rooms[otherroom2].bbmin[index];
|
|
}
|
|
|
|
if (portalbbmax.f[index] < g_Rooms[otherroom2].bbmax[index]) {
|
|
portalbbmax.f[index] = g_Rooms[otherroom2].bbmax[index];
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
portalbbmin.f[0] *= mult;
|
|
portalbbmin.f[1] *= mult;
|
|
portalbbmin.f[2] *= mult;
|
|
|
|
portalbbmax.f[0] *= mult;
|
|
portalbbmax.f[1] *= mult;
|
|
portalbbmax.f[2] *= mult;
|
|
|
|
exp->bbs[exp->numbb].bbmin.x = portalbbmin.f[0];
|
|
exp->bbs[exp->numbb].bbmin.y = portalbbmin.f[1];
|
|
exp->bbs[exp->numbb].bbmin.z = portalbbmin.f[2];
|
|
exp->bbs[exp->numbb].bbmax.x = portalbbmax.f[0];
|
|
exp->bbs[exp->numbb].bbmax.y = portalbbmax.f[1];
|
|
exp->bbs[exp->numbb].bbmax.z = portalbbmax.f[2];
|
|
exp->bbs[exp->numbb].room = otherroom;
|
|
exp->bbs[exp->numbb].room2 = otherroom2;
|
|
|
|
exp->numbb++;
|
|
|
|
if (exp->numbb >= 5) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
exp->bbs[0].bbmin.x *= mult;
|
|
exp->bbs[0].bbmin.y *= mult;
|
|
exp->bbs[0].bbmin.z *= mult;
|
|
exp->bbs[0].bbmax.x *= mult;
|
|
exp->bbs[0].bbmax.y *= mult;
|
|
exp->bbs[0].bbmax.z *= mult;
|
|
}
|
|
|
|
if (makescorch) {
|
|
exp->unk3d0.x = arg6->x;
|
|
exp->unk3d0.y = arg6->y;
|
|
exp->unk3d0.z = arg6->z;
|
|
exp->room = room;
|
|
exp->unk3dc.x = arg8->x;
|
|
exp->unk3dc.y = arg8->y;
|
|
exp->unk3dc.z = arg8->z;
|
|
} else {
|
|
exp->unk3d0.x = 999999.875f;
|
|
}
|
|
|
|
exp->parts[0].frame = 1;
|
|
exp->parts[0].pos.x = exppos->x;
|
|
exp->parts[0].pos.y = exppos->y;
|
|
exp->parts[0].pos.z = exppos->z;
|
|
exp->parts[0].size = g_ExplosionTypes[type].innersize * (RANDOMFRAC() * 0.5f + 1);
|
|
exp->parts[0].rot = RANDOMFRAC() * M_BADTAU;
|
|
exp->parts[0].bb = 0;
|
|
|
|
if (g_Vars.mplayerisrunning) {
|
|
smokeClearSomeTypes();
|
|
}
|
|
|
|
explosionAlertChrs(&g_ExplosionTypes[type].rangeh, exppos);
|
|
}
|
|
}
|
|
|
|
return exp != NULL;
|
|
}
|
|
|
|
/**
|
|
* Start a shake without any explosion.
|
|
*
|
|
* This function is unused.
|
|
*/
|
|
void explosionShake(void)
|
|
{
|
|
g_ExplosionShakeTotalTimer = 6;
|
|
g_ExplosionShakeIntensityTimer = 6;
|
|
}
|
|
|
|
void explosionsUpdateShake(struct coord *arg0, struct coord *arg1, struct coord *arg2)
|
|
{
|
|
u32 stack[4];
|
|
f32 sp54;
|
|
f32 sp50;
|
|
s32 i;
|
|
f32 intensity;
|
|
|
|
if (g_ExplosionShakeTotalTimer == 0) {
|
|
viShake(0);
|
|
return;
|
|
}
|
|
|
|
sp54 = cosf(0.8f) * arg1->f[0] - sinf(0.8f) * arg1->f[2];
|
|
sp50 = sinf(0.8f) * arg1->f[0] + cosf(0.8f) * arg1->f[2];
|
|
|
|
intensity = 0.0f;
|
|
|
|
for (i = 0; i < g_MaxExplosions; i++) {
|
|
struct prop *prop = g_Explosions[i].prop;
|
|
|
|
if (prop) {
|
|
f32 xdiff = prop->pos.x - arg0->x;
|
|
f32 ydiff = prop->pos.y - arg0->y;
|
|
f32 zdiff = prop->pos.z - arg0->z;
|
|
|
|
f32 dist = sqrtf(xdiff * xdiff + ydiff * ydiff + zdiff * zdiff);
|
|
f32 mult;
|
|
|
|
if (dist == 0.0f) {
|
|
dist = 0.0001f;
|
|
}
|
|
|
|
mult = g_ExplosionTypes[g_Explosions[i].type].innersize / dist;
|
|
|
|
intensity += mult * 15.0f;
|
|
}
|
|
}
|
|
|
|
if (g_ExplosionShakeIntensityTimer > 0) {
|
|
g_ExplosionShakeIntensityTimer--;
|
|
intensity++;
|
|
}
|
|
|
|
g_ExplosionShakeTotalTimer--;
|
|
|
|
if (g_ExplosionShakeTotalTimer & 2) {
|
|
arg2->y = intensity;
|
|
intensity = -intensity;
|
|
} else {
|
|
arg2->y = -intensity;
|
|
}
|
|
|
|
arg2->x = intensity * sp54;
|
|
arg2->z = intensity * sp50;
|
|
|
|
viShake(g_ExplosionShakeTotalTimer * intensity);
|
|
}
|
|
|
|
/**
|
|
* Check if a prop is either fully or partially overlapping the given explosion.
|
|
*
|
|
* minpos and maxpos are the bounding boxes of the prop.
|
|
*/
|
|
bool explosionOverlapsProp(struct explosion *exp, struct prop *prop, struct coord *minpos, struct coord *maxpos)
|
|
{
|
|
bool result = false;
|
|
s32 i;
|
|
RoomNum rooms[8];
|
|
|
|
if (exp->type == EXPLOSIONTYPE_HUGE25) {
|
|
result = true;
|
|
} else {
|
|
for (i = 0; i < exp->numbb; i++) {
|
|
rooms[0] = exp->bbs[i].room;
|
|
|
|
if (exp->bbs[i].room2 != -1) {
|
|
rooms[1] = exp->bbs[i].room2;
|
|
} else {
|
|
rooms[1] = -1;
|
|
}
|
|
|
|
rooms[2] = -1;
|
|
|
|
if (arrayIntersects(prop->rooms, rooms)
|
|
&& minpos->x <= exp->bbs[i].bbmax.x
|
|
&& minpos->y <= exp->bbs[i].bbmax.y
|
|
&& minpos->z <= exp->bbs[i].bbmax.z
|
|
&& maxpos->x >= exp->bbs[i].bbmin.x
|
|
&& maxpos->y >= exp->bbs[i].bbmin.y
|
|
&& maxpos->z >= exp->bbs[i].bbmin.z) {
|
|
result = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
void explosionInflictDamage(struct prop *expprop)
|
|
{
|
|
s32 stack;
|
|
struct explosion *exp = expprop->explosion;
|
|
struct explosiontype *type = &g_ExplosionTypes[exp->type];
|
|
s16 *propnumptr;
|
|
s16 propnums[256];
|
|
bool isfirstframe = exp->age <= 0;
|
|
s32 i;
|
|
f32 k;
|
|
s32 j;
|
|
f32 damageradius;
|
|
|
|
if (g_Vars.lvupdate60 <= 0) {
|
|
return;
|
|
}
|
|
|
|
if (type->damage <= 0.0f) {
|
|
return;
|
|
}
|
|
|
|
if (isfirstframe) {
|
|
damageradius = type->damageradius;
|
|
} else {
|
|
#if PAL
|
|
damageradius = type->blastradius + (type->damageradius - type->blastradius) * exp->age / (type->duration * 0.8333333f);
|
|
#else
|
|
damageradius = type->blastradius + (type->damageradius - type->blastradius) * exp->age / type->duration;
|
|
#endif
|
|
|
|
if (damageradius > type->damageradius) {
|
|
damageradius = type->damageradius;
|
|
}
|
|
}
|
|
|
|
#if PAL
|
|
if (exp->age > (s32)((type->duration + 7.0f * type->flarespeed) * 0.8333333f)) {
|
|
return;
|
|
}
|
|
#else
|
|
if (exp->age > (s32)(type->duration + 7.0f * type->flarespeed)) {
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
// Flicker room lighting
|
|
for (i = 0; expprop->rooms[i] != -1; i++) {
|
|
if (random() % 2048 <= 240) {
|
|
roomFlashLighting(expprop->rooms[i], type->rangeh, 255);
|
|
}
|
|
}
|
|
|
|
// Break lights
|
|
for (i = 0; expprop->rooms[i] != -1; i++) {
|
|
s32 roomnum = expprop->rooms[i];
|
|
|
|
if (roomnum != 0) {
|
|
s32 numlights = g_Rooms[roomnum].numlights;
|
|
f32 xdist = expprop->pos.f[0];
|
|
f32 ydist = expprop->pos.f[1];
|
|
f32 zdist = expprop->pos.f[2];
|
|
struct coord sp164;
|
|
|
|
xdist -= g_BgRooms[roomnum].pos.f[0];
|
|
ydist -= g_BgRooms[roomnum].pos.f[1];
|
|
zdist -= g_BgRooms[roomnum].pos.f[2];
|
|
|
|
for (j = 0; j < numlights; j++) {
|
|
if (lightIsHealthy(roomnum, j)
|
|
&& lightIsVulnerable(roomnum, j)
|
|
&& lightGetBboxCentre(roomnum, j, &sp164)) {
|
|
struct coord sp158;
|
|
struct coord sp14c;
|
|
|
|
sp14c.f[0] = sp164.f[0] - xdist; \
|
|
sp14c.f[1] = sp164.f[1] - ydist; \
|
|
sp14c.f[2] = sp164.f[2] - zdist;
|
|
|
|
sp158.f[0] = damageradius;
|
|
sp158.f[1] = damageradius;
|
|
sp158.f[2] = damageradius;
|
|
|
|
if (func0f1773c8(&sp14c, &sp158)) {
|
|
roomSetLightBroken(roomnum, j);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Damage props
|
|
roomGetProps(expprop->rooms, propnums, 256);
|
|
|
|
propnumptr = propnums;
|
|
|
|
while (*propnumptr >= 0) {
|
|
struct prop *prop = &g_Vars.props[*propnumptr];
|
|
|
|
if (prop != exp->source && prop->timetoregen == 0) {
|
|
if (prop->type == PROPTYPE_OBJ || prop->type == PROPTYPE_WEAPON || prop->type == PROPTYPE_DOOR) {
|
|
f32 xdist;
|
|
f32 ydist;
|
|
f32 zdist;
|
|
struct coord sp130;
|
|
struct coord sp124;
|
|
struct defaultobj *obj = prop->obj;
|
|
bool candamage = false;
|
|
|
|
xdist = prop->pos.x - expprop->pos.x;
|
|
ydist = prop->pos.y - expprop->pos.y;
|
|
zdist = prop->pos.z - expprop->pos.z;
|
|
|
|
if (candamage);
|
|
|
|
#if VERSION >= VERSION_NTSC_1_0
|
|
if (obj)
|
|
#endif
|
|
{
|
|
if (xdist <= damageradius && xdist >= -damageradius
|
|
&& ydist <= damageradius && ydist >= -damageradius
|
|
&& zdist <= damageradius && zdist >= -damageradius) {
|
|
|
|
if (setup0f092304(obj, &sp130, &sp124)) {
|
|
if (explosionOverlapsProp(exp, prop, &sp130, &sp124)) {
|
|
candamage = true;
|
|
}
|
|
} else {
|
|
candamage = true;
|
|
}
|
|
}
|
|
|
|
if (candamage && prop->type == PROPTYPE_WEAPON) {
|
|
struct weaponobj *weapon = prop->weapon;
|
|
|
|
if (weapon && weapon->weaponnum == WEAPON_SKROCKET) {
|
|
weapon->timer240 = 0;
|
|
}
|
|
}
|
|
|
|
if (candamage) {
|
|
f32 f0;
|
|
f32 xfrac;
|
|
f32 yfrac;
|
|
f32 zfrac;
|
|
f32 minfrac;
|
|
|
|
xfrac = f0 = xdist / damageradius;
|
|
|
|
if (xfrac < 0.0f) {
|
|
xfrac = -xfrac;
|
|
}
|
|
|
|
xfrac = 1.0f - xfrac;
|
|
|
|
yfrac = f0 = ydist / damageradius;
|
|
|
|
if (yfrac < 0.0f) {
|
|
yfrac = -yfrac;
|
|
}
|
|
|
|
yfrac = 1.0f - yfrac;
|
|
|
|
zfrac = f0 = zdist / damageradius;
|
|
|
|
if (zfrac < 0.0f) {
|
|
zfrac = -zfrac;
|
|
}
|
|
|
|
zfrac = 1.0f - zfrac;
|
|
|
|
minfrac = xfrac;
|
|
|
|
if (yfrac < minfrac) {
|
|
minfrac = yfrac;
|
|
}
|
|
|
|
if (zfrac < minfrac) {
|
|
minfrac = zfrac;
|
|
}
|
|
|
|
minfrac = (minfrac * 0.7f + 0.3f) * type->damage;
|
|
|
|
if (g_Vars.antiplayernum >= 0
|
|
&& g_Vars.antiplayernum == exp->owner
|
|
&& (obj->flags2 & OBJFLAG2_IMMUNETOANTI)) {
|
|
// anti cannot damage this obj
|
|
} else if (isfirstframe) {
|
|
// Unblock path if this object is a path blocker
|
|
objUpdateLinkedScenery(obj, expprop);
|
|
|
|
// Damage the object
|
|
if ((obj->hidden & OBJHFLAG_00001000) == 0
|
|
&& (obj->flags2 & (OBJFLAG2_LINKEDTOSAFE | OBJFLAG2_IMMUNETOEXPLOSIONS)) == 0) {
|
|
func0f085050(prop, (RANDOMFRAC() * 0.5f + 1.0f) * minfrac, &prop->pos, 0x22, exp->owner);
|
|
}
|
|
|
|
// Give object momentum if it's a hover obj
|
|
if ((obj->hidden & OBJHFLAG_MOUNTED) == 0
|
|
&& (obj->hidden & OBJHFLAG_GRABBED) == 0
|
|
&& (obj->flags3 & OBJFLAG3_PUSHABLE)) {
|
|
f32 dist;
|
|
struct coord spf4;
|
|
spf4.x = prop->pos.x - expprop->pos.x;
|
|
spf4.y = 0.0f;
|
|
spf4.z = prop->pos.z - expprop->pos.z;
|
|
|
|
if (spf4.f[0] != 0.0f || spf4.f[2] != 0.0f) {
|
|
dist = sqrtf(spf4.f[0] * spf4.f[0] + spf4.f[2] * spf4.f[2]);
|
|
|
|
if (dist > 0.0f) {
|
|
f32 tmp = minfrac * 4.0f / dist;
|
|
spf4.x *= tmp;
|
|
spf4.z *= tmp;
|
|
}
|
|
}
|
|
|
|
objApplyMomentum(obj, &spf4, 0.0f, true, true);
|
|
}
|
|
} else if (objIsHealthy(obj)) {
|
|
// Sustained damage
|
|
minfrac *= 0.05f * g_Vars.lvupdate60freal;
|
|
|
|
if ((obj->hidden & OBJHFLAG_00001000) == 0
|
|
&& (obj->flags2 & (OBJFLAG2_LINKEDTOSAFE | OBJFLAG2_IMMUNETOEXPLOSIONS)) == 0) {
|
|
func0f085050(prop, (RANDOMFRAC() * 0.5f + 1.0f) * minfrac,
|
|
&prop->pos, 0x22, exp->owner);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} else if (prop->type == PROPTYPE_CHR || prop->type == PROPTYPE_PLAYER) {
|
|
f32 xdist = prop->pos.f[0] - expprop->pos.f[0];
|
|
f32 ydist = prop->pos.f[1] - expprop->pos.f[1];
|
|
f32 zdist = prop->pos.f[2] - expprop->pos.f[2];
|
|
f32 radius;
|
|
f32 ymax;
|
|
f32 ymin;
|
|
struct coord spcc;
|
|
struct coord spc0;
|
|
|
|
bool candamage = false;
|
|
|
|
if (prop->type == PROPTYPE_CHR);
|
|
|
|
if (xdist <= damageradius && xdist >= -damageradius
|
|
&& ydist <= damageradius && ydist >= -damageradius
|
|
&& zdist <= damageradius && zdist >= -damageradius) {
|
|
propGetBbox(prop, &radius, &ymax, &ymin);
|
|
|
|
radius -= 20.0f;
|
|
|
|
if (radius <= 0.0f) {
|
|
radius = 0.0f;
|
|
}
|
|
|
|
spcc.f[0] = prop->pos.f[0] - radius;
|
|
spcc.f[1] = ymin;
|
|
spcc.f[2] = prop->pos.f[2] - radius;
|
|
|
|
spc0.f[0] = prop->pos.f[0] + radius;
|
|
spc0.f[1] = ymax;
|
|
spc0.f[2] = prop->pos.f[2] + radius;
|
|
|
|
if (explosionOverlapsProp(exp, prop, &spcc, &spc0)) {
|
|
candamage = true;
|
|
}
|
|
}
|
|
|
|
if (candamage) {
|
|
struct prop *ownerprop = NULL;
|
|
f32 xfrac = xdist / damageradius;
|
|
f32 yfrac = ydist / damageradius;
|
|
f32 zfrac = zdist / damageradius;
|
|
struct coord spa0 = {0, 0, 0};
|
|
struct chrdata *chr = prop->chr;
|
|
f32 minfrac;
|
|
|
|
if (xfrac < 0.0f) {
|
|
xfrac = -xfrac;
|
|
}
|
|
|
|
if (yfrac < 0.0f) {
|
|
yfrac = -yfrac;
|
|
}
|
|
|
|
if (zfrac < 0.0f) {
|
|
zfrac = -zfrac;
|
|
}
|
|
|
|
xfrac = 1.0f - xfrac;
|
|
yfrac = 1.0f - yfrac;
|
|
zfrac = 1.0f - zfrac;
|
|
|
|
minfrac = xfrac;
|
|
|
|
if (yfrac < minfrac) {
|
|
minfrac = yfrac;
|
|
}
|
|
|
|
if (zfrac < minfrac) {
|
|
minfrac = zfrac;
|
|
}
|
|
|
|
minfrac *= minfrac;
|
|
minfrac = minfrac * type->damage * 8.0f;
|
|
|
|
if (isfirstframe) {
|
|
if (xdist != 0.0f || zdist != 0.0f) {
|
|
f32 dist = sqrtf(xdist * xdist + zdist * zdist);
|
|
|
|
if (dist > 0.0f) {
|
|
xdist *= 1.0f / dist;
|
|
zdist *= 1.0f / dist;
|
|
|
|
spa0.x = xdist;
|
|
spa0.y = 0.0f;
|
|
spa0.z = zdist;
|
|
}
|
|
}
|
|
} else {
|
|
minfrac *= 0.05f * g_Vars.lvupdate60freal;
|
|
}
|
|
|
|
if (g_Vars.normmplayerisrunning) {
|
|
struct chrdata *ownerchr = mpGetChrFromPlayerIndex(exp->owner);
|
|
|
|
if (ownerchr) {
|
|
ownerprop = ownerchr->prop;
|
|
}
|
|
} else if (exp->owner == g_Vars.bondplayernum) {
|
|
ownerprop = g_Vars.bond->prop;
|
|
} else if (g_Vars.coopplayernum >= 0 && exp->owner == g_Vars.coopplayernum) {
|
|
ownerprop = g_Vars.coop->prop;
|
|
} else if (g_Vars.antiplayernum >= 0 && exp->owner == g_Vars.antiplayernum) {
|
|
ownerprop = g_Vars.anti->prop;
|
|
}
|
|
|
|
chrDamageByExplosion(chr, minfrac, &spa0, ownerprop, &expprop->pos);
|
|
|
|
if (prop->type == PROPTYPE_CHR && !isfirstframe) {
|
|
chrDisfigure(chr, &expprop->pos, damageradius);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
propnumptr++;
|
|
}
|
|
}
|
|
|
|
u32 explosionTick(struct prop *prop)
|
|
{
|
|
struct explosion *exp = prop->explosion;
|
|
struct explosiontype *type = &g_ExplosionTypes[exp->type];
|
|
s32 i;
|
|
s32 j;
|
|
s32 k;
|
|
f32 hrange;
|
|
f32 vrange;
|
|
f32 lvupdate;
|
|
struct coord bbmin;
|
|
struct coord bbmax;
|
|
s16 maxage;
|
|
s32 numpartstocreate;
|
|
struct coord sp11c;
|
|
struct coord sp110;
|
|
u32 stack[2];
|
|
struct coord spfc;
|
|
struct coord spf0;
|
|
s32 bb;
|
|
bool xlu;
|
|
struct chrdata *chr;
|
|
f32 scorchsize;
|
|
struct hitthing hitthing;
|
|
|
|
maxage = TICKS(type->duration);
|
|
|
|
if (g_Vars.lvupdate60 == 0) {
|
|
return TICKOP_NONE;
|
|
}
|
|
|
|
lvupdate = g_Vars.lvupdate60 < TICKS(15) ? g_Vars.lvupdate60 : (s32)TICKS(15);
|
|
|
|
#if PAL
|
|
if (exp->age >= 7 && exp->age < maxage)
|
|
#else
|
|
if (exp->age >= 8 && exp->age < maxage)
|
|
#endif
|
|
{
|
|
hrange = explosionGetHorizontalRangeAtFrame(exp, exp->age);
|
|
vrange = explosionGetVerticalRangeAtFrame(exp, exp->age);
|
|
|
|
sp11c.x = prop->pos.x - hrange * 0.5f;
|
|
sp11c.y = prop->pos.y - vrange * 0.5f;
|
|
sp11c.z = prop->pos.z - hrange * 0.5f;
|
|
|
|
sp110.x = prop->pos.x + hrange * 0.5f;
|
|
sp110.y = prop->pos.y + vrange * 0.5f;
|
|
sp110.z = prop->pos.z + hrange * 0.5f;
|
|
|
|
// Barrel explosions ascend upwards
|
|
if (exp->type == EXPLOSIONTYPE_GASBARREL && exp->age < TICKS(32)) {
|
|
prop->pos.y += PALUPF(10.0f) * lvupdate;
|
|
}
|
|
|
|
// Create new parts
|
|
numpartstocreate = (s32)((f32)type->propagationrate * exp->age / maxage) + 1;
|
|
|
|
for (i = 0; i < numpartstocreate; i++) {
|
|
for (j = 0; j < 40; j++) {
|
|
if (exp->parts[j].frame == 0) {
|
|
if (exp->numbb == 0 || exp->type == EXPLOSIONTYPE_HUGE25) {
|
|
spfc.f[0] = sp11c.f[0];
|
|
spfc.f[1] = sp11c.f[1];
|
|
spfc.f[2] = sp11c.f[2];
|
|
|
|
spf0.f[0] = sp110.f[0];
|
|
spf0.f[1] = sp110.f[1];
|
|
spf0.f[2] = sp110.f[2];
|
|
|
|
bb = 0;
|
|
} else {
|
|
bb = j % exp->numbb;
|
|
|
|
spfc.x = exp->bbs[bb].bbmin.x;
|
|
spfc.y = exp->bbs[bb].bbmin.y;
|
|
spfc.z = exp->bbs[bb].bbmin.z;
|
|
|
|
spf0.x = exp->bbs[bb].bbmax.x;
|
|
spf0.y = exp->bbs[bb].bbmax.y;
|
|
spf0.z = exp->bbs[bb].bbmax.z;
|
|
|
|
if (spfc.x < sp11c.x) {
|
|
spfc.x = sp11c.x;
|
|
}
|
|
|
|
if (spfc.y < sp11c.y) {
|
|
spfc.y = sp11c.y;
|
|
}
|
|
|
|
if (spfc.z < sp11c.z) {
|
|
spfc.z = sp11c.z;
|
|
}
|
|
|
|
if (sp110.x < spf0.x) {
|
|
spf0.x = sp110.x;
|
|
}
|
|
|
|
if (sp110.y < spf0.y) {
|
|
spf0.y = sp110.y;
|
|
}
|
|
|
|
if (sp110.z < spf0.z) {
|
|
spf0.z = sp110.z;
|
|
}
|
|
|
|
if (spf0.x <= spfc.x || spf0.y <= spfc.y || spf0.z <= spfc.z) {
|
|
bb = 0;
|
|
|
|
spfc.x = exp->bbs[bb].bbmin.x;
|
|
spfc.y = exp->bbs[bb].bbmin.y;
|
|
spfc.z = exp->bbs[bb].bbmin.z;
|
|
|
|
spf0.x = exp->bbs[bb].bbmax.x;
|
|
spf0.y = exp->bbs[bb].bbmax.y;
|
|
spf0.z = exp->bbs[bb].bbmax.z;
|
|
|
|
if (spfc.x < sp11c.x) {
|
|
spfc.x = sp11c.x;
|
|
}
|
|
|
|
if (spfc.y < sp11c.y) {
|
|
spfc.y = sp11c.y;
|
|
}
|
|
|
|
if (spfc.z < sp11c.z) {
|
|
spfc.z = sp11c.z;
|
|
}
|
|
|
|
if (sp110.x < spf0.x) {
|
|
spf0.x = sp110.x;
|
|
}
|
|
|
|
if (sp110.y < spf0.y) {
|
|
spf0.y = sp110.y;
|
|
}
|
|
|
|
if (sp110.z < spf0.z) {
|
|
spf0.z = sp110.z;
|
|
}
|
|
}
|
|
}
|
|
|
|
exp->parts[j].pos.f[0] = spfc.f[0] + RANDOMFRAC() * (spf0.f[0] - spfc.f[0]);
|
|
exp->parts[j].pos.f[1] = spfc.f[1] + RANDOMFRAC() * (spf0.f[1] - spfc.f[1]);
|
|
exp->parts[j].pos.f[2] = spfc.f[2] + RANDOMFRAC() * (spf0.f[2] - spfc.f[2]);
|
|
exp->parts[j].bb = bb;
|
|
exp->parts[j].frame = 1;
|
|
exp->parts[j].size = (1.0f + RANDOMFRAC() * 0.5f) * type->innersize;
|
|
exp->parts[j].rot = RANDOMFRAC() * M_BADTAU;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
explosionGetBboxAtFrame(&bbmin, &bbmax, exp->age, prop);
|
|
bgFindEnteredRooms(&bbmin, &bbmax, prop->rooms, 7, false);
|
|
explosionInflictDamage(prop);
|
|
|
|
// Play boom sound if this is the first frame
|
|
if (exp->age == 0) {
|
|
psCreate(NULL, NULL, type->sound, -1, -1, 0, 0, PSTYPE_NONE, &exp->prop->pos, -1.0f, exp->prop->rooms, -1, -1.0f, -1.0f, -1.0f);
|
|
}
|
|
|
|
for (k = 0; k < (s32)lvupdate; k++) {
|
|
exp->age++;
|
|
|
|
for (j = 0; j < 40; j++) {
|
|
if (exp->parts[j].frame > 0) {
|
|
exp->parts[j].frame++;
|
|
}
|
|
}
|
|
|
|
// Create smoke
|
|
if (((exp->age == TICKS(15) && exp->type == EXPLOSIONTYPE_GASBARREL)
|
|
|| (exp->age == maxage - TICKS(20) && exp->type != EXPLOSIONTYPE_GASBARREL))
|
|
&& (exp->type != EXPLOSIONTYPE_BULLETHOLE || (random() % 2) == 0)) {
|
|
if (exp->source) {
|
|
smokeCreateSimple(&exp->source->pos, exp->source->rooms, type->smoketype);
|
|
} else {
|
|
smokeCreateSimple(&prop->pos, prop->rooms, type->smoketype);
|
|
}
|
|
}
|
|
|
|
// Make scorch at half duration
|
|
if (exp->age == (maxage >> 1) && exp->makescorch) {
|
|
xlu = false;
|
|
scorchsize = 2.0f * type->innersize;
|
|
|
|
if (scorchsize > 100.0f) {
|
|
scorchsize = 100.0f;
|
|
}
|
|
|
|
scorchsize *= 0.8f + 0.2f * RANDOMFRAC();
|
|
|
|
if (g_Vars.normmplayerisrunning) {
|
|
chr = mpGetChrFromPlayerIndex(exp->owner);
|
|
} else if (g_Vars.antiplayernum >= 0 && exp->owner == g_Vars.antiplayernum) {
|
|
chr = g_Vars.anti->prop->chr;
|
|
} else if (g_Vars.coopplayernum >= 0 && exp->owner == g_Vars.coopplayernum) {
|
|
chr = g_Vars.coop->prop->chr;
|
|
} else {
|
|
chr = g_Vars.bond->prop->chr;
|
|
}
|
|
|
|
if (g_Rooms[exp->room].gfxdata) {
|
|
if (g_Rooms[exp->room].gfxdata->xlublocks && bgTestHitInRoom(&prop->pos, &exp->unk3d0, exp->room, &hitthing)) {
|
|
xlu = hitthing.unk2c == 2;
|
|
}
|
|
|
|
wallhitCreateWith20Args(&exp->unk3d0, &exp->unk3dc, &prop->pos, NULL,
|
|
0, WALLHITTEX_SCORCH, exp->room, 0,
|
|
0, -1, 0, chr,
|
|
scorchsize, scorchsize, 0xff, 0xff,
|
|
0, 0, 0, xlu);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Free explosion if finished
|
|
#if PAL
|
|
if (exp->age >= maxage + (s32)((16.0f * type->flarespeed) * 0.8333333f))
|
|
#else
|
|
if (exp->age >= maxage + (s32)(16.0f * type->flarespeed))
|
|
#endif
|
|
{
|
|
if (exp->type != EXPLOSIONTYPE_BULLETHOLE) {
|
|
propUnsetDangerous(exp->prop);
|
|
}
|
|
|
|
exp->prop = NULL;
|
|
return TICKOP_FREE;
|
|
}
|
|
|
|
return TICKOP_NONE;
|
|
}
|
|
|
|
u32 explosionTickPlayer(struct prop *prop)
|
|
{
|
|
Mtxf *matrix = camGetWorldToScreenMtxf();
|
|
|
|
prop->z = -(matrix->m[0][2] * prop->pos.x + matrix->m[1][2] * prop->pos.y + matrix->m[2][2] * prop->pos.z + matrix->m[3][2]);
|
|
|
|
if (prop->z < 100) {
|
|
prop->z *= 0.5f;
|
|
} else {
|
|
prop->z -= 100;
|
|
}
|
|
|
|
prop->flags |= PROPFLAG_ONANYSCREENTHISTICK | PROPFLAG_ONTHISSCREENTHISTICK;
|
|
|
|
return TICKOP_NONE;
|
|
}
|
|
|
|
Gfx *explosionRender(struct prop *prop, Gfx *gdl, bool xlupass)
|
|
{
|
|
struct explosion *exp = prop->explosion;
|
|
s32 roomnum;
|
|
s32 i;
|
|
s32 j;
|
|
|
|
if (!xlupass) {
|
|
return gdl;
|
|
}
|
|
|
|
j = 0;
|
|
roomnum = prop->rooms[j];
|
|
|
|
while (roomnum != -1) {
|
|
if (g_Rooms[roomnum].gfxdata && g_Rooms[roomnum].loaded240 && (g_Rooms[roomnum].flags & ROOMFLAG_ONSCREEN)) {
|
|
break;
|
|
}
|
|
|
|
j++;
|
|
roomnum = prop->rooms[j];
|
|
}
|
|
|
|
if (roomnum != -1) {
|
|
struct screenbox screenbox;
|
|
struct coord *coord = roomGetPosPtr(roomnum);
|
|
Col *colours;
|
|
s32 tmp;
|
|
|
|
if (func0f08e5a8(prop->rooms, &screenbox) > 0) {
|
|
gdl = bgScissorWithinViewport(gdl, screenbox.xmin, screenbox.ymin, screenbox.xmax, screenbox.ymax);
|
|
} else {
|
|
gdl = bgScissorToViewport(gdl);
|
|
}
|
|
|
|
gSPClearGeometryMode(gdl++, G_CULL_BOTH | G_FOG);
|
|
gSPMatrix(gdl++, osVirtualToPhysical(camGetOrthogonalMtxL()), G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_PROJECTION);
|
|
|
|
gdl = roomApplyMtx(gdl, roomnum);
|
|
|
|
gSPDisplayList(gdl++, g_TexGdl2);
|
|
|
|
colours = gfxAllocateColours(1);
|
|
|
|
if (USINGDEVICE(DEVICE_NIGHTVISION) || USINGDEVICE(DEVICE_IRSCANNER)) {
|
|
colours[0].word = 0xffffffff;
|
|
} else if (g_Vars.currentplayer->visionmode == VISIONMODE_XRAY) {
|
|
u32 alpha = 0x80;
|
|
u32 red;
|
|
u32 green;
|
|
f32 expdist = sqrtf(ERASERSQDIST(prop->pos.f));
|
|
|
|
if (g_Vars.currentplayer->eraserpropdist < expdist) {
|
|
return gdl;
|
|
}
|
|
|
|
if (g_Vars.currentplayer->eraserpropdist - 150.0f < expdist) {
|
|
alpha = (1.0f - (expdist - (g_Vars.currentplayer->eraserpropdist - 150.0f)) / 150.0f) * 128.0f;
|
|
}
|
|
|
|
expdist = expdist / g_Vars.currentplayer->eraserpropdist;
|
|
|
|
if (expdist > 1.0f) {
|
|
expdist = 1.0f;
|
|
}
|
|
|
|
red = expdist * 127.0f;
|
|
green = (1.0f - expdist) * 127.0f;
|
|
|
|
colours[0].word = PD_BE32(red << 24 | green << 16 | alpha | 0x80800000);
|
|
} else {
|
|
static u32 var8007e93c = 0xffffffff;
|
|
mainOverrideVariable("ecol", &var8007e93c);
|
|
colours[0].word = 0xffffffff;
|
|
colours[0].word = var8007e93c;
|
|
}
|
|
|
|
gSPColor(gdl++, osVirtualToPhysical(colours), 1);
|
|
|
|
for (i = 14; i >= 0; i--) {
|
|
gDPSetTextureImage(gdl++, G_IM_FMT_IA, G_IM_SIZ_16b, 1, g_ExplosionTexturePairs[i].texturenum1);
|
|
gDPLoadSync(gdl++);
|
|
gDPLoadBlock(gdl++, G_TX_LOADTILE, 0, 0, 1567, 0);
|
|
|
|
gDPSetTextureImage(gdl++, G_IM_FMT_RGBA, G_IM_SIZ_16b, 1, g_ExplosionTexturePairs[i].texturenum2);
|
|
gDPLoadSync(gdl++);
|
|
gDPLoadBlock(gdl++, 5, 0, 0, 223, 0);
|
|
|
|
gDPPipeSync(gdl++);
|
|
|
|
for (j = 0; j < ARRAYCOUNT(exp->parts); j++) {
|
|
if (exp->parts[j].frame > 0) {
|
|
#if PAL
|
|
if (i == (s32)((f32)(exp->parts[j].frame - 1) / (g_ExplosionTypes[exp->type].flarespeed * 0.83333331346512f))) {
|
|
gdl = explosionRenderPart(exp, &exp->parts[j], gdl, coord, i);
|
|
}
|
|
#else
|
|
if (i == (s32)((f32)(exp->parts[j].frame - 1) / g_ExplosionTypes[exp->type].flarespeed)) {
|
|
gdl = explosionRenderPart(exp, &exp->parts[j], gdl, coord, i);
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
|
|
gSPMatrix(gdl++, osVirtualToPhysical(camGetPerspectiveMtxL()), G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_PROJECTION);
|
|
|
|
#if PAL
|
|
tmp = (g_ExplosionTypes[exp->type].flarespeed * 15.0f) * 0.83333331346512f;
|
|
#else
|
|
tmp = g_ExplosionTypes[exp->type].flarespeed * 15.0f;
|
|
#endif
|
|
|
|
for (j = 0; j < ARRAYCOUNT(exp->parts); j++) {
|
|
if (exp->parts[j].frame > tmp) {
|
|
exp->parts[j].frame = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
return gdl;
|
|
}
|
|
|
|
Gfx *explosionRenderPart(struct explosion *exp, struct explosionpart *part, Gfx *gdl, struct coord *coord, s32 arg4)
|
|
{
|
|
Vtx *vertices = gfxAllocateVertices(4);
|
|
Mtxf *mtx = camGetProjectionMtxF();
|
|
struct coord spbc;
|
|
struct coord spb0;
|
|
struct coord spa4;
|
|
struct coord sp98;
|
|
f32 x;
|
|
f32 y;
|
|
f32 z;
|
|
s32 i;
|
|
s32 j;
|
|
f32 size;
|
|
f32 cosine;
|
|
f32 max;
|
|
f32 size2;
|
|
struct coord pos;
|
|
f32 sine;
|
|
s32 bbnum;
|
|
f32 value;
|
|
|
|
size = part->size;
|
|
|
|
x = part->pos.x;
|
|
y = part->pos.y;
|
|
z = part->pos.z;
|
|
|
|
if (exp->numbb > 0) {
|
|
pos.x = x;
|
|
pos.y = y;
|
|
pos.z = z;
|
|
|
|
bbnum = part->bb;
|
|
max = 0.0f;
|
|
|
|
for (i = 0; i < exp->numbb; i++) {
|
|
if (pos.f[0] >= exp->bbs[i].bbmin.f[0] && pos.f[0] <= exp->bbs[i].bbmax.f[0]
|
|
&& pos.f[1] >= exp->bbs[i].bbmin.f[1] && pos.f[1] <= exp->bbs[i].bbmax.f[1]
|
|
&& pos.f[2] >= exp->bbs[i].bbmin.f[2] && pos.f[2] <= exp->bbs[i].bbmax.f[2]) {
|
|
f32 min = 65536.0f;
|
|
|
|
for (j = 0; j < 3; j++) {
|
|
value = pos.f[j] - exp->bbs[i].bbmin.f[j];
|
|
|
|
if (value < min) {
|
|
min = value;
|
|
}
|
|
|
|
value = exp->bbs[i].bbmax.f[j] - pos.f[j];
|
|
|
|
if (value < min) {
|
|
min = value;
|
|
}
|
|
}
|
|
|
|
if (min > max) {
|
|
max = min;
|
|
bbnum = i;
|
|
}
|
|
}
|
|
}
|
|
|
|
size2 = size * 0.7f;
|
|
|
|
for (i = 0; i < 3; i++) {
|
|
if ((exp->bbs[bbnum].bbmax.f[i] - exp->bbs[bbnum].bbmin.f[i]) * 0.8f < size2) {
|
|
size = (exp->bbs[bbnum].bbmax.f[i] - exp->bbs[bbnum].bbmin.f[i]) * 0.8f / 0.7f;
|
|
|
|
if (part->size * 0.65f > size) {
|
|
size = part->size * 0.65f;
|
|
}
|
|
|
|
size2 = size * 0.7f;
|
|
}
|
|
}
|
|
|
|
if (arg4 == 1) {
|
|
size2 *= 0.58928573f;
|
|
} else if (arg4 == 2) {
|
|
size2 *= 0.6964286f;
|
|
} else if (arg4 == 3) {
|
|
size2 *= 0.8214286f;
|
|
} else if (arg4 == 4) {
|
|
size2 *= 0.96428573f;
|
|
}
|
|
|
|
for (i = 0; i < 3; i++) {
|
|
if (exp->bbs[bbnum].bbmax.f[i] - exp->bbs[bbnum].bbmin.f[i] < size2) {
|
|
pos.f[i] = (exp->bbs[bbnum].bbmax.f[i] + exp->bbs[bbnum].bbmin.f[i]) * 0.5f;
|
|
} else {
|
|
value = exp->bbs[bbnum].bbmin.f[i] + size2 - pos.f[i];
|
|
|
|
if (value > 0.0f) {
|
|
pos.f[i] += value;
|
|
} else {
|
|
value = pos.f[i] - (exp->bbs[bbnum].bbmax.f[i] - size2);
|
|
|
|
if (value > 0.0f) {
|
|
pos.f[i] -= value;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
x = pos.x;
|
|
y = pos.y;
|
|
z = pos.z;
|
|
}
|
|
|
|
cosine = cosf(part->rot) * size;
|
|
sine = sinf(part->rot) * size;
|
|
|
|
spbc.x = mtx->m[0][0] * cosine;
|
|
spbc.y = mtx->m[0][1] * cosine;
|
|
spbc.z = mtx->m[0][2] * cosine;
|
|
|
|
spb0.x = mtx->m[0][0] * sine;
|
|
spb0.y = mtx->m[0][1] * sine;
|
|
spb0.z = mtx->m[0][2] * sine;
|
|
|
|
spa4.x = mtx->m[1][0] * cosine;
|
|
spa4.y = mtx->m[1][1] * cosine;
|
|
spa4.z = mtx->m[1][2] * cosine;
|
|
|
|
sp98.x = mtx->m[1][0] * sine;
|
|
sp98.y = mtx->m[1][1] * sine;
|
|
sp98.z = mtx->m[1][2] * sine;
|
|
|
|
vertices[0].x = x - spbc.f[0] - sp98.f[0] - coord->f[0];
|
|
vertices[0].y = y - spbc.f[1] - sp98.f[1] - coord->f[1];
|
|
vertices[0].z = z - spbc.f[2] - sp98.f[2] - coord->f[2];
|
|
vertices[0].s = 1760;
|
|
vertices[0].t = 0;
|
|
|
|
vertices[1].x = x + spb0.f[0] - spa4.f[0] - coord->f[0];
|
|
vertices[1].y = y + spb0.f[1] - spa4.f[1] - coord->f[1];
|
|
vertices[1].z = z + spb0.f[2] - spa4.f[2] - coord->f[2];
|
|
vertices[1].s = 0;
|
|
vertices[1].t = 0;
|
|
|
|
vertices[2].x = x + spbc.f[0] + sp98.f[0] - coord->f[0];
|
|
vertices[2].y = y + spbc.f[1] + sp98.f[1] - coord->f[1];
|
|
vertices[2].z = z + spbc.f[2] + sp98.f[2] - coord->f[2];
|
|
vertices[2].s = 0;
|
|
vertices[2].t = 1760;
|
|
|
|
vertices[3].x = x - spb0.f[0] + spa4.f[0] - coord->f[0];
|
|
vertices[3].y = y - spb0.f[1] + spa4.f[1] - coord->f[1];
|
|
vertices[3].z = z - spb0.f[2] + spa4.f[2] - coord->f[2];
|
|
vertices[3].s = 1760;
|
|
vertices[3].t = 1760;
|
|
|
|
for (j = 0; j < 4; j++) {
|
|
vertices[j].colour = 0;
|
|
}
|
|
|
|
gSPVertex(gdl++, osVirtualToPhysical(vertices), 4, 0);
|
|
|
|
gSPTri2(gdl++, 0, 1, 2, 0, 2, 3);
|
|
|
|
return gdl;
|
|
}
|