diff --git a/include/z64player.h b/include/z64player.h index ab7e099c50..412ced0e61 100644 --- a/include/z64player.h +++ b/include/z64player.h @@ -673,8 +673,8 @@ typedef struct PlayerAgeProperties { /* 0x92 */ u16 unk_92; /* 0x94 */ u16 unk_94; /* 0x98 */ LinkAnimationHeader* unk_98; - /* 0x9C */ LinkAnimationHeader* unk_9C; - /* 0xA0 */ LinkAnimationHeader* unk_A0; + /* 0x9C */ LinkAnimationHeader* timeTravelStartAnim; + /* 0xA0 */ LinkAnimationHeader* timeTravelEndAnim; /* 0xA4 */ LinkAnimationHeader* unk_A4; /* 0xA8 */ LinkAnimationHeader* unk_A8; /* 0xAC */ LinkAnimationHeader* unk_AC[4]; @@ -899,16 +899,22 @@ typedef struct Player { /* 0x084F */ union { s8 actionVar1; - s8 facingUpSlope; // Player_Action_SlideOnSlope: facing uphill when sliding on a slope + s8 startedAnim; // Player_Action_EndTimeTravel: Started playing the animation that was previously frozen + s8 facingUpSlope; // Player_Action_SlideOnSlope: Facing uphill when sliding on a slope + s8 isLakeHyliaCs; // Player_Action_BlueWarpArrive: In Lake Hylia CS after Water Temple. Floating down is delayed until a specific point in the cutscene. s8 bottleCatchType; // Player_Action_SwingBottle: entry type for `sBottleCatchInfo`, corresponds to actor caught in a bottle } av1; // "Action Variable 1": context dependent variable that has different meanings depending on what action is currently running /* 0x0850 */ union { s16 actionVar2; s16 fallDamageStunTimer; // Player_Action_Idle: Prevents any movement and shakes model up and down quickly to indicate fall damage stun - s16 bonked; // Player_Action_Roll: set to true after bonking into a wall or an actor + s16 bonked; // Player_Action_Roll: Set to true after bonking into a wall or an actor + s16 animDelayTimer; // Player_Action_EndTimeTravel: Delays playing animation until finished counting down s16 startedTextbox; // Player_Action_SwingBottle: set to true when the textbox is started - s16 inWater; // Player_Action_SwingBottle: true if a bottle is swung in water. Used to determine which bottle swing animation to use. + s16 inWater; // Player_Action_SwingBottle: True if a bottle is swung in water. Used to determine which bottle swing animation to use. + s16 csDelayTimer; // Player_Action_WaitForCutscene: Number of frames to wait before responding to a cutscene + s16 playedLandingSfx; // Player_Action_BlueWarpArrive: Played sfx when landing on the ground + s16 appearTimer; // Player_Action_FaroresWindArrive: Counts up, appear at 20 frames (1 second) } 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_player_actor/z_player.c b/src/overlays/actors/ovl_player_actor/z_player.c index 13ad79e644..082f6ccfca 100644 --- a/src/overlays/actors/ovl_player_actor/z_player.c +++ b/src/overlays/actors/ovl_player_actor/z_player.c @@ -303,26 +303,26 @@ void Player_Action_8084E368(Player* this, PlayState* play); void Player_Action_8084E3C4(Player* this, PlayState* play); void Player_Action_8084E604(Player* this, PlayState* play); void Player_Action_8084E6D4(Player* this, PlayState* play); -void Player_Action_8084E9AC(Player* this, PlayState* play); +void Player_Action_TimeTravelEnd(Player* this, PlayState* play); void Player_Action_8084EAC0(Player* this, PlayState* play); void Player_Action_SwingBottle(Player* this, PlayState* play); void Player_Action_8084EED8(Player* this, PlayState* play); void Player_Action_8084EFC0(Player* this, PlayState* play); void Player_Action_ExchangeItem(Player* this, PlayState* play); void Player_Action_SlideOnSlope(Player* this, PlayState* play); -void Player_Action_8084F608(Player* this, PlayState* play); -void Player_Action_8084F698(Player* this, PlayState* play); -void Player_Action_8084F710(Player* this, PlayState* play); +void Player_Action_WaitForCutscene(Player* this, PlayState* play); +void Player_Action_StartWarpSongArrive(Player* this, PlayState* play); +void Player_Action_BlueWarpArrive(Player* this, PlayState* play); void Player_Action_8084F88C(Player* this, PlayState* play); -void Player_Action_8084F9A0(Player* this, PlayState* play); -void Player_Action_8084F9C0(Player* this, PlayState* play); +void Player_Action_TryOpeningDoor(Player* this, PlayState* play); +void Player_Action_ExitGrotto(Player* this, PlayState* play); void Player_Action_8084FA54(Player* this, PlayState* play); void Player_Action_8084FB10(Player* this, PlayState* play); void Player_Action_8084FBF4(Player* this, PlayState* play); void Player_Action_808502D0(Player* this, PlayState* play); void Player_Action_808505DC(Player* this, PlayState* play); void Player_Action_8085063C(Player* this, PlayState* play); -void Player_Action_8085076C(Player* this, PlayState* play); +void Player_Action_FaroresWindArrive(Player* this, PlayState* play); void Player_Action_808507F4(Player* this, PlayState* play); void Player_Action_HookshotFly(Player* this, PlayState* play); void Player_Action_80850C68(Player* this, PlayState* play); @@ -418,8 +418,8 @@ static PlayerAgeProperties sAgeProperties[] = { 0, // unk_92 0x80, // unk_94 &gPlayerAnim_link_demo_Tbox_open, // unk_98 - &gPlayerAnim_link_demo_back_to_past, // unk_9C - &gPlayerAnim_link_demo_return_to_past, // unk_A0 + &gPlayerAnim_link_demo_back_to_past, // timeTravelStartAnim + &gPlayerAnim_link_demo_return_to_past, // timeTravelEndAnim &gPlayerAnim_link_normal_climb_startA, // unk_A4 &gPlayerAnim_link_normal_climb_startB, // unk_A8 { &gPlayerAnim_link_normal_climb_upL, &gPlayerAnim_link_normal_climb_upR, &gPlayerAnim_link_normal_Fclimb_upL, @@ -470,8 +470,8 @@ static PlayerAgeProperties sAgeProperties[] = { 0x20, // unk_92 0, // unk_94 &gPlayerAnim_clink_demo_Tbox_open, // unk_98 - &gPlayerAnim_clink_demo_goto_future, // unk_9C - &gPlayerAnim_clink_demo_return_to_future, // unk_A0 + &gPlayerAnim_clink_demo_goto_future, // timeTravelStartAnim + &gPlayerAnim_clink_demo_return_to_future, // timeTravelEndAnim &gPlayerAnim_clink_normal_climb_startA, // unk_A4 &gPlayerAnim_clink_normal_climb_startB, // unk_A8 { &gPlayerAnim_clink_normal_climb_upL, &gPlayerAnim_clink_normal_climb_upR, &gPlayerAnim_link_normal_Fclimb_upL, @@ -5347,7 +5347,7 @@ s32 Player_ActionHandler_1(Player* this, PlayState* play) { if ((this->doorType != PLAYER_DOORTYPE_NONE) && (!(this->stateFlags1 & PLAYER_STATE1_CARRYING_ACTOR) || ((this->heldActor != NULL) && (this->heldActor->id == ACTOR_EN_RU1)))) { - if (CHECK_BTN_ALL(sControlInput->press.button, BTN_A) || (Player_Action_8084F9A0 == this->actionFunc)) { + if (CHECK_BTN_ALL(sControlInput->press.button, BTN_A) || (Player_Action_TryOpeningDoor == this->actionFunc)) { doorActor = this->doorActor; if (this->doorType <= PLAYER_DOORTYPE_AJAR) { @@ -5584,7 +5584,7 @@ void func_8083A0F4(PlayState* play, Player* this) { if (interactActorId == ACTOR_BG_TOKI_SWD) { this->interactRangeActor->parent = &this->actor; - Player_SetupAction(play, this, Player_Action_8084F608, 0); + Player_SetupAction(play, this, Player_Action_WaitForCutscene, 0); this->stateFlags1 |= PLAYER_STATE1_29; } else { LinkAnimationHeader* anim; @@ -10501,15 +10501,17 @@ void Player_StartMode_Nothing(PlayState* play, Player* this) { } void Player_StartMode_BlueWarp(PlayState* play, Player* this) { - Player_SetupAction(play, this, Player_Action_8084F710, 0); + Player_SetupAction(play, this, Player_Action_BlueWarpArrive, 0); if ((play->sceneId == SCENE_LAKE_HYLIA) && IS_CUTSCENE_LAYER) { - this->av1.actionVar1 = 1; + this->av1.isLakeHyliaCs = true; } this->stateFlags1 |= PLAYER_STATE1_29; LinkAnimation_Change(play, &this->skelAnime, &gPlayerAnim_link_okarina_warp_goal, 2.0f / 3.0f, 0.0f, 24.0f, ANIMMODE_ONCE, 0.0f); + + // Start high up in the air this->actor.world.pos.y += 800.0f; } @@ -10543,14 +10545,16 @@ void Player_PutSwordInHand(PlayState* play, Player* this, s32 playSfx) { void Player_StartMode_TimeTravel(PlayState* play, Player* this) { static Vec3f sPedestalPos = { -1.0f, 69.0f, 20.0f }; - Player_SetupAction(play, this, Player_Action_8084E9AC, 0); + Player_SetupAction(play, this, Player_Action_TimeTravelEnd, 0); this->stateFlags1 |= PLAYER_STATE1_29; Math_Vec3f_Copy(&this->actor.world.pos, &sPedestalPos); this->yaw = this->actor.shape.rot.y = -0x8000; - LinkAnimation_Change(play, &this->skelAnime, this->ageProperties->unk_A0, 2.0f / 3.0f, 0.0f, 0.0f, ANIMMODE_ONCE, - 0.0f); + // The start frame and end frame are both set to 0 so that that the animation is frozen. + // `Player_Action_TimeTravelEnd` will play the animation after `animDelayTimer` completes. + LinkAnimation_Change(play, &this->skelAnime, this->ageProperties->timeTravelEndAnim, 2.0f / 3.0f, 0.0f, 0.0f, + ANIMMODE_ONCE, 0.0f); Player_StartAnimMovement(play, this, PLAYER_ANIM_MOVEMENT_RESET_BY_AGE | ANIM_FLAG_UPDATE_XZ | ANIM_FLAG_UPDATE_Y | ANIM_FLAG_DISABLE_CHILD_ROOT_ADJUSTMENT | ANIM_FLAG_ENABLE_MOVEMENT | @@ -10560,11 +10564,11 @@ void Player_StartMode_TimeTravel(PlayState* play, Player* this) { Player_PutSwordInHand(play, this, false); } - this->av2.actionVar2 = 20; + this->av2.animDelayTimer = 20; } void Player_StartMode_Door(PlayState* play, Player* this) { - Player_SetupAction(play, this, Player_Action_8084F9A0, 0); + Player_SetupAction(play, this, Player_Action_TryOpeningDoor, 0); Player_StartAnimMovement(play, this, ANIM_FLAG_UPDATE_XZ | ANIM_FLAG_UPDATE_Y | ANIM_FLAG_ENABLE_MOVEMENT | ANIM_FLAG_ADJUST_STARTING_POS | ANIM_FLAG_OVERRIDE_MOVEMENT); @@ -10572,7 +10576,7 @@ void Player_StartMode_Door(PlayState* play, Player* this) { void Player_StartMode_Grotto(PlayState* play, Player* this) { func_808389E8(this, &gPlayerAnim_link_normal_jump, 12.0f, play); - Player_SetupAction(play, this, Player_Action_8084F9C0, 0); + Player_SetupAction(play, this, Player_Action_ExitGrotto, 0); this->stateFlags1 |= PLAYER_STATE1_29; this->fallStartHeight = this->actor.world.pos.y; OnePointCutscene_Init(play, 5110, 40, &this->actor, CAM_ID_MAIN); @@ -10583,8 +10587,8 @@ void Player_StartMode_KnockedOver(PlayState* play, Player* this) { } void Player_StartMode_WarpSong(PlayState* play, Player* this) { - Player_SetupAction(play, this, Player_Action_8084F698, 0); - this->actor.draw = NULL; + Player_SetupAction(play, this, Player_Action_StartWarpSongArrive, 0); + this->actor.draw = NULL; // Start invisible this->stateFlags1 |= PLAYER_STATE1_29; } @@ -10596,8 +10600,8 @@ Actor* Player_SpawnMagicSpell(PlayState* play, Player* this, s32 spell) { } void Player_StartMode_FaroresWind(PlayState* play, Player* this) { - this->actor.draw = NULL; - Player_SetupAction(play, this, Player_Action_8085076C, 0); + this->actor.draw = NULL; // Start invisible + Player_SetupAction(play, this, Player_Action_FaroresWindArrive, 0); this->stateFlags1 |= PLAYER_STATE1_29; } @@ -13895,26 +13899,22 @@ void Player_Action_8084E6D4(Player* this, PlayState* play) { } } -static AnimSfxEntry D_808549F0[] = { - { NA_SE_IT_MASTER_SWORD_SWING, -ANIMSFX_DATA(ANIMSFX_TYPE_GENERAL, 60) }, -}; - void func_8084E988(Player* this) { + static AnimSfxEntry D_808549F0[] = { + { NA_SE_IT_MASTER_SWORD_SWING, -ANIMSFX_DATA(ANIMSFX_TYPE_GENERAL, 60) }, + }; + Player_ProcessAnimSfxList(this, D_808549F0); } -#if OOT_VERSION >= PAL_1_0 -static AnimSfxEntry D_808549F4[] = { - { NA_SE_VO_LI_AUTO_JUMP, ANIMSFX_DATA(ANIMSFX_TYPE_VOICE, 5) }, - { 0, -ANIMSFX_DATA(ANIMSFX_TYPE_LANDING, 15) }, -}; -#endif - -void Player_Action_8084E9AC(Player* this, PlayState* play) { +void Player_Action_TimeTravelEnd(Player* this, PlayState* play) { if (LinkAnimation_Update(play, &this->skelAnime)) { - if (this->av1.actionVar1 == 0) { - if (DECR(this->av2.actionVar2) == 0) { - this->av1.actionVar1 = 1; + if (!this->av1.startedAnim) { + if (DECR(this->av2.animDelayTimer) == 0) { + this->av1.startedAnim = true; + + // endFrame was previously set to 0 to freeze the animation. + // Set it properly to allow the animation to play. this->skelAnime.endFrame = this->skelAnime.animLength - 1.0f; } } else { @@ -13928,13 +13928,20 @@ void Player_Action_8084E9AC(Player* this, PlayState* play) { #if OOT_VERSION < PAL_1_0 if (!LINK_IS_ADULT && LinkAnimation_OnFrame(&this->skelAnime, 5.0f)) { + // There is a jump sound when leaving the pedestal, but no landing sound when hitting the floor. + // This is fixed in PAL 1.0 and above. Player_PlayVoiceSfx(this, NA_SE_VO_LI_AUTO_JUMP); } else if (LINK_IS_ADULT) { func_8084E988(this); } #else if (!LINK_IS_ADULT) { - Player_ProcessAnimSfxList(this, D_808549F4); + static AnimSfxEntry sJumpOffPedestalAnimSfxList[] = { + { NA_SE_VO_LI_AUTO_JUMP, ANIMSFX_DATA(ANIMSFX_TYPE_VOICE, 5) }, + { 0, -ANIMSFX_DATA(ANIMSFX_TYPE_LANDING, 15) }, + }; + + Player_ProcessAnimSfxList(this, sJumpOffPedestalAnimSfxList); } else { func_8084E988(this); } @@ -14271,29 +14278,40 @@ void Player_Action_SlideOnSlope(Player* this, PlayState* play) { } } -void Player_Action_8084F608(Player* this, PlayState* play) { - if ((DECR(this->av2.actionVar2) == 0) && Player_StartCsAction(play, this)) { +/** + * Waits to start processing a Cutscene Action. + * First, the timer `csDelayTimer` much reach 0. + * Then, there must be a CS action available to start processing. + * + * When starting the cutscene action, `draw` will be set to make + * Player appear, if he was invisible. + */ +void Player_Action_WaitForCutscene(Player* this, PlayState* play) { + if ((DECR(this->av2.csDelayTimer) == 0) && Player_StartCsAction(play, this)) { func_80852280(play, this, NULL); Player_SetupAction(play, this, Player_Action_CsAction, 0); Player_Action_CsAction(this, play); } } -void Player_Action_8084F698(Player* this, PlayState* play) { - Player_SetupAction(play, this, Player_Action_8084F608, 0); - this->av2.actionVar2 = 40; +void Player_Action_StartWarpSongArrive(Player* this, PlayState* play) { + Player_SetupAction(play, this, Player_Action_WaitForCutscene, 0); + this->av2.csDelayTimer = 40; + + // Note: The warp song sparkles actor is responsible for starting the warp-in cutscene script Actor_Spawn(&play->actorCtx, play, ACTOR_DEMO_KANKYO, 0.0f, 0.0f, 0.0f, 0, 0, 0, DEMOKANKYO_WARP_IN); } -void Player_Action_8084F710(Player* this, PlayState* play) { +void Player_Action_BlueWarpArrive(Player* this, PlayState* play) { s32 pad; - if ((this->av1.actionVar1 != 0) && (play->csCtx.curFrame < 305)) { + if ((this->av1.isLakeHyliaCs) && (play->csCtx.curFrame < 305)) { + // Delay falling down until frame 306 of the Lake Hylia cutscene after completing Water Temple this->actor.gravity = 0.0f; this->actor.velocity.y = 0.0f; } else if (sYDistToFloor < 150.0f) { if (LinkAnimation_Update(play, &this->skelAnime)) { - if (this->av2.actionVar2 == 0) { + if (!this->av2.playedLandingSfx) { if (this->actor.bgCheckFlags & BGCHECKFLAG_GROUND) { this->skelAnime.endFrame = this->skelAnime.animLength - 1.0f; Player_PlayLandingSfx(this); @@ -14303,9 +14321,11 @@ void Player_Action_8084F710(Player* this, PlayState* play) { if ((play->sceneId == SCENE_KOKIRI_FOREST) && Player_StartCsAction(play, this)) { return; } + func_80853080(this, play); } } + Math_SmoothStepToF(&this->actor.velocity.y, 2.0f, 0.3f, 8.0f, 0.5f); } @@ -14314,9 +14334,10 @@ void Player_Action_8084F710(Player* this, PlayState* play) { } if ((play->csCtx.state != CS_STATE_IDLE) && (play->csCtx.playerCue != NULL)) { - f32 sp28 = this->actor.world.pos.y; + f32 savedYPos = this->actor.world.pos.y; + func_808529D0(play, this, play->csCtx.playerCue); - this->actor.world.pos.y = sp28; + this->actor.world.pos.y = savedYPos; } } @@ -14348,11 +14369,15 @@ void Player_Action_8084F88C(Player* this, PlayState* play) { } } -void Player_Action_8084F9A0(Player* this, PlayState* play) { +/** + * Automatically open a door (no need for the A button). + * Note: If no door is in useable range, a softlock will occur. + */ +void Player_Action_TryOpeningDoor(Player* this, PlayState* play) { Player_ActionHandler_1(this, play); } -void Player_Action_8084F9C0(Player* this, PlayState* play) { +void Player_Action_ExitGrotto(Player* this, PlayState* play) { this->actor.gravity = -1.0f; LinkAnimation_Update(play, &this->skelAnime); @@ -14708,17 +14733,17 @@ void Player_Action_8085063C(Player* this, PlayState* play) { } } -void Player_Action_8085076C(Player* this, PlayState* play) { +void Player_Action_FaroresWindArrive(Player* this, PlayState* play) { s32 respawnData = gSaveContext.respawn[RESPAWN_MODE_TOP].data; - if (this->av2.actionVar2 > 20) { + if (this->av2.appearTimer > 20) { this->actor.draw = Player_Draw; this->actor.world.pos.y += 60.0f; func_80837B9C(this, play); return; } - if (this->av2.actionVar2++ == 20) { + if (this->av2.appearTimer++ == 20) { gSaveContext.respawn[RESPAWN_MODE_TOP].data = respawnData + 1; Sfx_PlaySfxAtPos(&gSaveContext.respawn[RESPAWN_MODE_TOP].pos, NA_SE_PL_MAGIC_WIND_WARP); } @@ -15449,12 +15474,12 @@ static LinkAnimationHeader* D_80855190[] = { &gPlayerAnim_clink_demo_goto_future, }; -static Vec3f D_80855198 = { -1.0f, 70.0f, 20.0f }; - void func_808519EC(PlayState* play, Player* this, CsCmdActorCue* cue) { - Math_Vec3f_Copy(&this->actor.world.pos, &D_80855198); + static Vec3f sPedestalPos = { -1.0f, 70.0f, 20.0f }; + + Math_Vec3f_Copy(&this->actor.world.pos, &sPedestalPos); this->actor.shape.rot.y = -0x8000; - Player_AnimPlayOnceAdjusted(play, this, this->ageProperties->unk_9C); + Player_AnimPlayOnceAdjusted(play, this, this->ageProperties->timeTravelStartAnim); Player_StartAnimMovement(play, this, PLAYER_ANIM_MOVEMENT_RESET_BY_AGE | ANIM_FLAG_UPDATE_XZ | ANIM_FLAG_UPDATE_Y | ANIM_FLAG_DISABLE_CHILD_ROOT_ADJUSTMENT | ANIM_FLAG_ENABLE_MOVEMENT |