diff --git a/include/z64player.h b/include/z64player.h index 6f62392515c..cb6edcb467d 100644 --- a/include/z64player.h +++ b/include/z64player.h @@ -701,7 +701,7 @@ typedef struct { #define PLAYER_STATE2_17 (1 << 17) #define PLAYER_STATE2_CRAWLING (1 << 18) // Crawling through a crawlspace #define PLAYER_STATE2_19 (1 << 19) -#define PLAYER_STATE2_20 (1 << 20) +#define PLAYER_STATE2_NAVI_ACTIVE (1 << 20) // Navi is visible and active. Could be hovering idle near Link or hovering over other actors. #define PLAYER_STATE2_21 (1 << 21) #define PLAYER_STATE2_22 (1 << 22) #define PLAYER_STATE2_23 (1 << 23) @@ -835,7 +835,7 @@ typedef struct Player { /* 0x0830 */ f32 upperAnimInterpWeight; /* 0x0834 */ s16 unk_834; /* 0x0836 */ s8 unk_836; - /* 0x0837 */ u8 unk_837; + /* 0x0837 */ u8 putAwayCooldownTimer; /* 0x0838 */ f32 speedXZ; // Controls horizontal speed, used for `actor.speed`. Current or target value depending on context. /* 0x083C */ s16 yaw; // General yaw value, used both for world and shape rotation. Current or target value depending on context. /* 0x083E */ s16 zTargetYaw; // yaw relating to Z targeting/"parallel" mode @@ -854,6 +854,7 @@ typedef struct Player { /* 0x0850 */ union { s16 actionVar2; + s16 bonked; // Player_Action_Roll: set to true after bonking into a wall or an actor } av2; // "Action Variable 2": context dependent variable that has different meanings depending on what action is currently running /* 0x0854 */ f32 unk_854; diff --git a/src/overlays/actors/ovl_En_Elf/z_en_elf.c b/src/overlays/actors/ovl_En_Elf/z_en_elf.c index 6de4a0c58be..6e3973b2669 100644 --- a/src/overlays/actors/ovl_En_Elf/z_en_elf.c +++ b/src/overlays/actors/ovl_En_Elf/z_en_elf.c @@ -1095,7 +1095,7 @@ void func_80A0461C(EnElf* this, PlayState* play) { } else if (arrowPointedActor == NULL || arrowPointedActor->category == ACTORCAT_NPC) { if (arrowPointedActor != NULL) { this->unk_2C0 = 100; - player->stateFlags2 |= PLAYER_STATE2_20; + player->stateFlags2 |= PLAYER_STATE2_NAVI_ACTIVE; temp = 0; } else { switch (this->unk_2A8) { @@ -1116,7 +1116,7 @@ void func_80A0461C(EnElf* this, PlayState* play) { this->unk_2AE--; temp = 7; } else { - player->stateFlags2 |= PLAYER_STATE2_20; + player->stateFlags2 |= PLAYER_STATE2_NAVI_ACTIVE; temp = 0; } } else { @@ -1146,7 +1146,7 @@ void func_80A0461C(EnElf* this, PlayState* play) { switch (temp) { case 0: - if (!(player->stateFlags2 & PLAYER_STATE2_20)) { + if (!(player->stateFlags2 & PLAYER_STATE2_NAVI_ACTIVE)) { temp = 7; if (this->unk_2C7 == 0) { Actor_PlaySfx(&this->actor, NA_SE_EV_NAVY_VANISH); @@ -1154,7 +1154,7 @@ void func_80A0461C(EnElf* this, PlayState* play) { } break; case 8: - if (player->stateFlags2 & PLAYER_STATE2_20) { + if (player->stateFlags2 & PLAYER_STATE2_NAVI_ACTIVE) { func_80A0299C(this, 0x32); this->unk_2C0 = 42; temp = 11; @@ -1164,10 +1164,10 @@ void func_80A0461C(EnElf* this, PlayState* play) { } break; case 7: - player->stateFlags2 &= ~PLAYER_STATE2_20; + player->stateFlags2 &= ~PLAYER_STATE2_NAVI_ACTIVE; break; default: - player->stateFlags2 |= PLAYER_STATE2_20; + player->stateFlags2 |= PLAYER_STATE2_NAVI_ACTIVE; break; } } diff --git a/src/overlays/actors/ovl_player_actor/z_player.c b/src/overlays/actors/ovl_player_actor/z_player.c index 13632f4f118..42e80a1ef95 100644 --- a/src/overlays/actors/ovl_player_actor/z_player.c +++ b/src/overlays/actors/ovl_player_actor/z_player.c @@ -285,7 +285,7 @@ void Player_Action_80843954(Player* this, PlayState* play); void Player_Action_80843A38(Player* this, PlayState* play); void Player_Action_80843CEC(Player* this, PlayState* play); void Player_Action_8084411C(Player* this, PlayState* play); -void Player_Action_80844708(Player* this, PlayState* play); +void Player_Action_Roll(Player* this, PlayState* play); void Player_Action_80844A44(Player* this, PlayState* play); void Player_Action_80844AF4(Player* this, PlayState* play); void Player_Action_80844E68(Player* this, PlayState* play); @@ -3770,7 +3770,7 @@ typedef enum { /* 3 */ PLAYER_ACTION_CHG_3, /* 4 */ PLAYER_ACTION_CHG_4, /* 5 */ PLAYER_ACTION_CHG_5, - /* 6 */ PLAYER_ACTION_CHG_6, + /* 6 */ PLAYER_ACTION_CHG_ROLL, /* 7 */ PLAYER_ACTION_CHG_7, /* 8 */ PLAYER_ACTION_CHG_8, /* 9 */ PLAYER_ACTION_CHG_9, @@ -3788,13 +3788,21 @@ static s8 sActionChangeList1[] = { static s8 sActionChangeList2[] = { PLAYER_ACTION_CHG_13, PLAYER_ACTION_CHG_1, PLAYER_ACTION_CHG_2, PLAYER_ACTION_CHG_5, PLAYER_ACTION_CHG_3, PLAYER_ACTION_CHG_4, PLAYER_ACTION_CHG_9, PLAYER_ACTION_CHG_10, - PLAYER_ACTION_CHG_11, PLAYER_ACTION_CHG_7, PLAYER_ACTION_CHG_8, -PLAYER_ACTION_CHG_6, + PLAYER_ACTION_CHG_11, PLAYER_ACTION_CHG_7, PLAYER_ACTION_CHG_8, -PLAYER_ACTION_CHG_ROLL, }; static s8 sActionChangeList3[] = { - PLAYER_ACTION_CHG_13, PLAYER_ACTION_CHG_1, PLAYER_ACTION_CHG_2, PLAYER_ACTION_CHG_3, - PLAYER_ACTION_CHG_4, PLAYER_ACTION_CHG_9, PLAYER_ACTION_CHG_10, PLAYER_ACTION_CHG_11, - PLAYER_ACTION_CHG_8, PLAYER_ACTION_CHG_7, -PLAYER_ACTION_CHG_6, + PLAYER_ACTION_CHG_13, + PLAYER_ACTION_CHG_1, + PLAYER_ACTION_CHG_2, + PLAYER_ACTION_CHG_3, + PLAYER_ACTION_CHG_4, + PLAYER_ACTION_CHG_9, + PLAYER_ACTION_CHG_10, + PLAYER_ACTION_CHG_11, + PLAYER_ACTION_CHG_8, + PLAYER_ACTION_CHG_7, + -PLAYER_ACTION_CHG_ROLL, }; static s8 sActionChangeList4[] = { @@ -3812,21 +3820,39 @@ static s8 sActionChangeList6[] = { }; static s8 sActionChangeList7[] = { - PLAYER_ACTION_CHG_0, PLAYER_ACTION_CHG_11, PLAYER_ACTION_CHG_1, PLAYER_ACTION_CHG_2, - PLAYER_ACTION_CHG_3, PLAYER_ACTION_CHG_5, PLAYER_ACTION_CHG_4, PLAYER_ACTION_CHG_9, - PLAYER_ACTION_CHG_8, PLAYER_ACTION_CHG_7, -PLAYER_ACTION_CHG_6, + PLAYER_ACTION_CHG_0, + PLAYER_ACTION_CHG_11, + PLAYER_ACTION_CHG_1, + PLAYER_ACTION_CHG_2, + PLAYER_ACTION_CHG_3, + PLAYER_ACTION_CHG_5, + PLAYER_ACTION_CHG_4, + PLAYER_ACTION_CHG_9, + PLAYER_ACTION_CHG_8, + PLAYER_ACTION_CHG_7, + -PLAYER_ACTION_CHG_ROLL, }; static s8 sActionChangeList8[] = { PLAYER_ACTION_CHG_0, PLAYER_ACTION_CHG_11, PLAYER_ACTION_CHG_1, PLAYER_ACTION_CHG_2, PLAYER_ACTION_CHG_3, PLAYER_ACTION_CHG_12, PLAYER_ACTION_CHG_5, PLAYER_ACTION_CHG_4, - PLAYER_ACTION_CHG_9, PLAYER_ACTION_CHG_8, PLAYER_ACTION_CHG_7, -PLAYER_ACTION_CHG_6, + PLAYER_ACTION_CHG_9, PLAYER_ACTION_CHG_8, PLAYER_ACTION_CHG_7, -PLAYER_ACTION_CHG_ROLL, }; static s8 sActionChangeList9[] = { - PLAYER_ACTION_CHG_13, PLAYER_ACTION_CHG_1, PLAYER_ACTION_CHG_2, PLAYER_ACTION_CHG_3, PLAYER_ACTION_CHG_12, - PLAYER_ACTION_CHG_5, PLAYER_ACTION_CHG_4, PLAYER_ACTION_CHG_9, PLAYER_ACTION_CHG_10, PLAYER_ACTION_CHG_11, - PLAYER_ACTION_CHG_8, PLAYER_ACTION_CHG_7, -PLAYER_ACTION_CHG_6, + PLAYER_ACTION_CHG_13, + PLAYER_ACTION_CHG_1, + PLAYER_ACTION_CHG_2, + PLAYER_ACTION_CHG_3, + PLAYER_ACTION_CHG_12, + PLAYER_ACTION_CHG_5, + PLAYER_ACTION_CHG_4, + PLAYER_ACTION_CHG_9, + PLAYER_ACTION_CHG_10, + PLAYER_ACTION_CHG_11, + PLAYER_ACTION_CHG_8, + PLAYER_ACTION_CHG_7, + -PLAYER_ACTION_CHG_ROLL, }; static s8 sActionChangeList10[] = { @@ -3848,7 +3874,7 @@ s32 Player_ActionChange_2(Player* this, PlayState* play); s32 Player_ActionChange_3(Player* this, PlayState* play); s32 Player_ActionChange_4(Player* this, PlayState* play); s32 Player_ActionChange_5(Player* this, PlayState* play); -s32 Player_ActionChange_6(Player* this, PlayState* play); +s32 Player_ActionChange_HandleRolling(Player* this, PlayState* play); s32 Player_ActionChange_7(Player* this, PlayState* play); s32 Player_ActionChange_8(Player* this, PlayState* play); s32 Player_ActionChange_9(Player* this, PlayState* play); @@ -3864,7 +3890,7 @@ static s32 (*sActionChangeFuncs[])(Player* this, PlayState* play) = { /* PLAYER_ACTION_CHG_3 */ Player_ActionChange_3, /* PLAYER_ACTION_CHG_4 */ Player_ActionChange_4, /* PLAYER_ACTION_CHG_5 */ Player_ActionChange_5, - /* PLAYER_ACTION_CHG_6 */ Player_ActionChange_6, + /* PLAYER_ACTION_CHG_ROLL */ Player_ActionChange_HandleRolling, /* PLAYER_ACTION_CHG_7 */ Player_ActionChange_7, /* PLAYER_ACTION_CHG_8 */ Player_ActionChange_8, /* PLAYER_ACTION_CHG_9 */ Player_ActionChange_9, @@ -5880,22 +5906,22 @@ s32 func_8083BBA0(Player* this, PlayState* play) { return 0; } -void func_8083BC04(Player* this, PlayState* play) { - Player_SetupAction(play, this, Player_Action_80844708, 0); +void Player_SetupRoll(Player* this, PlayState* play) { + Player_SetupAction(play, this, Player_Action_Roll, 0); LinkAnimation_PlayOnceSetSpeed(play, &this->skelAnime, GET_PLAYER_ANIM(PLAYER_ANIMGROUP_landing_roll, this->modelAnimType), 1.25f * D_808535E8); } -s32 func_8083BC7C(Player* this, PlayState* play) { +s32 Player_TryRolling(Player* this, PlayState* play) { if ((this->controlStickDirections[this->controlStickDataIndex] == PLAYER_STICK_DIR_FORWARD) && (sFloorType != FLOOR_TYPE_7)) { - func_8083BC04(this, play); + Player_SetupRoll(this, play); - return 1; + return true; } - return 0; + return false; } void func_8083BCD0(Player* this, PlayState* play, s32 controlStickDirection) { @@ -5929,13 +5955,13 @@ s32 Player_ActionChange_10(Player* this, PlayState* play) { if (controlStickDirection <= PLAYER_STICK_DIR_NONE) { func_808389E8(this, &gPlayerAnim_link_normal_jump, REG(69) / 100.0f, play); } else { - func_8083BC04(this, play); + Player_SetupRoll(this, play); } } else { if ((Player_GetMeleeWeaponHeld(this) != 0) && Player_CanUpdateItems(this)) { func_8083BA90(play, this, PLAYER_MWA_JUMPSLASH_START, 5.0f, 5.0f); } else { - func_8083BC04(this, play); + Player_SetupRoll(this, play); } } @@ -6008,20 +6034,29 @@ void func_8083C148(Player* this, PlayState* play) { this->stateFlags1 &= ~(PLAYER_STATE1_13 | PLAYER_STATE1_14 | PLAYER_STATE1_20); } -s32 Player_ActionChange_6(Player* this, PlayState* play) { +/** + * Handles setting up a roll if it is appropriate. + * + * If a roll is not applicable, there are two extra behaviors that could occur: + * - If an item is currently held in hand, it can be put away. + * - Navi can be toggled on and off. She will either appear and fly around Link's head, or fly back into his body. + * + * These extra behaviors are not new actions themselves, so they will result in `false` being returned + * even if they occur. + */ +s32 Player_ActionChange_HandleRolling(Player* this, PlayState* play) { if (!func_80833B54(this) && !sUpperBodyIsBusy && !(this->stateFlags1 & PLAYER_STATE1_23) && CHECK_BTN_ALL(sControlInput->press.button, BTN_A)) { - if (func_8083BC7C(this, play)) { - return 1; - } - if ((this->unk_837 == 0) && (this->heldItemAction >= PLAYER_IA_SWORD_MASTER)) { + if (Player_TryRolling(this, play)) { + return true; + } else if ((this->putAwayCooldownTimer == 0) && (this->heldItemAction >= PLAYER_IA_SWORD_MASTER)) { Player_UseItem(play, this, ITEM_NONE); } else { - this->stateFlags2 ^= PLAYER_STATE2_20; + this->stateFlags2 ^= PLAYER_STATE2_NAVI_ACTIVE; } } - return 0; + return false; } s32 Player_ActionChange_11(Player* this, PlayState* play) { @@ -9069,7 +9104,7 @@ void Player_Action_8084411C(Player* this, PlayState* play) { } else if ((this->fallDistance < 800) && (this->controlStickDirections[this->controlStickDataIndex] == PLAYER_STICK_DIR_FORWARD) && !(this->stateFlags1 & PLAYER_STATE1_11)) { - func_8083BC04(this, play); + Player_SetupRoll(this, play); return; } @@ -9089,17 +9124,17 @@ void Player_Action_8084411C(Player* this, PlayState* play) { } } -static AnimSfxEntry D_8085460C[] = { +static AnimSfxEntry sRollAnimSfxList[] = { { NA_SE_VO_LI_SWORD_N, ANIMSFX_DATA(ANIMSFX_TYPE_4, 1) }, { NA_SE_PL_WALK_GROUND, ANIMSFX_DATA(ANIMSFX_TYPE_3, 6) }, { NA_SE_PL_ROLL, ANIMSFX_DATA(ANIMSFX_TYPE_1, 6) }, { 0, -ANIMSFX_DATA(ANIMSFX_TYPE_5, 18) }, }; -void Player_Action_80844708(Player* this, PlayState* play) { - Actor* cylinderOc; +void Player_Action_Roll(Player* this, PlayState* play) { + Actor* ocCollidedActor; s32 interruptResult; - s32 sp44; + s32 animDone; DynaPolyActor* wallPolyActor; s32 pad; f32 speedTarget; @@ -9107,37 +9142,42 @@ void Player_Action_80844708(Player* this, PlayState* play) { this->stateFlags2 |= PLAYER_STATE2_5; - cylinderOc = NULL; - sp44 = LinkAnimation_Update(play, &this->skelAnime); + ocCollidedActor = NULL; + animDone = LinkAnimation_Update(play, &this->skelAnime); if (LinkAnimation_OnFrame(&this->skelAnime, 8.0f)) { func_80837AFC(this, -10); } - if (func_80842964(this, play) == 0) { - if (this->av2.actionVar2 != 0) { + if (!func_80842964(this, play)) { + if (this->av2.bonked) { Math_StepToF(&this->speedXZ, 0.0f, 2.0f); interruptResult = Player_TryActionInterrupt(play, this, &this->skelAnime, 5.0f); if ((interruptResult != PLAYER_INTERRUPT_NEW_ACTION) && - ((interruptResult >= PLAYER_INTERRUPT_MOVE) || sp44)) { + ((interruptResult >= PLAYER_INTERRUPT_MOVE) || animDone)) { func_8083A060(this, play); } } else { + // Must have a speed of 7 or above to be able to bonk into something if (this->speedXZ >= 7.0f) { if (((this->actor.bgCheckFlags & BGCHECKFLAG_PLAYER_WALL_INTERACT) && (sWorldYawToTouchedWall < 0x2000)) || ((this->cylinder.base.ocFlags1 & OC1_HIT) && - (cylinderOc = this->cylinder.base.oc, - ((cylinderOc->id == ACTOR_EN_WOOD02) && - (ABS((s16)(this->actor.world.rot.y - cylinderOc->yawTowardsPlayer)) > 0x6000))))) { - - if (cylinderOc != NULL) { - cylinderOc->home.rot.y = 1; + (ocCollidedActor = this->cylinder.base.oc, + ((ocCollidedActor->id == ACTOR_EN_WOOD02) && + (ABS((s16)(this->actor.world.rot.y - ocCollidedActor->yawTowardsPlayer)) > 0x6000))))) { + if (ocCollidedActor != NULL) { + // The EN_WOOD02 actor uses home y rotation as a flag to signal that it has been + // bonked into and should try to spawn a drop. + ocCollidedActor->home.rot.y = 1; } else if (this->actor.wallBgId != BGCHECK_SCENE) { wallPolyActor = DynaPoly_GetActor(&play->colCtx, this->actor.wallBgId); + if ((wallPolyActor != NULL) && (wallPolyActor->actor.id == ACTOR_OBJ_KIBAKO2)) { + // The OBJ_KIBAKO2 actor uses home z rotation as a flag to signal that it has been + // bonked into and should break. wallPolyActor->actor.home.rot.z = 1; } } @@ -9148,7 +9188,8 @@ void Player_Action_80844708(Player* this, PlayState* play) { Player_RequestRumble(this, 255, 20, 150, 0); Player_PlaySfx(this, NA_SE_PL_BODY_HIT); func_80832698(this, NA_SE_VO_LI_CLIMB_END); - this->av2.actionVar2 = 1; + this->av2.bonked = true; + return; } } @@ -9156,12 +9197,17 @@ void Player_Action_80844708(Player* this, PlayState* play) { if ((this->skelAnime.curFrame < 15.0f) || !Player_ActionChange_7(this, play)) { if (this->skelAnime.curFrame >= 20.0f) { func_8083A060(this, play); + return; } Player_GetMovementSpeedAndYaw(this, &speedTarget, &yawTarget, SPEED_MODE_CURVED, play); + // `speedTarget` at this point is the speed that would be used for regular walking. + // Rolling speed is 1.5 times faster than what the walking speed would be for the current control stick + // input. speedTarget *= 1.5f; + if ((speedTarget < 3.0f) || (this->controlStickDirections[this->controlStickDataIndex] != PLAYER_STICK_DIR_FORWARD)) { speedTarget = 3.0f; @@ -9173,7 +9219,7 @@ void Player_Action_80844708(Player* this, PlayState* play) { func_8002F8F0(&this->actor, NA_SE_PL_ROLL_DUST - SFX_FLAG); } - Player_ProcessAnimSfxList(this, D_8085460C); + Player_ProcessAnimSfxList(this, sRollAnimSfxList); } } } @@ -10346,7 +10392,7 @@ void Player_UpdateInterface(PlayState* play, Player* this) { (controlStickDirection >= PLAYER_STICK_DIR_LEFT)) { doAction = DO_ACTION_JUMP; } else if ((this->heldItemAction >= PLAYER_IA_SWORD_MASTER) || - ((this->stateFlags2 & PLAYER_STATE2_20) && + ((this->stateFlags2 & PLAYER_STATE2_NAVI_ACTIVE) && (play->actorCtx.targetCtx.arrowPointedActor == NULL))) { doAction = DO_ACTION_PUTAWAY; } @@ -10355,10 +10401,12 @@ void Player_UpdateInterface(PlayState* play, Player* this) { } if (doAction != DO_ACTION_PUTAWAY) { - this->unk_837 = 20; - } else if (this->unk_837 != 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 doAction = DO_ACTION_NONE; - this->unk_837--; + this->putAwayCooldownTimer--; } Interface_SetDoAction(play, doAction); @@ -12660,7 +12708,7 @@ void Player_Action_8084CC98(Player* this, PlayState* play) { if ((this->csAction != PLAYER_CSACTION_NONE) || (!func_8083224C(play) && ((rideActor->actor.speed != 0.0f) || !Player_ActionChange_4(this, play)) && - !Player_ActionChange_6(this, play))) { + !Player_ActionChange_HandleRolling(this, play))) { if (!sUpperBodyIsBusy) { if (this->av1.actionVar1 != 0) { if (LinkAnimation_Update(play, &this->upperSkelAnime)) {