summaryrefslogtreecommitdiff
path: root/src/code
diff options
context:
space:
mode:
authorengineer124 <47598039+engineer124@users.noreply.github.com>2024-10-24 11:44:44 +1100
committerGitHub <noreply@github.com>2024-10-23 17:44:44 -0700
commitd60a032b114484a67add15bbfd6ba6a958a5bee3 (patch)
tree677086bc1ab52d63a7a88aba3e6ae49bcbf3c034 /src/code
parent5a958cad4d5ee29bab345381fd2f366ad620beb1 (diff)
Further Attention Docs (#1732)
* more attention docs * more docs * cleanup * more cleanup * spacing * more attention docs * small cleanup * consistency
Diffstat (limited to 'src/code')
-rw-r--r--src/code/z_actor.c464
-rw-r--r--src/code/z_camera.c21
-rw-r--r--src/code/z_en_item00.c2
3 files changed, 263 insertions, 224 deletions
diff --git a/src/code/z_actor.c b/src/code/z_actor.c
index a470420bf..4d15beb51 100644
--- a/src/code/z_actor.c
+++ b/src/code/z_actor.c
@@ -14,21 +14,21 @@ FaultClient sActorFaultClient; // 2 funcs
struct CollisionPoly* D_801ED8B0; // 1 func
s32 D_801ED8B4; // 2 funcs
-struct Actor* sTargetableNearestActor;
-struct Actor* sTargetablePrioritizedActor;
-struct Actor* D_801ED8C0;
-struct Actor* D_801ED8C4;
+struct Actor* sNearestAttentionActor;
+struct Actor* sPrioritizedAttentionActor;
+struct Actor* sNearestCameraDriftActor;
+struct Actor* sPrioritizedCameraDriftActor;
-f32 sTargetableNearestActorDistSq;
+f32 sNearestAttentionActorDistSq;
f32 sBgmEnemyDistSq;
-f32 D_801ED8D0;
-s32 sTargetablePrioritizedPriority;
-s32 D_801ED8D8;
-s16 sTargetPlayerRotY;
+f32 sNearestCameraDriftActorDistSq;
+s32 sHighestAttentionPriority;
+s32 sHighestCameraDriftPriority;
+s16 sAttentionPlayerRotY;
Mtx sActorHiliteMtx;
-struct Actor* D_801ED920; // 2 funcs. 1 out of z_actor
+struct Actor* gCameraDriftActor;
#include "z64actor.h"
@@ -64,8 +64,8 @@ struct Actor* D_801ED920; // 2 funcs. 1 out of z_actor
void Actor_KillAllOnHalfDayChange(PlayState* play, ActorContext* actorCtx);
Actor* Actor_SpawnEntry(ActorContext* actorCtx, ActorEntry* actorEntry, PlayState* play);
Actor* Actor_Delete(ActorContext* actorCtx, Actor* actor, PlayState* play);
-void Attention_GetTargetActor(PlayState* play, ActorContext* actorCtx, Actor** targetableP, Actor** arg3,
- Player* player);
+void Attention_FindActor(PlayState* play, ActorContext* actorCtx, Actor** attentionActorP, Actor** cameraDriftActorP,
+ Player* player);
s32 func_800BA2FC(PlayState* play, Actor* actor, Vec3f* projectedPos, f32 projectedW);
void Actor_AddToCategory(ActorContext* actorCtx, Actor* actor, u8 actorCategory);
Actor* Actor_RemoveFromCategory(PlayState* play, ActorContext* actorCtx, Actor* actorToRemove);
@@ -428,21 +428,12 @@ void Actor_GetProjectedPos(PlayState* play, Vec3f* worldPos, Vec3f* projectedPos
*invW = (*invW < 1.0f) ? 1.0f : (1.0f / *invW);
}
-void Attention_SetReticlePos(Attention* attention, s32 reticleNum, f32 x, f32 y, f32 z) {
- attention->lockOnReticles[reticleNum].pos.x = x;
- attention->lockOnReticles[reticleNum].pos.y = y;
- attention->lockOnReticles[reticleNum].pos.z = z;
-
- attention->lockOnReticles[reticleNum].radius = attention->reticleRadius;
-}
+typedef struct AttentionColor {
+ /* 0x0 */ Color_RGBA8 primary; // Used for Tatl's inner color, lock-on arrow, and lock-on reticle
+ /* 0x4 */ Color_RGBA8 secondary; // Used for Tatl's outer color
+} AttentionColor; // size = 0x8
-typedef struct {
- /* 0x0 */ Color_RGBA8 inner;
- /* 0x4 */ Color_RGBA8 outer;
-} TatlColor; // size = 0x8
-
-// For whatever reason it has an extra entry
-TatlColor sTatlColorList[] = {
+AttentionColor sAttentionColors[ACTORCAT_MAX + 1] = {
{ { 0, 255, 0, 255 }, { 0, 255, 0, 0 } }, // ACTORCAT_SWITCH
{ { 0, 255, 0, 255 }, { 0, 255, 0, 0 } }, // ACTORCAT_BG
{ { 255, 255, 230, 255 }, { 220, 160, 80, 0 } }, // ACTORCAT_PLAYER
@@ -455,59 +446,70 @@ TatlColor sTatlColorList[] = {
{ { 255, 255, 0, 255 }, { 200, 155, 0, 0 } }, // ACTORCAT_BOSS
{ { 0, 255, 0, 255 }, { 0, 255, 0, 0 } }, // ACTORCAT_DOOR
{ { 0, 255, 0, 255 }, { 0, 255, 0, 0 } }, // ACTORCAT_CHEST
- { { 0, 255, 0, 255 }, { 0, 255, 0, 0 } }, // ACTORCAT_MAX
+ { { 0, 255, 0, 255 }, { 0, 255, 0, 0 } }, // unused extra entry
};
+void Attention_SetReticlePos(Attention* attention, s32 reticleNum, f32 x, f32 y, f32 z) {
+ attention->lockOnReticles[reticleNum].pos.x = x;
+ attention->lockOnReticles[reticleNum].pos.y = y;
+ attention->lockOnReticles[reticleNum].pos.z = z;
+
+ attention->lockOnReticles[reticleNum].radius = attention->reticleRadius;
+}
+
void Attention_InitReticle(Attention* attention, ActorType actorCategory, PlayState* play) {
- TatlColor* tatlColorEntry;
- s32 i;
LockOnReticle* reticle;
+ AttentionColor* attentionColor = &sAttentionColors[actorCategory];
+ s32 i;
Math_Vec3f_Copy(&attention->reticlePos, &play->view.eye);
+
+ attention->reticleRadius = 500.0f; // radius starts wide to zoom in on the actor
attention->reticleFadeAlphaControl = 256;
- tatlColorEntry = &sTatlColorList[actorCategory];
- attention->reticleRadius = 500.0f;
- reticle = attention->lockOnReticles;
+ reticle = &attention->lockOnReticles[0];
+
for (i = 0; i < ARRAY_COUNT(attention->lockOnReticles); i++, reticle++) {
Attention_SetReticlePos(attention, i, 0.0f, 0.0f, 0.0f);
- reticle->color.r = tatlColorEntry->inner.r;
- reticle->color.g = tatlColorEntry->inner.g;
- reticle->color.b = tatlColorEntry->inner.b;
+ reticle->color.r = attentionColor->primary.r;
+ reticle->color.g = attentionColor->primary.g;
+ reticle->color.b = attentionColor->primary.b;
}
}
-void Attention_SetFairyState(Attention* attention, Actor* actor, ActorType type, PlayState* play) {
- attention->fairyPos.x = actor->focus.pos.x;
- attention->fairyPos.y = actor->focus.pos.y + (actor->targetArrowOffset * actor->scale.y);
- attention->fairyPos.z = actor->focus.pos.z;
+void Attention_SetTatlState(Attention* attention, Actor* actor, ActorType actorCategory, PlayState* play) {
+ AttentionColor* attentionColor = &sAttentionColors[actorCategory];
+
+ attention->tatlHoverPos.x = actor->focus.pos.x;
+ attention->tatlHoverPos.y = actor->focus.pos.y + (actor->lockOnArrowOffset * actor->scale.y);
+ attention->tatlHoverPos.z = actor->focus.pos.z;
+
+ attention->tatlInnerColor.r = attentionColor->primary.r;
+ attention->tatlInnerColor.g = attentionColor->primary.g;
+ attention->tatlInnerColor.b = attentionColor->primary.b;
+ attention->tatlInnerColor.a = attentionColor->primary.a;
- attention->fairyInnerColor.r = sTatlColorList[type].inner.r;
- attention->fairyInnerColor.g = sTatlColorList[type].inner.g;
- attention->fairyInnerColor.b = sTatlColorList[type].inner.b;
- attention->fairyInnerColor.a = sTatlColorList[type].inner.a;
- attention->fairyOuterColor.r = sTatlColorList[type].outer.r;
- attention->fairyOuterColor.g = sTatlColorList[type].outer.g;
- attention->fairyOuterColor.b = sTatlColorList[type].outer.b;
- attention->fairyOuterColor.a = sTatlColorList[type].outer.a;
+ attention->tatlOuterColor.r = attentionColor->secondary.r;
+ attention->tatlOuterColor.g = attentionColor->secondary.g;
+ attention->tatlOuterColor.b = attentionColor->secondary.b;
+ attention->tatlOuterColor.a = attentionColor->secondary.a;
}
void Attention_Init(Attention* attention, Actor* actor, PlayState* play) {
- attention->bgmEnemy = NULL;
- attention->forcedTargetActor = NULL;
- attention->reticleActor = NULL;
- attention->fairyActor = NULL;
+ attention->tatlHoverActor = attention->reticleActor = attention->forcedLockOnActor = attention->bgmEnemy = NULL;
+
attention->reticleSpinCounter = 0;
attention->curReticle = 0;
- attention->fairyMoveProgressFactor = 0.0f;
- Attention_SetFairyState(attention, actor, actor->category, play);
+ attention->tatlMoveProgressFactor = 0.0f;
+
+ Attention_SetTatlState(attention, actor, actor->category, play);
Attention_InitReticle(attention, actor->category, play);
}
void Attention_Draw(Attention* attention, PlayState* play) {
Player* player = GET_PLAYER(play);
- Actor* actor;
+ Actor* actor; // used for both the reticle actor and arrow hover actor
if (player->stateFlags1 & (PLAYER_STATE1_2 | PLAYER_STATE1_40 | PLAYER_STATE1_80 | PLAYER_STATE1_200 |
PLAYER_STATE1_400 | PLAYER_STATE1_10000000 | PLAYER_STATE1_20000000)) {
@@ -521,7 +523,7 @@ void Attention_Draw(Attention* attention, PlayState* play) {
if (attention->reticleFadeAlphaControl != 0) {
LockOnReticle* reticle;
s16 alpha = 255;
- f32 projectdPosScale = 1.0f;
+ f32 projectedPosScale = 1.0f;
Vec3f projectedPos;
s32 numReticles;
f32 invW;
@@ -540,7 +542,7 @@ void Attention_Draw(Attention* attention, PlayState* play) {
if (actor != NULL) {
Math_Vec3f_Copy(&attention->reticlePos, &actor->focus.pos);
- projectdPosScale = (500.0f - attention->reticleRadius) / 420.0f;
+ projectedPosScale = (500.0f - attention->reticleRadius) / 420.0f;
} else {
// Not locked on, start fading out
attention->reticleFadeAlphaControl -= 120;
@@ -556,13 +558,13 @@ void Attention_Draw(Attention* attention, PlayState* play) {
Actor_GetProjectedPos(play, &attention->reticlePos, &projectedPos, &invW);
- projectedPos.x = ((SCREEN_WIDTH / 2) * (projectedPos.x * invW)) * projectdPosScale;
+ projectedPos.x = ((SCREEN_WIDTH / 2) * (projectedPos.x * invW)) * projectedPosScale;
projectedPos.x = CLAMP(projectedPos.x, -SCREEN_WIDTH, SCREEN_WIDTH);
- projectedPos.y = ((SCREEN_HEIGHT / 2) * (projectedPos.y * invW)) * projectdPosScale;
+ projectedPos.y = ((SCREEN_HEIGHT / 2) * (projectedPos.y * invW)) * projectedPosScale;
projectedPos.y = CLAMP(projectedPos.y, -SCREEN_HEIGHT, SCREEN_HEIGHT);
- projectedPos.z *= projectdPosScale;
+ projectedPos.z *= projectedPosScale;
attention->curReticle--;
@@ -616,19 +618,20 @@ void Attention_Draw(Attention* attention, PlayState* play) {
}
}
- actor = attention->arrowPointedActor;
+ actor = attention->arrowHoverActor;
if ((actor != NULL) && !(actor->flags & ACTOR_FLAG_LOCK_ON_DISABLED)) {
- TatlColor* color = &sTatlColorList[actor->category];
+ AttentionColor* attentionColor = &sAttentionColors[actor->category];
POLY_XLU_DISP = Gfx_SetupDL(POLY_XLU_DISP, SETUPDL_7);
- Matrix_Translate(actor->focus.pos.x, actor->focus.pos.y + (actor->targetArrowOffset * actor->scale.y) + 17.0f,
+ Matrix_Translate(actor->focus.pos.x, actor->focus.pos.y + (actor->lockOnArrowOffset * actor->scale.y) + 17.0f,
actor->focus.pos.z, MTXMODE_NEW);
Matrix_RotateYS(play->gameplayFrames * 0xBB8, MTXMODE_APPLY);
Matrix_Scale((iREG(27) + 35) / 1000.0f, (iREG(28) + 60) / 1000.0f, (iREG(29) + 50) / 1000.0f, MTXMODE_APPLY);
- gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, color->inner.r, color->inner.g, color->inner.b, 255);
+ gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, attentionColor->primary.r, attentionColor->primary.g,
+ attentionColor->primary.b, 255);
MATRIX_FINALIZE_AND_LOAD(POLY_XLU_DISP++, play->state.gfxCtx);
gSPDisplayList(POLY_XLU_DISP++, gLockOnArrowDL);
}
@@ -638,24 +641,32 @@ void Attention_Draw(Attention* attention, PlayState* play) {
void Attention_Update(Attention* attention, Player* player, Actor* playerFocusActor, PlayState* play) {
s32 pad;
- Actor* actor = NULL;
+ Actor* actor; // used for both the Tatl hover actor and reticle actor
s32 category;
Vec3f projectedPos;
f32 invW;
- // If currently not locked on to an actor and not pressing down on the analog stick then try to find a targetable
- // actor
+ actor = NULL;
+
if ((player->focusActor != NULL) && (player->unk_AE3[player->unk_ADE] == 2)) {
- attention->arrowPointedActor = NULL;
+ // Holding backward on the control stick prevents an arrow appearing over the next lock-on actor.
+ // This helps escape a lock-on loop when using Switch Targeting, but note that this still works for
+ // Hold Targeting as well.
+ attention->arrowHoverActor = NULL;
} else {
- Attention_GetTargetActor(play, &play->actorCtx, &actor, &D_801ED920, player);
- attention->arrowPointedActor = actor;
+ // Find the next attention actor so Tatl and an arrow can hover over it (if applicable)
+ Attention_FindActor(play, &play->actorCtx, &actor, &gCameraDriftActor, player);
+ attention->arrowHoverActor = actor;
}
- if (attention->forcedTargetActor != NULL) {
- actor = attention->forcedTargetActor;
- attention->forcedTargetActor = NULL;
+ if (attention->forcedLockOnActor != NULL) {
+ // This lock-on actor takes precedence over anything else
+ // (this feature is never used in practice)
+ actor = attention->forcedLockOnActor;
+ attention->forcedLockOnActor = NULL;
} else if (playerFocusActor != NULL) {
+ // Stay locked-on to the same actor, if there is one.
+ // This also makes Tatl fly over to the current focus actor, if there is one.
actor = playerFocusActor;
}
@@ -665,77 +676,94 @@ void Attention_Update(Attention* attention, Player* player, Actor* playerFocusAc
category = player->actor.category;
}
- if ((actor != attention->fairyActor) || (category != attention->fairyActorCategory)) {
- attention->fairyActor = actor;
- attention->fairyActorCategory = category;
- attention->fairyMoveProgressFactor = 1.0f;
+ if ((actor != attention->tatlHoverActor) || (category != attention->tatlHoverActorCategory)) {
+ // Set Tatl to hover over a new actor
+ attention->tatlHoverActor = actor;
+ attention->tatlHoverActorCategory = category;
+ attention->tatlMoveProgressFactor = 1.0f;
}
if (actor == NULL) {
+ // Setting the actor to Player will make Tatl return to him
actor = &player->actor;
}
- if (!Math_StepToF(&attention->fairyMoveProgressFactor, 0.0f, 0.25f)) {
- f32 fairyMoveScale = 0.25f / attention->fairyMoveProgressFactor;
- f32 x = actor->focus.pos.x - attention->fairyPos.x;
- f32 y = (actor->focus.pos.y + (actor->targetArrowOffset * actor->scale.y)) - attention->fairyPos.y;
- f32 z = actor->focus.pos.z - attention->fairyPos.z;
+ if (!Math_StepToF(&attention->tatlMoveProgressFactor, 0.0f, 0.25f)) {
+ f32 moveScale = 0.25f / attention->tatlMoveProgressFactor;
+ f32 x = actor->focus.pos.x - attention->tatlHoverPos.x;
+ f32 y = (actor->focus.pos.y + (actor->lockOnArrowOffset * actor->scale.y)) - attention->tatlHoverPos.y;
+ f32 z = actor->focus.pos.z - attention->tatlHoverPos.z;
- attention->fairyPos.x += x * fairyMoveScale;
- attention->fairyPos.y += y * fairyMoveScale;
- attention->fairyPos.z += z * fairyMoveScale;
+ attention->tatlHoverPos.x += x * moveScale;
+ attention->tatlHoverPos.y += y * moveScale;
+ attention->tatlHoverPos.z += z * moveScale;
} else {
- Attention_SetFairyState(attention, actor, category, play);
+ // Set Tatl pos and color after reaching destination
+ Attention_SetTatlState(attention, actor, category, play);
}
if ((playerFocusActor != NULL) && (attention->reticleSpinCounter == 0)) {
Actor_GetProjectedPos(play, &playerFocusActor->focus.pos, &projectedPos, &invW);
+
if ((projectedPos.z <= 0.0f) || (fabsf(projectedPos.x * invW) >= 1.0f) ||
(fabsf(projectedPos.y * invW) >= 1.0f)) {
+ // Release the reticle if the actor is off screen.
+ // It is possible to move far enough away from an actor that it goes off screen, despite being
+ // locked onto it. In this case the reticle will release, but the lock-on will remain
+ // because Player is still updating focusActor.
+ // It is unclear if this is intentional, or if it is a bug and the lock-on as a whole is supposed
+ // to release.
playerFocusActor = NULL;
}
}
if (playerFocusActor != NULL) {
if (playerFocusActor != attention->reticleActor) {
- s32 sfxId;
+ s32 lockOnSfxId;
- // Lock On entries need to be re-initialized when changing the targeted actor
+ // Set up a new reticle
Attention_InitReticle(attention, playerFocusActor->category, play);
-
attention->reticleActor = playerFocusActor;
if (playerFocusActor->id == ACTOR_EN_BOOM) {
- // Avoid drawing the lock on triangles on a zora boomerang
+ // Don't draw the reticle when locked onto the zora fin boomerang.
+ // Note that it isn't possible to lock onto the boomerang, so this code doesn't do anything.
+ // This implies that the boomerang camera lock may have been implemented with Z-Targeting at one point,
+ // but was eventually implemented as its own camera mode instead.
attention->reticleFadeAlphaControl = 0;
}
- sfxId = CHECK_FLAG_ALL(playerFocusActor->flags, ACTOR_FLAG_ATTENTION_ENABLED | ACTOR_FLAG_HOSTILE)
- ? NA_SE_SY_LOCK_ON
- : NA_SE_SY_LOCK_ON_HUMAN;
- Audio_PlaySfx(sfxId);
+ lockOnSfxId = CHECK_FLAG_ALL(playerFocusActor->flags, ACTOR_FLAG_ATTENTION_ENABLED | ACTOR_FLAG_HOSTILE)
+ ? NA_SE_SY_LOCK_ON
+ : NA_SE_SY_LOCK_ON_HUMAN;
+ Audio_PlaySfx(lockOnSfxId);
}
+ // Update reticle
+
attention->reticlePos.x = playerFocusActor->world.pos.x;
attention->reticlePos.y =
playerFocusActor->world.pos.y - (playerFocusActor->shape.yOffset * playerFocusActor->scale.y);
attention->reticlePos.z = playerFocusActor->world.pos.z;
if (attention->reticleSpinCounter == 0) {
- f32 lockOnStep = (500.0f - attention->reticleRadius) * 3.0f;
-
- lockOnStep = CLAMP(lockOnStep, 30.0f, 100.0f);
+ f32 step = (500.0f - attention->reticleRadius) * 3.0f;
+ f32 reticleZoomStep = CLAMP(step, 30.0f, 100.0f);
- if (Math_StepToF(&attention->reticleRadius, 80.0f, lockOnStep)) {
+ if (Math_StepToF(&attention->reticleRadius, 80.0f, reticleZoomStep)) {
+ // Non-zero counter indicates the reticle is done zooming in
attention->reticleSpinCounter++;
}
} else {
- // 0x80 is or'd to avoid getting this value be set to zero
+ // Finished zooming in, spin the reticle around the lock-on actor
+
+ // 0x80 is or'd to avoid a value of zero.
// This rotation value gets multiplied by 0x200, which multiplied by 0x80 gives a full turn (0x10000)
attention->reticleSpinCounter = (attention->reticleSpinCounter + 3) | 0x80;
attention->reticleRadius = 120.0f;
}
} else {
+ // Expand the radius quickly as the reticle is released
attention->reticleActor = NULL;
Math_StepToF(&attention->reticleRadius, 500.0f, 80.0f);
}
@@ -1856,32 +1884,32 @@ PosRot Actor_GetWorldPosShapeRot(Actor* actor) {
/**
* Returns the squared xyz distance from the actor to Player.
- *
- * This distance will be adjusted smaller if Player is already targeting an actor. The more Player is facing the actor,
- * the smaller the distance is adjusted.
+ * This distance will be weighted if Player is already locked onto another actor.
*/
-f32 Attention_GetAdjustedDistSq(Actor* actor, Player* player, s16 playerShapeYaw) {
+f32 Attention_WeightedDistToPlayerSq(Actor* actor, Player* player, s16 playerShapeYaw) {
f32 adjDistSq;
- s16 yawDiff;
-
- // The yaw, with player as the origin, from where player is facing to where the actor is positioned
- yawDiff = ABS_ALT(BINANG_SUB(BINANG_SUB(actor->yawTowardsPlayer, 0x8000), playerShapeYaw));
+ s16 yawDiffAbs = ABS_ALT(BINANG_SUB(BINANG_SUB(actor->yawTowardsPlayer, 0x8000), playerShapeYaw));
if (player->focusActor != NULL) {
- if ((yawDiff > 0x4000) || (actor->flags & ACTOR_FLAG_LOCK_ON_DISABLED)) {
+ if ((yawDiffAbs > 0x4000) || (actor->flags & ACTOR_FLAG_LOCK_ON_DISABLED)) {
return FLT_MAX;
}
- // Linear scaling, yaw being 90 degree means it will return the original distance, 0 degree will adjust to 60%
- // of the distance
+ // The distance returned is scaled down as the player faces more toward the actor.
+ // At 90 degrees, 100% of the original distance will be returned.
+ // This scales down linearly to 60% when facing 0 degrees away.
adjDistSq =
- actor->xyzDistToPlayerSq - ((actor->xyzDistToPlayerSq * 0.8f) * ((0x4000 - yawDiff) * (1.0f / 0x8000)));
+ actor->xyzDistToPlayerSq - ((actor->xyzDistToPlayerSq * 0.8f) * ((0x4000 - yawDiffAbs) * (1.0f / 0x8000)));
+
return adjDistSq;
}
- if (yawDiff > (0x10000 / 6)) {
+ // Player has to be facing less than ~60 degrees away from the actor
+ if (yawDiffAbs > (0x10000 / 6)) {
return FLT_MAX;
}
+
+ // Unweighted distSq
return actor->xyzDistToPlayerSq;
}
@@ -1903,37 +1931,46 @@ AttentionRangeParams gAttentionRanges[ATTENTION_RANGE_MAX] = {
};
/**
- * Checks if an actor at distance `distSq` is inside the range specified by its attentionRangeType
+ * Checks if an actor at `distSq` is inside the range specified by its `attentionRangeType`.
+ *
+ * Note that this gets used for both the attention range check and for the lock-on leash range check.
+ * Despite how the data is presented in `gAttentionRanges`, the leash range is stored as a scale factor value.
+ * When checking the leash range, this scale factor is applied to the input distance and checked against
+ * the base `attentionRangeSq` value, which was used to initiate the lock-on in the first place.
*/
-s32 Attention_IsActorInRange(Actor* actor, f32 distSq) {
- return distSq < gAttentionRanges[actor->attentionRangeType].rangeSq;
+s32 Attention_ActorIsInRange(Actor* actor, f32 distSq) {
+ return distSq < gAttentionRanges[actor->attentionRangeType].attentionRangeSq;
}
/**
- * Returns true if the actor is outside the leash distance to player.
+ * Returns true if an actor lock-on should be released.
+ * This function does not actually release the lock-on, as that is Player's responsibility.
+ *
+ * If an actor's update function is NULL or `ACTOR_FLAG_ATTENTION_ENABLED` is unset, the lock-on should be released.
+ *
+ * There is also a check for Player exceeding the lock-on leash distance.
+ * Note that this check will be ignored if `ignoreLeash` is true.
*
- * Passing true to ignoreLeash avoids the distance and yaw checks, and considers the actor inside the leash distance.
*/
-s32 Attention_OutsideLeashRange(Actor* actor, Player* player, s32 ignoreLeash) {
+s32 Attention_ShouldReleaseLockOn(Actor* actor, Player* player, s32 ignoreLeash) {
if ((actor->update == NULL) || !(actor->flags & ACTOR_FLAG_ATTENTION_ENABLED) ||
(actor->flags & ACTOR_FLAG_LOCK_ON_DISABLED)) {
return true;
}
if (!ignoreLeash) {
- s16 yawDiff;
+ s16 yawDiffAbs = ABS_ALT(BINANG_SUB(BINANG_SUB(actor->yawTowardsPlayer, 0x8000), player->actor.shape.rot.y));
f32 distSq;
- // The yaw, with player as the origin, from where player is facing to where the actor is positioned
- yawDiff = ABS_ALT(BINANG_SUB(BINANG_SUB(actor->yawTowardsPlayer, 0x8000), player->actor.shape.rot.y));
-
- if ((player->focusActor == NULL) && (yawDiff > (0x10000 / 6))) {
+ if ((player->focusActor == NULL) && (yawDiffAbs > (0x10000 / 6))) {
+ // This function is only called (and is only relevant) when `player->focusActor != NULL`.
+ // This is unreachable.
distSq = FLT_MAX;
} else {
distSq = actor->xyzDistToPlayerSq;
}
- return !Attention_IsActorInRange(actor, gAttentionRanges[actor->attentionRangeType].leashScale * distSq);
+ return !Attention_ActorIsInRange(actor, gAttentionRanges[actor->attentionRangeType].lockOnLeashScale * distSq);
}
return false;
@@ -3493,12 +3530,12 @@ Actor* Actor_Delete(ActorContext* actorCtx, Actor* actor, PlayState* play) {
Camera_ChangeMode(Play_GetCamera(play, Play_GetActiveCamId(play)), CAM_MODE_NORMAL);
}
- if (actor == actorCtx->attention.fairyActor) {
- actorCtx->attention.fairyActor = NULL;
+ if (actor == actorCtx->attention.tatlHoverActor) {
+ actorCtx->attention.tatlHoverActor = NULL;
}
- if (actor == actorCtx->attention.forcedTargetActor) {
- actorCtx->attention.forcedTargetActor = NULL;
+ if (actor == actorCtx->attention.forcedLockOnActor) {
+ actorCtx->attention.forcedLockOnActor = NULL;
}
if (actor == actorCtx->attention.bgmEnemy) {
@@ -3519,61 +3556,57 @@ Actor* Actor_Delete(ActorContext* actorCtx, Actor* actor, PlayState* play) {
return newHead;
}
-bool Attention_InTargetableScreenRegion(PlayState* play, Actor* actor) {
- s16 screenPosX;
- s16 screenPosY;
+/**
+ * Checks that an actor is on-screen enough to be considered an attention actor.
+ *
+ * Note that the screen bounds checks are larger than the actual screen region
+ * to give room for error.
+ */
+bool Attention_ActorOnScreen(PlayState* play, Actor* actor) {
+ s16 x;
+ s16 y;
+
+ Actor_GetScreenPos(play, actor, &x, &y);
- Actor_GetScreenPos(play, actor, &screenPosX, &screenPosY);
+#define X_LEEWAY 20
+#define Y_LEEWAY 160
- return (screenPosX > -20) && (screenPosX < gScreenWidth + 20) && (screenPosY > -160) &&
- (screenPosY < gScreenHeight + 160);
+ return (x > (0 - X_LEEWAY)) && (x < (gScreenWidth + X_LEEWAY)) && (y > (0 - Y_LEEWAY)) &&
+ (y < (gScreenHeight + Y_LEEWAY));
}
/**
- * Search for targetable actors of the `actorCategory` category.
+ * Search for attention actors or camera drift actors within the specified category.
*
- * Looks for the actor of said category with higher targetPriority and the one that is nearest to player. This actor
- * must be within the range (relative to player) speicified by its attentionRangeType.
+ * To be considered an attention actor the actor needs to:
+ * - Have a non-NULL update function (still active)
+ * - Not be player (this is technically a redundant check because the PLAYER category is never searched)
+ * - Have `ACTOR_FLAG_ATTENTION_ENABLED` or `ACTOR_FLAG_FOCUS_ACTOR_REFINDABLE` set
+ * - Not be the current focus actor unless `ACTOR_FLAG_FOCUS_ACTOR_REFINDABLE` is set
+ * - Be the closest attention actor found so far
+ * - Be within range, specified by attentionRangeType
+ * - Be roughly on-screen
+ * - Not be blocked by a surface
*
- * The actor must be on-screen
- *
- * The highest priority actor is stored in `sTargetablePrioritizedActor`, while the nearest actor is stored in
- * `sTargetableNearestActor`. The higher priority / smaller distance of those actors are stored in
- * `sTargetablePrioritizedPriority` and `sTargetableNearestActorDistSq`.
- *
- * There is not much info to infer anything about ACTOR_FLAG_40000000, D_801ED8C4/D_801ED8C0, D_801ED8D8/D_801ED8D0.
- * All appear unused in any meaningful way.
- *
- * For an actor to be taken in consideration by this function it needs:
- * - Non-NULL update function (maybe obvious?)
- * - Not be Player itself.
- * - It must be targetable or ACTOR_FLAG_40000000
- * - Not be the already targeted actor, unless it has the ACTOR_FLAG_80000 flag
- * - Be withing the range specified by its attentionRangeType.
- * - It must be on-screen (within a margin)
- * - Must not be blocked by a surface (?)
- *
- * This function also checks for the nearest enemy actor, which allows determining if enemy background music should be
- * played. This actor is stored in `attention.bgmEnemy` and its distance is stored in `sBgmEnemyDistSq`
+ * If an actor has a priority value set and the value is the lowest found so far, it will be set as the prioritized
+ * attention actor. Otherwise, it is set as the nearest attention actor or camera drift actor.
*
* This function is expected to be called with almost every actor category in each cycle. On a new cycle its global
- * variables must be reset by the caller, otherwise the information of the previous cycle will be retained on this one.
+ * variables must be reset by the caller, otherwise the information of the previous cycle will be retained.
*/
-void Attention_FindTargetableActorForCategory(PlayState* play, ActorContext* actorCtx, Player* player,
- ActorType actorCategory) {
+void Attention_FindActorInCategory(PlayState* play, ActorContext* actorCtx, Player* player, ActorType actorCategory) {
f32 distSq;
Actor* actor = actorCtx->actorLists[actorCategory].first;
Actor* playerFocusActor = player->focusActor;
- s32 isNearestTargetableActor;
- s32 phi_s2_2;
+ s32 isNearestAttentionActor;
+ s32 isNearestCameraDriftActor;
for (; actor != NULL; actor = actor->next) {
if ((actor->update == NULL) || ((Player*)actor == player)) {
continue;
}
- // Actor must be at least either targetable or ACTOR_FLAG_40000000
- if (!(actor->flags & (ACTOR_FLAG_ATTENTION_ENABLED | ACTOR_FLAG_40000000))) {
+ if (!(actor->flags & (ACTOR_FLAG_ATTENTION_ENABLED | ACTOR_FLAG_CAMERA_DRIFT_ENABLED))) {
continue;
}
@@ -3586,107 +3619,110 @@ void Attention_FindTargetableActorForCategory(PlayState* play, ActorContext* act
}
}
- // If this actor is the currently targeted one, then ignore it unless it has the ACTOR_FLAG_80000 flag
- if ((actor == playerFocusActor) && !(actor->flags & ACTOR_FLAG_80000)) {
+ // Ignore the current focus actor unless it is refindable
+ if ((actor == playerFocusActor) && !(actor->flags & ACTOR_FLAG_FOCUS_ACTOR_REFINDABLE)) {
continue;
}
- distSq = Attention_GetAdjustedDistSq(actor, player, sTargetPlayerRotY);
+ distSq = Attention_WeightedDistToPlayerSq(actor, player, sAttentionPlayerRotY);
+
+ isNearestAttentionActor =
+ (actor->flags & ACTOR_FLAG_ATTENTION_ENABLED) && (distSq < sNearestAttentionActorDistSq);
- isNearestTargetableActor =
- (actor->flags & ACTOR_FLAG_ATTENTION_ENABLED) && (distSq < sTargetableNearestActorDistSq);
- phi_s2_2 = (actor->flags & ACTOR_FLAG_40000000) && (distSq < D_801ED8D0);
+ isNearestCameraDriftActor =
+ (actor->flags & ACTOR_FLAG_CAMERA_DRIFT_ENABLED) && (distSq < sNearestCameraDriftActorDistSq);
- if (!isNearestTargetableActor && !phi_s2_2) {
+ if (!isNearestAttentionActor && !isNearestCameraDriftActor) {
continue;
}
- if (Attention_IsActorInRange(actor, distSq) && Attention_InTargetableScreenRegion(play, actor)) {
+ if (Attention_ActorIsInRange(actor, distSq) && Attention_ActorOnScreen(play, actor)) {
CollisionPoly* poly;
s32 bgId;
- Vec3f posResult;
+ Vec3f lineTestResultPos;
- if (BgCheck_CameraLineTest1(&play->colCtx, &player->actor.focus.pos, &actor->focus.pos, &posResult, &poly,
- true, true, true, true, &bgId)) {
+ if (BgCheck_CameraLineTest1(&play->colCtx, &player->actor.focus.pos, &actor->focus.pos, &lineTestResultPos,
+ &poly, true, true, true, true, &bgId)) {
if (!SurfaceType_IsIgnoredByProjectiles(&play->colCtx, poly, bgId)) {
continue;
}
}
if (actor->targetPriority != 0) {
- if (isNearestTargetableActor && (actor->targetPriority < sTargetablePrioritizedPriority)) {
- sTargetablePrioritizedActor = actor;
- sTargetablePrioritizedPriority = actor->targetPriority;
+ if (isNearestAttentionActor && (actor->targetPriority < sHighestAttentionPriority)) {
+ sPrioritizedAttentionActor = actor;
+ sHighestAttentionPriority = actor->targetPriority;
}
- if (phi_s2_2 && (actor->targetPriority < D_801ED8D8)) {
- D_801ED8C4 = actor;
- D_801ED8D8 = actor->targetPriority;
+ if (isNearestCameraDriftActor && (actor->targetPriority < sHighestCameraDriftPriority)) {
+ sPrioritizedCameraDriftActor = actor;
+ sHighestCameraDriftPriority = actor->targetPriority;
}
} else {
- if (isNearestTargetableActor) {
- sTargetableNearestActor = actor;
- sTargetableNearestActorDistSq = distSq;
+ if (isNearestAttentionActor) {
+ sNearestAttentionActor = actor;
+ sNearestAttentionActorDistSq = distSq;
}
- if (phi_s2_2) {
- D_801ED8C0 = actor;
- D_801ED8D0 = distSq;
+ if (isNearestCameraDriftActor) {
+ sNearestCameraDriftActor = actor;
+ sNearestCameraDriftActorDistSq = distSq;
}
}
}
}
}
-u8 sTargetableActorCategories[] = {
+u8 sAttentionCategorySearchOrder[] = {
ACTORCAT_BOSS, ACTORCAT_ENEMY, ACTORCAT_BG, ACTORCAT_EXPLOSIVES, ACTORCAT_NPC, ACTORCAT_ITEMACTION,
ACTORCAT_CHEST, ACTORCAT_SWITCH, ACTORCAT_PROP, ACTORCAT_MISC, ACTORCAT_DOOR, ACTORCAT_SWITCH,
};
/**
- * Search for the nearest targetable actor.
- *
- * The specific criteria is specified in Attention_FindTargetableActorForCategory.
+ * Search for the nearest attention actor and camera drift actor by iterating through most actor categories.
+ * See `Attention_FindActorInCategory` for more details on search criteria.
*
- * The actor found is stored in the targetableP parameter. It may be NULL if no actor that fulfills the criteria is
- * found.
+ * The attention actor found is stored in the `attentionActorP` parameter, while the camera drift actor is stored in
+ * `cameraDriftActorP` They may be NULL if no actor that fulfills the criteria is found.
*/
-void Attention_GetTargetActor(PlayState* play, ActorContext* actorCtx, Actor** targetableP, Actor** arg3,
- Player* player) {
- u8* actorCategories;
+void Attention_FindActor(PlayState* play, ActorContext* actorCtx, Actor** attentionActorP, Actor** cameraDriftActorP,
+ Player* player) {
+ u8* category;
s32 i;
- sTargetableNearestActor = sTargetablePrioritizedActor = D_801ED8C0 = D_801ED8C4 = NULL;
- sTargetableNearestActorDistSq = D_801ED8D0 = sBgmEnemyDistSq = FLT_MAX;
- sTargetablePrioritizedPriority = D_801ED8D8 = INT32_MAX;
+ sNearestAttentionActor = sPrioritizedAttentionActor = sNearestCameraDriftActor = sPrioritizedCameraDriftActor =
+ NULL;
+ sNearestAttentionActorDistSq = sNearestCameraDriftActorDistSq = sBgmEnemyDistSq = FLT_MAX;
+ sHighestAttentionPriority = sHighestCameraDriftPriority = INT32_MAX;
actorCtx->attention.bgmEnemy = NULL;
- sTargetPlayerRotY = player->actor.shape.rot.y;
+ sAttentionPlayerRotY = player->actor.shape.rot.y;
- actorCategories = sTargetableActorCategories;
+ category = sAttentionCategorySearchOrder;
- // Try to search for a targetable actor that's a Boss, Enemy or Bg first
+ // Search the first 3 actor categories first for an attention actor
+ // These are Boss, Enemy, and Bg, in order.
for (i = 0; i < 3; i++) {
- Attention_FindTargetableActorForCategory(play, actorCtx, player, *actorCategories);
- actorCategories++;
+ Attention_FindActorInCategory(play, actorCtx, player, *category);
+ category++;
}
- // If no actor in the above categories was found then try to search for one in every other category
- if (sTargetableNearestActor == NULL) {
- for (; i < ARRAY_COUNT(sTargetableActorCategories); i++) {
- Attention_FindTargetableActorForCategory(play, actorCtx, player, *actorCategories);
- actorCategories++;
+ // If no actor in the above categories was found, then try searching in the remaining categories
+ if (sNearestAttentionActor == NULL) {
+ for (; i < ARRAY_COUNT(sAttentionCategorySearchOrder); i++) {
+ Attention_FindActorInCategory(play, actorCtx, player, *category);
+ category++;
}
}
- if (sTargetableNearestActor == NULL) {
- *targetableP = sTargetablePrioritizedActor;
+ if (sNearestAttentionActor == NULL) {
+ *attentionActorP = sPrioritizedAttentionActor;
} else {
- *targetableP = sTargetableNearestActor;
+ *attentionActorP = sNearestAttentionActor;
}
- if (D_801ED8C0 == NULL) {
- *arg3 = D_801ED8C4;
+ if (sNearestCameraDriftActor == NULL) {
+ *cameraDriftActorP = sPrioritizedCameraDriftActor;
} else {
- *arg3 = D_801ED8C0;
+ *cameraDriftActorP = sNearestCameraDriftActor;
}
}
diff --git a/src/code/z_camera.c b/src/code/z_camera.c
index ab9252ad9..b2570386f 100644
--- a/src/code/z_camera.c
+++ b/src/code/z_camera.c
@@ -1274,9 +1274,12 @@ f32 Camera_CalcSlopeYAdj(Vec3f* floorNorm, s16 playerYRot, s16 eyeAtYaw, f32 adj
return (Camera_fabsf(tmp) * adjAmt) * Math_CosS(playerYRot - eyeAtYaw);
}
-f32 func_800CD6CC(Actor* actor) {
- return sqrtf(gAttentionRanges[actor->attentionRangeType].rangeSq /
- gAttentionRanges[actor->attentionRangeType].leashScale);
+/**
+ * Gets the distance that the attention actor is in range.
+ */
+f32 Camera_GetAttentionActorRange(Actor* actor) {
+ return sqrtf(gAttentionRanges[actor->attentionRangeType].attentionRangeSq /
+ gAttentionRanges[actor->attentionRangeType].lockOnLeashScale);
}
/**
@@ -1950,7 +1953,7 @@ s32 Camera_Normal1(Camera* camera) {
Vec3f spD8;
f32 spD4;
f32 spD0;
- Vec3f* temp;
+ Vec3f* cameraDriftActorPos;
f32 spC8;
f32 spC4;
f32 spC0;
@@ -2095,7 +2098,7 @@ s32 Camera_Normal1(Camera* camera) {
camera->yawUpdateRateInv, phi_f2, 0.1f);
if ((roData->interfaceFlags & NORMAL1_FLAG_3) && (camera->speedRatio > 0.01f)) {
camera->pitchUpdateRateInv = Camera_ScaledStepToCeilF(100.0f, camera->pitchUpdateRateInv, 0.5f, 0.1f);
- } else if (D_801ED920 != NULL) {
+ } else if (gCameraDriftActor != NULL) {
camera->pitchUpdateRateInv = Camera_ScaledStepToCeilF(32.0f, camera->pitchUpdateRateInv, 0.5f, 0.1f);
} else {
camera->pitchUpdateRateInv = Camera_ScaledStepToCeilF(16.0f, camera->pitchUpdateRateInv, 0.2f, 0.1f);
@@ -2202,7 +2205,7 @@ s32 Camera_Normal1(Camera* camera) {
spB4.yaw = sp9C.yaw;
spB4.pitch = sp9C.pitch;
camera->animState = 20;
- } else if (D_801ED920 != NULL) {
+ } else if (gCameraDriftActor != NULL) {
VecGeo sp74;
s16 sp72;
f32 sp6C;
@@ -2210,8 +2213,8 @@ s32 Camera_Normal1(Camera* camera) {
//! FAKE:
if (1) {}
- temp = &D_801ED920->world.pos;
- sp74 = OLib_Vec3fDiffToVecGeo(&focalActorPosRot->pos, temp);
+ cameraDriftActorPos = &gCameraDriftActor->world.pos;
+ sp74 = OLib_Vec3fDiffToVecGeo(&focalActorPosRot->pos, cameraDriftActorPos);
sp72 = focalActorPosRot->rot.y - sp74.yaw;
// Interface and shrink-window flags
if ((roData->interfaceFlags & 0xFF00) == 0xFF00) {
@@ -3683,7 +3686,7 @@ s32 Camera_Battle1(Camera* camera) {
sp120.y += focalActorHeight;
spA4 = OLib_Vec3fDiffToVecGeo(&sp120, &camera->targetPosRot.pos);
- sp104 = func_800CD6CC(camera->target);
+ sp104 = Camera_GetAttentionActorRange(camera->target);
if (sp104 > (PREG(86) + 800.0f)) {
sp104 = PREG(86) + 800.0f;
}
diff --git a/src/code/z_en_item00.c b/src/code/z_en_item00.c
index ff7944597..0be9a94a1 100644
--- a/src/code/z_en_item00.c
+++ b/src/code/z_en_item00.c
@@ -59,7 +59,7 @@ static ColliderCylinderInit sCylinderInit = {
};
static InitChainEntry sInitChain[] = {
- ICHAIN_F32(targetArrowOffset, 2000, ICHAIN_STOP),
+ ICHAIN_F32(lockOnArrowOffset, 2000, ICHAIN_STOP),
};
static s32 sBssPad;