diff --git a/assets/xml/objects/object_link_child.xml b/assets/xml/objects/object_link_child.xml
index 7d353d9edf2..3d76cd8e1df 100644
--- a/assets/xml/objects/object_link_child.xml
+++ b/assets/xml/objects/object_link_child.xml
@@ -3,16 +3,16 @@
-
-
-
-
-
+
+
+
+
+
-
-
-
+
+
+
diff --git a/assets/xml/objects/object_link_zora.xml b/assets/xml/objects/object_link_zora.xml
index be4c542746b..08936791a8e 100644
--- a/assets/xml/objects/object_link_zora.xml
+++ b/assets/xml/objects/object_link_zora.xml
@@ -3,16 +3,16 @@
-
-
-
-
-
+
+
+
+
+
-
-
-
+
+
+
diff --git a/assets/xml/objects/object_test3.xml b/assets/xml/objects/object_test3.xml
index c32732b35fa..133fe458aa8 100644
--- a/assets/xml/objects/object_test3.xml
+++ b/assets/xml/objects/object_test3.xml
@@ -9,8 +9,8 @@
-
-
+
+
@@ -20,16 +20,16 @@
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
diff --git a/assets/xml/objects/object_zm.xml b/assets/xml/objects/object_zm.xml
index 1848aa222ed..61163d50b4c 100644
--- a/assets/xml/objects/object_zm.xml
+++ b/assets/xml/objects/object_zm.xml
@@ -28,7 +28,7 @@
-
+
@@ -36,8 +36,8 @@
-
-
+
+
diff --git a/include/face_change.h b/include/face_change.h
new file mode 100644
index 00000000000..1fa28045768
--- /dev/null
+++ b/include/face_change.h
@@ -0,0 +1,13 @@
+#ifndef FACE_CHANGE_H
+#define FACE_CHANGE_H
+
+typedef struct FaceChange {
+ /* 0x0 */ s16 face;
+ /* 0x2 */ s16 timer;
+} FaceChange; // size = 0x4
+
+s16 FaceChange_UpdateBlinking(FaceChange* faceChange, s16 blinkIntervalBase, s16 blinkIntervalRandRange, s16 blinkDuration);
+s16 FaceChange_UpdateBlinkingAlt(FaceChange* faceChange, s16 blinkIntervalBase, s16 blinkIntervalRandRange, s16 blinkDuration);
+s16 FaceChange_UpdateRandomSet(FaceChange* faceChange, s16 changeTimerBase, s16 changeTimerRandRange, s16 faceSetRange);
+
+#endif
diff --git a/include/z64actor.h b/include/z64actor.h
index ef4e113110d..abe5ee152c5 100644
--- a/include/z64actor.h
+++ b/include/z64actor.h
@@ -54,7 +54,7 @@ typedef void (*ActorShadowFunc)(struct Actor* actor, struct Lights* mapper, stru
typedef struct {
/* 0x00 */ Vec3s rot; // Current actor shape rotation
- /* 0x06 */ s16 face; // Used to index eyebrow/eye/mouth textures. Only used by player
+ /* 0x06 */ s16 face; // Used to index eyes and mouth textures. Only used by player
/* 0x08 */ f32 yOffset; // Model y axis offset. Represents model space units
/* 0x0C */ ActorShadowFunc shadowDraw; // Shadow draw function
/* 0x10 */ f32 shadowScale; // Changes the size of the shadow
@@ -759,11 +759,6 @@ typedef struct NpcInteractInfo {
/* 0x24 */ UNK_TYPE1 unk_24[0x4];
} NpcInteractInfo; // size = 0x28
-typedef struct BlinkInfo {
- /* 0x0 */ s16 eyeTexIndex;
- /* 0x2 */ s16 blinkTimer;
-} BlinkInfo; // size = 0x4
-
extern AttentionRangeParams gAttentionRanges[ATTENTION_RANGE_MAX];
extern s16 D_801AED48[8];
extern Gfx D_801AEF88[];
@@ -911,9 +906,6 @@ Actor* Actor_SpawnAsChild(ActorContext* actorCtx, Actor* parent, struct PlayStat
f32 posY, f32 posZ, s16 rotX, s16 rotY, s16 rotZ, s32 params);
void Actor_SpawnTransitionActors(struct PlayState* play, ActorContext* actorCtx);
void Enemy_StartFinishingBlow(struct PlayState* play, Actor* actor);
-s16 func_800BBAC0(BlinkInfo* info, s16 arg1, s16 arg2, s16 arg3);
-s16 func_800BBB74(BlinkInfo* info, s16 arg1, s16 arg2, s16 arg3);
-s16 func_800BBC20(BlinkInfo* info, s16 arg1, s16 arg2, s16 arg3);
void Actor_SpawnBodyParts(Actor* actor, struct PlayState* play, s32 partParams, Gfx** dList);
void Actor_SpawnFloorDustRing(struct PlayState* play, Actor* actor, Vec3f* posXZ, f32 radius, s32 countMinusOne,
f32 randAccelWeight, s16 scale, s16 scaleStep, u8 useLighting);
diff --git a/include/z64player.h b/include/z64player.h
index 277af318b2f..7afbcfe1cd2 100644
--- a/include/z64player.h
+++ b/include/z64player.h
@@ -9,6 +9,7 @@
#include "z64interface.h"
#include "z64item.h"
#include "z64light.h"
+#include "face_change.h"
struct Player;
struct PlayState;
@@ -473,44 +474,50 @@ typedef enum PlayerModelGroup {
/* 15 */ PLAYER_MODELGROUP_MAX
} PlayerModelGroup;
-typedef enum PlayerEyeIndex {
+typedef struct PlayerFaceIndices {
+ /* 0x0 */ u8 eyeIndex;
+ /* 0x1 */ u8 mouthIndex;
+} PlayerFaceIndices; // size = 0x2
+
+typedef enum PlayerEyes {
/* 0 */ PLAYER_EYES_OPEN,
/* 1 */ PLAYER_EYES_HALF,
/* 2 */ PLAYER_EYES_CLOSED,
- /* 3 */ PLAYER_EYES_ROLL_RIGHT,
- /* 4 */ PLAYER_EYES_ROLL_LEFT,
- /* 5 */ PLAYER_EYES_ROLL_UP,
- /* 6 */ PLAYER_EYES_ROLL_DOWN,
- /* 7 */ PLAYER_EYES_7,
+ /* 3 */ PLAYER_EYES_RIGHT,
+ /* 4 */ PLAYER_EYES_LEFT,
+ /* 5 */ PLAYER_EYES_UP,
+ /* 6 */ PLAYER_EYES_DOWN,
+ /* 7 */ PLAYER_EYES_WINCING, // For Goron, this is a surprised eye
/* 8 */ PLAYER_EYES_MAX
-} PlayerEyeIndex;
+} PlayerEyes;
-typedef enum PlayerMouthIndex {
+typedef enum PlayerMouth {
/* 0 */ PLAYER_MOUTH_CLOSED,
- /* 1 */ PLAYER_MOUTH_TEETH,
- /* 2 */ PLAYER_MOUTH_ANGRY,
- /* 3 */ PLAYER_MOUTH_HAPPY,
+ /* 1 */ PLAYER_MOUTH_HALF,
+ /* 2 */ PLAYER_MOUTH_OPEN,
+ /* 3 */ PLAYER_MOUTH_SMILE,
/* 4 */ PLAYER_MOUTH_MAX
-} PlayerMouthIndex;
-
-typedef enum PlayerFacialExpression {
- /* 0 */ PLAYER_FACE_0,
- /* 1 */ PLAYER_FACE_1,
- /* 2 */ PLAYER_FACE_2,
- /* 3 */ PLAYER_FACE_3,
- /* 4 */ PLAYER_FACE_4,
- /* 5 */ PLAYER_FACE_5,
- /* 6 */ PLAYER_FACE_6,
- /* 7 */ PLAYER_FACE_7,
- /* 8 */ PLAYER_FACE_8,
- /* 9 */ PLAYER_FACE_9,
- /* 10 */ PLAYER_FACE_10,
- /* 11 */ PLAYER_FACE_11,
- /* 12 */ PLAYER_FACE_12,
- /* 13 */ PLAYER_FACE_13,
- /* 14 */ PLAYER_FACE_14,
- /* 15 */ PLAYER_FACE_15
-} PlayerFacialExpression;
+} PlayerMouth;
+
+typedef enum PlayerFace {
+ /* 0 */ PLAYER_FACE_NEUTRAL,
+ /* 1 */ PLAYER_FACE_NEUTRAL_BLINKING_HALF,
+ /* 2 */ PLAYER_FACE_NEUTRAL_BLINKING_CLOSED,
+ /* 3 */ PLAYER_FACE_NEUTRAL_2,
+ /* 4 */ PLAYER_FACE_NEUTRAL_BLINKING_HALF_2,
+ /* 5 */ PLAYER_FACE_NEUTRAL_BLINKING_CLOSED_2,
+ /* 6 */ PLAYER_FACE_LOOK_LEFT,
+ /* 7 */ PLAYER_FACE_SURPRISED,
+ /* 8 */ PLAYER_FACE_HURT,
+ /* 9 */ PLAYER_FACE_GASP,
+ /* 10 */ PLAYER_FACE_LOOK_RIGHT,
+ /* 11 */ PLAYER_FACE_LOOK_LEFT_2,
+ /* 12 */ PLAYER_FACE_EYES_CLOSED_MOUTH_OPEN,
+ /* 13 */ PLAYER_FACE_OPENING,
+ /* 14 */ PLAYER_FACE_EYES_AND_MOUTH_OPEN,
+ /* 15 */ PLAYER_FACE_SMILE,
+ /* 16 */ PLAYER_FACE_MAX
+} PlayerFace;
typedef enum PlayerLimb {
/* 0x00 */ PLAYER_LIMB_NONE,
@@ -1165,7 +1172,7 @@ typedef struct Player {
/* 0x2C8 */ SkelAnime unk_2C8;
/* 0x30C */ Vec3s jointTable[5];
/* 0x32A */ Vec3s morphTable[5];
- /* 0x348 */ BlinkInfo blinkInfo;
+ /* 0x348 */ FaceChange faceChange;
/* 0x34C */ Actor* heldActor;
/* 0x350 */ PosRot leftHandWorld;
/* 0x364 */ Actor* rightHandActor;
diff --git a/src/code/game.c b/src/code/game.c
index 290f474a9ce..e60e9c48c71 100644
--- a/src/code/game.c
+++ b/src/code/game.c
@@ -1,5 +1,6 @@
#include "z64game.h"
+#include "prevent_bss_reordering.h"
#include "global.h"
#include "audiomgr.h"
#include "libu64/debug.h"
diff --git a/src/code/z_actor.c b/src/code/z_actor.c
index 9b1d38d0e01..164d8816ccf 100644
--- a/src/code/z_actor.c
+++ b/src/code/z_actor.c
@@ -3831,51 +3831,90 @@ void Enemy_StartFinishingBlow(PlayState* play, Actor* actor) {
SoundSource_PlaySfxAtFixedWorldPos(play, &actor->world.pos, 20, NA_SE_EN_LAST_DAMAGE);
}
-// blinking routine
-s16 func_800BBAC0(BlinkInfo* info, s16 arg1, s16 arg2, s16 arg3) {
- if (DECR(info->blinkTimer) == 0) {
- info->blinkTimer = Rand_S16Offset(arg1, arg2);
- }
-
- if (info->blinkTimer - arg3 > 0) {
- info->eyeTexIndex = 0;
- } else if ((info->blinkTimer - arg3 >= -1) || (info->blinkTimer < 2)) {
- info->eyeTexIndex = 1;
+/**
+ * Updates `FaceChange` data for a blinking pattern.
+ * This system expects that the actor using the system has defined 3 faces in this exact order:
+ * "eyes open", "eyes half open", "eyes closed".
+ *
+ * @param faceChange pointer to an actor's faceChange data
+ * @param blinkIntervalBase The base number of frames between blinks
+ * @param blinkIntervalRandRange The range for a random number of frames that can be added to `blinkIntervalBase`
+ * @param blinkDuration The number of frames it takes for a single blink to occur
+ */
+s16 FaceChange_UpdateBlinking(FaceChange* faceChange, s16 blinkIntervalBase, s16 blinkIntervalRandRange,
+ s16 blinkDuration) {
+ if (DECR(faceChange->timer) == 0) {
+ faceChange->timer = Rand_S16Offset(blinkIntervalBase, blinkIntervalRandRange);
+ }
+
+ if (faceChange->timer - blinkDuration > 0) {
+ // `timer - duration` is positive so this is the default state: "eyes open" face
+ faceChange->face = 0;
+ } else if ((faceChange->timer - blinkDuration >= -1) || (faceChange->timer < 2)) {
+ // This condition aims to catch both cases where the "eyes half open" face is needed.
+ // Note that the comparison assumes the duration of the "eyes half open" phase is 2 frames, irrespective of the
+ // value of `blinkDuration`. The duration for the "eyes closed" phase is `blinkDuration - 4`.
+ // For Player's use case `blinkDuration` is 6, so the "eyes closed" phase happens to have
+ // the same duration as each "eyes half open" phase.
+ faceChange->face = 1;
} else {
- info->eyeTexIndex = 2;
+ // If both conditions above fail, the only possibility left is the "eyes closed" face
+ faceChange->face = 2;
}
- return info->eyeTexIndex;
+ return faceChange->face;
}
-// blinking routine
-s16 func_800BBB74(BlinkInfo* info, s16 arg1, s16 arg2, s16 arg3) {
- if (DECR(info->blinkTimer) == 0) {
- info->blinkTimer = Rand_S16Offset(arg1, arg2);
+/**
+ * Updates `FaceChange` data for a blinking pattern.
+ * This system expects that the actor using the system has defined 3 faces in this exact order:
+ * "eyes open", "eyes half open", "eyes closed".
+ *
+ * @param faceChange pointer to an actor's faceChange data
+ * @param blinkIntervalBase The base number of frames between blinks
+ * @param blinkIntervalRandRange The range for a random number of frames that can be added to `blinkIntervalBase`
+ * @param blinkDuration The number of frames it takes for a single blink to occur
+ */
+s16 FaceChange_UpdateBlinkingAlt(FaceChange* faceChange, s16 blinkIntervalBase, s16 blinkIntervalRandRange,
+ s16 blinkDuration) {
+ if (DECR(faceChange->timer) == 0) {
+ faceChange->timer = Rand_S16Offset(blinkIntervalBase, blinkIntervalRandRange);
}
- if (info->blinkTimer - arg3 > 0) {
- info->eyeTexIndex = 0;
- } else if (info->blinkTimer - arg3 == 0) {
- info->eyeTexIndex = 1;
+ if (faceChange->timer - blinkDuration > 0) {
+ // `timer - duration` is positive so this is the default state: "eyes open" face
+ faceChange->face = 0;
+ } else if (faceChange->timer - blinkDuration == 0) {
+ faceChange->face = 1;
} else {
- info->eyeTexIndex = 2;
+ // If both conditions above fail, the only possibility left is the "eyes closed" face
+ faceChange->face = 2;
}
- return info->eyeTexIndex;
+ return faceChange->face;
}
-// unused blinking routine
-s16 func_800BBC20(BlinkInfo* info, s16 arg1, s16 arg2, s16 arg3) {
- if (DECR(info->blinkTimer) == 0) {
- info->blinkTimer = Rand_S16Offset(arg1, arg2);
- info->eyeTexIndex++;
- if ((info->eyeTexIndex % 3) == 0) {
- info->eyeTexIndex = (s32)(Rand_ZeroOne() * arg3) * 3;
+/**
+ * Updates `FaceChange` data for randomly selected face sets.
+ * Each set contains 3 faces. After the timer runs out, the next face in the set is used.
+ * After the third face in a set is used, a new face set is randomly chosen.
+ *
+ * @param faceChange pointer to an actor's faceChange data
+ * @param changeTimerBase The base number of frames between each face change
+ * @param changeTimerRandRange The range for a random number of frames that can be added to `changeTimerBase`
+ * @param faceSetRange The max number of face sets that will be chosen from
+ */
+s16 FaceChange_UpdateRandomSet(FaceChange* faceChange, s16 changeTimerBase, s16 changeTimerRandRange,
+ s16 faceSetRange) {
+ if (DECR(faceChange->timer) == 0) {
+ faceChange->timer = Rand_S16Offset(changeTimerBase, changeTimerRandRange);
+ faceChange->face++;
+ if ((faceChange->face % 3) == 0) {
+ faceChange->face = (s32)(Rand_ZeroOne() * faceSetRange) * 3;
}
}
- return info->eyeTexIndex;
+ return faceChange->face;
}
void Actor_SpawnBodyParts(Actor* actor, PlayState* play, s32 partParams, Gfx** dList) {
diff --git a/src/code/z_player_lib.c b/src/code/z_player_lib.c
index 81bb0cb774f..a72b2c78157 100644
--- a/src/code/z_player_lib.c
+++ b/src/code/z_player_lib.c
@@ -1824,46 +1824,156 @@ Gfx gCullFrontDList[] = {
gsSPEndDisplayList(),
};
-TexturePtr sPlayerEyesTextures[PLAYER_EYES_MAX] = {
- gLinkHumanEyesOpenTex, // PLAYER_EYES_OPEN
- gLinkHumanEyesHalfTex, // PLAYER_EYES_HALF
- gLinkHumanEyesClosedTex, // PLAYER_EYES_CLOSED
- gLinkHumanEyesRollRightTex, // PLAYER_EYES_ROLL_RIGHT
- gLinkHumanEyesRollLeftTex, // PLAYER_EYES_ROLL_LEFT
- gLinkHumanEyesRollUpTex, // PLAYER_EYES_ROLL_UP
- gLinkHumanEyesRollDownTex, // PLAYER_EYES_ROLL_DOWN
- object_link_child_Tex_003800, // PLAYER_EYES_7
-};
-
-TexturePtr sPlayerMouthTextures[PLAYER_MOUTH_MAX] = {
+/**
+ * Link's eyes and mouth textures are placed at the exact same place in all player form's respective object files.
+ * This allows the array to only contain the symbols for one file and have it apply to both. This is a problem for
+ * shiftability, and changes will need to be made in the code to account for this in a modding scenario. The symbols
+ * from human Link's object are used here.
+ *
+ * Note that some player forms do not use the eyes and mouth textures loaded into segments 0x08 and 0x09 respectively.
+ * Therefore, the segment will point at gargage data, but this does not cause issues as the data is not read from.
+ */
+#ifndef AVOID_UB
+static TexturePtr sEyeTextures[PLAYER_EYES_MAX] = {
+ gLinkHumanEyesOpenTex, // PLAYER_EYES_OPEN
+ gLinkHumanEyesHalfTex, // PLAYER_EYES_HALF
+ gLinkHumanEyesClosedTex, // PLAYER_EYES_CLOSED
+ gLinkHumanEyesRightTex, // PLAYER_EYES_RIGHT
+ gLinkHumanEyesLeftTex, // PLAYER_EYES_LEFT
+ gLinkHumanEyesUpTex, // PLAYER_EYES_UP
+ gLinkHumanEyesDownTex, // PLAYER_EYES_DOWN
+ gLinkHumanEyesWincingTex, // PLAYER_EYES_WINCING
+};
+
+static TexturePtr sMouthTextures[PLAYER_MOUTH_MAX] = {
gLinkHumanMouthClosedTex, // PLAYER_MOUTH_CLOSED
- gLinkHumanMouthTeethTex, // PLAYER_MOUTH_TEETH
- gLinkHumanMouthAngryTex, // PLAYER_MOUTH_ANGRY
- gLinkHumanMouthHappyTex, // PLAYER_MOUTH_HAPPY
-};
-
-typedef struct PlayerFaceIndices {
- /* 0x0 */ u8 eyeIndex;
- /* 0x1 */ u8 mouthIndex;
-} PlayerFaceIndices; // size = 0x2
-
-PlayerFaceIndices sPlayerFaces[] = {
- { PLAYER_EYES_OPEN, PLAYER_MOUTH_CLOSED }, // PLAYER_FACE_0
- { PLAYER_EYES_HALF, PLAYER_MOUTH_CLOSED }, // PLAYER_FACE_1
- { PLAYER_EYES_CLOSED, PLAYER_MOUTH_CLOSED }, // PLAYER_FACE_2
- { PLAYER_EYES_OPEN, PLAYER_MOUTH_CLOSED }, // PLAYER_FACE_3
- { PLAYER_EYES_HALF, PLAYER_MOUTH_CLOSED }, // PLAYER_FACE_4
- { PLAYER_EYES_CLOSED, PLAYER_MOUTH_CLOSED }, // PLAYER_FACE_5
- { PLAYER_EYES_ROLL_LEFT, PLAYER_MOUTH_CLOSED }, // PLAYER_FACE_6
- { PLAYER_EYES_ROLL_UP, PLAYER_MOUTH_TEETH }, // PLAYER_FACE_7
- { PLAYER_EYES_7, PLAYER_MOUTH_ANGRY }, // PLAYER_FACE_8
- { PLAYER_EYES_OPEN, PLAYER_MOUTH_ANGRY }, // PLAYER_FACE_9
- { PLAYER_EYES_ROLL_RIGHT, PLAYER_MOUTH_CLOSED }, // PLAYER_FACE_10
- { PLAYER_EYES_ROLL_LEFT, PLAYER_MOUTH_CLOSED }, // PLAYER_FACE_11
- { PLAYER_EYES_CLOSED, PLAYER_MOUTH_ANGRY }, // PLAYER_FACE_12
- { PLAYER_EYES_HALF, PLAYER_MOUTH_TEETH }, // PLAYER_FACE_13
- { PLAYER_EYES_OPEN, PLAYER_MOUTH_ANGRY }, // PLAYER_FACE_14
- { PLAYER_EYES_OPEN, PLAYER_MOUTH_HAPPY }, // PLAYER_FACE_15
+ gLinkHumanMouthHalfTex, // PLAYER_MOUTH_HALF
+ gLinkHumanMouthOpenTex, // PLAYER_MOUTH_OPEN
+ gLinkHumanMouthSmileTex, // PLAYER_MOUTH_SMILE
+};
+#else
+static TexturePtr sEyeTextures[PLAYER_FORM_MAX][PLAYER_EYES_MAX] = {
+ // PLAYER_FORM_FIERCE_DEITY
+ {
+ NULL, // PLAYER_EYES_OPEN
+ NULL, // PLAYER_EYES_HALF
+ NULL, // PLAYER_EYES_CLOSED
+ NULL, // PLAYER_EYES_RIGHT
+ NULL, // PLAYER_EYES_LEFT
+ NULL, // PLAYER_EYES_UP
+ NULL, // PLAYER_EYES_DOWN
+ NULL, // PLAYER_EYES_WINCING
+ },
+ // PLAYER_FORM_GORON
+ // Note: use PLAYER_EYES_WINCING to access `gLinkGoronEyesSurprisedTex`. See `Player_DrawImpl`.
+ {
+ gLinkGoronEyesOpenTex, // PLAYER_EYES_OPEN
+ gLinkGoronEyesHalfTex, // PLAYER_EYES_HALF
+ gLinkGoronEyesClosedTex, // PLAYER_EYES_CLOSED
+ gLinkGoronEyesSurprisedTex, // PLAYER_EYES_RIGHT
+ NULL, // PLAYER_EYES_LEFT
+ NULL, // PLAYER_EYES_UP
+ NULL, // PLAYER_EYES_DOWN
+ NULL, // PLAYER_EYES_WINCING
+ },
+ // PLAYER_FORM_ZORA
+ {
+ gLinkZoraEyesOpenTex, // PLAYER_EYES_OPEN
+ gLinkZoraEyesHalfTex, // PLAYER_EYES_HALF
+ gLinkZoraEyesClosedTex, // PLAYER_EYES_CLOSED
+ gLinkZoraEyesRightTex, // PLAYER_EYES_RIGHT
+ gLinkZoraEyesLeftTex, // PLAYER_EYES_LEFT
+ gLinkZoraEyesUpTex, // PLAYER_EYES_UP
+ gLinkZoraEyesDownTex, // PLAYER_EYES_DOWN
+ gLinkZoraEyesWincingTex, // PLAYER_EYES_WINCING
+ },
+ // PLAYER_FORM_DEKU
+ {
+ NULL, // PLAYER_EYES_OPEN
+ NULL, // PLAYER_EYES_HALF
+ NULL, // PLAYER_EYES_CLOSED
+ NULL, // PLAYER_EYES_RIGHT
+ NULL, // PLAYER_EYES_LEFT
+ NULL, // PLAYER_EYES_UP
+ NULL, // PLAYER_EYES_DOWN
+ NULL, // PLAYER_EYES_WINCING
+ },
+ // PLAYER_FORM_HUMAN
+ {
+ gLinkHumanEyesOpenTex, // PLAYER_EYES_OPEN
+ gLinkHumanEyesHalfTex, // PLAYER_EYES_HALF
+ gLinkHumanEyesClosedTex, // PLAYER_EYES_CLOSED
+ gLinkHumanEyesRightTex, // PLAYER_EYES_RIGHT
+ gLinkHumanEyesLeftTex, // PLAYER_EYES_LEFT
+ gLinkHumanEyesUpTex, // PLAYER_EYES_UP
+ gLinkHumanEyesDownTex, // PLAYER_EYES_DOWN
+ gLinkHumanEyesWincingTex, // PLAYER_EYES_WINCING
+ },
+};
+
+static TexturePtr sMouthTextures[PLAYER_FORM_MAX][PLAYER_MOUTH_MAX] = {
+ // PLAYER_FORM_FIERCE_DEITY
+ {
+ NULL, // PLAYER_MOUTH_CLOSED
+ NULL, // PLAYER_MOUTH_HALF
+ NULL, // PLAYER_MOUTH_OPEN
+ NULL, // PLAYER_MOUTH_SMILE
+ },
+ // PLAYER_FORM_GORON
+ {
+ NULL, // PLAYER_MOUTH_CLOSED
+ NULL, // PLAYER_MOUTH_HALF
+ NULL, // PLAYER_MOUTH_OPEN
+ NULL, // PLAYER_MOUTH_SMILE
+ },
+ // PLAYER_FORM_ZORA
+ {
+ gLinkZoraMouthClosedTex, // PLAYER_MOUTH_CLOSED
+ gLinkZoraMouthHalfTex, // PLAYER_MOUTH_HALF
+ gLinkZoraMouthOpenTex, // PLAYER_MOUTH_OPEN
+ gLinkZoraMouthSmileTex, // PLAYER_MOUTH_SMILE
+ },
+ // PLAYER_FORM_DEKU
+ {
+ NULL, // PLAYER_MOUTH_CLOSED
+ NULL, // PLAYER_MOUTH_HALF
+ NULL, // PLAYER_MOUTH_OPEN
+ NULL, // PLAYER_MOUTH_SMILE
+ },
+ // PLAYER_FORM_HUMAN
+ {
+ gLinkHumanMouthClosedTex, // PLAYER_MOUTH_CLOSED
+ gLinkHumanMouthHalfTex, // PLAYER_MOUTH_HALF
+ gLinkHumanMouthOpenTex, // PLAYER_MOUTH_OPEN
+ gLinkHumanMouthSmileTex, // PLAYER_MOUTH_SMILE
+ },
+};
+#endif
+
+PlayerFaceIndices sPlayerFaces[PLAYER_FACE_MAX] = {
+ // The first 6 faces defined must be default blinking faces. See relevant code in `Player_UpdateCommon`.
+ { PLAYER_EYES_OPEN, PLAYER_MOUTH_CLOSED }, // PLAYER_FACE_NEUTRAL
+ { PLAYER_EYES_HALF, PLAYER_MOUTH_CLOSED }, // PLAYER_FACE_NEUTRAL_BLINKING_HALF
+ { PLAYER_EYES_CLOSED, PLAYER_MOUTH_CLOSED }, // PLAYER_FACE_NEUTRAL_BLINKING_CLOSED
+
+ // This duplicate set of blinking faces is defined because Player will choose between the first and second set
+ // based on gameplayFrames. See relevant code in `Player_UpdateCommon`.
+ // This, in theory, allows for psuedo-random variance in the faces used. But in practice, duplicate faces are used.
+ { PLAYER_EYES_OPEN, PLAYER_MOUTH_CLOSED }, // PLAYER_FACE_NEUTRAL_2
+ { PLAYER_EYES_HALF, PLAYER_MOUTH_CLOSED }, // PLAYER_FACE_NEUTRAL_BLINKING_HALF_2
+ { PLAYER_EYES_CLOSED, PLAYER_MOUTH_CLOSED }, // PLAYER_FACE_NEUTRAL_BLINKING_CLOSED_2
+
+ // Additional faces. Most faces are encoded within animations.
+ { PLAYER_EYES_LEFT, PLAYER_MOUTH_CLOSED }, // PLAYER_FACE_LOOK_LEFT
+ { PLAYER_EYES_UP, PLAYER_MOUTH_HALF }, // PLAYER_FACE_SURPRISED
+ { PLAYER_EYES_WINCING, PLAYER_MOUTH_OPEN }, // PLAYER_FACE_HURT
+ { PLAYER_EYES_OPEN, PLAYER_MOUTH_OPEN }, // PLAYER_FACE_GASP
+ { PLAYER_EYES_RIGHT, PLAYER_MOUTH_CLOSED }, // PLAYER_FACE_LOOK_RIGHT
+ { PLAYER_EYES_LEFT, PLAYER_MOUTH_CLOSED }, // PLAYER_FACE_LOOK_LEFT_2
+ { PLAYER_EYES_CLOSED, PLAYER_MOUTH_OPEN }, // PLAYER_FACE_EYES_CLOSED_MOUTH_OPEN
+ { PLAYER_EYES_HALF, PLAYER_MOUTH_HALF }, // PLAYER_FACE_OPENING
+ { PLAYER_EYES_OPEN, PLAYER_MOUTH_OPEN }, // PLAYER_FACE_EYES_AND_MOUTH_OPEN
+ { PLAYER_EYES_OPEN, PLAYER_MOUTH_SMILE }, // PLAYER_FACE_SMILE
};
// Note the correct pointer to pass as the jointTable is the jointTable pointer from the SkelAnime struct, not the
@@ -1879,25 +1989,41 @@ void Player_DrawImpl(PlayState* play, void** skeleton, Vec3s* jointTable, s32 dL
gfx = POLY_OPA_DISP;
+ // If the eyes index provided by the animation is negative, use the value provided by the `face` argument instead
if (eyeIndex < 0) {
eyeIndex = sPlayerFaces[face].eyeIndex;
}
if (playerForm == PLAYER_FORM_GORON) {
- if ((eyeIndex >= PLAYER_EYES_ROLL_RIGHT) && (eyeIndex <= PLAYER_EYES_ROLL_DOWN)) {
+ // Goron does not have the eye textures to look in different directions
+ if ((eyeIndex >= PLAYER_EYES_RIGHT) && (eyeIndex <= PLAYER_EYES_DOWN)) {
eyeIndex = PLAYER_EYES_OPEN;
- } else if (eyeIndex == PLAYER_EYES_7) {
- eyeIndex = PLAYER_EYES_ROLL_RIGHT;
+ } else if (eyeIndex == PLAYER_EYES_WINCING) {
+ // Goron form puts a surpised expression where the eyes-right normally goes
+ eyeIndex = PLAYER_EYES_RIGHT;
}
}
- gSPSegment(&gfx[0], 0x08, Lib_SegmentedToVirtual(sPlayerEyesTextures[eyeIndex]));
+ // Only Human, Zora, and Goron will read the eye textures in the head limb display list.
+ // Fierce Deity and Deku will point this segment to garbage data, but it will be unread from.
+#ifndef AVOID_UB
+ gSPSegment(&gfx[0], 0x08, Lib_SegmentedToVirtual(sEyeTextures[eyeIndex]));
+#else
+ gSPSegment(&gfx[0], 0x08, Lib_SegmentedToVirtual(sEyeTextures[playerForm][eyeIndex]));
+#endif
+ // If the mouth index provided by the animation is negative, use the value provided by the `face` argument instead
if (mouthIndex < 0) {
mouthIndex = sPlayerFaces[face].mouthIndex;
}
- gSPSegment(&gfx[1], 0x09, Lib_SegmentedToVirtual(sPlayerMouthTextures[mouthIndex]));
+ // Only Human and Zora will read the mouth textures in the head limb display list.
+ // Goron, Fierce Deity, and Deku will point this segment to garbage data, but it will be unread from.
+#ifndef AVOID_UB
+ gSPSegment(&gfx[1], 0x09, Lib_SegmentedToVirtual(sMouthTextures[mouthIndex]));
+#else
+ gSPSegment(&gfx[1], 0x09, Lib_SegmentedToVirtual(sMouthTextures[playerForm][mouthIndex]));
+#endif
POLY_OPA_DISP = &gfx[2];
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 a0c17b2b963..fc72a8f764a 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
@@ -169,7 +169,7 @@ static TexturePtr sMouthTextures[] = {
gCremiaMouthHangingOpenTex,
};
-static TexturePtr sEyesTextures[] = {
+static TexturePtr sEyeTextures[] = {
gCremiaEyeOpenTex, gCremiaEyeHalfTex, gCremiaEyeClosedTex, gCremiaEyeHappyTex, gCremiaEyeAngryTex, gCremiaEyeSadTex,
};
@@ -1523,7 +1523,7 @@ void EnMaYto_Draw(Actor* thisx, PlayState* play) {
Gfx_SetupDL25_Opa(play->state.gfxCtx);
gSPSegment(POLY_OPA_DISP++, 0x09, Lib_SegmentedToVirtual(sMouthTextures[this->mouthTexIndex]));
- gSPSegment(POLY_OPA_DISP++, 0x08, Lib_SegmentedToVirtual(sEyesTextures[this->eyeTexIndex]));
+ gSPSegment(POLY_OPA_DISP++, 0x08, Lib_SegmentedToVirtual(sEyeTextures[this->eyeTexIndex]));
SkelAnime_DrawFlexOpa(play, this->skelAnime.skeleton, this->skelAnime.jointTable, this->skelAnime.dListCount,
EnMaYto_OverrideLimbDraw, EnMaYto_PostLimbDraw, &this->actor);
diff --git a/src/overlays/actors/ovl_En_Test3/z_en_test3.c b/src/overlays/actors/ovl_En_Test3/z_en_test3.c
index a263daa9c75..0d8d639bf56 100644
--- a/src/overlays/actors/ovl_En_Test3/z_en_test3.c
+++ b/src/overlays/actors/ovl_En_Test3/z_en_test3.c
@@ -1214,33 +1214,56 @@ void EnTest3_PostLimbDraw(PlayState* play, s32 limbIndex, Gfx** dList1, Gfx** dL
}
}
-static TexturePtr sEyeTextures[] = {
- gKafeiEyesOpenTex, gKafeiEyesHalfTex, gKafeiEyesClosedTex, gKafeiEyesRollRightTex,
- gKafeiEyesRollLeftTex, gKafeiEyesRollUpTex, gKafeiEyesRollDownTex, object_test3_Tex_006680,
+static TexturePtr sEyeTextures[PLAYER_EYES_MAX] = {
+ gKafeiEyesOpenTex, // PLAYER_EYES_OPEN
+ gKafeiEyesHalfTex, // PLAYER_EYES_HALF
+ gKafeiEyesClosedTex, // PLAYER_EYES_CLOSED
+ gKafeiEyesRightTex, // PLAYER_EYES_RIGHT
+ gKafeiEyesLeftTex, // PLAYER_EYES_LEFT
+ gKafeiEyesUpTex, // PLAYER_EYES_UP
+ gKafeiEyesDownTex, // PLAYER_EYES_DOWN
+ gKafeiEyesWincingTex, // PLAYER_EYES_WINCING
};
-static TexturePtr sMouthTextures[] = {
- gKafeiMouthClosedTex,
- gKafeiMouthTeethTex,
- gKafeiMouthAngryTex,
- gKafeiMouthHappyTex,
+static TexturePtr sMouthTextures[PLAYER_MOUTH_MAX] = {
+ gKafeiMouthClosedTex, // PLAYER_MOUTH_CLOSED
+ gKafeiMouthHalfTex, // PLAYER_MOUTH_HALF
+ gKafeiMouthOpenTex, // PLAYER_MOUTH_OPEN
+ gKafeiMouthSmileTex, // PLAYER_MOUTH_SMILE
};
-typedef struct {
- /* 0x0 */ u8 eyeIndex;
- /* 0x1 */ u8 mouthIndex;
-} KafeiFace; // size = 0x2
-
-static KafeiFace sFaceExpressions[] = {
- { 0, 0 }, { 1, 0 }, { 2, 0 }, { 0, 0 }, { 1, 0 }, { 2, 0 }, { 4, 0 }, { 5, 1 }, { 7, 2 }, { 0, 2 },
- { 3, 0 }, { 4, 0 }, { 2, 2 }, { 1, 1 }, { 0, 2 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 },
+static PlayerFaceIndices sKafeiFaces[PLAYER_FACE_MAX] = {
+ // The first 6 faces defined must be default blinking faces. See relevant code in `Player_UpdateCommon`.
+ { PLAYER_EYES_OPEN, PLAYER_MOUTH_CLOSED }, // PLAYER_FACE_NEUTRAL
+ { PLAYER_EYES_HALF, PLAYER_MOUTH_CLOSED }, // PLAYER_FACE_NEUTRAL_BLINKING_HALF
+ { PLAYER_EYES_CLOSED, PLAYER_MOUTH_CLOSED }, // PLAYER_FACE_NEUTRAL_BLINKING_CLOSED
+
+ // This duplicate set of blinking faces is defined because Player will choose between the first and second set
+ // based on gameplayFrames. See relevant code in `Player_UpdateCommon`.
+ // This, in theory, allows for psuedo-random variance in the faces used. But in practice, duplicate faces are used.
+ { PLAYER_EYES_OPEN, PLAYER_MOUTH_CLOSED }, // PLAYER_FACE_NEUTRAL_2
+ { PLAYER_EYES_HALF, PLAYER_MOUTH_CLOSED }, // PLAYER_FACE_NEUTRAL_BLINKING_HALF_2
+ { PLAYER_EYES_CLOSED, PLAYER_MOUTH_CLOSED }, // PLAYER_FACE_NEUTRAL_BLINKING_CLOSED_2
+
+ // Additional faces. Most faces are encoded within animations.
+ { PLAYER_EYES_LEFT, PLAYER_MOUTH_CLOSED }, // PLAYER_FACE_LOOK_LEFT
+ { PLAYER_EYES_UP, PLAYER_MOUTH_HALF }, // PLAYER_FACE_SURPRISED
+ { PLAYER_EYES_WINCING, PLAYER_MOUTH_OPEN }, // PLAYER_FACE_HURT
+ { PLAYER_EYES_OPEN, PLAYER_MOUTH_OPEN }, // PLAYER_FACE_GASP
+ { PLAYER_EYES_RIGHT, PLAYER_MOUTH_CLOSED }, // PLAYER_FACE_LOOK_RIGHT
+ { PLAYER_EYES_LEFT, PLAYER_MOUTH_CLOSED }, // PLAYER_FACE_LOOK_LEFT_2
+ { PLAYER_EYES_CLOSED, PLAYER_MOUTH_OPEN }, // PLAYER_FACE_EYES_CLOSED_MOUTH_OPEN
+ { PLAYER_EYES_HALF, PLAYER_MOUTH_HALF }, // PLAYER_FACE_OPENING
+ { PLAYER_EYES_OPEN, PLAYER_MOUTH_OPEN }, // PLAYER_FACE_EYES_AND_MOUTH_OPEN
+ // The mouth in this entry deviates from player. Similar to OoT's `sPlayerFaces`.
+ { PLAYER_EYES_OPEN, PLAYER_MOUTH_CLOSED }, // PLAYER_FACE_EYES_AND_MOUTH_OPEN
};
void EnTest3_Draw(Actor* thisx, PlayState* play2) {
PlayState* play = play2;
EnTest3* this = (EnTest3*)thisx;
- s32 eyeTexIndex = GET_EYE_INDEX_FROM_JOINT_TABLE(this->player.skelAnime.jointTable);
- s32 mouthTexIndex = GET_MOUTH_INDEX_FROM_JOINT_TABLE(this->player.skelAnime.jointTable);
+ s32 eyeIndex = GET_EYE_INDEX_FROM_JOINT_TABLE(this->player.skelAnime.jointTable);
+ s32 mouthIndex = GET_MOUTH_INDEX_FROM_JOINT_TABLE(this->player.skelAnime.jointTable);
Gfx* gfx;
OPEN_DISPS(play->state.gfxCtx);
@@ -1266,25 +1289,33 @@ void EnTest3_Draw(Actor* thisx, PlayState* play2) {
gfx = POLY_OPA_DISP;
- if (eyeTexIndex < 0) {
- eyeTexIndex = sFaceExpressions[this->player.actor.shape.face].eyeIndex;
+ // If the eyes index provided by the animation is negative, use the value provided by the `face` argument instead
+ if (eyeIndex < 0) {
+ eyeIndex = sKafeiFaces[this->player.actor.shape.face].eyeIndex;
}
- gSPSegment(&gfx[0], 0x08, Lib_SegmentedToVirtual(sEyeTextures[eyeTexIndex]));
- if (mouthTexIndex < 0) {
- mouthTexIndex = sFaceExpressions[this->player.actor.shape.face].mouthIndex;
+
+ gSPSegment(&gfx[0], 0x08, Lib_SegmentedToVirtual(sEyeTextures[eyeIndex]));
+
+ // If the mouth index provided by the animation is negative, use the value provided by the `face` argument instead
+ if (mouthIndex < 0) {
+ mouthIndex = sKafeiFaces[this->player.actor.shape.face].mouthIndex;
}
- gSPSegment(&gfx[1], 0x09, Lib_SegmentedToVirtual(sMouthTextures[mouthTexIndex]));
+
+ gSPSegment(&gfx[1], 0x09, Lib_SegmentedToVirtual(sMouthTextures[mouthIndex]));
POLY_OPA_DISP = &gfx[2];
SkelAnime_DrawFlexLod(play, this->player.skelAnime.skeleton, this->player.skelAnime.jointTable,
this->player.skelAnime.dListCount, EnTest3_OverrideLimbDraw, EnTest3_PostLimbDraw,
&this->player.actor, 0);
+
if (this->player.invincibilityTimer > 0) {
POLY_OPA_DISP = Play_SetFog(play, POLY_OPA_DISP);
}
+
if ((this->player.getItemDrawIdPlusOne - 1) != GID_NONE) {
Player_DrawGetItem(play, &this->player);
}
+
CLOSE_DISPS(play->state.gfxCtx);
}
diff --git a/src/overlays/actors/ovl_En_Toto/z_en_toto.c b/src/overlays/actors/ovl_En_Toto/z_en_toto.c
index c893e9b4083..30534db13d9 100644
--- a/src/overlays/actors/ovl_En_Toto/z_en_toto.c
+++ b/src/overlays/actors/ovl_En_Toto/z_en_toto.c
@@ -198,7 +198,7 @@ void func_80BA383C(EnToto* this, PlayState* play) {
}
Animation_PlayOnce(&this->skelAnime, sAnimations[this->animIndex]);
}
- func_800BBB74(&this->blinkInfo, 20, 80, 3);
+ FaceChange_UpdateBlinkingAlt(&this->faceChange, 20, 80, 3);
}
void func_80BA3930(EnToto* this, PlayState* play) {
@@ -299,7 +299,7 @@ void func_80BA3D38(EnToto* this, PlayState* play) {
this->text = ENTOTO_WEEK_EVENT_FLAGS ? &D_80BA5088[13] : &D_80BA5088[0];
func_80BA4C0C(this, play);
play->actorCtx.flags |= ACTORCTX_FLAG_5;
- this->blinkInfo.eyeTexIndex = 0;
+ this->faceChange.face = 0;
}
void func_80BA3DBC(EnToto* this, PlayState* play) {
@@ -702,14 +702,14 @@ void EnToto_Update(Actor* thisx, PlayState* play) {
}
void EnToto_Draw(Actor* thisx, PlayState* play) {
- TexturePtr sp4C[] = { object_zm_Tex_008AE8, object_zm_Tex_00A068, object_zm_Tex_00A468 };
+ TexturePtr eyeTextures[] = { gTotoEyesOpenTex, gTotoEyesHalfTex, gTotoEyesClosedTex };
EnToto* this = (EnToto*)thisx;
s32 pad;
OPEN_DISPS(play->state.gfxCtx);
Gfx_SetupDL25_Opa(play->state.gfxCtx);
- gSPSegment(POLY_OPA_DISP++, 0x08, Lib_SegmentedToVirtual(sp4C[this->blinkInfo.eyeTexIndex]));
+ gSPSegment(POLY_OPA_DISP++, 0x08, Lib_SegmentedToVirtual(eyeTextures[this->faceChange.face]));
Scene_SetRenderModeXlu(play, 0, 1);
SkelAnime_DrawFlexOpa(play, this->skelAnime.skeleton, this->skelAnime.jointTable, this->skelAnime.dListCount, NULL,
NULL, &this->actor);
diff --git a/src/overlays/actors/ovl_En_Toto/z_en_toto.h b/src/overlays/actors/ovl_En_Toto/z_en_toto.h
index 0ee203aa74e..04f8733e408 100644
--- a/src/overlays/actors/ovl_En_Toto/z_en_toto.h
+++ b/src/overlays/actors/ovl_En_Toto/z_en_toto.h
@@ -2,6 +2,7 @@
#define Z_EN_TOTO_H
#include "global.h"
+#include "face_change.h"
#include "assets/objects/object_zm/object_zm.h"
struct EnToto;
@@ -31,7 +32,7 @@ typedef struct EnToto {
/* 0x144 */ SkelAnime skelAnime;
/* 0x188 */ Vec3s jointTable[OBJECT_ZM_LIMB_MAX];
/* 0x1F4 */ Vec3s morphTable[OBJECT_ZM_LIMB_MAX];
- /* 0x260 */ BlinkInfo blinkInfo;
+ /* 0x260 */ FaceChange faceChange;
/* 0x264 */ ColliderCylinder collider;
/* 0x2B0 */ u8 actionFuncIndex;
/* 0x2B1 */ u8 unk2B1;
diff --git a/src/overlays/actors/ovl_En_Zod/z_en_zod.c b/src/overlays/actors/ovl_En_Zod/z_en_zod.c
index f6bece3de67..93d0ed597be 100644
--- a/src/overlays/actors/ovl_En_Zod/z_en_zod.c
+++ b/src/overlays/actors/ovl_En_Zod/z_en_zod.c
@@ -627,7 +627,7 @@ void EnZod_DrawDrums(EnZod* this, PlayState* play) {
}
void EnZod_Draw(Actor* thisx, PlayState* play) {
- static TexturePtr sTijoEyesTextures[] = { &gTijoEyesOpenTex, &gTijoEyesHalfOpenTex, &gTijoEyesClosedTex };
+ static TexturePtr sTijoEyeTextures[] = { &gTijoEyesOpenTex, &gTijoEyesHalfOpenTex, &gTijoEyesClosedTex };
EnZod* this = (EnZod*)thisx;
Gfx* gfx;
@@ -641,7 +641,7 @@ void EnZod_Draw(Actor* thisx, PlayState* play) {
gfx = POLY_OPA_DISP;
- gSPSegment(&gfx[0], 0x08, Lib_SegmentedToVirtual(sTijoEyesTextures[this->eyeIndex]));
+ gSPSegment(&gfx[0], 0x08, Lib_SegmentedToVirtual(sTijoEyeTextures[this->eyeIndex]));
gSPSegment(&gfx[1], 0x09, Lib_SegmentedToVirtual(&gTijoMouthClosedTex));
POLY_OPA_DISP = &gfx[2];
diff --git a/src/overlays/actors/ovl_player_actor/z_player.c b/src/overlays/actors/ovl_player_actor/z_player.c
index 9a8d4e4a8b8..7984e770127 100644
--- a/src/overlays/actors/ovl_player_actor/z_player.c
+++ b/src/overlays/actors/ovl_player_actor/z_player.c
@@ -12520,12 +12520,12 @@ void Player_UpdateCommon(Player* this, PlayState* play, Input* input) {
}
if ((this->transformation >= PLAYER_FORM_GORON) && (this->transformation <= PLAYER_FORM_DEKU)) {
- func_800BBB74(&this->blinkInfo, 20, 80, 3);
+ FaceChange_UpdateBlinkingAlt(&this->faceChange, 20, 80, 3);
} else {
- func_800BBAC0(&this->blinkInfo, 20, 80, 6);
+ FaceChange_UpdateBlinking(&this->faceChange, 20, 80, 6);
}
- this->actor.shape.face = ((play->gameplayFrames & 0x20) ? 0 : 3) + this->blinkInfo.eyeTexIndex;
+ this->actor.shape.face = ((play->gameplayFrames & 0x20) ? 0 : 3) + this->faceChange.face;
if (this->currentMask == PLAYER_MASK_BUNNY) {
Player_UpdateBunnyEars(this);
@@ -17317,7 +17317,7 @@ void func_80852290(PlayState* play, Player* this) {
this->unk_B8A = 8;
} else {
f32 sp3C;
- s16 var_a1_3;
+ s16 upperLimbRotX;
s16 sp38;
if ((play->msgCtx.ocarinaMode == OCARINA_MODE_ACTIVE) &&
@@ -17348,20 +17348,20 @@ void func_80852290(PlayState* play, Player* this) {
sp38 = 0x2EE0;
}
- var_a1_3 = (sp3C * -100.0f);
- var_a1_3 = CLAMP_MAX(var_a1_3, 0xFA0);
- Math_SmoothStepToS(&this->upperLimbRot.x, var_a1_3, 4, 0x7D0, 0);
+ upperLimbRotX = (sp3C * -100.0f);
+ upperLimbRotX = CLAMP_MAX(upperLimbRotX, 0xFA0);
+ Math_SmoothStepToS(&this->upperLimbRot.x, upperLimbRotX, 4, 0x7D0, 0);
Math_SmoothStepToS(&this->upperLimbRot.y, sp38, 4, 0x7D0, 0);
this->headLimbRot.x = -this->upperLimbRot.x;
this->unk_AA6_rotFlags |= UNKAA6_ROT_HEAD_X | UNKAA6_ROT_UPPER_X | UNKAA6_ROT_UPPER_Y;
- var_a1_3 = ABS_ALT(this->upperLimbRot.x);
- if (var_a1_3 < 0x7D0) {
- this->actor.shape.face = 0;
- } else if (var_a1_3 < 0xFA0) {
- this->actor.shape.face = 13;
+ upperLimbRotX = ABS_ALT(this->upperLimbRot.x);
+ if (upperLimbRotX < 0x7D0) {
+ this->actor.shape.face = PLAYER_FACE_NEUTRAL;
+ } else if (upperLimbRotX < 0xFA0) {
+ this->actor.shape.face = PLAYER_FACE_OPENING;
} else {
- this->actor.shape.face = 8;
+ this->actor.shape.face = PLAYER_FACE_HURT;
}
}
@@ -20505,7 +20505,7 @@ void Player_CsAction_11(PlayState* play, Player* this, CsCmdActorCue* cue) {
}
void Player_CsAction_12(PlayState* play, Player* this, CsCmdActorCue* cue) {
- this->actor.shape.face = 0xF;
+ this->actor.shape.face = PLAYER_FACE_SMILE;
func_80840F90(play, this, cue, 0.0f, 0, 0);
}
diff --git a/src/overlays/kaleido_scope/ovl_kaleido_scope/z_kaleido_scope_NES.c b/src/overlays/kaleido_scope/ovl_kaleido_scope/z_kaleido_scope_NES.c
index e629ceeb61d..0029c675b98 100644
--- a/src/overlays/kaleido_scope/ovl_kaleido_scope/z_kaleido_scope_NES.c
+++ b/src/overlays/kaleido_scope/ovl_kaleido_scope/z_kaleido_scope_NES.c
@@ -4,6 +4,7 @@
* Description: Pause Menu
*/
+#include "prevent_bss_reordering.h"
#include "z_kaleido_scope.h"
#include "sys_cmpdma.h"
diff --git a/tools/disasm/functions.txt b/tools/disasm/functions.txt
index cc70b76fd46..6ca26ae0e6e 100644
--- a/tools/disasm/functions.txt
+++ b/tools/disasm/functions.txt
@@ -844,9 +844,9 @@
0x800BB604:("Attention_FindActorInCategory",),
0x800BB8EC:("Attention_FindActor",),
0x800BBA88:("Enemy_StartFinishingBlow",),
- 0x800BBAC0:("func_800BBAC0",),
- 0x800BBB74:("func_800BBB74",),
- 0x800BBC20:("func_800BBC20",),
+ 0x800BBAC0:("FaceChange_UpdateBlinking",),
+ 0x800BBB74:("FaceChange_UpdateBlinkingAlt",),
+ 0x800BBC20:("FaceChange_UpdateRandomSet",),
0x800BBCEC:("Actor_SpawnBodyParts",),
0x800BBDAC:("Actor_SpawnFloorDustRing",),
0x800BBFB0:("func_800BBFB0",),
diff --git a/tools/disasm/variables.txt b/tools/disasm/variables.txt
index 3d0ea3c5e58..782b6ad78dc 100644
--- a/tools/disasm/variables.txt
+++ b/tools/disasm/variables.txt
@@ -1201,8 +1201,8 @@
0x801C0838:("D_801C0838","UNK_TYPE1","",0x1),
0x801C0850:("gCullBackDList","UNK_TYPE1","",0x1),
0x801C0860:("gCullFrontDList","UNK_TYPE1","",0x1),
- 0x801C0870:("sPlayerEyesTextures","UNK_PTR","",0x4),
- 0x801C0890:("sPlayerMouthTextures","UNK_TYPE1","",0x1),
+ 0x801C0870:("sEyeTextures","UNK_PTR","",0x4),
+ 0x801C0890:("sMouthTextures","UNK_TYPE1","",0x1),
0x801C08A0:("sPlayerFaces","UNK_TYPE1","",0x20),
0x801C08C0:("D_801C08C0","UNK_TYPE1","",0x1),
0x801C08FC:("D_801C08FC","UNK_TYPE1","",0x1),
diff --git a/tools/sizes/code_functions.csv b/tools/sizes/code_functions.csv
index 061c1dfbd7b..9b4c2a92178 100644
--- a/tools/sizes/code_functions.csv
+++ b/tools/sizes/code_functions.csv
@@ -358,9 +358,9 @@ asm/non_matchings/code/z_actor/Attention_ActorOnScreen.s,Attention_ActorOnScreen
asm/non_matchings/code/z_actor/Attention_FindActorInCategory.s,Attention_FindActorInCategory,0x800BB604,0xBA
asm/non_matchings/code/z_actor/Attention_FindActor.s,Attention_FindActor,0x800BB8EC,0x67
asm/non_matchings/code/z_actor/Enemy_StartFinishingBlow.s,Enemy_StartFinishingBlow,0x800BBA88,0xE
-asm/non_matchings/code/z_actor/func_800BBAC0.s,func_800BBAC0,0x800BBAC0,0x2D
-asm/non_matchings/code/z_actor/func_800BBB74.s,func_800BBB74,0x800BBB74,0x2B
-asm/non_matchings/code/z_actor/func_800BBC20.s,func_800BBC20,0x800BBC20,0x33
+asm/non_matchings/code/z_actor/FaceChange_UpdateBlinking.s,FaceChange_UpdateBlinking,0x800BBAC0,0x2D
+asm/non_matchings/code/z_actor/FaceChange_UpdateBlinkingAlt.s,FaceChange_UpdateBlinkingAlt,0x800BBB74,0x2B
+asm/non_matchings/code/z_actor/FaceChange_UpdateRandomSet.s,FaceChange_UpdateRandomSet,0x800BBC20,0x33
asm/non_matchings/code/z_actor/Actor_SpawnBodyParts.s,Actor_SpawnBodyParts,0x800BBCEC,0x30
asm/non_matchings/code/z_actor/Actor_SpawnFloorDustRing.s,Actor_SpawnFloorDustRing,0x800BBDAC,0x81
asm/non_matchings/code/z_actor/func_800BBFB0.s,func_800BBFB0,0x800BBFB0,0x69