diff --git a/include/functions.h b/include/functions.h index bd20666ea7..0094464fb1 100644 --- a/include/functions.h +++ b/include/functions.h @@ -803,7 +803,7 @@ void Player_SetEquipmentData(PlayState* play, Player* this); void Player_UpdateBottleHeld(PlayState* play, Player* this, s32 item, s32 itemAction); void func_8008EDF0(Player* this); void func_8008EE08(Player* this); -void func_8008EEAC(PlayState* play, Actor* actor); +void Player_SetAutoLockOnActor(PlayState* play, Actor* actor); s32 func_8008EF44(PlayState* play, s32 ammo); int Player_IsBurningStickInRange(PlayState* play, Vec3f* pos, f32 xzRange, f32 yRange); s32 Player_GetStrength(void); diff --git a/include/z64player.h b/include/z64player.h index 7591e632ac..581c7f7b4a 100644 --- a/include/z64player.h +++ b/include/z64player.h @@ -803,7 +803,7 @@ typedef struct Player { /* 0x0678 */ PlayerAgeProperties* ageProperties; /* 0x067C */ u32 stateFlags1; /* 0x0680 */ u32 stateFlags2; - /* 0x0684 */ Actor* unk_684; + /* 0x0684 */ Actor* autoLockOnActor; // Actor that is locked onto automatically without player input; see `Player_SetAutoLockOnActor` /* 0x0688 */ Actor* boomerangActor; /* 0x068C */ Actor* naviActor; /* 0x0690 */ s16 naviTextId; diff --git a/src/code/z_player_lib.c b/src/code/z_player_lib.c index aefc14db8b..f589748f0d 100644 --- a/src/code/z_player_lib.c +++ b/src/code/z_player_lib.c @@ -742,12 +742,26 @@ void func_8008EE08(Player* this) { func_8008EDF0(this); } -void func_8008EEAC(PlayState* play, Actor* actor) { +/** + * Sets the "auto lock-on actor" to lock onto an actor without Player's input. + * This function will first release any existing lock-on or (try to) release parallel. + * + * When using Switch Targeting, it is not possible to carry an auto lock-on actor into a normal + * lock-on when the auto lock-on is finished. + * This is because the `PLAYER_STATE2_LOCK_ON_WITH_SWITCH` flag is never set with an auto lock-on. + * With Hold Targeting it is possible to keep the auto lock-on going by keeping the Z button held down. + * + * The auto lock-on is considered "friendly" even if the actor is actually hostile. If the auto lock-on is hostile, + * Player's battle response will not occur (if he is actionable) and the camera behaves differently. + * When transitioning from auto lock-on to normal lock-on (with Hold Targeting) there will be a noticeable change + * when it switches from "friendly" mode to "hostile" mode. + */ +void Player_SetAutoLockOnActor(PlayState* play, Actor* actor) { Player* this = GET_PLAYER(play); func_8008EE08(this); this->focusActor = actor; - this->unk_684 = actor; + this->autoLockOnActor = actor; this->stateFlags1 |= PLAYER_STATE1_FRIENDLY_ACTOR_FOCUS; Camera_SetViewParam(Play_GetCamera(play, CAM_ID_MAIN), CAM_VIEW_TARGET, actor); Camera_RequestMode(Play_GetCamera(play, CAM_ID_MAIN), CAM_MODE_Z_TARGET_FRIENDLY); diff --git a/src/overlays/actors/ovl_En_Dh/z_en_dh.c b/src/overlays/actors/ovl_En_Dh/z_en_dh.c index f1cbd0cb5b..1c4abe8a3e 100644 --- a/src/overlays/actors/ovl_En_Dh/z_en_dh.c +++ b/src/overlays/actors/ovl_En_Dh/z_en_dh.c @@ -233,7 +233,7 @@ void EnDh_Wait(EnDh* this, PlayState* play) { Math_SmoothStepToS(&this->actor.shape.rot.y, this->actor.yawTowardsPlayer, 1, 0x7D0, 0); SkelAnime_Update(&this->skelAnime); if (this->actor.params != ENDH_START_ATTACK_BOMB) { - func_8008EEAC(play, &this->actor); + Player_SetAutoLockOnActor(play, &this->actor); } } } diff --git a/src/overlays/actors/ovl_En_Rd/z_en_rd.c b/src/overlays/actors/ovl_En_Rd/z_en_rd.c index a6592d3c95..9509a1961c 100644 --- a/src/overlays/actors/ovl_En_Rd/z_en_rd.c +++ b/src/overlays/actors/ovl_En_Rd/z_en_rd.c @@ -361,8 +361,17 @@ void EnRd_WalkToPlayer(EnRd* this, PlayState* play) { if (this->playerStunWaitTimer == 0) { if (!(this->rdFlags & 0x80)) { player->actor.freezeTimer = 40; - func_8008EEAC(play, &this->actor); - GET_PLAYER(play)->unk_684 = &this->actor; + + // `player->actor.freezeTimer` gets set above which will prevent Player from updating. + // Because of this, he cannot update things related to Z-Targeting. + // If Player can't update, `player->zTargetActiveTimer` won't update, which means + // the Attention system will not be notified of a new actor lock-on occuring. + // So, no reticle will appear. But the camera will still focus on the actor. + Player_SetAutoLockOnActor(play, &this->actor); + + // This is redundant, `autoLockOnActor` gets set by `Player_SetAutoLockOnActor` above + GET_PLAYER(play)->autoLockOnActor = &this->actor; + Rumble_Request(this->actor.xzDistToPlayer, 255, 20, 150); } @@ -606,7 +615,9 @@ void EnRd_AttemptPlayerFreeze(EnRd* this, PlayState* play) { if (!(this->rdFlags & 0x80)) { player->actor.freezeTimer = 60; Rumble_Request(this->actor.xzDistToPlayer, 255, 20, 150); - func_8008EEAC(play, &this->actor); + + // The same note mentioned with this function call in `EnRd_WalkToPlayer` applies here too + Player_SetAutoLockOnActor(play, &this->actor); } Actor_PlaySfx(&this->actor, NA_SE_EN_REDEAD_AIM); diff --git a/src/overlays/actors/ovl_player_actor/z_player.c b/src/overlays/actors/ovl_player_actor/z_player.c index 4a27921909..15b5847169 100644 --- a/src/overlays/actors/ovl_player_actor/z_player.c +++ b/src/overlays/actors/ovl_player_actor/z_player.c @@ -3612,7 +3612,8 @@ void func_80836BEC(Player* this, PlayState* play) { (this->stateFlags3 & PLAYER_STATE3_FLYING_WITH_HOOKSHOT)) { // Don't allow Z-Targeting in various states this->zTargetActiveTimer = 0; - } else if (zButtonHeld || (this->stateFlags2 & PLAYER_STATE2_LOCK_ON_WITH_SWITCH) || (this->unk_684 != NULL)) { + } else if (zButtonHeld || (this->stateFlags2 & PLAYER_STATE2_LOCK_ON_WITH_SWITCH) || + (this->autoLockOnActor != NULL)) { // While a lock-on is active, decrement the timer and hold it at 5. // Values under 5 indicate a lock-on has ended and will make the reticle release. // See usage toward the end of `Actor_UpdateAll`. @@ -3698,15 +3699,19 @@ void func_80836BEC(Player* this, PlayState* play) { } if (this->focusActor != NULL) { - if ((this->actor.category == ACTORCAT_PLAYER) && (this->focusActor != this->unk_684) && + if ((this->actor.category == ACTORCAT_PLAYER) && (this->focusActor != this->autoLockOnActor) && Attention_ShouldReleaseLockOn(this->focusActor, this, ignoreLeash)) { func_8008EDF0(this); this->stateFlags1 |= PLAYER_STATE1_LOCK_ON_FORCED_TO_RELEASE; } else if (this->focusActor != NULL) { this->focusActor->attentionPriority = 40; } - } else if (this->unk_684 != NULL) { - this->focusActor = this->unk_684; + } else if (this->autoLockOnActor != NULL) { + // Becaue of the previous if condition above, `autoLockOnActor` does not take precedence + // over `focusActor` if it already exists. + // However, `autoLockOnActor` is expected to be set with `Player_SetAutoLockOnActor` + // which will release any existing lock-on before setting the new one. + this->focusActor = this->autoLockOnActor; } } @@ -11495,7 +11500,7 @@ void Player_UpdateCommon(Player* this, PlayState* play, Input* input) { this->doorType = PLAYER_DOORTYPE_NONE; this->unk_8A1 = 0; - this->unk_684 = NULL; + this->autoLockOnActor = NULL; phi_f12 = ((this->bodyPartsPos[PLAYER_BODYPART_L_FOOT].y + this->bodyPartsPos[PLAYER_BODYPART_R_FOOT].y) * 0.5f) +