1
0
Fork 0
mirror of https://github.com/zeldaret/oot.git synced 2025-07-04 23:14:37 +00:00

Document NPC talking and player tracking (#1426)

* Rename npc dialog state variable

* Rename and doc NPC actor talking funtion

* Introduce NpcTalkState enum

* Rename NPC_TALK_STATE enum values

* Document NpcPlayerInteractionState and related functions

* Rename player tracking opt enum variants

* Rename npc functions, interact info

* Minor npc actor function tweaks

* Minor comment fixes for npc

* Generalize NPC player tracking to point tracking

* Change unused NpcInteractInfo field type and name

Co-authored-by: engineer124 <47598039+engineer124@users.noreply.github.com>

* Rename headRot and torsoRot

* Rename GetTalkState to UpdateTalkState

* Minor comment fixes

* Rename rotateActorShape and clarify related comments

* Remove unneeded parentheses in z_en_heishi4.c

* Reformat

* Remove unclear comment

* Rename yPosOffset to yOffset

Co-authored-by: engineer124 <47598039+engineer124@users.noreply.github.com>
This commit is contained in:
Lauri Koskela 2022-11-25 19:52:28 +02:00 committed by GitHub
parent 9c35716fe2
commit b57f2162ee
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
66 changed files with 936 additions and 808 deletions

View file

@ -3659,24 +3659,40 @@ Hilite* func_8003435C(Vec3f* object, PlayState* play) {
return func_8002EB44(object, &play->view.eye, &lightDir, play->state.gfxCtx);
}
s32 func_800343CC(PlayState* play, Actor* actor, s16* arg2, f32 interactRange, callback1_800343CC unkFunc1,
callback2_800343CC unkFunc2) {
/**
* 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) {
s16 x;
s16 y;
if (Actor_ProcessTalkRequest(actor, play)) {
*arg2 = 1;
*talkState = NPC_TALK_STATE_TALKING;
return true;
}
if (*arg2 != 0) {
*arg2 = unkFunc2(play, actor);
if (*talkState != NPC_TALK_STATE_IDLE) {
*talkState = updateTalkState(play, actor);
return false;
}
Actor_GetScreenPos(play, actor, &x, &y);
if ((x < 0) || (x > SCREEN_WIDTH) || (y < 0) || (y > SCREEN_HEIGHT)) {
// Actor is offscreen
return false;
}
@ -3684,162 +3700,242 @@ s32 func_800343CC(PlayState* play, Actor* actor, s16* arg2, f32 interactRange, c
return false;
}
actor->textId = unkFunc1(play, actor);
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_80116130_0; // size = 0x10
/* 0x00 */ s16 maxHeadYaw;
/* 0x02 */ s16 minHeadPitch;
/* 0x04 */ s16 maxHeadPitch;
/* 0x06 */ s16 maxTorsoYaw;
/* 0x08 */ s16 minTorsoPitch;
/* 0x0A */ s16 maxTorsoPitch;
/* 0x0C */ u8 rotateYaw;
} NpcTrackingRotLimits; // size = 0x10
typedef struct {
/* 0x00 */ struct_80116130_0 sub_00;
/* 0x10 */ f32 unk_10;
/* 0x14 */ s16 unk_14;
} struct_80116130; // 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
static struct_80116130 D_80116130[] = {
{ { 0x2AA8, 0xF1C8, 0x18E2, 0x1554, 0x0000, 0x0000, 1 }, 170.0f, 0x3FFC },
{ { 0x2AA8, 0xEAAC, 0x1554, 0x1554, 0xF8E4, 0x0E38, 1 }, 170.0f, 0x3FFC },
{ { 0x31C4, 0xE390, 0x0E38, 0x0E38, 0xF1C8, 0x071C, 1 }, 170.0f, 0x3FFC },
{ { 0x1554, 0xF1C8, 0x0000, 0x071C, 0xF8E4, 0x0000, 1 }, 170.0f, 0x3FFC },
{ { 0x2AA8, 0xF8E4, 0x071C, 0x0E38, 0xD558, 0x2AA8, 1 }, 170.0f, 0x3FFC },
{ { 0x0000, 0xE390, 0x2AA8, 0x3FFC, 0xF1C8, 0x0E38, 1 }, 170.0f, 0x3FFC },
{ { 0x2AA8, 0xF1C8, 0x0E38, 0x0E38, 0x0000, 0x0000, 1 }, 0.0f, 0x0000 },
{ { 0x2AA8, 0xF1C8, 0x0000, 0x0E38, 0x0000, 0x1C70, 1 }, 0.0f, 0x0000 },
{ { 0x2AA8, 0xF1C8, 0xF1C8, 0x0000, 0x0000, 0x0000, 1 }, 0.0f, 0x0000 },
{ { 0x071C, 0xF1C8, 0x0E38, 0x1C70, 0x0000, 0x0000, 1 }, 0.0f, 0x0000 },
{ { 0x0E38, 0xF1C8, 0x0000, 0x1C70, 0x0000, 0x0E38, 1 }, 0.0f, 0x0000 },
{ { 0x2AA8, 0xE390, 0x1C70, 0x0E38, 0xF1C8, 0x0E38, 1 }, 0.0f, 0x0000 },
{ { 0x18E2, 0xF1C8, 0x0E38, 0x0E38, 0x0000, 0x0000, 1 }, 0.0f, 0x0000 },
/**
* Npc tracking angle limit presets to use with Npc_TrackPoint.
*
* @see Npc_TrackPoint
*/
static NpcTrackingParams sNpcTrackingPresets[] = {
{ { 0x2AA8, -0x0E38, 0x18E2, 0x1554, 0x0000, 0x0000, true }, 170.0f, 0x3FFC },
{ { 0x2AA8, -0x1554, 0x1554, 0x1554, -0x071C, 0x0E38, true }, 170.0f, 0x3FFC },
{ { 0x31C4, -0x1C70, 0x0E38, 0x0E38, -0x0E38, 0x071C, true }, 170.0f, 0x3FFC },
{ { 0x1554, -0x0E38, 0x0000, 0x071C, -0x071C, 0x0000, true }, 170.0f, 0x3FFC },
{ { 0x2AA8, -0x071C, 0x071C, 0x0E38, -0x2AA8, 0x2AA8, true }, 170.0f, 0x3FFC },
{ { 0x0000, -0x1C70, 0x2AA8, 0x3FFC, -0x0E38, 0x0E38, true }, 170.0f, 0x3FFC },
{ { 0x2AA8, -0x0E38, 0x0E38, 0x0E38, 0x0000, 0x0000, true }, 0.0f, 0x0000 },
{ { 0x2AA8, -0x0E38, 0x0000, 0x0E38, 0x0000, 0x1C70, true }, 0.0f, 0x0000 },
{ { 0x2AA8, -0x0E38, -0x0E38, 0x0000, 0x0000, 0x0000, true }, 0.0f, 0x0000 },
{ { 0x071C, -0x0E38, 0x0E38, 0x1C70, 0x0000, 0x0000, true }, 0.0f, 0x0000 },
{ { 0x0E38, -0x0E38, 0x0000, 0x1C70, 0x0000, 0x0E38, true }, 0.0f, 0x0000 },
{ { 0x2AA8, -0x1C70, 0x1C70, 0x0E38, -0x0E38, 0x0E38, true }, 0.0f, 0x0000 },
{ { 0x18E2, -0x0E38, 0x0E38, 0x0E38, 0x0000, 0x0000, true }, 0.0f, 0x0000 },
};
void func_800344BC(Actor* actor, struct_80034A14_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, 2000, 1);
temp1 = (ABS(sp40) >= 0x8000) ? 0 : ABS(sp40);
arg1->unk_08.y = CLAMP(arg1->unk_08.y, -temp1, temp1);
temp = (ABS(bodyYawDiff) >= 0x8000) ? 0 : ABS(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, 2000, 1);
temp1 = (ABS(sp40) >= 0x8000) ? 0 : ABS(sp40);
arg1->unk_0E.y = CLAMP(arg1->unk_0E.y, -temp1, temp1);
temp = (ABS(bodyYawDiff) >= 0x8000) ? 0 : ABS(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, 2000, 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, 2000, 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, 2000, 1);
}
s16 func_800347E8(s16 arg0) {
return D_80116130[arg0].unk_14;
s16 Npc_GetTrackingPresetMaxPlayerYaw(s16 presetIndex) {
return sNpcTrackingPresets[presetIndex].maxYawForPlayerTracking;
}
s16 func_80034810(Actor* actor, struct_80034A14_arg1* arg1, f32 arg2, s16 arg3, s16 arg4) {
/**
* 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 var;
s16 abs_var;
s16 yaw;
s16 yawDiff;
if (arg4 != 0) {
return arg4;
if (trackingMode != NPC_TRACKING_PLAYER_AUTO_TURN) {
return trackingMode;
}
if (arg1->unk_00 != 0) {
return 4;
if (interactInfo->talkState != NPC_TALK_STATE_IDLE) {
// When talking, always fully turn to face the player
return NPC_TRACKING_FULL_BODY;
}
if (arg2 < Math_Vec3f_DistXYZ(&actor->world.pos, &arg1->unk_18)) {
arg1->unk_04 = 0;
arg1->unk_06 = 0;
return 1;
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;
}
var = Math_Vec3f_Yaw(&actor->world.pos, &arg1->unk_18);
abs_var = ABS((s16)((f32)var - actor->shape.rot.y));
if (arg3 >= abs_var) {
arg1->unk_04 = 0;
arg1->unk_06 = 0;
return 2;
yaw = Math_Vec3f_Yaw(&actor->world.pos, &interactInfo->trackPos);
yawDiff = ABS((s16)((f32)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;
}
if (DECR(arg1->unk_04) != 0) {
return arg1->unk_02;
// 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 (arg1->unk_06) {
switch (interactInfo->autoTurnState) {
case 0:
case 2:
arg1->unk_04 = Rand_S16Offset(30, 30);
arg1->unk_06++;
return 1;
// Just stand still, not tracking the player
interactInfo->autoTurnTimer = Rand_S16Offset(30, 30);
interactInfo->autoTurnState++;
return NPC_TRACKING_NONE;
case 1:
arg1->unk_04 = Rand_S16Offset(10, 10);
arg1->unk_06++;
return 3;
// Glance at the player by only turning the head
interactInfo->autoTurnTimer = Rand_S16Offset(10, 10);
interactInfo->autoTurnState++;
return NPC_TRACKING_HEAD;
}
return 4;
// Auto-turn sequence complete, turn towards the player
return NPC_TRACKING_FULL_BODY;
}
void func_80034A14(Actor* actor, struct_80034A14_arg1* arg1, s16 arg2, s16 arg3) {
struct_80116130_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_80034810(actor, arg1, D_80116130[arg2].unk_10, D_80116130[arg2].unk_14, arg3);
interactInfo->trackingMode =
Npc_UpdateAutoTurn(actor, interactInfo, sNpcTrackingPresets[presetIndex].autoTurnDistanceRange,
sNpcTrackingPresets[presetIndex].maxYawForPlayerTracking, trackingMode);
sp38 = D_80116130[arg2].sub_00;
rotLimits = sNpcTrackingPresets[presetIndex].rotLimits;
switch (arg1->unk_02) {
case 1:
sp38.unk_00 = 0;
sp38.unk_04 = 0;
sp38.unk_02 = 0;
switch (interactInfo->trackingMode) {
case NPC_TRACKING_NONE:
rotLimits.maxHeadYaw = 0;
rotLimits.maxHeadPitch = 0;
rotLimits.minHeadPitch = 0;
FALLTHROUGH;
case 3:
sp38.unk_06 = 0;
sp38.unk_0A = 0;
sp38.unk_08 = 0;
case NPC_TRACKING_HEAD:
rotLimits.maxTorsoYaw = 0;
rotLimits.maxTorsoPitch = 0;
rotLimits.minTorsoPitch = 0;
FALLTHROUGH;
case 2:
sp38.unk_0C = 0;
case NPC_TRACKING_HEAD_AND_TORSO:
rotLimits.rotateYaw = false;
break;
}
func_800344BC(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* func_80034B28(GraphicsContext* gfxCtx) {