Zelda64Recomp/patches/particle_transform_tagging.c

532 lines
22 KiB
C

#include "patches.h"
#include "transform_ids.h"
#include "overlays/actors/ovl_En_Hanabi/z_en_hanabi.h"
#include "overlays/actors/ovl_Demo_Kankyo/z_demo_kankyo.h"
#include "overlays/actors/ovl_En_Water_Effect/z_en_water_effect.h"
extern EffectSsInfo sEffectSsInfo;
#define MAX_PARTICLES 256
u8 particle_reset_list[MAX_PARTICLES];
// @recomp Patched to track that the particle has been reset.
void EffectSS_ResetEntry(EffectSs* particle) {
u32 i;
particle->type = EFFECT_SS_MAX;
particle->accel.x = particle->accel.y = particle->accel.z = 0;
particle->velocity.x = particle->velocity.y = particle->velocity.z = 0;
particle->vec.x = particle->vec.y = particle->vec.z = 0;
particle->pos.x = particle->pos.y = particle->pos.z = 0;
particle->life = -1;
particle->flags = 0;
particle->priority = 128;
particle->draw = NULL;
particle->update = NULL;
particle->gfx = NULL;
particle->actor = NULL;
for (i = 0; i < ARRAY_COUNT(particle->regs); i++) {
particle->regs[i] = 0;
}
// @recomp Get this particle's index and mark it as being reset.
u32 particle_index = particle - &sEffectSsInfo.dataTable[0];
if (particle_index >= sEffectSsInfo.size) {
recomp_crash("Invalid particle was reset!\n");
}
particle_reset_list[particle_index] = true;
}
// @recomp Check numEntries to be sure enough space has been allocated for tracking particle statuses.
void EffectSS_Init(PlayState* play, s32 numEntries) {
u32 i;
EffectSs* effectsSs;
EffectSsOverlay* overlay;
// @recomp Perform the numEntries check.
if (numEntries > MAX_PARTICLES) {
recomp_crash("Particle reset list too small!\n");
}
sEffectSsInfo.dataTable = (EffectSs*)THA_AllocTailAlign16(&play->state.tha, numEntries * sizeof(EffectSs));
sEffectSsInfo.searchIndex = 0;
sEffectSsInfo.size = numEntries;
for (effectsSs = &sEffectSsInfo.dataTable[0]; effectsSs < &sEffectSsInfo.dataTable[sEffectSsInfo.size];
effectsSs++) {
EffectSS_ResetEntry(effectsSs);
}
overlay = &gParticleOverlayTable[0];
for (i = 0; i < EFFECT_SS_MAX; i++) {
overlay->loadedRamAddr = NULL;
overlay++;
}
}
// @recomp Add transform tags to particles
void EffectSS_DrawParticle(PlayState* play, s32 index) {
EffectSs* entry = &sEffectSsInfo.dataTable[index];
OPEN_DISPS(play->state.gfxCtx);
// @recomp If this particle was just reset then skip interpolation.
if (particle_reset_list[index]) {
gEXMatrixGroupDecomposedSkipAll(POLY_OPA_DISP++, PARTICLE_TRANSFORM_ID_START + index, G_EX_PUSH, G_MTX_MODELVIEW, G_EX_EDIT_NONE);
gEXMatrixGroupDecomposedSkipAll(POLY_XLU_DISP++, PARTICLE_TRANSFORM_ID_START + index, G_EX_PUSH, G_MTX_MODELVIEW, G_EX_EDIT_NONE);
}
else {
gEXMatrixGroupDecomposedNormal(POLY_OPA_DISP++, PARTICLE_TRANSFORM_ID_START + index, G_EX_PUSH, G_MTX_MODELVIEW, G_EX_EDIT_ALLOW);
gEXMatrixGroupDecomposedNormal(POLY_XLU_DISP++, PARTICLE_TRANSFORM_ID_START + index, G_EX_PUSH, G_MTX_MODELVIEW, G_EX_EDIT_ALLOW);
}
// @recomp Clear this particle's reset state.
particle_reset_list[index] = false;
if (entry->draw != NULL) {
entry->draw(play, index, entry);
}
gEXPopMatrixGroup(POLY_OPA_DISP++, G_MTX_MODELVIEW);
gEXPopMatrixGroup(POLY_XLU_DISP++, G_MTX_MODELVIEW);
CLOSE_DISPS(play->state.gfxCtx);
}
extern u64 gSun1Tex[];
extern Gfx gSunSparkleMaterialDL[];
extern Gfx gSunSparkleModelDL[];
extern u8 D_80B23C40[];
extern u8 D_80B23C2C[];
// @recomp Modified to take the actor as an argument for relocation and to tag firework transforms.
void func_80B22FA8_patched(Actor* thisx, EnHanabiStruct* arg0, PlayState* play2) {
PlayState* play = play2;
GraphicsContext* gfxCtx = play->state.gfxCtx;
s32 i;
u8 sp53;
OPEN_DISPS(gfxCtx);
Gfx_SetupDL25_Xlu(play->state.gfxCtx);
POLY_XLU_DISP = Gfx_SetupDL(POLY_XLU_DISP, SETUPDL_20);
gSPSegment(POLY_XLU_DISP++, 0x08, Lib_SegmentedToVirtual(gSun1Tex));
gSPDisplayList(POLY_XLU_DISP++, gSunSparkleMaterialDL);
sp53 = 0xFF;
// @recomp Manually relocate, TODO remove when automated by recompiler.
u8* D_80B23C40_relocated = (u8*)actor_relocate(thisx, D_80B23C40);
u8* D_80B23C2C_relocated = (u8*)actor_relocate(thisx, D_80B23C2C);
for (i = 0; i < 400; i++, arg0++) {
if (arg0->unk_00 != 1) {
continue;
}
Matrix_Translate(arg0->unk_08.x, arg0->unk_08.y, arg0->unk_08.z, MTXMODE_NEW);
Matrix_ReplaceRotation(&play->billboardMtxF);
if (arg0->unk_01 < 40) {
Matrix_Scale(arg0->unk_04 * 0.025f * arg0->unk_01, arg0->unk_04 * 0.025f * arg0->unk_01, 1.0f,
MTXMODE_APPLY);
} else {
Matrix_Scale(arg0->unk_04, arg0->unk_04, 1.0f, MTXMODE_APPLY);
}
Matrix_RotateZS(play->gameplayFrames * 4864, MTXMODE_APPLY);
// @recomp Tag the matrix.
gEXMatrixGroupDecomposedNormal(POLY_XLU_DISP++, actor_transform_id(thisx) + i, G_EX_PUSH, G_MTX_MODELVIEW, G_EX_EDIT_ALLOW);
gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(play->state.gfxCtx), G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW);
if (sp53 != arg0->unk_02) {
gDPPipeSync(POLY_XLU_DISP++);
gDPSetEnvColor(POLY_XLU_DISP++, D_80B23C40_relocated[arg0->unk_02], D_80B23C40_relocated[arg0->unk_02 + 1],
D_80B23C40_relocated[arg0->unk_02 + 2], 255);
sp53 = arg0->unk_02;
}
if (arg0->unk_01 < 6) {
gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, D_80B23C2C_relocated[arg0->unk_02], D_80B23C2C_relocated[arg0->unk_02 + 1],
D_80B23C2C_relocated[arg0->unk_02 + 2], arg0->unk_01 * 50);
} else {
gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, D_80B23C2C_relocated[arg0->unk_02], D_80B23C2C_relocated[arg0->unk_02 + 1],
D_80B23C2C_relocated[arg0->unk_02 + 2], 255);
}
gSPDisplayList(POLY_XLU_DISP++, gSunSparkleModelDL);
// @recomp Pop the matrix group.
gEXPopMatrixGroup(POLY_XLU_DISP++, G_MTX_MODELVIEW);
}
CLOSE_DISPS(gfxCtx);
}
// @recomp Patched to call a custom version of a vanilla function.
void EnHanabi_Draw(Actor* thisx, PlayState* play) {
EnHanabi* this = (EnHanabi*)thisx;
Matrix_Push();
// @recomp Call a modified version of the function that takes the actor for relocation purposes.
func_80B22FA8_patched(thisx, this->unk_148, play);
Matrix_Pop();
}
Vec3f kankyo_prev_pos_base[DEMOKANKYO_EFFECT_COUNT] = {0};
// @recomp Patched to draw the lost woods particles outside the 4:3 region and tag their transforms.
void DemoKakyo_DrawLostWoodsSparkle(Actor* thisx, PlayState* play2) {
PlayState* play = play2;
DemoKankyo* this = (DemoKankyo*)thisx;
s16 i;
f32 scaleAlpha;
Vec3f worldPos;
Vec3f screenPos;
// if not underwater
if (!(play->cameraPtrs[CAM_ID_MAIN]->stateFlags & CAM_STATE_UNDERWATER)) {
OPEN_DISPS(play->state.gfxCtx);
POLY_XLU_DISP = Gfx_SetupDL(POLY_XLU_DISP, SETUPDL_20);
gSPSegment(POLY_XLU_DISP++, 0x08, Lib_SegmentedToVirtual(gSun1Tex));
gSPDisplayList(POLY_XLU_DISP++, gSunSparkleMaterialDL);
for (i = 0; i < play->envCtx.precipitation[PRECIP_SNOW_MAX]; i++) {
worldPos.x = this->effects[i].posBase.x + this->effects[i].posOffset.x;
worldPos.y = this->effects[i].posBase.y + this->effects[i].posOffset.y;
worldPos.z = this->effects[i].posBase.z + this->effects[i].posOffset.z;
// @recomp Render the particle regardless of its position on screen.
// Play_GetScreenPos(play, &worldPos, &screenPos);
// // checking if particle is on screen
// if ((screenPos.x >= 0.0f) && (screenPos.x < SCREEN_WIDTH) && (screenPos.y >= 0.0f) &&
// (screenPos.y < SCREEN_HEIGHT)) {
if (true) {
Matrix_Translate(worldPos.x, worldPos.y, worldPos.z, MTXMODE_NEW);
scaleAlpha = this->effects[i].alpha / 50.0f;
if (scaleAlpha > 1.0f) {
scaleAlpha = 1.0f;
}
Matrix_Scale(this->effects[i].scale * scaleAlpha, this->effects[i].scale * scaleAlpha,
this->effects[i].scale * scaleAlpha, MTXMODE_APPLY);
// adjust transparency of this particle
if (i < 32) {
// Skyfish particles
if (this->effects[i].state != DEMO_KANKYO_STATE_SKYFISH) {
// still initializing
if (this->effects[i].alpha > 0) { // NOT DECR
this->effects[i].alpha--;
}
} else if (this->effects[i].alpha < 100) {
this->effects[i].alpha++;
}
} else if (this->effects[i].state != DEMO_KANKYO_STATE_SKYFISH) {
if ((this->effects[i].alphaClock & 31) < 16) {
if (this->effects[i].alpha < 235) {
this->effects[i].alpha += 20;
}
} else if (this->effects[i].alpha > 20) {
this->effects[i].alpha -= 20;
}
} else if ((this->effects[i].alphaClock & 15) < 8) {
if (this->effects[i].alpha < 255) {
this->effects[i].alpha += 100;
}
} else if (this->effects[i].alpha > 10) {
this->effects[i].alpha -= 10;
}
gDPPipeSync(POLY_XLU_DISP++);
switch (i & 1) {
case 0: // gold particles
gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 255, 255, 155, this->effects[i].alpha);
gDPSetEnvColor(POLY_XLU_DISP++, 250, 180, 0, this->effects[i].alpha);
break;
case 1: // silver particles
gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 255, 255, 255, this->effects[i].alpha);
gDPSetEnvColor(POLY_XLU_DISP++, 0, 100, 255, this->effects[i].alpha);
break;
}
Matrix_Mult(&play->billboardMtxF, MTXMODE_APPLY);
Matrix_RotateZF(DEG_TO_RAD(play->state.frames * 20.0f), MTXMODE_APPLY);
// @recomp Tag the particle's matrix. Skip if the particle's base position changed.
if (this->effects[i].state == DEMO_KANKYO_STATE_SKYFISH || (
kankyo_prev_pos_base[i].x == this->effects[i].posBase.x &&
kankyo_prev_pos_base[i].y == this->effects[i].posBase.y &&
kankyo_prev_pos_base[i].z == this->effects[i].posBase.z)) {
gEXMatrixGroupDecomposedNormal(POLY_XLU_DISP++, actor_transform_id(thisx) + i, G_EX_PUSH, G_MTX_MODELVIEW, G_EX_EDIT_ALLOW);
}
else {
gEXMatrixGroupDecomposedSkipAll(POLY_XLU_DISP++, actor_transform_id(thisx) + i, G_EX_PUSH, G_MTX_MODELVIEW, G_EX_EDIT_NONE);
}
gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(play->state.gfxCtx),
G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW);
gSPDisplayList(POLY_XLU_DISP++, gSunSparkleModelDL);
// @recomp Pop the matrix tag and record the particle's current base position.
gEXPopMatrixGroup(POLY_XLU_DISP++, G_MTX_MODELVIEW);
kankyo_prev_pos_base[i] = this->effects[i].posBase;
}
}
CLOSE_DISPS(play->state.gfxCtx);
}
}
extern Gfx gLightOrbMaterial1DL[];
extern Gfx gBubbleDL[];
extern Gfx gLightOrbModelDL[];
// @recomp Patched to draw the lost woods particles outside the 4:3 region and tag their transforms.
void DemoKankyo_DrawMoonAndGiant(Actor* thisx, PlayState* play2) {
PlayState* play = play2;
DemoKankyo* this = (DemoKankyo*)thisx;
s16 i;
f32 alphaScale;
if (this->isSafeToDrawGiants != false) {
Vec3f worldPos;
Vec3f screenPos;
s32 pad;
GraphicsContext* gfxCtx = play->state.gfxCtx;
OPEN_DISPS(gfxCtx);
Gfx_SetupDL25_Xlu(gfxCtx);
for (i = 0; i < play->envCtx.precipitation[PRECIP_SNOW_MAX]; i++) {
worldPos.x = this->effects[i].posBase.x + this->effects[i].posOffset.x;
worldPos.y = this->effects[i].posBase.y + this->effects[i].posOffset.y;
worldPos.z = this->effects[i].posBase.z + this->effects[i].posOffset.z;
// @recomp Render the particle regardless of its position on screen.
// Play_GetScreenPos(play, &worldPos, &screenPos);
// checking if effect is on screen
// if ((screenPos.x >= 0.0f) && (screenPos.x < SCREEN_WIDTH) && (screenPos.y >= 0.0f) &&
// (screenPos.y < SCREEN_HEIGHT)) {
if (true) {
Matrix_Translate(worldPos.x, worldPos.y, worldPos.z, MTXMODE_NEW);
alphaScale = this->effects[i].alpha / 50.0f;
if (alphaScale > 1.0f) {
alphaScale = 1.0f;
}
Matrix_Scale(this->effects[i].scale * alphaScale, this->effects[i].scale * alphaScale,
this->effects[i].scale * alphaScale, MTXMODE_APPLY);
alphaScale = Math_Vec3f_DistXYZ(&worldPos, &play->view.eye) / 300.0f;
alphaScale = CLAMP(1.0f - alphaScale, 0.0f, 1.0f);
if (this->actor.params == DEMO_KANKYO_TYPE_GIANTS) {
this->effects[i].alpha = 255.0f * alphaScale;
} else {
this->effects[i].alpha = 160.0f * alphaScale;
}
gDPPipeSync(POLY_XLU_DISP++);
switch (i & 1) { // half/half slightly different shades of yellow/tan
case 0:
gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 230, 230, 220, this->effects[i].alpha);
gDPSetEnvColor(POLY_XLU_DISP++, 230, 230, 30, this->effects[i].alpha);
break;
case 1:
gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 200, 200, 190, this->effects[i].alpha);
gDPSetEnvColor(POLY_XLU_DISP++, 200, 200, 30, this->effects[i].alpha);
break;
}
gSPDisplayList(POLY_XLU_DISP++, gLightOrbMaterial1DL);
Matrix_Mult(&play->billboardMtxF, MTXMODE_APPLY);
Matrix_RotateZF(DEG_TO_RAD(play->state.frames * 20.0f), MTXMODE_APPLY);
gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(play->state.gfxCtx),
G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW);
// @recomp Tag the particle's matrix. Skip if the particle's base position changed.
// Allow the Y base position to change if this is a moon particle.
if (kankyo_prev_pos_base[i].x == this->effects[i].posBase.x &&
(kankyo_prev_pos_base[i].y == this->effects[i].posBase.y || this->actor.params == DEMO_KANKYO_TYPE_MOON) &&
kankyo_prev_pos_base[i].z == this->effects[i].posBase.z) {
gEXMatrixGroupDecomposedNormal(POLY_XLU_DISP++, actor_transform_id(thisx) + i, G_EX_PUSH, G_MTX_MODELVIEW, G_EX_EDIT_ALLOW);
}
else {
gEXMatrixGroupDecomposedSkipAll(POLY_XLU_DISP++, actor_transform_id(thisx) + i, G_EX_PUSH, G_MTX_MODELVIEW, G_EX_EDIT_NONE);
}
if (this->actor.params == DEMO_KANKYO_TYPE_GIANTS) {
gSPDisplayList(POLY_XLU_DISP++, gBubbleDL);
} else {
gSPDisplayList(POLY_XLU_DISP++, gLightOrbModelDL);
}
// @recomp Pop the matrix tag and record the particle's current base position.
gEXPopMatrixGroup(POLY_XLU_DISP++, G_MTX_MODELVIEW);
kankyo_prev_pos_base[i] = this->effects[i].posBase;
}
}
CLOSE_DISPS(gfxCtx);
}
}
static Vec3f D_80A5AFB0 = { 0.0f, 0.0f, 0.0f };
static Vec3f D_80A5AFBC = { 0.0f, -1.0f, 0.0f };
// The byte after unk_01 in EnWaterEffectStruct is unused, so we'll use it as a respawn flag.
#define WATER_EFFECT_RESPAWNED(ptr) (&(ptr)->unk_01)[1]
// @recomp Mark respawned water effect particles so they can be skipped for the first frame.
void func_80A599E8(EnWaterEffect* this, Vec3f* arg1, u8 arg2) {
s16 i;
EnWaterEffectStruct* ptr = &this->unk_144[0];
for (i = 0; i < ARRAY_COUNT(this->unk_144); i++, ptr++) {
if (ptr->unk_00 == 0) {
ptr->unk_00 = 4;
// @recomp Use an unused byte to mark that this particle was respawned.
WATER_EFFECT_RESPAWNED(ptr) = true;
ptr->unk_04 = *arg1;
ptr->unk_1C = D_80A5AFB0;
ptr->unk_10 = D_80A5AFB0;
if ((arg2 == 0) || (arg2 == 2)) {
ptr->unk_1C.y = -1.0f;
}
if (arg2 >= 2) {
if (arg2 == 2) {
ptr->unk_10.x = Rand_CenteredFloat(10.0f);
ptr->unk_10.y = Rand_ZeroFloat(3.0f) + 5.0f;
ptr->unk_10.z = Rand_CenteredFloat(10.0f);
}
ptr->unk_2C.z = 0.0017f;
ptr->unk_2C.x = 0.003f;
ptr->unk_2C.y = 0.0018f;
} else {
ptr->unk_2C.z = 0.003f;
ptr->unk_2C.x = 0.003f;
ptr->unk_2C.y = 0.003f;
}
ptr->unk_38 = 255.0f;
ptr->unk_28 = Rand_ZeroFloat(0x10000);
ptr->unk_3C = 255;
ptr->unk_01 = Rand_ZeroFloat(200.0f);
ptr->unk_2A = arg2;
break;
}
}
}
extern Gfx object_water_effect_DL_004340[];
extern Gfx object_water_effect_DL_0043E8[];
extern Gfx gameplay_keep_DL_06AB30[];
// @recomp Tag the transforms for falling fire rocks.
void func_80A5A184(Actor* thisx, PlayState* play2) {
PlayState* play = play2;
EnWaterEffect* this = (EnWaterEffect*)thisx;
GraphicsContext* gfxCtx = play->state.gfxCtx;
EnWaterEffectStruct* ptr = &this->unk_144[0];
u8 flag = false;
s16 i;
OPEN_DISPS(gfxCtx);
Gfx_SetupDL25_Opa(play->state.gfxCtx);
Gfx_SetupDL25_Xlu(play->state.gfxCtx);
for (i = 0; i < ARRAY_COUNT(this->unk_144); i++, ptr++) {
if (ptr->unk_00 == 4) {
if (!flag) {
gSPDisplayList(POLY_XLU_DISP++, object_water_effect_DL_004340);
gDPSetEnvColor(POLY_XLU_DISP++, 255, 10, 0, 0);
POLY_OPA_DISP = Gfx_SetFog(POLY_OPA_DISP, 255, 0, 0, 255, 500, 3600);
flag++;
}
gDPSetPrimColor(POLY_XLU_DISP++, 0x80, 0x80, (u8)ptr->unk_38, 0, 0, (u8)ptr->unk_3C);
gSPSegment(POLY_XLU_DISP++, 0x08,
Gfx_TwoTexScroll(play->state.gfxCtx, 0, 0, 0, 0x20, 0x40, 1, 0, (ptr->unk_01 * -20) & 0x1FF,
0x20, 0x80));
Matrix_Translate(ptr->unk_04.x, ptr->unk_04.y, ptr->unk_04.z, MTXMODE_NEW);
if (ptr->unk_2A >= 2) {
Matrix_ReplaceRotation(&play->billboardMtxF);
} else {
Matrix_RotateYS(Camera_GetInputDirYaw(GET_ACTIVE_CAM(play)), MTXMODE_APPLY);
}
Matrix_Scale(ptr->unk_2C.x, ptr->unk_2C.y, 1.0f, MTXMODE_APPLY);
if ((i & 1) != 0) {
Matrix_RotateYF(M_PI, MTXMODE_APPLY);
}
gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(play->state.gfxCtx), G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW);
// @recomp Tag the matrix group. Skip all interpolation if this particle just respawned. Otherwise,
// skip rotation interpolation if there was a camera cut since this is a billboard.
if (WATER_EFFECT_RESPAWNED(ptr)) {
gEXMatrixGroupDecomposedSkipAll(POLY_XLU_DISP++, actor_transform_id(thisx) + (i * 2) + 0, G_EX_PUSH, G_MTX_MODELVIEW, G_EX_EDIT_NONE);
}
else if (camera_was_skipped()) {
gEXMatrixGroupDecomposedSkipRot(POLY_XLU_DISP++, actor_transform_id(thisx) + (i * 2) + 0, G_EX_PUSH, G_MTX_MODELVIEW, G_EX_EDIT_NONE);
}
else {
gEXMatrixGroupDecomposedNormal(POLY_XLU_DISP++, actor_transform_id(thisx) + (i * 2) + 0, G_EX_PUSH, G_MTX_MODELVIEW, G_EX_EDIT_NONE);
}
gSPDisplayList(POLY_XLU_DISP++, object_water_effect_DL_0043E8);
// @recomp Pop the matrix group.
gEXPopMatrixGroup(POLY_XLU_DISP++, G_MTX_MODELVIEW);
if ((ptr->unk_2A & 1) == 0) {
Matrix_Translate(ptr->unk_04.x, ptr->unk_04.y + 5.0f, ptr->unk_04.z, MTXMODE_NEW);
Matrix_RotateXS(ptr->unk_28, MTXMODE_APPLY);
Matrix_Scale(ptr->unk_2C.z, ptr->unk_2C.z, ptr->unk_2C.z, MTXMODE_APPLY);
gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(play->state.gfxCtx),
G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW);
// @recomp Tag the matrix group. Skip all interpolation if the base particle just respawned.
if (WATER_EFFECT_RESPAWNED(ptr)) {
gEXMatrixGroupDecomposedSkipAll(POLY_OPA_DISP++, actor_transform_id(thisx) + (i * 2) + 1, G_EX_PUSH, G_MTX_MODELVIEW, G_EX_EDIT_ALLOW);
}
else {
gEXMatrixGroupDecomposedNormal(POLY_OPA_DISP++, actor_transform_id(thisx) + (i * 2) + 1, G_EX_PUSH, G_MTX_MODELVIEW, G_EX_EDIT_ALLOW);
}
gSPDisplayList(POLY_OPA_DISP++, gameplay_keep_DL_06AB30);
// @recomp Pop the matrix group.
gEXPopMatrixGroup(POLY_OPA_DISP++, G_MTX_MODELVIEW);
}
// @recomp Clear the respawned state of this particle.
WATER_EFFECT_RESPAWNED(ptr) = false;
}
}
POLY_OPA_DISP = Play_SetFog(play, POLY_OPA_DISP);
CLOSE_DISPS(gfxCtx);
}