diff --git a/patches/actor_transform_tagging.c b/patches/actor_transform_tagging.c index 003bddd..7bc50eb 100644 --- a/patches/actor_transform_tagging.c +++ b/patches/actor_transform_tagging.c @@ -83,7 +83,11 @@ void Actor_Init(Actor* actor, PlayState* play) { Gfx* push_limb_matrix_group(Gfx* dlist, Actor* actor, u32 limb_index) { if (actor != NULL) { u32 cur_transform_id = actor_transform_id(actor); - gEXMatrixGroupDecomposedNormal(dlist++, cur_transform_id + limb_index, G_EX_PUSH, G_MTX_MODELVIEW, G_EX_EDIT_ALLOW); + if (actor_get_interpolation_skipped(actor)) { + gEXMatrixGroupDecomposedSkip(dlist++, cur_transform_id + limb_index, G_EX_PUSH, G_MTX_MODELVIEW, G_EX_EDIT_NONE); + } else { + gEXMatrixGroupDecomposedNormal(dlist++, cur_transform_id + limb_index, G_EX_PUSH, G_MTX_MODELVIEW, G_EX_EDIT_ALLOW); + } } return dlist; } @@ -92,7 +96,11 @@ Gfx* push_limb_matrix_group(Gfx* dlist, Actor* actor, u32 limb_index) { Gfx* push_post_limb_matrix_group(Gfx* dlist, Actor* actor, u32 limb_index) { if (actor != NULL) { u32 cur_transform_id = actor_transform_id(actor); - gEXMatrixGroupDecomposedNormal(dlist++, cur_transform_id + limb_index + ACTOR_TRANSFORM_LIMB_COUNT, G_EX_PUSH, G_MTX_MODELVIEW, G_EX_EDIT_ALLOW); + if (actor_get_interpolation_skipped(actor)) { + gEXMatrixGroupDecomposedSkip(dlist++, cur_transform_id + limb_index, G_EX_PUSH, G_MTX_MODELVIEW, G_EX_EDIT_NONE); + } else { + gEXMatrixGroupDecomposedNormal(dlist++, cur_transform_id + limb_index + ACTOR_TRANSFORM_LIMB_COUNT, G_EX_PUSH, G_MTX_MODELVIEW, G_EX_EDIT_ALLOW); + } } return dlist; } @@ -101,7 +109,11 @@ Gfx* push_post_limb_matrix_group(Gfx* dlist, Actor* actor, u32 limb_index) { Gfx* push_skin_limb_matrix_group(Gfx* dlist, Actor* actor, u32 limb_index) { if (actor != NULL) { u32 cur_transform_id = actor_transform_id(actor); - gEXMatrixGroupDecomposedVerts(dlist++, cur_transform_id + limb_index, G_EX_PUSH, G_MTX_MODELVIEW, G_EX_EDIT_ALLOW); + if (actor_get_interpolation_skipped(actor)) { + gEXMatrixGroupDecomposedSkip(dlist++, cur_transform_id + limb_index, G_EX_PUSH, G_MTX_MODELVIEW, G_EX_EDIT_NONE); + } else { + gEXMatrixGroupDecomposedVerts(dlist++, cur_transform_id + limb_index, G_EX_PUSH, G_MTX_MODELVIEW, G_EX_EDIT_ALLOW); + } } return dlist; } @@ -1257,5 +1269,8 @@ void Actor_Draw(PlayState* play, Actor* actor) { } actor->isDrawn = true; + // @recomp Clear the actor's interpolation skipped flag. + actor_clear_interpolation_skipped(actor); + CLOSE_DISPS(play->state.gfxCtx); } diff --git a/patches/dummy_headers/assets/objects/object_fall2/object_fall2.h b/patches/dummy_headers/assets/objects/object_fall2/object_fall2.h new file mode 100644 index 0000000..a2023d3 --- /dev/null +++ b/patches/dummy_headers/assets/objects/object_fall2/object_fall2.h @@ -0,0 +1,31 @@ +// Required to include MM decomp headers without having built the repo + +#ifndef OBJECT_FALL2_H +#define OBJECT_FALL2_H 1 + +extern Vtx object_fall2Vtx_000000[]; +extern Gfx gOpenMouthMoonDL[]; +extern u64 gOpenMouthMoonFarSideTLUT[]; +extern u64 gOpenMouthMoonFaceTLUT[]; +extern u64 gOpenMouthMoonEyesTex[]; +extern u64 gOpenMouthMoonFarSideTex[]; +extern u64 gOpenMouthMoonFaceTex[]; +extern u64 gOpenMouthMoonTeethTex[]; +extern u8 object_fall2_Blob_005EF4[]; +extern Vtx object_fall2_Vtx_005F10[]; +extern Gfx object_fall2_DL_006E00[]; +extern Gfx object_fall2_DL_006EF0[]; +extern Gfx object_fall2_DL_006FF8[]; +extern Gfx object_fall2_DL_007100[]; +extern Gfx object_fall2_DL_007208[]; +extern Gfx object_fall2_DL_007310[]; +extern Gfx object_fall2_DL_007418[]; +extern Gfx object_fall2_DL_007520[]; +extern Gfx object_fall2_DL_007628[]; +extern Gfx object_fall2_DL_007730[]; +extern u64 object_fall2_Tex_007838[]; +extern u64 object_fall2_Tex_008038[]; +extern AnimatedMatTexScrollParams object_fall2_Matanimheader_008840TexScrollParams_008838[]; +extern AnimatedMaterial object_fall2_Matanimheader_008840[]; +extern u8 object_fall2_Blob_008898[]; +#endif diff --git a/patches/particle_transform_tagging.c b/patches/particle_transform_tagging.c index a0cbb3f..b2483a5 100644 --- a/patches/particle_transform_tagging.c +++ b/patches/particle_transform_tagging.c @@ -1,5 +1,6 @@ #include "patches.h" #include "transform_ids.h" +#include "overlays/actors/ovl_En_Hanabi/z_en_hanabi.h" extern EffectSsInfo sEffectSsInfo; @@ -90,3 +91,86 @@ void EffectSS_DrawParticle(PlayState* play, s32 index) { 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(); +} diff --git a/patches/specific_actor_transform_tagging.c b/patches/specific_actor_transform_tagging.c index a91dfa5..8bb3607 100644 --- a/patches/specific_actor_transform_tagging.c +++ b/patches/specific_actor_transform_tagging.c @@ -5,6 +5,8 @@ #include "overlays/actors/ovl_Boss_04/z_boss_04.h" #include "overlays/actors/ovl_Boss_Hakugin/z_boss_hakugin.h" #include "overlays/actors/ovl_En_Water_Effect/z_en_water_effect.h" +#include "overlays/actors/ovl_En_Osn/z_en_osn.h" +#include "overlays/actors/ovl_En_Fall2/z_en_fall2.h" extern EnTanron2* D_80BB8458[82]; extern Boss04* D_80BB8450; @@ -684,3 +686,233 @@ void func_80B0C398(BossHakugin* this, PlayState* play) { CLOSE_DISPS(play->state.gfxCtx); } + +// @recomp Skip interpolation for the Happy Mask Salesman when his animation changes. +extern AnimationInfo sHappyMaskSalesmanAnimationInfo[]; + +typedef enum { + /* 0 */ OSN_ANIM_IDLE, + /* 1 */ OSN_ANIM_ARMS_OUT, + /* 2 */ OSN_ANIM_BOWING, + /* 3 */ OSN_ANIM_REMINISCE, + /* 4 */ OSN_ANIM_HANDS_CLASPED, + /* 5 */ OSN_ANIM_BELIEVE, + /* 6 */ OSN_ANIM_THINK, + /* 7 */ OSN_ANIM_SHAKE_HEAD, + /* 8 */ OSN_ANIM_ORGAN_TALK, + /* 9 */ OSN_ANIM_ORGAN_PLAY, + /* 10 */ OSN_ANIM_SHAKE, + /* 11 */ OSN_ANIM_CHOKE, + /* 12 */ OSN_ANIM_DESPAIR, + /* 13 */ OSN_ANIM_FAST_BOWS, + /* 14 */ OSN_ANIM_HAND_OUT, + /* 15 */ OSN_ANIM_LYING_DOWN_FACE_UP, + /* 16 */ OSN_ANIM_LYING_DOWN_FACE_DOWN, + /* 17 */ OSN_ANIM_MASK_LOOK_AT, + /* 18 */ OSN_ANIM_TURN_AROUND_START, + /* 19 */ OSN_ANIM_TURN_AROUND_LOOP, + /* 20 */ OSN_ANIM_WALK_AWAY, + /* 21 */ OSN_ANIM_MASK_LOOK_FROM_START, + /* 22 */ OSN_ANIM_MASK_LOOK_FROM_LOOP, + /* 23 */ OSN_ANIM_HAND_OUT_2, // Exact same as OSN_ANIM_HAND_OUT + /* 24 */ OSN_ANIM_WALK_AWAY_END, // Only the last frame of OSN_ANIM_WALK_AWAY + /* 25 */ OSN_ANIM_MAX +} OsnAnimation; + +void EnOsn_HandleCsAction(EnOsn* this, PlayState* play); +void EnOsn_Idle(EnOsn* this, PlayState* play); + +// @recomp Patched to skip interpolation when the Happy Mask Salesman changes animations. +void EnOsn_ChooseAction(EnOsn* this, PlayState* play) { + // @recomp Manually relocate the static symbol. + AnimationInfo* sAnimationInfo = (AnimationInfo*)actor_relocate(&this->actor, sHappyMaskSalesmanAnimationInfo); + + u32 isSwitchFlagSet = Flags_GetSwitch(play, 0); + + this->csId = this->actor.csId; + + Actor_ChangeAnimationByInfo(&this->skelAnime, sAnimationInfo, OSN_ANIM_IDLE); + if (!isSwitchFlagSet) { + // @recomp Manual relocation, TODO remove when automated by the recompiler. + this->actionFunc = (EnOsnActionFunc)actor_relocate(&this->actor, EnOsn_HandleCsAction); + } else { + // @recomp Manual relocation, TODO remove when automated by the recompiler. + this->actionFunc = (EnOsnActionFunc)actor_relocate(&this->actor, EnOsn_Idle); + } + + // @recomp Skip interpolation this frame. + actor_set_interpolation_skipped(&this->actor); +} + +void EnOsn_TurnAround(EnOsn* this); +void EnOsn_LookFromMask(EnOsn* this); +void EnOsn_FadeOut(EnOsn* this); + +// @recomp Patched to skip interpolation when the Happy Mask Salesman changes animations. +void EnOsn_HandleCsAction(EnOsn* this, PlayState* play) { + u8 pad; + s32 cueChannel; + + if (Cutscene_IsCueInChannel(play, CS_CMD_ACTOR_CUE_130)) { + cueChannel = Cutscene_GetCueChannel(play, CS_CMD_ACTOR_CUE_130); + this->shouldRotateHead = false; + if (this->cueId != play->csCtx.actorCues[cueChannel]->id) { + this->cueId = play->csCtx.actorCues[cueChannel]->id; + switch (play->csCtx.actorCues[cueChannel]->id) { + case 1: + this->animIndex = OSN_ANIM_BOWING; + break; + + case 2: + this->animIndex = OSN_ANIM_ARMS_OUT; + break; + + case 3: + this->animIndex = OSN_ANIM_SHAKE_HEAD; + break; + + case 4: + this->animIndex = OSN_ANIM_REMINISCE; + break; + + case 5: + this->animIndex = OSN_ANIM_THINK; + break; + + case 6: + this->animIndex = OSN_ANIM_BELIEVE; + break; + + case 7: + this->animIndex = OSN_ANIM_HANDS_CLASPED; + break; + + case 8: + this->animIndex = OSN_ANIM_IDLE; + break; + + case 10: + this->animIndex = OSN_ANIM_ORGAN_TALK; + break; + + case 11: + this->animIndex = OSN_ANIM_ORGAN_PLAY; + break; + + case 13: + this->animIndex = OSN_ANIM_SHAKE; + break; + + case 15: + this->animIndex = OSN_ANIM_CHOKE; + break; + + case 16: + this->animIndex = OSN_ANIM_DESPAIR; + break; + + case 17: + this->animIndex = OSN_ANIM_FAST_BOWS; + break; + + case 18: + this->animIndex = OSN_ANIM_HAND_OUT; + break; + + case 19: + this->animIndex = OSN_ANIM_MASK_LOOK_AT; + break; + + case 20: + this->animIndex = OSN_ANIM_TURN_AROUND_START; + break; + + case 21: + this->animIndex = OSN_ANIM_WALK_AWAY; + break; + + case 22: + this->animIndex = OSN_ANIM_MASK_LOOK_FROM_START; + break; + + case 23: + this->animIndex = OSN_ANIM_HAND_OUT_2; + break; + + case 24: + this->animIndex = OSN_ANIM_WALK_AWAY_END; + break; + + default: + this->animIndex = OSN_ANIM_IDLE; + break; + } + // @recomp Manually relocate the static symbol. + AnimationInfo* sAnimationInfo = (AnimationInfo*)actor_relocate(&this->actor, sHappyMaskSalesmanAnimationInfo); + + Actor_ChangeAnimationByInfo(&this->skelAnime, sAnimationInfo, this->animIndex); + + // @recomp Skip interpolation this frame. + actor_set_interpolation_skipped(&this->actor); + + } + + if ((this->animIndex == OSN_ANIM_BELIEVE) && (play->sceneId == SCENE_SPOT00) && + (gSaveContext.sceneLayer == 0xB) && (play->csCtx.curFrame == 400)) { + Actor_PlaySfx(&this->actor, NA_SE_VO_OMVO00); + } + + if (this->animIndex == OSN_ANIM_TURN_AROUND_START) { + EnOsn_TurnAround(this); + } + + if (this->animIndex == OSN_ANIM_MASK_LOOK_FROM_START) { + EnOsn_LookFromMask(this); + } + + if (this->animIndex == OSN_ANIM_WALK_AWAY_END) { + EnOsn_FadeOut(this); + } + + if ((this->animIndex == OSN_ANIM_WALK_AWAY) && + (Animation_OnFrame(&this->skelAnime, 17.0f) || Animation_OnFrame(&this->skelAnime, 27.0f) || + Animation_OnFrame(&this->skelAnime, 37.0f) || Animation_OnFrame(&this->skelAnime, 47.0f) || + Animation_OnFrame(&this->skelAnime, 57.0f) || Animation_OnFrame(&this->skelAnime, 67.0f))) { + Actor_PlaySfx(&this->actor, NA_SE_EV_OMENYA_WALK); + } + Cutscene_ActorTranslateAndYaw(&this->actor, play, cueChannel); + } else { + this->shouldRotateHead = true; + this->cueId = 99; + EnOsn_ChooseAction(this, play); + } +} + +// @recomp Patched to tag this actor's draws using linear order matching. +void EnFall2_Draw(Actor* thisx, PlayState* play) { + s32 pad; + EnFall2* this = (EnFall2*)thisx; + Mtx* mtx; + + if (!(this->alphaLevel <= 0.0f)) { + Gfx_SetupDL25_Xlu(play->state.gfxCtx); + AnimatedMat_DrawXlu(play, Lib_SegmentedToVirtual(object_fall2_Matanimheader_008840)); + + mtx = GRAPH_ALLOC(play->state.gfxCtx, this->skeletonInfo.unk_18->unk_1 * sizeof(Mtx)); + + if (mtx != NULL) { + Gfx_SetupDL25_Xlu(play->state.gfxCtx); + Matrix_RotateYS((s16)(Camera_GetCamDirYaw(GET_ACTIVE_CAM(play)) + 0x8000), MTXMODE_APPLY); + + // @recomp Tag this actor's matrices. + OPEN_DISPS(play->state.gfxCtx); + gEXMatrixGroupDecomposedNormal(POLY_XLU_DISP++, actor_transform_id(thisx), G_EX_PUSH, G_MTX_MODELVIEW, G_EX_EDIT_NONE); + + func_8018450C(play, &this->skeletonInfo, mtx, NULL, NULL, &this->actor); + + // @recomp Pop the matrix group. + gEXPopMatrixGroup(POLY_XLU_DISP++, G_MTX_MODELVIEW); + CLOSE_DISPS(play->state.gfxCtx); + } + } +} diff --git a/patches/syms.ld b/patches/syms.ld index c9fd76d..e51375e 100644 --- a/patches/syms.ld +++ b/patches/syms.ld @@ -3,6 +3,7 @@ __start = 0x80000000; /* Static symbols that aren't in the elf */ sSceneEntranceTable = 0x801C5720; D_808DE5B0 = 0x808DE5B0; +sHappyMaskSalesmanAnimationInfo = 0x80AD22C0; /* Dummy addresses that get recompiled into function calls */ recomp_puts = 0x8F000000; diff --git a/patches/transform_ids.h b/patches/transform_ids.h index 867e0f6..8bcf12b 100644 --- a/patches/transform_ids.h +++ b/patches/transform_ids.h @@ -28,7 +28,7 @@ #define EFFECT_SHIELD_PARTICLE_TRANSFORM_ID_START (EFFECT_BLURE_TRANSFORM_ID_START + 2 * EFFECT_TRANSFORM_ID_COUNT) #define EFFECT_TIRE_MARK_TRANSFORM_ID_START (EFFECT_SHIELD_PARTICLE_TRANSFORM_ID_START + 2 * EFFECT_TRANSFORM_ID_COUNT) -#define ACTOR_TRANSFORM_LIMB_COUNT 128 +#define ACTOR_TRANSFORM_LIMB_COUNT 256 #define ACTOR_TRANSFORM_ID_COUNT (ACTOR_TRANSFORM_LIMB_COUNT * 2) // One ID for each limb and another for each post-draw #define ACTOR_TRANSFORM_ID_START 0x1000000U @@ -37,9 +37,10 @@ #define actorIdByte0(actor) ((u8*)(actor))[0x22] // 0x23 between halfDaysBits and world #define actorIdByte1(actor) ((u8*)(actor))[0x23] +// 0x3A between audioFlags and focus +#define actorIdByte2(actor) ((u8*)(actor))[0x3A] // Other unused padding: -// 0x3A between audioFlags and focus // 0x3B between audioFlags and focus static inline u32 actor_transform_id(Actor* actor) { @@ -50,6 +51,22 @@ static inline u32 actor_transform_id(Actor* actor) { return (actor_id * ACTOR_TRANSFORM_ID_COUNT) + ACTOR_TRANSFORM_ID_START; } +typedef enum { + ACTOR_TRANSFORM_FLAG_INTERPOLATION_SKIPPED = 1 << 0, +} ActorTransformFlags; + +static inline u32 actor_get_interpolation_skipped(Actor* actor) { + return (actorIdByte2(actor) & ACTOR_TRANSFORM_FLAG_INTERPOLATION_SKIPPED) != 0; +} + +static inline void actor_set_interpolation_skipped(Actor* actor) { + actorIdByte2(actor) |= ACTOR_TRANSFORM_FLAG_INTERPOLATION_SKIPPED; +} + +static inline void actor_clear_interpolation_skipped(Actor* actor) { + actorIdByte2(actor) &= ~ACTOR_TRANSFORM_FLAG_INTERPOLATION_SKIPPED; +} + void force_camera_interpolation(); void force_camera_skip_interpolation();