From d49228691d247d96ce1902415ea08d5dffe7b238 Mon Sep 17 00:00:00 2001 From: Garrett Cox Date: Sun, 16 Feb 2025 22:51:04 -0600 Subject: [PATCH] Add actor update/init events and save init event (#536) --- patches/actor_transform_tagging.c | 122 +++++++++++++++++++++++++++++- patches/save_patches.c | 73 ++++++++++++++++++ 2 files changed, 193 insertions(+), 2 deletions(-) diff --git a/patches/actor_transform_tagging.c b/patches/actor_transform_tagging.c index 2484db0..7e5e8b6 100644 --- a/patches/actor_transform_tagging.c +++ b/patches/actor_transform_tagging.c @@ -1,6 +1,7 @@ #include "patches.h" #include "fault.h" #include "transform_ids.h" +#include "z64actor.h" u16 next_actor_transform = 0; @@ -47,6 +48,11 @@ u32 create_actor_transform_id() { return ret; } +RECOMP_DECLARE_EVENT(recomp_should_actor_init(PlayState* play, Actor* actor, bool* should)); +RECOMP_DECLARE_EVENT(recomp_after_actor_init(PlayState* play, Actor* actor)); +RECOMP_DECLARE_EVENT(recomp_should_actor_update(PlayState* play, Actor* actor, bool* should)); +RECOMP_DECLARE_EVENT(recomp_after_actor_update(PlayState* play, Actor* actor)); + RECOMP_PATCH void Actor_Init(Actor* actor, PlayState* play) { Actor_SetWorldToHome(actor); Actor_SetShapeRotToWorld(actor); @@ -69,8 +75,18 @@ RECOMP_PATCH void Actor_Init(Actor* actor, PlayState* play) { ActorShape_Init(&actor->shape, 0.0f, NULL, 0.0f); if (Object_IsLoaded(&play->objectCtx, actor->objectSlot)) { Actor_SetObjectDependency(play, actor); - actor->init(actor, play); - actor->init = NULL; + + // @recomp Augmented, allowing us to prevent actor init and hook into after init + bool shouldInit = true; + recomp_should_actor_init(play, actor, &shouldInit); + if (shouldInit) { + actor->init(actor, play); + actor->init = NULL; + recomp_after_actor_init(play, actor); + } else { + actor->init = NULL; + Actor_Kill(actor); + } } // @recomp Pick a transform ID for this actor and encode it into struct padding @@ -79,6 +95,108 @@ RECOMP_PATCH void Actor_Init(Actor* actor, PlayState* play) { actorIdByte1(actor) = (cur_transform_id >> 8) & 0xFF;; } +// @recomp Copied from z_actor.c +typedef struct { + /* 0x00 */ PlayState* play; + /* 0x04 */ Actor* actor; + /* 0x08 */ u32 freezeExceptionFlag; + /* 0x0C */ u32 canFreezeCategory; + /* 0x10 */ Actor* talkActor; + /* 0x14 */ Player* player; + /* 0x18 */ u32 updateActorFlagsMask; // Actor will update only if at least 1 actor flag is set in this bitmask +} UpdateActor_Params; // size = 0x1C + +void Actor_Destroy(Actor* actor, PlayState* play); + +RECOMP_PATCH Actor* Actor_UpdateActor(UpdateActor_Params* params) { + PlayState* play = params->play; + Actor* actor = params->actor; + Actor* nextActor; + + if (actor->world.pos.y < -25000.0f) { + actor->world.pos.y = -25000.0f; + } + + actor->sfxId = 0; + actor->audioFlags &= ~(((1 << 4) | (1 << 3) | (1 << 2) | (1 << 1) | (1 << 0)) | ((1 << 6) | (1 << 5))); // ACTOR_AUDIO_FLAG_ALL + + if (actor->init != NULL) { + if (Object_IsLoaded(&play->objectCtx, actor->objectSlot)) { + Actor_SetObjectDependency(play, actor); + + // @recomp Augmented, allowing us to prevent actor init and hook into after init + bool shouldInit = true; + recomp_should_actor_init(play, actor, &shouldInit); + if (shouldInit) { + actor->init(actor, play); + actor->init = NULL; + recomp_after_actor_init(play, actor); + } else { + actor->init = NULL; + Actor_Kill(actor); + } + } + nextActor = actor->next; + } else if (actor->update == NULL) { + if (!actor->isDrawn) { + nextActor = Actor_Delete(&play->actorCtx, actor, play); + } else { + Actor_Destroy(actor, play); + nextActor = actor->next; + } + } else { + if (!Object_IsLoaded(&play->objectCtx, actor->objectSlot)) { + Actor_Kill(actor); + } else if (((params->freezeExceptionFlag != 0) && !(actor->flags & params->freezeExceptionFlag)) || + (((!params->freezeExceptionFlag) != 0) && + (!(actor->flags & (1 << 20)) || + ((actor->category == ACTORCAT_EXPLOSIVES) && (params->player->stateFlags1 & PLAYER_STATE1_200))) && + params->canFreezeCategory && (actor != params->talkActor) && (actor != params->player->heldActor) && + (actor->parent != ¶ms->player->actor))) { + CollisionCheck_ResetDamage(&actor->colChkInfo); + } else { + Math_Vec3f_Copy(&actor->prevPos, &actor->world.pos); + actor->xzDistToPlayer = Actor_WorldDistXZToActor(actor, ¶ms->player->actor); + actor->playerHeightRel = Actor_HeightDiff(actor, ¶ms->player->actor); + actor->xyzDistToPlayerSq = SQ(actor->xzDistToPlayer) + SQ(actor->playerHeightRel); + + actor->yawTowardsPlayer = Actor_WorldYawTowardActor(actor, ¶ms->player->actor); + actor->flags &= ~(1 << 24); + + if ((DECR(actor->freezeTimer) == 0) && (actor->flags & params->updateActorFlagsMask)) { + if (actor == params->player->lockOnActor) { + actor->isLockedOn = true; + } else { + actor->isLockedOn = false; + } + + if ((actor->targetPriority != 0) && (params->player->lockOnActor == NULL)) { + actor->targetPriority = 0; + } + + Actor_SetObjectDependency(play, actor); + + if (actor->colorFilterTimer != 0) { + actor->colorFilterTimer--; + } + + // @recomp Augmented, allowing us to prevent actor update and hook into after update + bool shouldUpdate = true; + recomp_should_actor_update(play, actor, &shouldUpdate); + if (shouldUpdate) { + actor->update(actor, play); + recomp_after_actor_update(play, actor); + } + DynaPoly_UnsetAllInteractFlags(play, &play->colCtx.dyna, actor); + } + + CollisionCheck_ResetDamage(&actor->colChkInfo); + } + nextActor = actor->next; + } + return nextActor; +} + // Extract the transform ID for this actor, add the limb index and write that as the matrix group to POLY_OPA_DISP. Gfx* push_limb_matrix_group(Gfx* dlist, Actor* actor, u32 limb_index) { if (actor != NULL) { diff --git a/patches/save_patches.c b/patches/save_patches.c index 75644f5..5eeba3a 100644 --- a/patches/save_patches.c +++ b/patches/save_patches.c @@ -2,10 +2,14 @@ #include "sys_flashrom.h" #include "PR/os_internal_flash.h" #include "fault.h" +#include "overlays/gamestates/ovl_file_choose/z_file_select.h" +#include "overlays/kaleido_scope/ovl_kaleido_scope/z_kaleido_scope.h" extern OSMesgQueue sFlashromMesgQueue; s32 SysFlashrom_IsInit(void); void Sleep_Msec(u32 ms); +extern u16 D_801F6AF0; +extern u8 D_801F6AF2; // @recomp Patched to not wait a hardcoded amount of time for the save to complete. RECOMP_PATCH void Sram_UpdateWriteToFlashDefault(SramContext* sramCtx) { @@ -56,3 +60,72 @@ RECOMP_PATCH void Sram_UpdateWriteToFlashOwlSave(SramContext* sramCtx) { Lib_MemCpy(&gSaveContext, sramCtx->saveBuf, offsetof(SaveContext, fileNum)); } } + +RECOMP_DECLARE_EVENT(recomp_after_init_save(FileSelectState* fileSelect, SramContext* sramCtx)); + +// @recomp Patched to expose recomp_on_save_init event +RECOMP_PATCH void Sram_InitSave(FileSelectState* fileSelect2, SramContext* sramCtx) { + s32 phi_v0; + u16 i; + FileSelectState* fileSelect = fileSelect2; + s16 maskCount; + + if (gSaveContext.flashSaveAvailable) { + Sram_InitNewSave(); + if (fileSelect->buttonIndex == 0) { + gSaveContext.save.cutsceneIndex = 0xFFF0; + } + + for (phi_v0 = 0; phi_v0 < ARRAY_COUNT(gSaveContext.save.saveInfo.playerData.playerName); phi_v0++) { + gSaveContext.save.saveInfo.playerData.playerName[phi_v0] = + fileSelect->fileNames[fileSelect->buttonIndex][phi_v0]; + } + + gSaveContext.save.saveInfo.playerData.newf[0] = 'Z'; + gSaveContext.save.saveInfo.playerData.newf[1] = 'E'; + gSaveContext.save.saveInfo.playerData.newf[2] = 'L'; + gSaveContext.save.saveInfo.playerData.newf[3] = 'D'; + gSaveContext.save.saveInfo.playerData.newf[4] = 'A'; + gSaveContext.save.saveInfo.playerData.newf[5] = '3'; + + recomp_after_init_save(fileSelect, sramCtx); + + gSaveContext.save.saveInfo.checksum = Sram_CalcChecksum(&gSaveContext.save, sizeof(Save)); + + Lib_MemCpy(sramCtx->saveBuf, &gSaveContext.save, sizeof(Save)); + Lib_MemCpy(&sramCtx->saveBuf[0x2000], &gSaveContext.save, sizeof(Save)); + + for (i = 0; i < ARRAY_COUNT(gSaveContext.save.saveInfo.playerData.newf); i++) { + fileSelect->newf[fileSelect->buttonIndex][i] = gSaveContext.save.saveInfo.playerData.newf[i]; + } + + fileSelect->threeDayResetCount[fileSelect->buttonIndex] = + gSaveContext.save.saveInfo.playerData.threeDayResetCount; + + for (i = 0; i < ARRAY_COUNT(gSaveContext.save.saveInfo.playerData.playerName); i++) { + fileSelect->fileNames[fileSelect->buttonIndex][i] = gSaveContext.save.saveInfo.playerData.playerName[i]; + } + + fileSelect->healthCapacity[fileSelect->buttonIndex] = gSaveContext.save.saveInfo.playerData.healthCapacity; + fileSelect->health[fileSelect->buttonIndex] = gSaveContext.save.saveInfo.playerData.health; + fileSelect->defenseHearts[fileSelect->buttonIndex] = gSaveContext.save.saveInfo.inventory.defenseHearts; + fileSelect->questItems[fileSelect->buttonIndex] = gSaveContext.save.saveInfo.inventory.questItems; + fileSelect->time[fileSelect->buttonIndex] = CURRENT_TIME; + fileSelect->day[fileSelect->buttonIndex] = gSaveContext.save.day; + fileSelect->isOwlSave[fileSelect->buttonIndex] = gSaveContext.save.isOwlSave; + fileSelect->rupees[fileSelect->buttonIndex] = gSaveContext.save.saveInfo.playerData.rupees; + fileSelect->walletUpgrades[fileSelect->buttonIndex] = CUR_UPG_VALUE(UPG_WALLET); + + for (i = 0, maskCount = 0; i < MASK_NUM_SLOTS; i++) { + if (gSaveContext.save.saveInfo.inventory.items[i + ITEM_NUM_SLOTS] != ITEM_NONE) { + maskCount++; + } + } + + fileSelect->maskCount[fileSelect->buttonIndex] = maskCount; + fileSelect->heartPieceCount[fileSelect->buttonIndex] = GET_QUEST_HEART_PIECE_COUNT; + } + + gSaveContext.save.time = D_801F6AF0; + gSaveContext.flashSaveAvailable = D_801F6AF2; +}