From e8f906100b73872174004a6ca354ba459a3e9bb4 Mon Sep 17 00:00:00 2001 From: engineer124 <47598039+engineer124@users.noreply.github.com> Date: Tue, 7 Mar 2023 21:33:16 -0500 Subject: [PATCH] NPC Tracking (#1184) * npc tracking * fix func declaration * more cleanup * rm named var * more cleanup --- include/functions.h | 4 +- include/variables.h | 1 - include/z64.h | 13 - include/z64actor.h | 29 ++ src/code/z_actor.c | 361 ++++++++++++------ src/overlays/actors/ovl_En_Ma4/z_en_ma4.c | 32 +- src/overlays/actors/ovl_En_Ma4/z_en_ma4.h | 2 +- .../actors/ovl_En_Ma_Yto/z_en_ma_yto.c | 46 +-- .../actors/ovl_En_Ma_Yto/z_en_ma_yto.h | 2 +- .../actors/ovl_En_Ma_Yts/z_en_ma_yts.c | 30 +- .../actors/ovl_En_Ma_Yts/z_en_ma_yts.h | 2 +- tools/disasm/functions.txt | 10 +- tools/disasm/variables.txt | 2 +- tools/sizes/code_functions.csv | 10 +- 14 files changed, 338 insertions(+), 206 deletions(-) diff --git a/include/functions.h b/include/functions.h index 55b034dbdd..0313b9e233 100644 --- a/include/functions.h +++ b/include/functions.h @@ -752,8 +752,8 @@ void Actor_SetColorFilter(Actor* actor, u16 colorFlag, u16 colorIntensityMax, u1 Hilite* func_800BCBF4(Vec3f* arg0, PlayState* play); Hilite* func_800BCC68(Vec3f* arg0, PlayState* play); void Actor_GetClosestPosOnPath(Vec3s* points, s32 numPoints, Vec3f* srcPos, Vec3f* dstPos, s32 isPathLoop); -s32 func_800BD2B4(PlayState* play, Actor* actor, s16* arg2, f32 arg3, u16 (*textIdCallback)(PlayState*, Actor*), s16 (*arg5)(PlayState*, Actor*)); -void func_800BD888(Actor* actor, struct_800BD888_arg1* arg1, s16 arg2, s16 arg3); +s32 Npc_UpdateTalking(PlayState* play, Actor* actor, s16* talkState, f32 interactRange, NpcGetTextIdFunc getTextId, NpcUpdateTalkStateFunc updateTalkState); +void Npc_TrackPoint(Actor* actor, NpcInteractInfo* interactInfo, s16 presetIndex, s16 trackingMode); void func_800BD9E0(PlayState* play, SkelAnime* skelAnime, OverrideLimbDraw overrideLimbDraw, PostLimbDraw postLimbDraw, Actor* actor, s16 alpha); void func_800BDAA0(PlayState* play, SkelAnime* skelAnime, OverrideLimbDraw overrideLimbDraw, PostLimbDraw postLimbDraw, Actor* actor, s16 alpha); void Actor_ChangeAnimationByInfo(SkelAnime* skelAnime, AnimationInfo* animationInfo, s32 animIndex); diff --git a/include/variables.h b/include/variables.h index a5752eb7f7..b64507bfa4 100644 --- a/include/variables.h +++ b/include/variables.h @@ -417,7 +417,6 @@ extern Color_RGBA8 actorDefaultHitColor; // extern UNK_TYPE4 D_801AEDD4; // extern UNK_TYPE4 D_801AEE28; // extern UNK_TYPE4 D_801AEE30; -// extern UNK_TYPE4 D_801AEE38; // extern UNK_TYPE2 D_801AEE4C; extern Gfx D_801AEF88[]; extern Gfx D_801AEFA0[]; diff --git a/include/z64.h b/include/z64.h index a26da18afb..d35265402a 100644 --- a/include/z64.h +++ b/include/z64.h @@ -839,19 +839,6 @@ typedef struct { /* 0x10 */ Color_RGBA8_u32 envColor; } Struct_80140E80; // size = 0x14 -// From OoT's struct_80034A14_arg1 -typedef struct { - /* 0x00 */ s16 unk_00; - /* 0x02 */ s16 unk_02; - /* 0x04 */ s16 unk_04; - /* 0x06 */ s16 unk_06; - /* 0x08 */ Vec3s unk_08; - /* 0x0E */ Vec3s unk_0E; - /* 0x14 */ f32 unk_14; - /* 0x18 */ Vec3f unk_18; // Usually setted to Player's position or Player's focus - /* 0x24 */ s16 unk_24; -} struct_800BD888_arg1; // size = 0x28 - typedef struct { /* 0x0 */ u32 type; /* 0x4 */ u32 setScissor; diff --git a/include/z64actor.h b/include/z64actor.h index d2b28cb385..e61ebd7a70 100644 --- a/include/z64actor.h +++ b/include/z64actor.h @@ -22,6 +22,8 @@ struct EnBox; struct EnTorch2; typedef void(*ActorFunc)(struct Actor* this, struct PlayState* play); +typedef u16 (*NpcGetTextIdFunc)(struct PlayState*, struct Actor*); +typedef s16 (*NpcUpdateTalkStateFunc)(struct PlayState*, struct Actor*); typedef struct { /* 0x00 */ Vec3f pos; @@ -639,4 +641,31 @@ typedef enum { /* 0xFF */ TATL_HINT_ID_NONE = 0xFF } TatlHintId; +typedef enum NpcTalkState { + /* 0 */ NPC_TALK_STATE_IDLE, // NPC not currently talking to player + /* 1 */ NPC_TALK_STATE_TALKING, // NPC is currently talking to player + /* 2 */ NPC_TALK_STATE_ACTION, // An NPC-defined action triggered in the conversation + /* 3 */ NPC_TALK_STATE_ITEM_GIVEN // NPC finished giving an item and text box is done +} NpcTalkState; + +typedef enum NpcTrackingMode { + /* 0 */ NPC_TRACKING_PLAYER_AUTO_TURN, // Determine tracking mode based on player position, see Npc_UpdateAutoTurn + /* 1 */ NPC_TRACKING_NONE, // Don't track the target (usually the player) + /* 2 */ NPC_TRACKING_HEAD_AND_TORSO, // Track target by turning the head and the torso + /* 3 */ NPC_TRACKING_HEAD, // Track target by turning the head + /* 4 */ NPC_TRACKING_FULL_BODY // Track target by turning the body, torso and head +} NpcTrackingMode; + +typedef struct NpcInteractInfo { + /* 0x00 */ s16 talkState; + /* 0x02 */ s16 trackingMode; + /* 0x04 */ s16 autoTurnTimer; + /* 0x06 */ s16 autoTurnState; + /* 0x08 */ Vec3s headRot; + /* 0x0E */ Vec3s torsoRot; + /* 0x14 */ f32 yOffset; // Y position offset to add to actor position when calculating angle to target + /* 0x18 */ Vec3f trackPos; + /* 0x24 */ UNK_TYPE1 unk_24[0x4]; +} NpcInteractInfo; // size = 0x28 + #endif diff --git a/src/code/z_actor.c b/src/code/z_actor.c index f6039a8edf..d00061bf52 100644 --- a/src/code/z_actor.c +++ b/src/code/z_actor.c @@ -4015,170 +4015,285 @@ void Actor_GetClosestPosOnPath(Vec3s* points, s32 numPoints, Vec3f* srcPos, Vec3 } } -// unused -s32 func_800BD2B4(PlayState* play, Actor* actor, s16* arg2, f32 arg3, u16 (*textIdCallback)(PlayState*, Actor*), - s16 (*arg5)(PlayState*, Actor*)) { +/** + * Updates NPC talking state. Checks for a talk request and updates + * the talkState parameter when a dialog is ongoing. Otherwise checks if + * the actor is onscreen, advertises the interaction in a range and sets + * the current text id if necessary. + * + * The talk state values are defined in the NpcTalkState enum. + * + * @see NpcTalkState + * + * @param[in,out] talkState Talk state + * @param interactRange The interact (talking) range for the actor + * @param getTextId Callback for getting the next text id + * @param updateTalkState Callback for getting the next talkState value + * @return True if a new dialog was started (player talked to the actor). False otherwise. + */ +s32 Npc_UpdateTalking(PlayState* play, Actor* actor, s16* talkState, f32 interactRange, NpcGetTextIdFunc getTextId, + NpcUpdateTalkStateFunc updateTalkState) { if (Actor_ProcessTalkRequest(actor, &play->state)) { - *arg2 = true; + *talkState = NPC_TALK_STATE_TALKING; return true; - } else if (*arg2) { - *arg2 = arg5(play, actor); - return false; - } else if (!Actor_OnScreen(play, actor)) { - return false; - } else if (!func_800B8614(actor, play, arg3)) { - return false; - } else { - actor->textId = textIdCallback(play, actor); + } + + if (*talkState != NPC_TALK_STATE_IDLE) { + *talkState = updateTalkState(play, actor); return false; } + + if (!Actor_OnScreen(play, actor)) { + return false; + } + + if (!func_800B8614(actor, play, interactRange)) { + return false; + } + + actor->textId = getTextId(play, actor); + + return false; } typedef struct { - /* 0x00 */ s16 unk_00; - /* 0x02 */ s16 unk_02; - /* 0x04 */ s16 unk_04; - /* 0x06 */ s16 unk_06; - /* 0x08 */ s16 unk_08; - /* 0x0A */ s16 unk_0A; - /* 0x0C */ u8 unk_0C; -} struct_801AEE38_0; // size = 0x10 + /* 0x0 */ s16 maxHeadYaw; + /* 0x2 */ s16 minHeadPitch; + /* 0x4 */ s16 maxHeadPitch; + /* 0x6 */ s16 maxTorsoYaw; + /* 0x8 */ s16 minTorsoPitch; + /* 0xA */ s16 maxTorsoPitch; + /* 0xC */ u8 rotateYaw; +} NpcTrackingRotLimits; // size = 0x10 typedef struct { - /* 0x00 */ struct_801AEE38_0 sub_00; - /* 0x10 */ f32 unk_10; - /* 0x14 */ s16 unk_14; -} struct_801AEE38; // size = 0x18 + /* 0x00 */ NpcTrackingRotLimits rotLimits; + // Fields specific to NPC_TRACKING_PLAYER_AUTO_TURN mode + /* 0x10 */ f32 autoTurnDistanceRange; // Max distance to player to enable tracking and auto-turn + /* 0x14 */ s16 maxYawForPlayerTracking; // Player is tracked if within this yaw +} NpcTrackingParams; // size = 0x18 -struct_801AEE38 D_801AEE38[] = { - { { 0x1C20, 0xE390, 0x1C70, 0x1554, 0x0000, 0x0000, 0x0000 }, 170.0f, 0x3FFC }, - { { 0x2AA8, 0xEAAC, 0x1554, 0x1554, 0xF8E4, 0x0E38, 0x0001 }, 170.0f, 0x3FFC }, - { { 0x31C4, 0xE390, 0x0E38, 0x0E38, 0xF1C8, 0x071C, 0x0001 }, 170.0f, 0x3FFC }, - { { 0x1554, 0xF1C8, 0x0000, 0x071C, 0xF8E4, 0x0000, 0x0001 }, 170.0f, 0x3FFC }, - { { 0x2AA8, 0xF8E4, 0x071C, 0x0E38, 0xD558, 0x2AA8, 0x0001 }, 170.0f, 0x3FFC }, - { { 0x0000, 0xE390, 0x2AA8, 0x3FFC, 0xF1C8, 0x0E38, 0x0001 }, 170.0f, 0x3FFC }, - { { 0x2AA8, 0xF1C8, 0x0E38, 0x0E38, 0x0000, 0x0000, 0x0001 }, 0.0f, 0x0000 }, - { { 0x2AA8, 0xF1C8, 0x0000, 0x0E38, 0x0000, 0x1C70, 0x0001 }, 0.0f, 0x0000 }, - { { 0x2AA8, 0xF1C8, 0xF1C8, 0x0000, 0x0000, 0x0000, 0x0001 }, 0.0f, 0x0000 }, - { { 0x071C, 0xF1C8, 0x0E38, 0x1C70, 0x0000, 0x0000, 0x0001 }, 0.0f, 0x0000 }, - { { 0x0E38, 0xF1C8, 0x0000, 0x1C70, 0x0000, 0x0E38, 0x0001 }, 0.0f, 0x0000 }, - { { 0x2AA8, 0xE390, 0x1C70, 0x0E38, 0xF1C8, 0x0E38, 0x0001 }, 0.0f, 0x0000 }, - { { 0x18E2, 0xF1C8, 0x0E38, 0x0E38, 0x0000, 0x0000, 0x0001 }, 0.0f, 0x0000 }, - { { 0x2A6C, 0xE390, 0x1C70, 0x1554, 0x0000, 0x0000, 0x0000 }, 170.0f, 0x3FFC }, +/** + * Npc tracking angle limit presets to use with Npc_TrackPoint. + * + * @see Npc_TrackPoint + */ +NpcTrackingParams sNpcTrackingPresets[] = { + { { 0x1C20, 0xE390, 0x1C70, 0x1554, 0x0000, 0x0000, false }, 170.0f, 0x3FFC }, + { { 0x2AA8, 0xEAAC, 0x1554, 0x1554, 0xF8E4, 0x0E38, true }, 170.0f, 0x3FFC }, + { { 0x31C4, 0xE390, 0x0E38, 0x0E38, 0xF1C8, 0x071C, true }, 170.0f, 0x3FFC }, + { { 0x1554, 0xF1C8, 0x0000, 0x071C, 0xF8E4, 0x0000, true }, 170.0f, 0x3FFC }, + { { 0x2AA8, 0xF8E4, 0x071C, 0x0E38, 0xD558, 0x2AA8, true }, 170.0f, 0x3FFC }, + { { 0x0000, 0xE390, 0x2AA8, 0x3FFC, 0xF1C8, 0x0E38, true }, 170.0f, 0x3FFC }, + { { 0x2AA8, 0xF1C8, 0x0E38, 0x0E38, 0x0000, 0x0000, true }, 0.0f, 0x0000 }, + { { 0x2AA8, 0xF1C8, 0x0000, 0x0E38, 0x0000, 0x1C70, true }, 0.0f, 0x0000 }, + { { 0x2AA8, 0xF1C8, 0xF1C8, 0x0000, 0x0000, 0x0000, true }, 0.0f, 0x0000 }, + { { 0x071C, 0xF1C8, 0x0E38, 0x1C70, 0x0000, 0x0000, true }, 0.0f, 0x0000 }, + { { 0x0E38, 0xF1C8, 0x0000, 0x1C70, 0x0000, 0x0E38, true }, 0.0f, 0x0000 }, + { { 0x2AA8, 0xE390, 0x1C70, 0x0E38, 0xF1C8, 0x0E38, true }, 0.0f, 0x0000 }, + { { 0x18E2, 0xF1C8, 0x0E38, 0x0E38, 0x0000, 0x0000, true }, 0.0f, 0x0000 }, + { { 0x2A6C, 0xE390, 0x1C70, 0x1554, 0x0000, 0x0000, false }, 170.0f, 0x3FFC }, }; -void func_800BD384(Actor* actor, struct_800BD888_arg1* arg1, s16 arg2, s16 arg3, s16 arg4, s16 arg5, s16 arg6, s16 arg7, - u8 arg8) { - s16 sp46; - s16 sp44; - s16 temp2; - s16 sp40; - s16 temp1; - Vec3f sp30; +/** + * Smoothly turns the actor's whole body and updates torso and head rotations in + * NpcInteractInfo so that the actor tracks the point specified in NpcInteractInfo.trackPos. + * Rotations are limited to specified angles. + * + * Head and torso rotation angles are determined by calculating the pitch and yaw + * from the actor position to the given target position. + * + * The y position of the actor is offset by NpcInteractInfo.yOffset + * before calculating the angles. It can be used to configure the height difference + * between the actor and the target. + * + * @param maxHeadYaw maximum head yaw difference from neutral position + * @param maxHeadPitch maximum head pitch angle + * @param minHeadPitch minimum head pitch angle + * @param maxTorsoYaw maximum torso yaw difference from neutral position + * @param maxTorsoPitch maximum torso pitch angle + * @param minTorsoPitch minimum torso pitch angle + * @param rotateYaw if true, the actor's yaw (shape.rot.y) is updated to turn the actor's whole body + */ +void Npc_TrackPointWithLimits(Actor* actor, NpcInteractInfo* interactInfo, s16 maxHeadYaw, s16 maxHeadPitch, + s16 minHeadPitch, s16 maxTorsoYaw, s16 maxTorsoPitch, s16 minTorsoPitch, u8 rotateYaw) { + s16 pitchTowardsTarget; + s16 yawTowardsTarget; + s16 torsoPitch; + s16 bodyYawDiff; + s16 temp; + Vec3f offsetActorPos; - sp30.x = actor->world.pos.x; - sp30.y = actor->world.pos.y + arg1->unk_14; - sp30.z = actor->world.pos.z; + offsetActorPos.x = actor->world.pos.x; + offsetActorPos.y = actor->world.pos.y + interactInfo->yOffset; + offsetActorPos.z = actor->world.pos.z; - sp46 = Math_Vec3f_Pitch(&sp30, &arg1->unk_18); - sp44 = Math_Vec3f_Yaw(&sp30, &arg1->unk_18); - sp40 = Math_Vec3f_Yaw(&actor->world.pos, &arg1->unk_18) - actor->shape.rot.y; + pitchTowardsTarget = Math_Vec3f_Pitch(&offsetActorPos, &interactInfo->trackPos); + yawTowardsTarget = Math_Vec3f_Yaw(&offsetActorPos, &interactInfo->trackPos); + bodyYawDiff = Math_Vec3f_Yaw(&actor->world.pos, &interactInfo->trackPos) - actor->shape.rot.y; - temp1 = CLAMP(sp40, -arg2, arg2); - Math_SmoothStepToS(&arg1->unk_08.y, temp1, 6, 2000, 1); + temp = CLAMP(bodyYawDiff, -maxHeadYaw, maxHeadYaw); + Math_SmoothStepToS(&interactInfo->headRot.y, temp, 6, 0x7D0, 1); - temp1 = (ABS_ALT(sp40) >= 0x8000) ? 0 : ABS_ALT(sp40); - arg1->unk_08.y = CLAMP(arg1->unk_08.y, -temp1, temp1); + temp = (ABS_ALT(bodyYawDiff) >= 0x8000) ? 0 : ABS_ALT(bodyYawDiff); + interactInfo->headRot.y = CLAMP(interactInfo->headRot.y, -temp, temp); - sp40 -= arg1->unk_08.y; + bodyYawDiff -= interactInfo->headRot.y; - temp1 = CLAMP(sp40, -arg5, arg5); - Math_SmoothStepToS(&arg1->unk_0E.y, temp1, 6, 2000, 1); + temp = CLAMP(bodyYawDiff, -maxTorsoYaw, maxTorsoYaw); + Math_SmoothStepToS(&interactInfo->torsoRot.y, temp, 6, 0x7D0, 1); - temp1 = (ABS_ALT(sp40) >= 0x8000) ? 0 : ABS_ALT(sp40); - arg1->unk_0E.y = CLAMP(arg1->unk_0E.y, -temp1, temp1); + temp = (ABS_ALT(bodyYawDiff) >= 0x8000) ? 0 : ABS_ALT(bodyYawDiff); + interactInfo->torsoRot.y = CLAMP(interactInfo->torsoRot.y, -temp, temp); - if (arg8) { - Math_SmoothStepToS(&actor->shape.rot.y, sp44, 6, 2000, 1); + if (rotateYaw) { + Math_SmoothStepToS(&actor->shape.rot.y, yawTowardsTarget, 6, 0x7D0, 1); } - temp1 = CLAMP(sp46, arg4, (s16)(u16)arg3); - Math_SmoothStepToS(&arg1->unk_08.x, temp1, 6, 2000, 1); + temp = CLAMP(pitchTowardsTarget, minHeadPitch, (s16)(u16)maxHeadPitch); + Math_SmoothStepToS(&interactInfo->headRot.x, temp, 6, 0x7D0, 1); - temp2 = sp46 - arg1->unk_08.x; + torsoPitch = pitchTowardsTarget - interactInfo->headRot.x; - temp1 = CLAMP(temp2, arg7, arg6); - Math_SmoothStepToS(&arg1->unk_0E.x, temp1, 6, 2000, 1); + temp = CLAMP(torsoPitch, minTorsoPitch, maxTorsoPitch); + Math_SmoothStepToS(&interactInfo->torsoRot.x, temp, 6, 0x7D0, 1); } // unused -s16 func_800BD6B8(s16 arg0) { - return D_801AEE38[arg0].unk_14; +s16 Npc_GetTrackingPresetMaxPlayerYaw(s16 presetIndex) { + return sNpcTrackingPresets[presetIndex].maxYawForPlayerTracking; } -s16 func_800BD6E4(Actor* actor, struct_800BD888_arg1* arg1, f32 arg2, s16 arg3, s16 flag) { +/** + * Handles NPC tracking modes and auto-turning towards the player when + * NPC_TRACKING_PLAYER_AUTO_TURN tracking mode is used. + * + * Returns a tracking mode that will determine which actor limbs + * will be rotated towards the target. + * + * When the player is behind the actor (i.e. not in the yaw range in front of the actor + * defined by maxYawForPlayerTracking), the actor will start an auto-turn sequence: + * - look forward for 30-60 frames + * - turn head to look at the player for 10-20 frames + * - look forward for 30-60 frames + * - turn the entire body to face the player + * + * @param distanceRange Max distance to player that tracking and auto-turning will be active for + * @param maxYawForPlayerTracking Maximum angle for tracking the player. + * @param trackingMode The tracking mode selected by the actor. If this is not + * NPC_TRACKING_PLAYER_AUTO_TURN this function does nothing + * + * @return The tracking mode (NpcTrackingMode) to use for the current frame. + */ +s16 Npc_UpdateAutoTurn(Actor* actor, NpcInteractInfo* interactInfo, f32 distanceRange, s16 maxYawForPlayerTracking, + s16 trackingMode) { s32 pad; + s16 yaw; + s16 yawDiff; - if (flag) { - return flag; - } else if (arg1->unk_00 != 0) { - return 4; - } else if (arg2 < Math_Vec3f_DistXYZ(&actor->world.pos, &arg1->unk_18)) { - arg1->unk_04 = 0; - arg1->unk_06 = 0; - return 1; - } else { - s16 yaw = Math_Vec3f_Yaw(&actor->world.pos, &arg1->unk_18); - s16 phi_a0 = ABS_ALT(BINANG_SUB(yaw, actor->shape.rot.y)); + if (trackingMode != NPC_TRACKING_PLAYER_AUTO_TURN) { + return trackingMode; + } - if (arg3 >= phi_a0) { - arg1->unk_04 = 0; - arg1->unk_06 = 0; - return 2; - } else if (DECR(arg1->unk_04) != 0) { - return arg1->unk_02; - } else { - switch (arg1->unk_06) { - case 0: - case 2: - arg1->unk_04 = Rand_S16Offset(30, 30); - arg1->unk_06++; - return 1; + if (interactInfo->talkState != NPC_TALK_STATE_IDLE) { + // When talking, always fully turn to face the player + return NPC_TRACKING_FULL_BODY; + } - case 1: - arg1->unk_04 = Rand_S16Offset(10, 10); - arg1->unk_06++; - return 3; + if (distanceRange < Math_Vec3f_DistXYZ(&actor->world.pos, &interactInfo->trackPos)) { + // Player is too far away, do not track + interactInfo->autoTurnTimer = 0; + interactInfo->autoTurnState = 0; + return NPC_TRACKING_NONE; + } - default: - return 4; - } - } + yaw = Math_Vec3f_Yaw(&actor->world.pos, &interactInfo->trackPos); + yawDiff = ABS_ALT(BINANG_SUB(yaw, actor->shape.rot.y)); + + if (maxYawForPlayerTracking >= yawDiff) { + // Player is in front of the actor, track with the head and the torso + interactInfo->autoTurnTimer = 0; + interactInfo->autoTurnState = 0; + return NPC_TRACKING_HEAD_AND_TORSO; + } + + // Player is behind the actor, run the auto-turn sequence. + + if (DECR(interactInfo->autoTurnTimer) != 0) { + // While the timer is still running, return the previous tracking mode + return interactInfo->trackingMode; + } + + switch (interactInfo->autoTurnState) { + case 0: + case 2: + // Just stand still, not tracking the player + interactInfo->autoTurnTimer = Rand_S16Offset(30, 30); + interactInfo->autoTurnState++; + return NPC_TRACKING_NONE; + + case 1: + // Glance at the player by only turning the head + interactInfo->autoTurnTimer = Rand_S16Offset(10, 10); + interactInfo->autoTurnState++; + return NPC_TRACKING_HEAD; + + default: + // Auto-turn sequence complete, turn towards the player + return NPC_TRACKING_FULL_BODY; } } -// This function is very similar to OoT's func_80034A14 -void func_800BD888(Actor* actor, struct_800BD888_arg1* arg1, s16 arg2, s16 arg3) { - struct_801AEE38_0 sp38; +/** + * Rotates the actor's whole body, torso and head tracking the point specified in NpcInteractInfo.trackPos. + * Uses angle limits from a preset selected from from sNpcTrackingPresets. + * + * The trackingMode parameter controls whether the head and torso are turned towards the target. + * If not, they are smoothly turned towards zero. Setting the parameter to NPC_TRACKING_FULL_BODY + * causes the actor's whole body to be rotated to face the target. + * + * If NPC_TRACKING_PLAYER_AUTO_TURN is used, the actor will track the player with its head and torso as long + * as the player is in front of the actor (within a yaw angle specified in the option preset). + * If the player is outside of this angle, the actor will turn to face the player after a while. + * + * @see Npc_UpdateAutoTurn + * @see sNpcTrackingPresets + * @see NpcTrackingMode + * + * @param presetIndex The index to a preset in sNpcTrackingPresets + * @param trackingMode A value from NpcTrackingMode enum + */ +void Npc_TrackPoint(Actor* actor, NpcInteractInfo* interactInfo, s16 presetIndex, s16 trackingMode) { + NpcTrackingRotLimits rotLimits; - arg1->unk_02 = func_800BD6E4(actor, arg1, D_801AEE38[arg2].unk_10, D_801AEE38[arg2].unk_14, arg3); - sp38 = D_801AEE38[arg2].sub_00; + interactInfo->trackingMode = + Npc_UpdateAutoTurn(actor, interactInfo, sNpcTrackingPresets[presetIndex].autoTurnDistanceRange, + sNpcTrackingPresets[presetIndex].maxYawForPlayerTracking, trackingMode); - switch (arg1->unk_02) { - case 1: - sp38.unk_00 = 0; - sp38.unk_04 = 0; - sp38.unk_02 = 0; - case 3: - sp38.unk_06 = 0; - sp38.unk_0A = 0; - sp38.unk_08 = 0; - case 2: - sp38.unk_0C = 0; + rotLimits = sNpcTrackingPresets[presetIndex].rotLimits; + + switch (interactInfo->trackingMode) { + case NPC_TRACKING_NONE: + rotLimits.maxHeadYaw = 0; + rotLimits.maxHeadPitch = 0; + rotLimits.minHeadPitch = 0; + // fallthrough + case NPC_TRACKING_HEAD: + rotLimits.maxTorsoYaw = 0; + rotLimits.maxTorsoPitch = 0; + rotLimits.minTorsoPitch = 0; + // fallthrough + case NPC_TRACKING_HEAD_AND_TORSO: + rotLimits.rotateYaw = false; + break; + + default: + break; } - func_800BD384(actor, arg1, sp38.unk_00, sp38.unk_04, sp38.unk_02, sp38.unk_06, sp38.unk_0A, sp38.unk_08, - sp38.unk_0C); + Npc_TrackPointWithLimits(actor, interactInfo, rotLimits.maxHeadYaw, rotLimits.maxHeadPitch, rotLimits.minHeadPitch, + rotLimits.maxTorsoYaw, rotLimits.maxTorsoPitch, rotLimits.minTorsoPitch, + rotLimits.rotateYaw); } Gfx D_801AEF88[] = { diff --git a/src/overlays/actors/ovl_En_Ma4/z_en_ma4.c b/src/overlays/actors/ovl_En_Ma4/z_en_ma4.c index 802d112629..f189d2e9db 100644 --- a/src/overlays/actors/ovl_En_Ma4/z_en_ma4.c +++ b/src/overlays/actors/ovl_En_Ma4/z_en_ma4.c @@ -142,19 +142,21 @@ void EnMa4_ChangeAnim(EnMa4* this, s32 animIndex) { void func_80ABDD9C(EnMa4* this, PlayState* play) { Player* player = GET_PLAYER(play); - s16 flag; + s16 trackingMode; - if (this->unk_1D8.unk_00 == 0 && + if ((this->interactInfo.talkState == NPC_TALK_STATE_IDLE) && ((this->skelAnime.animation == &gRomaniRunAnim) || (this->skelAnime.animation == &gRomaniLookAroundAnim) || (this->skelAnime.animation == &gRomaniShootBowAnim))) { - flag = 1; + trackingMode = NPC_TRACKING_NONE; } else { - flag = (this->type == MA4_TYPE_ALIENS_WON && this->actionFunc != EnMa4_DialogueHandler) ? 1 : 0; + trackingMode = ((this->type == MA4_TYPE_ALIENS_WON) && (this->actionFunc != EnMa4_DialogueHandler)) + ? NPC_TRACKING_NONE + : NPC_TRACKING_PLAYER_AUTO_TURN; } - this->unk_1D8.unk_18 = player->actor.world.pos; - this->unk_1D8.unk_18.y -= -10.0f; - func_800BD888(&this->actor, &this->unk_1D8, 0, flag); + this->interactInfo.trackPos = player->actor.world.pos; + this->interactInfo.trackPos.y -= -10.0f; + Npc_TrackPoint(&this->actor, &this->interactInfo, 0, trackingMode); } void EnMa4_InitPath(EnMa4* this, PlayState* play) { @@ -192,7 +194,7 @@ void EnMa4_Init(Actor* thisx, PlayState* play) { Actor_SetScale(&this->actor, 0.01f); this->actor.targetMode = 0; - this->unk_1D8.unk_00 = 0; + this->interactInfo.talkState = NPC_TALK_STATE_IDLE; this->unk_334 = 0; this->hasBow = true; this->mouthTexIndex = 0; @@ -1027,17 +1029,17 @@ void EnMa4_Update(Actor* thisx, PlayState* play) { s32 EnMa4_OverrideLimbDraw(PlayState* play, s32 limbIndex, Gfx** dList, Vec3f* pos, Vec3s* rot, Actor* thisx) { EnMa4* this = THIS; - Vec3s sp4; + Vec3s limbRot; if (limbIndex == ROMANI_LIMB_HEAD) { - sp4 = this->unk_1D8.unk_08; - rot->x = rot->x + sp4.y; - rot->z = rot->z + sp4.x; + limbRot = this->interactInfo.headRot; + rot->x += limbRot.y; + rot->z += limbRot.x; } if (limbIndex == ROMANI_LIMB_TORSO) { - sp4 = this->unk_1D8.unk_0E; - rot->x = rot->x - sp4.y; - rot->z = rot->z - sp4.x; + limbRot = this->interactInfo.torsoRot; + rot->x -= limbRot.y; + rot->z -= limbRot.x; } return false; diff --git a/src/overlays/actors/ovl_En_Ma4/z_en_ma4.h b/src/overlays/actors/ovl_En_Ma4/z_en_ma4.h index 3ac405b009..91876c9386 100644 --- a/src/overlays/actors/ovl_En_Ma4/z_en_ma4.h +++ b/src/overlays/actors/ovl_En_Ma4/z_en_ma4.h @@ -13,7 +13,7 @@ typedef struct EnMa4 { /* 0x144 */ SkelAnime skelAnime; /* 0x188 */ EnMa4ActionFunc actionFunc; /* 0x18C */ ColliderCylinder collider; - /* 0x1D8 */ struct_800BD888_arg1 unk_1D8; + /* 0x1D8 */ NpcInteractInfo interactInfo; /* 0x200 */ Vec3s* pathPoints; /* 0x204 */ Vec3s jointTable[ROMANI_LIMB_MAX]; /* 0x28E */ UNK_TYPE1 unk28E[0x6]; diff --git a/src/overlays/actors/ovl_En_Ma_Yto/z_en_ma_yto.c b/src/overlays/actors/ovl_En_Ma_Yto/z_en_ma_yto.c index fb10d93111..d60f8fb3d0 100644 --- a/src/overlays/actors/ovl_En_Ma_Yto/z_en_ma_yto.c +++ b/src/overlays/actors/ovl_En_Ma_Yto/z_en_ma_yto.c @@ -1268,29 +1268,29 @@ void EnMaYto_ChangeAnim(EnMaYto* this, s32 animIndex) { void func_80B90C78(EnMaYto* this, PlayState* play) { Player* player = GET_PLAYER(play); - s16 flag; + s16 trackingMode; SkelAnime_Update(&this->skelAnime); - flag = this->unk31E == 2 ? true : false; + trackingMode = (this->unk31E == 2) ? NPC_TRACKING_NONE : NPC_TRACKING_PLAYER_AUTO_TURN; if (this->unk31E == 0) { - this->unk_1D8.unk_18 = player->actor.world.pos; - this->unk_1D8.unk_14 = 0.0f; + this->interactInfo.trackPos = player->actor.world.pos; + this->interactInfo.yOffset = 0.0f; } else if (this->unk31E == 1) { - Math_Vec3f_StepTo(&this->unk_1D8.unk_18, &this->actor.child->world.pos, 8.0f); - this->unk_1D8.unk_14 = 0.0f; + Math_Vec3f_StepTo(&this->interactInfo.trackPos, &this->actor.child->world.pos, 8.0f); + this->interactInfo.yOffset = 0.0f; } if (this->unk320 == 0) { if (this->actionFunc == EnMaYto_WarmFuzzyFeelingCs) { - this->unk_1D8.unk_08.y = 0; - this->unk_1D8.unk_08.x = 0; + this->interactInfo.headRot.y = 0; + this->interactInfo.headRot.x = 0; } else { - func_800BD888(&this->actor, &this->unk_1D8, 0xD, flag); + Npc_TrackPoint(&this->actor, &this->interactInfo, 13, trackingMode); } } else { - Math_SmoothStepToS(&this->unk_1D8.unk_08.y, 0, 3, 0x71C, 0xB6); - Math_SmoothStepToS(&this->unk_1D8.unk_08.x, 0x18E3, 5, 0x71C, 0xB6); + Math_SmoothStepToS(&this->interactInfo.headRot.y, 0, 3, 0x71C, 0xB6); + Math_SmoothStepToS(&this->interactInfo.headRot.x, 0x18E3, 5, 0x71C, 0xB6); } EnMaYto_UpdateEyes(this); @@ -1423,26 +1423,26 @@ void EnMaYto_Update(Actor* thisx, PlayState* play) { s32 EnMaYto_OverrideLimbDraw(PlayState* play, s32 limbIndex, Gfx** dList, Vec3f* pos, Vec3s* rot, Actor* thisx) { EnMaYto* this = THIS; - Vec3s sp4; + Vec3s limbRot; if (limbIndex == CREMIA_LIMB_HEAD) { - sp4 = this->unk_1D8.unk_08; + limbRot = this->interactInfo.headRot; - rot->x += sp4.y; - rot->z += sp4.x; + rot->x += limbRot.y; + rot->z += limbRot.x; } else if (limbIndex == CREMIA_LIMB_TORSO) { - if (this->skelAnime.animation != &gCremiaSittingPetCowAnim && - this->skelAnime.animation != &gCremiaSittingLookDownAnim) { - sp4 = this->unk_1D8.unk_0E; + if ((this->skelAnime.animation != &gCremiaSittingPetCowAnim) && + (this->skelAnime.animation != &gCremiaSittingLookDownAnim)) { + limbRot = this->interactInfo.torsoRot; - rot->x += sp4.y; - if (this->skelAnime.animation == &gCremiaIdleAnim || this->skelAnime.animation == &gCremiaSittingAnim || - this->skelAnime.animation == &gCremiaSittingLookDownAnim) { - rot->z += sp4.x; + rot->x += limbRot.y; + if ((this->skelAnime.animation == &gCremiaIdleAnim) || (this->skelAnime.animation == &gCremiaSittingAnim) || + (this->skelAnime.animation == &gCremiaSittingLookDownAnim)) { + rot->z += limbRot.x; } } } - return 0; + return false; } void EnMaYto_PostLimbDraw(PlayState* play, s32 limbIndex, Gfx** dList, Vec3s* rot, Actor* thisx) { diff --git a/src/overlays/actors/ovl_En_Ma_Yto/z_en_ma_yto.h b/src/overlays/actors/ovl_En_Ma_Yto/z_en_ma_yto.h index 0955cd8e8b..208e412704 100644 --- a/src/overlays/actors/ovl_En_Ma_Yto/z_en_ma_yto.h +++ b/src/overlays/actors/ovl_En_Ma_Yto/z_en_ma_yto.h @@ -13,7 +13,7 @@ typedef struct EnMaYto { /* 0x144 */ SkelAnime skelAnime; /* 0x188 */ EnMaYtoActionFunc actionFunc; /* 0x18C */ ColliderCylinder collider; - /* 0x1D8 */ struct_800BD888_arg1 unk_1D8; + /* 0x1D8 */ NpcInteractInfo interactInfo; /* 0x200 */ s32 unk200; // unused /* 0x204 */ s32 type; /* 0x208 */ Vec3s jointTable[CREMIA_LIMB_MAX]; diff --git a/src/overlays/actors/ovl_En_Ma_Yts/z_en_ma_yts.c b/src/overlays/actors/ovl_En_Ma_Yts/z_en_ma_yts.c index 64a6d138cb..b52378a5a2 100644 --- a/src/overlays/actors/ovl_En_Ma_Yts/z_en_ma_yts.c +++ b/src/overlays/actors/ovl_En_Ma_Yts/z_en_ma_yts.c @@ -121,17 +121,17 @@ void EnMaYts_ChangeAnim(EnMaYts* this, s32 animIndex) { void func_80B8D12C(EnMaYts* this, PlayState* play) { Player* player = GET_PLAYER(play); - s16 flag = this->unk_32C == 2 ? true : false; + s16 trackingMode = (this->unk_32C == 2) ? NPC_TRACKING_NONE : NPC_TRACKING_PLAYER_AUTO_TURN; if (this->unk_32C == 0 || this->actor.parent == NULL) { - this->unk_1D8.unk_18 = player->actor.world.pos; - this->unk_1D8.unk_18.y -= -10.0f; + this->interactInfo.trackPos = player->actor.world.pos; + this->interactInfo.trackPos.y -= -10.0f; } else { - Math_Vec3f_StepTo(&this->unk_1D8.unk_18, &this->actor.parent->world.pos, 8.0f); - this->unk_1D8.unk_18.y -= -10.0f; + Math_Vec3f_StepTo(&this->interactInfo.trackPos, &this->actor.parent->world.pos, 8.0f); + this->interactInfo.trackPos.y -= -10.0f; } - func_800BD888(&this->actor, &this->unk_1D8, 0, flag); + Npc_TrackPoint(&this->actor, &this->interactInfo, 0, trackingMode); } void EnMaYts_InitAnimation(EnMaYts* this, PlayState* play) { @@ -238,7 +238,7 @@ void EnMaYts_Init(Actor* thisx, PlayState* play) { Actor_UpdateBgCheckInfo(play, &this->actor, 0.0f, 0.0f, 0.0f, 0x4); Actor_SetScale(&this->actor, 0.01f); - this->unk_1D8.unk_00 = 0; + this->interactInfo.talkState = NPC_TALK_STATE_IDLE; this->unk_200 = 0; this->blinkTimer = 0; @@ -248,7 +248,7 @@ void EnMaYts_Init(Actor* thisx, PlayState* play) { this->hasBow = false; } - if (CURRENT_DAY == 1 || CHECK_WEEKEVENTREG(WEEKEVENTREG_22_01)) { + if ((CURRENT_DAY == 1) || CHECK_WEEKEVENTREG(WEEKEVENTREG_22_01)) { this->overrideEyeTexIndex = 0; this->eyeTexIndex = 0; this->mouthTexIndex = 0; @@ -501,18 +501,18 @@ void EnMaYts_Update(Actor* thisx, PlayState* play) { s32 EnMaYts_OverrideLimbDraw(PlayState* play, s32 limbIndex, Gfx** dList, Vec3f* pos, Vec3s* rot, Actor* thisx) { EnMaYts* this = THIS; - Vec3s sp4; + Vec3s limbRot; if (limbIndex == ROMANI_LIMB_HEAD) { - sp4 = this->unk_1D8.unk_08; - rot->x += sp4.y; + limbRot = this->interactInfo.headRot; + rot->x += limbRot.y; if ((this->skelAnime.animation == &gRomaniIdleAnim) || (this->skelAnime.animation == &gRomaniSittingAnim)) { - rot->z += sp4.x; + rot->z += limbRot.x; } } else if (limbIndex == ROMANI_LIMB_TORSO) { - sp4 = this->unk_1D8.unk_0E; - rot->x += sp4.y; - rot->z += sp4.x; + limbRot = this->interactInfo.torsoRot; + rot->x += limbRot.y; + rot->z += limbRot.x; } return false; diff --git a/src/overlays/actors/ovl_En_Ma_Yts/z_en_ma_yts.h b/src/overlays/actors/ovl_En_Ma_Yts/z_en_ma_yts.h index eb700d8c28..65e7a0eb62 100644 --- a/src/overlays/actors/ovl_En_Ma_Yts/z_en_ma_yts.h +++ b/src/overlays/actors/ovl_En_Ma_Yts/z_en_ma_yts.h @@ -13,7 +13,7 @@ typedef struct EnMaYts { /* 0x144 */ SkelAnime skelAnime; /* 0x188 */ EnMaYtsActionFunc actionFunc; /* 0x18C */ ColliderCylinder collider; - /* 0x1D8 */ struct_800BD888_arg1 unk_1D8; + /* 0x1D8 */ NpcInteractInfo interactInfo; /* 0x200 */ s32 unk_200; // Set, but not used /* 0x204 */ Vec3s jointTable[ROMANI_LIMB_MAX]; /* 0x28E */ UNK_TYPE1 unk_28E[0x6]; diff --git a/tools/disasm/functions.txt b/tools/disasm/functions.txt index 0cada88236..c488127f6a 100644 --- a/tools/disasm/functions.txt +++ b/tools/disasm/functions.txt @@ -868,11 +868,11 @@ 0x800BCBF4:("func_800BCBF4",), 0x800BCC68:("func_800BCC68",), 0x800BCCDC:("Actor_GetClosestPosOnPath",), - 0x800BD2B4:("func_800BD2B4",), - 0x800BD384:("func_800BD384",), - 0x800BD6B8:("func_800BD6B8",), - 0x800BD6E4:("func_800BD6E4",), - 0x800BD888:("func_800BD888",), + 0x800BD2B4:("Npc_UpdateTalking",), + 0x800BD384:("Npc_TrackPointWithLimits",), + 0x800BD6B8:("Npc_GetTrackingPresetMaxPlayerYaw",), + 0x800BD6E4:("Npc_UpdateAutoTurn",), + 0x800BD888:("Npc_TrackPoint",), 0x800BD9A0:("func_800BD9A0",), 0x800BD9E0:("func_800BD9E0",), 0x800BDAA0:("func_800BDAA0",), diff --git a/tools/disasm/variables.txt b/tools/disasm/variables.txt index 420606edbd..cf91938631 100644 --- a/tools/disasm/variables.txt +++ b/tools/disasm/variables.txt @@ -442,7 +442,7 @@ 0x801AEDD4:("D_801AEDD4","UNK_TYPE4","",0x4), 0x801AEE28:("D_801AEE28","UNK_TYPE4","",0x4), 0x801AEE30:("D_801AEE30","UNK_TYPE4","",0x4), - 0x801AEE38:("D_801AEE38","struct_801AEE38","[14]",0x150), + 0x801AEE38:("sNpcTrackingPresets","NpcTrackingParams","[14]",0x150), 0x801AEF88:("D_801AEF88","UNK_TYPE1","",0x1), 0x801AEFA0:("D_801AEFA0","UNK_TYPE1","",0x1), 0x801AEFA8:("sElectricSparkTextures","UNK_TYPE1","",0x1), diff --git a/tools/sizes/code_functions.csv b/tools/sizes/code_functions.csv index 4cafa01156..d7332f855e 100644 --- a/tools/sizes/code_functions.csv +++ b/tools/sizes/code_functions.csv @@ -382,11 +382,11 @@ asm/non_matchings/code/z_actor/Actor_SetColorFilter.s,Actor_SetColorFilter,0x800 asm/non_matchings/code/z_actor/func_800BCBF4.s,func_800BCBF4,0x800BCBF4,0x1D asm/non_matchings/code/z_actor/func_800BCC68.s,func_800BCC68,0x800BCC68,0x1D asm/non_matchings/code/z_actor/Actor_GetClosestPosOnPath.s,Actor_GetClosestPosOnPath,0x800BCCDC,0x176 -asm/non_matchings/code/z_actor/func_800BD2B4.s,func_800BD2B4,0x800BD2B4,0x34 -asm/non_matchings/code/z_actor/func_800BD384.s,func_800BD384,0x800BD384,0xCD -asm/non_matchings/code/z_actor/func_800BD6B8.s,func_800BD6B8,0x800BD6B8,0xB -asm/non_matchings/code/z_actor/func_800BD6E4.s,func_800BD6E4,0x800BD6E4,0x69 -asm/non_matchings/code/z_actor/func_800BD888.s,func_800BD888,0x800BD888,0x46 +asm/non_matchings/code/z_actor/Npc_UpdateTalking.s,Npc_UpdateTalking,0x800BD2B4,0x34 +asm/non_matchings/code/z_actor/Npc_TrackPointWithLimits.s,Npc_TrackPointWithLimits,0x800BD384,0xCD +asm/non_matchings/code/z_actor/Npc_GetTrackingPresetMaxPlayerYaw.s,Npc_GetTrackingPresetMaxPlayerYaw,0x800BD6B8,0xB +asm/non_matchings/code/z_actor/Npc_UpdateAutoTurn.s,Npc_UpdateAutoTurn,0x800BD6E4,0x69 +asm/non_matchings/code/z_actor/Npc_TrackPoint.s,Npc_TrackPoint,0x800BD888,0x46 asm/non_matchings/code/z_actor/func_800BD9A0.s,func_800BD9A0,0x800BD9A0,0x10 asm/non_matchings/code/z_actor/func_800BD9E0.s,func_800BD9E0,0x800BD9E0,0x30 asm/non_matchings/code/z_actor/func_800BDAA0.s,func_800BDAA0,0x800BDAA0,0x33