diff --git a/soh/include/z64player.h b/soh/include/z64player.h index e369f88983c..a9c466a1dbc 100644 --- a/soh/include/z64player.h +++ b/soh/include/z64player.h @@ -845,7 +845,7 @@ typedef struct Player { /* 0x06A4 */ f32 closestSecretDistSq; /* 0x06A8 */ Actor* unk_6A8; /* 0x06AC */ s8 idleType; - /* 0x06AD */ u8 unk_6AD; + /* 0x06AD */ u8 unk_6AD; // Camera related. 0 = normal, 1 = first person without weapon, 2 = first person with weapon, 3 = cutscene action, 4 = cutscene items /* 0x06AE */ u16 unk_6AE_rotFlags; // See `UNK6AE_ROT_` macros. If its flag isn't set, a rot steps to 0. /* 0x06B0 */ s16 upperLimbYawSecondary; /* 0x06B2 */ char unk_6B4[0x004]; diff --git a/soh/soh/Enhancements/AlwaysOnFixes.cpp b/soh/soh/Enhancements/AlwaysOnFixes.cpp index 7303bedef40..1fa98543639 100644 --- a/soh/soh/Enhancements/AlwaysOnFixes.cpp +++ b/soh/soh/Enhancements/AlwaysOnFixes.cpp @@ -73,6 +73,15 @@ void RegisterAlwaysOnFixes() { } }); + // Dismounting a ladder in a cutscene or using cutscene item (using restricted items glitch) i.e. + // `player->unk_6AD` == 3 or 4 softlocks as animation update stops. Let animation continue in that case. + COND_VB_SHOULD(VB_INTERRUPT_LADDER_DISMOUNT, true, { + u8* unk_6AD = va_arg(args, u8*); + if (*unk_6AD >= 3) { + *should = false; + } + }); + COND_VB_SHOULD(VB_PREVENT_HBA_FANFARE_SOFTLOCK_TIMER, true, { EnHorse* enHorse = va_arg(args, EnHorse*); if (enHorse->hbaFlags & 1) { diff --git a/soh/soh/Enhancements/game-interactor/vanilla-behavior/GIVanillaBehavior.h b/soh/soh/Enhancements/game-interactor/vanilla-behavior/GIVanillaBehavior.h index f37b91f2ec2..d953bb1a75e 100644 --- a/soh/soh/Enhancements/game-interactor/vanilla-behavior/GIVanillaBehavior.h +++ b/soh/soh/Enhancements/game-interactor/vanilla-behavior/GIVanillaBehavior.h @@ -3270,6 +3270,14 @@ typedef enum { // - `*EnItem00` VB_ITEM00_KILL, + // #### `result` + // ```c + // interruptResult == PLAYER_INTERRUPT_NEW_ACTION + // ``` + // #### `args` + // - `*u8 (&player->unk_6AD)` + VB_INTERRUPT_LADDER_DISMOUNT, + // #### `result` // ```c // true diff --git a/soh/src/overlays/actors/ovl_player_actor/z_player.c b/soh/src/overlays/actors/ovl_player_actor/z_player.c index 7ba300b6329..14366029f8e 100644 --- a/soh/src/overlays/actors/ovl_player_actor/z_player.c +++ b/soh/src/overlays/actors/ovl_player_actor/z_player.c @@ -300,7 +300,7 @@ void Player_Action_8084B9E4(Player* this, PlayState* play); void Player_Action_8084BBE4(Player* this, PlayState* play); void Player_Action_8084BDFC(Player* this, PlayState* play); void Player_Action_8084BF1C(Player* this, PlayState* play); -void Player_Action_8084C5F8(Player* this, PlayState* play); +void Player_Action_DismountLadder(Player* this, PlayState* play); void Player_Action_8084C760(Player* this, PlayState* play); void Player_Action_8084C81C(Player* this, PlayState* play); void Player_Action_8084CC98(Player* this, PlayState* play); @@ -7597,8 +7597,8 @@ s32 func_8083EC18(Player* this, PlayState* play, u32 wallFlags) { return false; } -void func_8083F070(Player* this, LinkAnimationHeader* anim, PlayState* play) { - Player_SetupActionPreserveAnimMovement(play, this, Player_Action_8084C5F8, 0); +void Player_SetupDismountLadder(Player* this, LinkAnimationHeader* anim, PlayState* play) { + Player_SetupActionPreserveAnimMovement(play, this, Player_Action_DismountLadder, 0); LinkAnimation_PlayOnceSetSpeed(play, &this->skelAnime, anim, (4.0f / 3.0f)); } @@ -13233,7 +13233,7 @@ void Player_Action_8084BF1C(Player* this, PlayState* play) { func_8083A9B8(this, &gPlayerAnim_link_normal_jump_climb_up_free, play); this->stateFlags1 |= PLAYER_STATE1_CLIMBING_LEDGE; } else { - func_8083F070(this, this->ageProperties->unk_CC[this->av2.actionVar2], play); + Player_SetupDismountLadder(this, this->ageProperties->unk_CC[this->av2.actionVar2], play); } } else { this->skelAnime.prevTransl = this->ageProperties->unk_4A[sp68]; @@ -13247,7 +13247,7 @@ void Player_Action_8084BF1C(Player* this, PlayState* play) { if (this->av2.actionVar2 != 0) { this->skelAnime.prevTransl = this->ageProperties->unk_44; } - func_8083F070(this, this->ageProperties->unk_C4[this->av2.actionVar2], play); + Player_SetupDismountLadder(this, this->ageProperties->unk_C4[this->av2.actionVar2], play); this->av2.actionVar2 = 1; } } else { @@ -13296,50 +13296,56 @@ void Player_Action_8084BF1C(Player* this, PlayState* play) { } } -static f32 D_80854898[] = { 10.0f, 20.0f }; -static f32 D_808548A0[] = { 40.0f, 50.0f }; +static f32 sUpDismountLadderFrames[] = { 10.0f, 20.0f }; +static f32 sDownDismountLadderFrames[] = { 40.0f, 50.0f }; -static AnimSfxEntry D_808548A8[] = { +static AnimSfxEntry sDownDismountLadderAnimSfx[] = { { NA_SE_PL_WALK_LADDER, ANIMSFX_DATA(ANIMSFX_TYPE_GENERAL, 10) }, { NA_SE_PL_WALK_LADDER, ANIMSFX_DATA(ANIMSFX_TYPE_GENERAL, 20) }, { NA_SE_PL_WALK_LADDER, -ANIMSFX_DATA(ANIMSFX_TYPE_GENERAL, 30) }, }; -void Player_Action_8084C5F8(Player* this, PlayState* play) { - s32 temp; - f32* sp38; - CollisionPoly* sp34; - s32 sp30; - Vec3f sp24; +/** + * Dismounting ladders, both upwards and downwards. + * `actionVar2` true if dismounting down + */ +void Player_Action_DismountLadder(Player* this, PlayState* play) { + s32 interruptResult; + f32* frame; + CollisionPoly* groundPoly; + s32 bgId; + Vec3f raycastPos; this->stateFlags2 |= PLAYER_STATE2_DISABLE_ROTATION_ALWAYS; - temp = Player_TryActionInterrupt(play, this, &this->skelAnime, 4.0f); + interruptResult = Player_TryActionInterrupt(play, this, &this->skelAnime, 4.0f); - if (temp == 0) { + // SoH: Prevent ladder cutscene softlock + if (GameInteractor_Should(VB_INTERRUPT_LADDER_DISMOUNT, interruptResult == PLAYER_INTERRUPT_NEW_ACTION, + &this->unk_6AD)) { this->stateFlags1 &= ~PLAYER_STATE1_CLIMBING_LADDER; return; } - if ((temp > 0) || LinkAnimation_Update(play, &this->skelAnime)) { + if ((interruptResult > PLAYER_INTERRUPT_NEW_ACTION) || LinkAnimation_Update(play, &this->skelAnime)) { func_8083C0E8(this, play); this->stateFlags1 &= ~PLAYER_STATE1_CLIMBING_LADDER; return; } - sp38 = D_80854898; + frame = sUpDismountLadderFrames; - if (this->av2.actionVar2 != 0) { - Player_ProcessAnimSfxList(this, D_808548A8); - sp38 = D_808548A0; + if (this->av2.actionVar2 != false) { + Player_ProcessAnimSfxList(this, sDownDismountLadderAnimSfx); + frame = sDownDismountLadderFrames; } - if (LinkAnimation_OnFrame(&this->skelAnime, sp38[0]) || LinkAnimation_OnFrame(&this->skelAnime, sp38[1])) { - sp24.x = this->actor.world.pos.x; - sp24.y = this->actor.world.pos.y + 20.0f; - sp24.z = this->actor.world.pos.z; - if (BgCheck_EntityRaycastFloor3(&play->colCtx, &sp34, &sp30, &sp24) != 0.0f) { - this->floorSfxOffset = func_80041F10(&play->colCtx, sp34, sp30); + if (LinkAnimation_OnFrame(&this->skelAnime, frame[0]) || LinkAnimation_OnFrame(&this->skelAnime, frame[1])) { + raycastPos.x = this->actor.world.pos.x; + raycastPos.y = this->actor.world.pos.y + 20.0f; + raycastPos.z = this->actor.world.pos.z; + if (BgCheck_EntityRaycastFloor3(&play->colCtx, &groundPoly, &bgId, &raycastPos) != 0.0f) { + this->floorSfxOffset = func_80041F10(&play->colCtx, groundPoly, bgId); Player_PlayLandingSfx(this); } }