summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorengineer124 <47598039+engineer124@users.noreply.github.com>2024-10-23 11:26:19 +1100
committerEblo <7004497+Eblo@users.noreply.github.com>2025-09-16 10:57:17 -0400
commitac7939f5ff5eb5715e40dd1de88cf5a48ba2348f (patch)
tree62882a995db3f30b0cbe88be0db39292827ed051
parent4e1d56167aa4acd006cee09a6c57a530b3c27731 (diff)
turn in place (#1728)
-rw-r--r--mm/include/z64player.h4
-rw-r--r--mm/src/overlays/actors/ovl_player_actor/z_player.c72
2 files changed, 47 insertions, 29 deletions
diff --git a/mm/include/z64player.h b/mm/include/z64player.h
index 3ad95d0a6..97502be7d 100644
--- a/mm/include/z64player.h
+++ b/mm/include/z64player.h
@@ -1243,7 +1243,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
@@ -1283,7 +1283,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/mm/src/overlays/actors/ovl_player_actor/z_player.c b/mm/src/overlays/actors/ovl_player_actor/z_player.c
index d9d22cb1d..4c4f3a42c 100644
--- a/mm/src/overlays/actors/ovl_player_actor/z_player.c
+++ b/mm/src/overlays/actors/ovl_player_actor/z_player.c
@@ -126,7 +126,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);
@@ -5313,7 +5313,7 @@ s8 sActionHandlerList5[] = {
/* 8 */ -PLAYER_ACTION_HANDLER_7,
};
-s8 sActionHandlerList6[] = {
+s8 sActionHandlerListTurnInPlace[] = {
/* 0 */ -PLAYER_ACTION_HANDLER_7,
};
@@ -8371,7 +8371,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) &&
GameInteractor_Should(VB_SHOULD_PUTAWAY, (this->transformation != PLAYER_FORM_FIERCE_DEITY))) {
Player_UseItem(play, this, ITEM_NONE);
} else {
@@ -8519,8 +8519,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);
}
@@ -8697,12 +8697,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) {
@@ -8710,12 +8710,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) {
@@ -8729,11 +8729,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);
}
@@ -8860,11 +8863,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) {
@@ -9007,7 +9010,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);
}
@@ -11589,11 +11592,11 @@ void Player_SetDoAction(PlayState* play, Player* this) {
if (doActionA != DO_ACTION_PUTAWAY) {
if (GameInteractor_Should(VB_RESET_PUTAWAY_TIMER, true)) {
- this->putAwayCountdown = 20;
+ this->putAwayCooldownTimer = 20;
}
- } else if (this->putAwayCountdown != 0) {
+ } else if (this->putAwayCooldownTimer != 0) {
doActionA = DO_ACTION_NONE;
- this->putAwayCountdown--;
+ this->putAwayCooldownTimer--;
}
Interface_SetAButtonDoAction(play, doActionA);
@@ -14544,7 +14547,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);
}
}
}
@@ -14617,7 +14620,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;
@@ -14844,7 +14847,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;
@@ -14858,18 +14868,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;
@@ -21308,7 +21326,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;
}