#include #include "constants.h" #include "game/dlights.h" #include "game/gfxmemory.h" #include "game/prop.h" #include "game/tex.h" #include "game/camera.h" #include "game/smoke.h" #include "game/bg.h" #include "game/room.h" #include "game/file.h" #include "game/propobj.h" #include "bss.h" #include "lib/rng.h" #include "lib/mtx.h" #include "data.h" #include "types.h" struct smoke *g_Smokes; s32 g_MaxSmokes; Mtx var800a3448; Mtx var800a3488; struct smoketype g_SmokeTypes[NUM_SMOKETYPES] = { // duration // | fadespeed // | | spreadspeed // | | | size // | | | | bgrotatespeed // | | | | | red // | | | | | | green // | | | | | | | blue // | | | | | | | | fgrotatespeed // | | | | | | | | | numclouds // | | | | | | | | | | #if PAL /*00*/ { 1, 50, 82, 0, 0, 0x80, 0x80, 0x80, 0.36, 144, 0.18, 0.36, 1 }, // SMOKETYPE_NONE /*01*/ { 183, 50, 37, 60, 0.024, 0x50, 0x50, 0x60, 0.36, 144, 0.18, 0.36, 1 }, // SMOKETYPE_ELECTRICAL /*02*/ { 183, 50, 41, 20, 0.012, 0x80, 0x80, 0x80, 0.36, 144, 0.18, 0.36, 1 }, // SMOKETYPE_MINI /*03*/ { 233, 50, 100, 100, 0.012, 0xc0, 0xc0, 0xc0, 0.36, 144, 0.18, 0.36, 1 }, /*04*/ { 233, 50, 50, 80, 0.024, 0x40, 0x40, 0x40, 0.36, 144, 0.18, 0.36, 1 }, // SMOKETYPE_SMALL /*05*/ { 283, 50, 41, 190, 0.018000001087785, 0x40, 0x40, 0x40, 0.36, 144, 0.18, 0.36, 1 }, // SMOKETYPE_MEDIUM /*06*/ { 316, 50, 58, 300, 0.012, 0x40, 0x40, 0x40, 0.36, 144, 0.18, 0.36, 1 }, // SMOKETYPE_LARGE /*07*/ { 50, 50, 6, 15, 0.03600000217557, 0xff, 0xff, 0xff, 0.36, 144, 0.18, 0.36, 1 }, /*08*/ { 16, 1, 5, 30, 0.03600000217557, 0xff, 0xff, 0xff, 2.4, 36, 0.18, 0.36, 1 }, // SMOKETYPE_ROCKETTAIL /*09*/ { 20, 1, 5, 16, 0.03600000217557, 0xe0, 0xe0, 0xe0, 3.6000001430511, 36, 0.18, 0.36, 1 }, // SMOKETYPE_GRENADETAIL /*10*/ { 750, 50, 58, 900, 0.012, 0x40, 0x40, 0x40, 0.36, 216, 0.18, 0.36, 1 }, /*11*/ { 16, 1, 5, 30, 0.03600000217557, 0x18, 0x20, 0x40, 2.4, 36, 0.18, 0.36, 1 }, // SMOKETYPE_HOMINGTAIL /*12*/ { 41, 20, 5, 2, 0.03600000217557, 0xff, 0xff, 0xbf, 0.36, 180, 0.18, 0.36, 1 }, /*13*/ { 10, 12, 5, 5, 0.03600000217557, 0x66, 0x40, 0x40, 1.2, 21, 0.18, 0.36, 1 }, // SMOKETYPE_SKCORPSE /*14*/ { 10, 12, 5, 5, 0.03600000217557, 0x66, 0x66, 0x00, 1.2, 21, 0.18, 0.36, 1 }, /*15*/ { 41, 4, 4, 3, 0.03600000217557, 0xff, 0xff, 0xff, 0.36, 180, 0, 0.54, 0 }, // SMOKETYPE_MUZZLE_PISTOL /*16*/ { 41, 4, 5, 3, 0.03600000217557, 0xaf, 0xff, 0xaf, 0.36, 180, 0.108000010252, 0.36, 0 }, // SMOKETYPE_MUZZLE_REAPER /*17*/ { 41, 4, 3, 3, 0.03600000217557, 0xff, 0xff, 0xff, 0.36, 180, 0, 0.4200000166893, 0 }, // SMOKETYPE_MUZZLE_AUTOMATIC /*18*/ { 41, 4, 3, 3, 0.03600000217557, 0xaf, 0x8f, 0x6f, 0.36, 180, 0.12000000476837, 0.36, 0 }, // SMOKETYPE_MUZZLE_SHOTGUN /*19*/ { 41, 1, 2, 16, 0.03600000217557, 0xff, 0xff, 0x80, 3.6000001430511, 36, 0.18, 0.36, 1 }, // SMOKETYPE_PINBALL /*20*/ { 150, 8, 6, 18, 0.072000004351139, 0xff, 0xff, 0xff, 0.36, 0, 0.228, 0.084000006318092, 1 }, // SMOKETYPE_WATER /*21*/ { 183, 33, 37, 60, 0.024, 0x20, 0x20, 0x20, 0.36, 36, 1.8000000715256, 2.16, 6 }, // SMOKETYPE_DEBRIS /*22*/ { 183, 4, 6, 60, 0.03600000217557, 0xaf, 0x8f, 0x6f, 0.36, 36, 1.8000000715256, 0.36, 1 }, // SMOKETYPE_UFO #else /*00*/ { 1, 60, 99, 0, 0, 0x80, 0x80, 0x80, 0.3, 120, 0.15, 0.3, 1 }, // SMOKETYPE_NONE /*01*/ { 220, 60, 45, 60, 0.02, 0x50, 0x50, 0x60, 0.3, 120, 0.15, 0.3, 1 }, // SMOKETYPE_ELECTRICAL /*02*/ { 220, 60, 50, 20, 0.01, 0x80, 0x80, 0x80, 0.3, 120, 0.15, 0.3, 1 }, // SMOKETYPE_MINI /*03*/ { 280, 60, 120, 100, 0.01, 0xc0, 0xc0, 0xc0, 0.3, 120, 0.15, 0.3, 1 }, /*04*/ { 280, 60, 60, 80, 0.02, 0x40, 0x40, 0x40, 0.3, 120, 0.15, 0.3, 1 }, // SMOKETYPE_SMALL /*05*/ { 340, 60, 50, 190, 0.015, 0x40, 0x40, 0x40, 0.3, 120, 0.15, 0.3, 1 }, // SMOKETYPE_MEDIUM /*06*/ { 380, 60, 70, 300, 0.01, 0x40, 0x40, 0x40, 0.3, 120, 0.15, 0.3, 1 }, // SMOKETYPE_LARGE /*07*/ { 60, 60, 8, 15, 0.03, 0xff, 0xff, 0xff, 0.3, 120, 0.15, 0.3, 1 }, /*08*/ { 20, 1, 6, 30, 0.03, 0xff, 0xff, 0xff, 2, 30, 0.15, 0.3, 1 }, // SMOKETYPE_ROCKETTAIL /*09*/ { 25, 1, 7, 16, 0.03, 0xe0, 0xe0, 0xe0, 3, 30, 0.15, 0.3, 1 }, // SMOKETYPE_GRENADETAIL /*10*/ { 900, 60, 70, 900, 0.01, 0x40, 0x40, 0x40, 0.3, 180, 0.15, 0.3, 1 }, /*11*/ { 20, 1, 6, 30, 0.03, 0x18, 0x20, 0x40, 2, 30, 0.15, 0.3, 1 }, // SMOKETYPE_HOMINGTAIL /*12*/ { 50, 25, 7, 2, 0.03, 0xff, 0xff, 0xbf, 0.3, 150, 0.15, 0.3, 1 }, /*13*/ { 12, 15, 7, 5, 0.03, 0x66, 0x40, 0x40, 1, 18, 0.15, 0.3, 1 }, // SMOKETYPE_SKCORPSE /*14*/ { 12, 15, 7, 5, 0.03, 0x66, 0x66, 0x00, 1, 18, 0.15, 0.3, 1 }, /*15*/ { 50, 5, 5, 3, 0.03, 0xff, 0xff, 0xff, 0.3, 150, 0, 0.45, 0 }, // SMOKETYPE_MUZZLE_PISTOL /*16*/ { 50, 5, 6, 3, 0.03, 0xaf, 0xff, 0xaf, 0.3, 150, 0.09, 0.3, 0 }, // SMOKETYPE_MUZZLE_REAPER /*17*/ { 50, 5, 3, 3, 0.03, 0xff, 0xff, 0xff, 0.3, 150, 0, 0.35, 0 }, // SMOKETYPE_MUZZLE_AUTOMATIC /*18*/ { 50, 5, 3, 3, 0.03, 0xaf, 0x8f, 0x6f, 0.3, 150, 0.1, 0.3, 0 }, // SMOKETYPE_MUZZLE_SHOTGUN /*19*/ { 50, 1, 2, 16, 0.03, 0xff, 0xff, 0x80, 3, 30, 0.15, 0.3, 1 }, // SMOKETYPE_PINBALL /*20*/ { 180, 10, 8, 18, 0.06, 0xff, 0xff, 0xff, 0.3, 0, 0.19, 0.07, 1 }, // SMOKETYPE_WATER /*21*/ { 220, 40, 45, 60, 0.02, 0x20, 0x20, 0x20, 0.3, 30, 1.5, 1.8, 6 }, // SMOKETYPE_DEBRIS /*22*/ { 220, 5, 8, 60, 0.03, 0xaf, 0x8f, 0x6f, 0.3, 30, 1.5, 0.3, 1 }, // SMOKETYPE_UFO #endif }; Gfx *smokeRenderPart(struct smoke *smoke, struct smokepart *part, Gfx *gdl, struct coord *coord, f32 size) { struct gfxvtx *vertices = gfxAllocateVertices(4); struct colour *colours = (struct colour *)gfxAllocateColours(1); Mtxf *mtx = camGetUnk174c(); struct coord spa0; struct coord sp94; struct coord sp88; struct coord sp7c; f32 sp78; f32 sp74; f32 sp70; f32 sp6c; f32 sp68; u8 alpha; struct coord *campos = &g_Vars.currentplayer->cam_pos; f32 sp5c; f32 sp58; f32 sp54; f32 distance; f32 range; f32 mult; f32 sp44; f32 sp40; f32 sp3c; f32 frac; u32 stack; if (g_SmokeTypes[smoke->type].fadespeed >= part->count) { alpha = part->alpha / (f32) g_SmokeTypes[smoke->type].fadespeed * part->count; } else { alpha = part->alpha; } sp78 = cosf(part->rot) * part->size; sp74 = sinf(part->rot) * part->size; sp70 = part->pos.x + 7.0f * sinf(part->offset1) * g_SmokeTypes[smoke->type].unk20; sp6c = part->pos.y; sp68 = part->pos.z + 7.0f * sinf(part->offset2) * g_SmokeTypes[smoke->type].unk20; sp5c = sp70 - campos->f[0]; sp58 = sp6c - campos->f[1]; sp54 = sp68 - campos->f[2]; distance = sqrtf(sp5c * sp5c + sp58 * sp58 + sp54 * sp54); if (distance > 30000.0f) { return gdl; } range = distance * 0.5f; if (range > 100.0f) { range = 100.0f; } if (distance == 0.0f) { mult = 0.0f; } else { mult = (distance - range) / distance; } sp78 = sp78 * mult; sp74 = sp74 * mult; sp70 = campos->f[0] + sp5c * mult; sp6c = campos->f[1] + sp58 * mult; sp68 = campos->f[2] + sp54 * mult; spa0.f[0] = mtx->m[0][0] * sp78; spa0.f[1] = mtx->m[0][1] * sp78; spa0.f[2] = mtx->m[0][2] * sp78; sp94.f[0] = mtx->m[0][0] * sp74; sp94.f[1] = mtx->m[0][1] * sp74; sp94.f[2] = mtx->m[0][2] * sp74; sp88.f[0] = mtx->m[1][0] * sp78; sp88.f[1] = mtx->m[1][1] * sp78; sp88.f[2] = mtx->m[1][2] * sp78; sp7c.f[0] = mtx->m[1][0] * sp74; sp7c.f[1] = mtx->m[1][1] * sp74; sp7c.f[2] = mtx->m[1][2] * sp74; sp44 = (sp70 - spa0.f[0] - sp7c.f[0]) * size - coord->f[0]; sp40 = (sp6c - spa0.f[1] - sp7c.f[1]) * size - coord->f[1]; sp3c = (sp68 - spa0.f[2] - sp7c.f[2]) * size - coord->f[2]; if (sp44 > 30000.0f || sp44 < -30000.0f || sp40 > 30000.0f || sp40 < -30000.0f || sp3c > 30000.0f || sp3c < -30000.0f) { return gdl; } if (g_Vars.currentplayer->visionmode == VISIONMODE_XRAY) { f32 alphamult = 0.5f; f32 value; distance = sqrtf((sp70 - g_Vars.currentplayer->eraserpos.f[0]) * (sp70 - g_Vars.currentplayer->eraserpos.f[0]) + (sp6c - g_Vars.currentplayer->eraserpos.f[1]) * (sp6c - g_Vars.currentplayer->eraserpos.f[1]) + (sp68 - g_Vars.currentplayer->eraserpos.f[2]) * (sp68 - g_Vars.currentplayer->eraserpos.f[2])); if (distance > g_Vars.currentplayer->eraserpropdist) { return gdl; } value = g_Vars.currentplayer->eraserpropdist - 150.0f; if (value < distance) { alphamult = (1.0f - (distance - value) / 150.0f) * 0.5f; } frac = distance / g_Vars.currentplayer->eraserpropdist; if (frac > 1.0f) { frac = 1.0f; } colours[0].r = frac * 255.0f; colours[0].g = (1.0f - frac) * 255.0f; colours[0].b = 0; colours[0].a = alpha * alphamult; } else { if (smoke->type != SMOKETYPE_PINBALL) { frac = func0f000a10(smoke->prop->rooms[0]) * (1.0f / 255.0f); if (frac > 1) { frac = 1; } colours[0].r = (u32)(g_SmokeTypes[smoke->type].r * frac) & 0xff; colours[0].g = (u32)(g_SmokeTypes[smoke->type].g * frac) & 0xff; colours[0].b = (u32)(g_SmokeTypes[smoke->type].b * frac) & 0xff; } else { colours[0].r = g_SmokeTypes[smoke->type].r; colours[0].g = g_SmokeTypes[smoke->type].g; colours[0].b = g_SmokeTypes[smoke->type].b; } colours[0].a = alpha; } vertices[0].x = sp44; vertices[0].y = sp40; vertices[0].z = sp3c; vertices[0].s = 1760; vertices[0].t = 0; vertices[0].colour = 0; vertices[1].x = (sp70 + sp94.f[0] - sp88.f[0]) * size - coord->f[0]; vertices[1].y = (sp6c + sp94.f[1] - sp88.f[1]) * size - coord->f[1]; vertices[1].z = (sp68 + sp94.f[2] - sp88.f[2]) * size - coord->f[2]; vertices[1].s = 0; vertices[1].t = 0; vertices[1].colour = 0; vertices[2].x = (sp70 + spa0.f[0] + sp7c.f[0]) * size - coord->f[0]; vertices[2].y = (sp6c + spa0.f[1] + sp7c.f[1]) * size - coord->f[1]; vertices[2].z = (sp68 + spa0.f[2] + sp7c.f[2]) * size - coord->f[2]; vertices[2].s = 0; vertices[2].t = 1760; vertices[2].colour = 0; vertices[3].x = (sp70 - sp94.f[0] + sp88.f[0]) * size - coord->f[0]; vertices[3].y = (sp6c - sp94.f[1] + sp88.f[1]) * size - coord->f[1]; vertices[3].z = (sp68 - sp94.f[2] + sp88.f[2]) * size - coord->f[2]; vertices[3].s = 1760; vertices[3].t = 1760; vertices[3].colour = 0; gDPSetColorArray(gdl++, osVirtualToPhysical(colours), 1); gDPSetVerticeArray(gdl++, osVirtualToPhysical(vertices), 4); gDPTri2(gdl++, 0, 1, 2, 0, 2, 3); return gdl; } struct smoke *smokeCreate(struct coord *pos, s16 *rooms, s16 type) { struct smoke *smoke = NULL; s32 playercount = PLAYERCOUNT(); s32 count = 0; s32 i; for (i = 0; i < g_MaxSmokes; i++) { if (g_Smokes[i].prop == NULL) { smoke = &g_Smokes[i]; break; } if (playercount >= 2) { // Multiplayer - clear existing smoke if it's not any of the following if ((g_Smokes + i)->type != SMOKETYPE_BULLETIMPACT && (g_Smokes + i)->type != SMOKETYPE_ROCKETTAIL && (g_Smokes + i)->type != SMOKETYPE_HOMINGTAIL && (g_Smokes + i)->type != SMOKETYPE_GRENADETAIL) { (g_Smokes + i)->age = g_SmokeTypes[(g_Smokes + i)->type].duration; } } else { // 1 player - if creating muzzle smoke, remove the third bullet impact smoke if (type >= SMOKETYPE_MUZZLE_PISTOL && type <= SMOKETYPE_MUZZLE_SHOTGUN) { struct smoke *smoke = g_Smokes + i; if (smoke->type == SMOKETYPE_BULLETIMPACT) { if (count == 3) { smoke->age = g_SmokeTypes[smoke->type].duration; } count++; } } } } if (smoke) { struct prop *prop = propAllocate(); if (prop) { prop->type = PROPTYPE_SMOKE; prop->smoke = smoke; prop->pos.x = pos->x; prop->pos.y = pos->y; prop->pos.z = pos->z; for (i = 0; rooms[i] != -1 && i < 7; i++) { prop->rooms[i] = rooms[i]; } prop->rooms[i] = -1; propActivateThisFrame(prop); propEnable(prop); smoke->prop = prop; smoke->age = 0; smoke->type = type; smoke->source = NULL; } } return smoke; } bool smokeCreateForHand(struct coord *pos, s16 *rooms, s16 type, s32 handnum) { struct smoke *smoke; s32 i; s32 j; for (i = 0; i < g_MaxSmokes; i++) { if (g_Smokes[i].prop && g_Smokes[i].option == handnum && g_Smokes[i].type >= SMOKETYPE_MUZZLE_PISTOL && g_Smokes[i].type <= SMOKETYPE_MUZZLE_SHOTGUN) { bool fail = false; if (g_Smokes[i].age < g_SmokeTypes[g_Smokes[i].type].duration) { for (j = 0; j < 10; j++) { if (g_Smokes[i].parts[j].size == 0) { fail = true; } } if (fail) { return false; } } } } smoke = smokeCreate(pos, rooms, type); if (smoke) { smoke->option = handnum; return true; } return false; } /** * Creates smoke, unless there is already smoke for this prop and any of the * smoke parts have a size of zero. Perhaps the caller is supposed to check if * this function returns false and reuse the zero-sized smoke parts if so? */ bool smokeCreateWithSource(void *source, struct coord *pos, s16 *rooms, s16 type, bool srcispadeffect) { struct smoke *smoke; s32 i; s32 j; bool checksmokes = true; if (type == SMOKETYPE_UFO) { checksmokes = false; } if (checksmokes) { for (i = 0; i < g_MaxSmokes; i++) { if (g_Smokes[i].prop && g_Smokes[i].source == source) { bool fail = false; if (g_Smokes[i].age < g_SmokeTypes[g_Smokes[i].type].duration) { for (j = 0; j < 10; j++) { if (g_Smokes[i].parts[j].size == 0) { fail = true; } } if (fail) { return false; } } } } } smoke = smokeCreate(pos, rooms, type); if (smoke) { smoke->source = source; smoke->option = srcispadeffect; return true; } return false; } void smokeCreateAtProp(struct prop *prop, s16 type) { smokeCreateWithSource(prop, &prop->pos, prop->rooms, type, false); } void smokeCreateAtPadEffect(struct padeffectobj *effect, struct coord *pos, s16 *rooms, s16 type) { smokeCreateWithSource(effect, pos, rooms, type, true); } void smokeClearForProp(struct prop *prop) { s32 i; for (i = 0; i < g_MaxSmokes; i++) { if (g_Smokes[i].prop && g_Smokes[i].source == prop && g_Smokes[i].option == 0) { g_Smokes[i].age = g_SmokeTypes[g_Smokes[i].type].duration; g_Smokes[i].source = NULL; } } } struct smoke *smokeCreateSimple(struct coord *pos, s16 *rooms, s16 type) { return smokeCreate(pos, rooms, type); } u32 smokeTick(struct prop *prop) { s32 i; s32 j; s32 k; s32 lvupdate; struct smoke *smoke; struct smokepart *part; struct coord bbmin; struct coord bbmax; bool free; smoke = prop->smoke; if (g_Vars.lvupdate240 == 0) { return TICKOP_NONE; } // These tick values aren't adjusted for PAL, // so smoke will hang around for longer in PAL versions lvupdate = g_Vars.lvupdate240_60 < 15 ? (f32)g_Vars.lvupdate240_60 : 15.0f; for (i = 0; i < lvupdate; i++) { smoke->age++; part = smoke->parts; for (j = 0; j < 10; j++) { if (part->size != 0.0f) { part->pos.y += g_SmokeTypes[smoke->type].unk1c; part->size += g_SmokeTypes[smoke->type].unk18; if (part->size < 0.0f) { part->size = 0.0f; } part->alpha -= g_SmokeTypes[smoke->type].fgrotatespeed; part->count++; part->rot += part->deltarot; part->offset1 += 0.02f + RANDOMFRAC() * 0.01f; part->offset2 += 0.02f + RANDOMFRAC() * 0.01f; if (part->alpha < 4.0f) { part->size = 0.0f; } } part++; } if (smoke->age < g_SmokeTypes[smoke->type].duration) { if (smoke->age % g_SmokeTypes[smoke->type].spreadspeed == 1) { part = smoke->parts; for (j = 0; j < 10; j++) { if (smoke->parts[j].size == 0.0f) { if (g_SmokeTypes[smoke->type].size == 0) { part->size = (RANDOMFRAC() * 0.5f + 1.0f) * 0.33f; } else { part->size = g_SmokeTypes[smoke->type].size * (RANDOMFRAC() * 0.5f + 1.0f); } part->alpha = (random() % 70) + 110.0f; part->count = 0; part->rot = RANDOMFRAC() * M_BADTAU; part->deltarot = (0.5f - RANDOMFRAC()) * g_SmokeTypes[smoke->type].bgrotatespeed; if (smoke->type >= SMOKETYPE_MUZZLE_PISTOL && smoke->type <= SMOKETYPE_MUZZLE_SHOTGUN) { part->pos.x = g_Vars.currentplayer->hands[smoke->option].muzzlepos.x; part->pos.y = g_Vars.currentplayer->hands[smoke->option].muzzlepos.y; part->pos.z = g_Vars.currentplayer->hands[smoke->option].muzzlepos.z; } else if (smoke->sourceprop && smoke->option == 0) { part->pos.x = smoke->sourceprop->pos.x; part->pos.y = smoke->sourceprop->pos.y; part->pos.z = smoke->sourceprop->pos.z; } else { part->pos.x = prop->pos.x; part->pos.y = prop->pos.y; part->pos.z = prop->pos.z; } if (smoke->type == SMOKETYPE_WATER) { part->pos.x += RANDOMFRAC() * 70.0f - 35.0f; part->pos.y += RANDOMFRAC() * 40.0f - 25.0f; part->pos.z += RANDOMFRAC() * 40.0f - 20.0f; part->alpha *= 0.23f; part->size *= RANDOMFRAC() + 1.0f; } part->offset1 = RANDOMFRAC() * 0.5f; part->offset2 = RANDOMFRAC() * 0.5f; if (smoke->age > g_SmokeTypes[smoke->type].duration - g_SmokeTypes[smoke->type].numclouds) { part->alpha *= (g_SmokeTypes[smoke->type].duration - smoke->age) / (f32)g_SmokeTypes[smoke->type].numclouds; } break; } part++; } } } } bbmin.x = prop->pos.x - 1.0f; bbmin.y = prop->pos.y - 1.0f; bbmin.z = prop->pos.z - 1.0f; bbmax.x = prop->pos.x + 1.0f; bbmax.y = prop->pos.y + 1.0f; bbmax.z = prop->pos.z + 1.0f; for (j = 0; j < 10; j++) { if (smoke->parts[j].size != 0.0f) { for (k = 0; k < 3; k++) { if (bbmin.f[k] > smoke->parts[j].pos.f[k] - smoke->parts[j].size) { bbmin.f[k] = smoke->parts[j].pos.f[k] - smoke->parts[j].size; } if (bbmax.f[k] < smoke->parts[j].size + smoke->parts[j].pos.f[k]) { bbmax.f[k] = smoke->parts[j].size + smoke->parts[j].pos.f[k]; } } } } func0f1650d0(&bbmin, &bbmax, prop->rooms, 7, 0); if (smoke->age > g_SmokeTypes[smoke->type].spreadspeed) { free = true; for (j = 0; j < 10; j++) { if (smoke->parts[j].size > 0.0f) { free = false; break; } } } else { free = false; } if (free) { smoke->prop = NULL; return TICKOP_FREE; } return TICKOP_NONE; } u32 smokeTickPlayer(struct prop *prop) { Mtxf *matrix = camGetMatrix1740(); 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 *smokeRender(struct prop *prop, Gfx *gdl, bool withalpha) { struct smoke *smoke = prop->smoke; s32 roomnum; struct screenbox screenbox; struct coord sp8c; f32 sp88; s32 i; struct coord *coord; struct coord worldoffset; bool near = true; if (!withalpha) { return gdl; } i = 0; roomnum = prop->rooms[i]; while (roomnum != -1) { if (g_Rooms[roomnum].gfxdata && g_Rooms[roomnum].loaded240 && (g_Rooms[roomnum].flags & ROOMFLAG_VISIBLEBYPLAYER)) { break; } i++; roomnum = prop->rooms[i]; } if (roomnum != -1) { coord = room0f166dd0(roomnum); room0f166df0(roomnum, &worldoffset); if (smoke->parts[0].size > 0) { f32 x = smoke->parts[0].pos.x - worldoffset.x; f32 y = smoke->parts[0].pos.y - worldoffset.y; f32 z = smoke->parts[0].pos.z - worldoffset.z; if (x < -2000 || x > 2000 || y < -2000 || y > 2000 || z < -2000 || z > 2000) { near = false; } } if (func0f08e5a8(prop->rooms, &screenbox) > 0) { gdl = currentPlayerScissorWithinViewport(gdl, screenbox.xmin, screenbox.ymin, screenbox.xmax, screenbox.ymax); } else { gdl = currentPlayerScissorToViewport(gdl); } gSPClearGeometryMode(gdl++, G_CULL_BOTH | G_FOG); gSPMatrix(gdl++, osVirtualToPhysical(camGetUnk1758()), G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_PROJECTION); gdl = room0f166d7c(gdl, roomnum); if (near) { gSPMatrix(gdl++, osVirtualToPhysical(&var800a3448), G_MTX_NOPUSH | G_MTX_MUL | G_MTX_MODELVIEW); } else { gSPMatrix(gdl++, osVirtualToPhysical(&var800a3488), G_MTX_NOPUSH | G_MTX_MUL | G_MTX_MODELVIEW); } gSPDisplayList(gdl++, g_TexGdl1); gDPSetColorDither(gdl++, G_CD_NOISE); if (near) { sp8c.x = coord->x * 10.0f; sp8c.y = coord->y * 10.0f; sp8c.z = coord->z * 10.0f; sp88 = 10; } else { sp8c.x = coord->x * 5; sp8c.y = coord->y * 5; sp8c.z = coord->z * 5; sp88 = 5; } for (i = 0; i < ARRAYCOUNT(smoke->parts); i++) { if (smoke->parts[i].size > 0) { gdl = smokeRenderPart(smoke, &smoke->parts[i], gdl, &sp8c, sp88); } else { smoke->parts[i].size = 0; } } gDPSetColorDither(gdl++, G_CD_BAYER); gSPMatrix(gdl++, osVirtualToPhysical(camGetUnk1750()), G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_PROJECTION); } return gdl; } void smokeClearSomeTypes(void) { s32 i; for (i = 0; i < g_MaxSmokes; i++) { if (g_Smokes[i].prop) { struct smoke *smoke = &g_Smokes[i]; if (smoke->type != SMOKETYPE_BULLETIMPACT && smoke->type != SMOKETYPE_ROCKETTAIL && smoke->type != SMOKETYPE_HOMINGTAIL && smoke->type != SMOKETYPE_GRENADETAIL) { smoke->age = g_SmokeTypes[smoke->type].duration; } } } }