From 694d3b396558a4d9faa3f9d91354ab484d6affa9 Mon Sep 17 00:00:00 2001 From: engineer124 <47598039+engineer124@users.noreply.github.com> Date: Wed, 23 Oct 2024 11:26:19 +1100 Subject: [PATCH] turn in place (#1728) --- include/z64player.h | 4 +- .../actors/ovl_player_actor/z_player.c | 74 ++++++++++++------- tools/disasm/functions.txt | 4 +- tools/disasm/variables.txt | 2 +- 4 files changed, 52 insertions(+), 32 deletions(-) diff --git a/include/z64player.h b/include/z64player.h index df40fc28c4..41d317bc13 100644 --- a/include/z64player.h +++ b/include/z64player.h @@ -1240,7 +1240,7 @@ typedef struct Player { /* 0xAC8 */ f32 skelAnimeUpperBlendWeight; /* 0xACC */ s16 unk_ACC; /* 0xACE */ s8 unk_ACE; - /* 0xACF */ u8 putAwayCountdown; // Frames to wait before showing "Put Away" on A + /* 0xACF */ u8 putAwayCooldownTimer; // Frames to wait before showing "Put Away" on A /* 0xAD0 */ f32 speedXZ; // Controls horizontal speed, used for `actor.speed`. Current or target value depending on context. /* 0xAD4 */ s16 yaw; // General yaw value, used both for world and shape rotation. Current or target value depending on context. /* 0xAD6 */ s16 parallelYaw; // yaw in "parallel" mode, Z-Target without an actor lock-on @@ -1280,7 +1280,7 @@ typedef struct Player { /* 0xB44 */ f32 unk_B44; /* 0xB48 */ f32 unk_B48; /* 0xB4C */ s16 unk_B4C; - /* 0xB4E */ s16 unk_B4E; + /* 0xB4E */ s16 turnRate; // Amount angle is changed every frame when turning in place /* 0xB50 */ f32 unk_B50; /* 0xB54 */ f32 yDistToLedge; // y distance to ground above an interact wall. LEDGE_DIST_MAX if no ground if found /* 0xB58 */ f32 distToInteractWall; // xyz distance to the interact wall diff --git a/src/overlays/actors/ovl_player_actor/z_player.c b/src/overlays/actors/ovl_player_actor/z_player.c index 59a8565210..46ab25a0cd 100644 --- a/src/overlays/actors/ovl_player_actor/z_player.c +++ b/src/overlays/actors/ovl_player_actor/z_player.c @@ -123,7 +123,7 @@ void Player_Action_6(Player* this, PlayState* play); void Player_Action_7(Player* this, PlayState* play); void Player_Action_8(Player* this, PlayState* play); void Player_Action_9(Player* this, PlayState* play); -void Player_Action_10(Player* this, PlayState* play); +void Player_Action_TurnInPlace(Player* this, PlayState* play); void Player_Action_11(Player* this, PlayState* play); void Player_Action_12(Player* this, PlayState* play); void Player_Action_13(Player* this, PlayState* play); @@ -5194,7 +5194,7 @@ s8 sActionHandlerList5[] = { /* 8 */ -PLAYER_ACTION_HANDLER_7, }; -s8 sActionHandlerList6[] = { +s8 sActionHandlerListTurnInPlace[] = { /* 0 */ -PLAYER_ACTION_HANDLER_7, }; @@ -8238,7 +8238,7 @@ s32 Player_ActionHandler_6(Player* this, PlayState* play) { return true; } - if ((this->putAwayCountdown == 0) && (this->heldItemAction >= PLAYER_IA_SWORD_KOKIRI) && + if ((this->putAwayCooldownTimer == 0) && (this->heldItemAction >= PLAYER_IA_SWORD_KOKIRI) && (this->transformation != PLAYER_FORM_FIERCE_DEITY)) { Player_UseItem(play, this, ITEM_NONE); } else { @@ -8383,8 +8383,8 @@ void func_8083A794(Player* this, PlayState* play) { Player_SetAction(play, this, func_8082FBE8(this) ? Player_Action_14 : Player_Action_13, 1); } -void func_8083A844(Player* this, PlayState* play, s16 currentYaw) { - this->yaw = currentYaw; +void func_8083A844(Player* this, PlayState* play, s16 yaw) { + this->yaw = yaw; this->actor.shape.rot.y = this->yaw; func_8083A794(this, play); } @@ -8560,12 +8560,12 @@ void Player_InitMode_F(PlayState* play, Player* this) { } } -void func_8083AECC(Player* this, s16 currentYaw, PlayState* play) { +void func_8083AECC(Player* this, s16 yaw, PlayState* play) { Player_SetAction(play, this, Player_Action_6, 1); PlayerAnimation_CopyJointToMorph(play, &this->skelAnime); this->unk_B38 = 0.0f; this->unk_B34 = 0.0f; - this->yaw = currentYaw; + this->yaw = yaw; } void func_8083AF30(Player* this, PlayState* play) { @@ -8573,12 +8573,12 @@ void func_8083AF30(Player* this, PlayState* play) { Player_Anim_PlayLoopMorph(play, this, D_8085BE84[PLAYER_ANIMGROUP_walk][this->modelAnimType]); } -void func_8083AF8C(Player* this, s16 currentYaw, PlayState* play) { +void func_8083AF8C(Player* this, s16 yaw, PlayState* play) { Player_SetAction(play, this, Player_Action_15, 1); PlayerAnimation_Change(play, &this->skelAnime, &gPlayerAnim_link_anchor_back_walk, PLAYER_ANIM_NORMAL_SPEED, 0.0f, Animation_GetLastFrame(&gPlayerAnim_link_anchor_back_walk), ANIMMODE_ONCE, -6.0f); this->speedXZ = 8.0f; - this->yaw = currentYaw; + this->yaw = yaw; } void func_8083B030(Player* this, PlayState* play) { @@ -8592,11 +8592,14 @@ void func_8083B090(Player* this, PlayState* play) { PlayerAnimation_PlayOnceSetSpeed(play, &this->skelAnime, &gPlayerAnim_link_anchor_back_brake, 6.0f / 3.0f); } -void func_8083B0E4(PlayState* play, Player* this, s16 currentYaw) { - this->yaw = currentYaw; - Player_SetAction(play, this, Player_Action_10, 1); - this->unk_B4E = 1200; - this->unk_B4E *= sWaterSpeedFactor; +void Player_SetupTurnInPlace(PlayState* play, Player* this, s16 yaw) { + this->yaw = yaw; + + Player_SetAction(play, this, Player_Action_TurnInPlace, 1); + + this->turnRate = 0x4B0; + this->turnRate *= sWaterSpeedFactor; // slow turn rate by half when in water + PlayerAnimation_Change(play, &this->skelAnime, D_8085BE84[PLAYER_ANIMGROUP_45_turn][this->modelAnimType], PLAYER_ANIM_NORMAL_SPEED, 0.0f, 0.0f, ANIMMODE_LOOP, -6.0f); } @@ -8723,11 +8726,11 @@ s32 func_8083B3B4(PlayState* play, Player* this, Input* input) { return false; } -void func_8083B73C(PlayState* play, Player* this, s16 currentYaw) { +void func_8083B73C(PlayState* play, Player* this, s16 yaw) { Player_SetAction(play, this, Player_Action_57, 0); Player_Anim_PlayLoopSlowMorph(play, this, &gPlayerAnim_link_swimer_swim); - this->actor.shape.rot.y = currentYaw; - this->yaw = currentYaw; + this->actor.shape.rot.y = yaw; + this->yaw = yaw; } void func_8083B798(PlayState* play, Player* this) { @@ -8870,7 +8873,7 @@ void func_8083BB4C(PlayState* play, Player* this) { (((Player_Action_56 != this->actionFunc) && !(this->stateFlags3 & PLAYER_STATE3_8000)) || (this->actor.bgCheckFlags & BGCHECKFLAG_GROUND))) { if (this->skelAnime.moveFlags == 0) { - func_8083B0E4(play, this, this->actor.shape.rot.y); + Player_SetupTurnInPlace(play, this, this->actor.shape.rot.y); } func_8083B32C(play, this, this->actor.velocity.y); } @@ -11421,10 +11424,12 @@ void Player_SetDoAction(PlayState* play, Player* this) { } if (doActionA != DO_ACTION_PUTAWAY) { - this->putAwayCountdown = 20; - } else if (this->putAwayCountdown != 0) { + this->putAwayCooldownTimer = 20; + } else if (this->putAwayCooldownTimer != 0) { + // Replace the "Put Away" Do Action label with a blank label while + // the cooldown timer is counting down doActionA = DO_ACTION_NONE; - this->putAwayCountdown--; + this->putAwayCooldownTimer--; } Interface_SetAButtonDoAction(play, doActionA); @@ -14157,7 +14162,7 @@ void Player_Action_3(Player* this, PlayState* play) { s16 temp_v0_2 = yawTarget - this->actor.shape.rot.y; if (ABS_ALT(temp_v0_2) > 0x320) { - func_8083B0E4(play, this, yawTarget); + Player_SetupTurnInPlace(play, this, yawTarget); } } } @@ -14228,7 +14233,7 @@ void Player_Action_Idle(Player* this, PlayState* play) { yawDiff = yawTarget - this->actor.shape.rot.y; if (ABS_ALT(yawDiff) > 0x320) { - func_8083B0E4(play, this, yawTarget); + Player_SetupTurnInPlace(play, this, yawTarget); } else { Math_ScaledStepToS(&this->actor.shape.rot.y, yawTarget, 0x4B0); this->yaw = this->actor.shape.rot.y; @@ -14455,7 +14460,14 @@ void Player_Action_9(Player* this, PlayState* play) { } } -void Player_Action_10(Player* this, PlayState* play) { +/** + * Turn in place until the angle pointed to by the control stick is reached. + * + * This is the state that the speedrunning community refers to as "ESS" or "ESS Position". + * See the bug comment below and https://www.zeldaspeedruns.com/mm/tech/ess-and-hess + * for more information. + */ +void Player_Action_TurnInPlace(Player* this, PlayState* play) { f32 speedTarget; s16 yawTarget; @@ -14469,18 +14481,26 @@ void Player_Action_10(Player* this, PlayState* play) { Player_GetMovementSpeedAndYaw(this, &speedTarget, &yawTarget, SPEED_MODE_CURVED, play); + //! @bug This action does not handle xzSpeed in any capacity. + //! Player's current speed value will be maintained the entire time this action is running. + //! This is the core bug that allows many different glitches to manifest. + //! + //! One possible fix is to kill all speed instantly in `Player_SetupTurnInPlace`. + //! Another possible fix is to gradually kill speed by calling `Player_DecelerateToZero` + //! here, which plenty of other "standing" actions do. + if ((this != GET_PLAYER(play)) && (this->focusActor == NULL)) { yawTarget = this->actor.home.rot.y; } - if (Player_TryActionHandlerList(play, this, sActionHandlerList6, true)) { + if (Player_TryActionHandlerList(play, this, sActionHandlerListTurnInPlace, true)) { return; } if (speedTarget != 0.0f) { this->actor.shape.rot.y = yawTarget; func_8083A794(this, play); - } else if (Math_ScaledStepToS(&this->actor.shape.rot.y, yawTarget, this->unk_B4E)) { + } else if (Math_ScaledStepToS(&this->actor.shape.rot.y, yawTarget, this->turnRate)) { func_80839E74(this, play); } this->yaw = this->actor.shape.rot.y; @@ -20841,7 +20861,7 @@ s32 Player_TryCsAction(PlayState* play, Player* this, PlayerCsAction csAction) { // Specific to Kafei, any negative csAction works if ((this->actor.id == ACTOR_EN_TEST3) && (csAction < 0)) { // PLAYER_CSACTION_NEG1 - func_8083B0E4(play, this, this->actor.home.rot.y); + Player_SetupTurnInPlace(play, this, this->actor.home.rot.y); return false; } diff --git a/tools/disasm/functions.txt b/tools/disasm/functions.txt index a2fd563546..8b8359ad88 100644 --- a/tools/disasm/functions.txt +++ b/tools/disasm/functions.txt @@ -4510,7 +4510,7 @@ 0x8083AF8C:("func_8083AF8C",), 0x8083B030:("func_8083B030",), 0x8083B090:("func_8083B090",), - 0x8083B0E4:("func_8083B0E4",), + 0x8083B0E4:("Player_SetupTurnInPlace",), 0x8083B1A0:("func_8083B1A0",), 0x8083B23C:("func_8083B23C",), 0x8083B29C:("func_8083B29C",), @@ -4671,7 +4671,7 @@ 0x8084A794:("Player_Action_7",), 0x8084A884:("Player_Action_8",), 0x8084A8E8:("Player_Action_9",), - 0x8084AB4C:("Player_Action_10",), + 0x8084AB4C:("Player_Action_TurnInPlace",), 0x8084AC84:("Player_Action_11",), 0x8084AEEC:("Player_Action_12",), 0x8084AF9C:("Player_Action_13",), diff --git a/tools/disasm/variables.txt b/tools/disasm/variables.txt index 573f6353db..f619532565 100644 --- a/tools/disasm/variables.txt +++ b/tools/disasm/variables.txt @@ -4854,7 +4854,7 @@ 0x8085CFF8:("sActionHandlerList3","UNK_TYPE1","",0x1), 0x8085D004:("sActionHandlerList4","UNK_TYPE1","",0x1), 0x8085D00C:("sActionHandlerList5","UNK_TYPE1","",0x1), - 0x8085D018:("sActionHandlerList6","UNK_TYPE1","",0x1), + 0x8085D018:("sActionHandlerListTurnInPlace","UNK_TYPE1","",0x1), 0x8085D01C:("sActionHandlerListIdle","UNK_TYPE1","",0x1), 0x8085D028:("sActionHandlerList8","UNK_TYPE1","",0x1), 0x8085D034:("sActionHandlerList9","UNK_TYPE1","",0x1),