diff --git a/assets/xml/objects/object_bal.xml b/assets/xml/objects/object_bal.xml
index 15b9971fc4..a31c0e29f3 100644
--- a/assets/xml/objects/object_bal.xml
+++ b/assets/xml/objects/object_bal.xml
@@ -76,6 +76,6 @@
-
+
diff --git a/include/functions.h b/include/functions.h
index 27c11bbe71..5c71053227 100644
--- a/include/functions.h
+++ b/include/functions.h
@@ -2551,9 +2551,11 @@ void SpeedMeter_DrawTimeEntries(void* displayList, GraphicsContext* gfxCtx);
// void func_80177A84(UNK_TYPE1 param_1, UNK_TYPE1 param_2, UNK_TYPE1 param_3, UNK_TYPE1 param_4, UNK_TYPE2 param_5, UNK_TYPE4 param_6, UNK_TYPE4 param_7, UNK_TYPE4 param_8, UNK_TYPE4 param_9);
//void func_80177AC8(void);
void SpeedMeter_DrawAllocEntries(void* displayList, GraphicsContext* gfxCtx, GameState* gameState);
-void func_801780F0(Mtx* param_1, f32 param_2, f32 param_3, f32 param_4, f32 param_5, f32 param_6, f32 param_7);
-// void func_801781EC(UNK_TYPE1 param_1, UNK_TYPE1 param_2, UNK_TYPE1 param_3, UNK_TYPE1 param_4, UNK_TYPE4 param_5);
-void func_8017842C(MtxF* arg0, f32 arg1, f32 arg2, f32 arg3, s16 arg4, f32 arg5, f32 arg6, f32 arg7, f32 arg8, f32 arg9, f32 arg10);
+
+void Mtx_SetTranslateScaleMtx(Mtx* mtx, f32 scaleX, f32 scaleY, f32 scaleZ, f32 translateX, f32 translateY, f32 translateZ);
+void Mtx_SetRotationMtx(Mtx* mtx, s32 angle, f32 axisX, f32 axisY, f32 axisZ);
+void Mtx_SetTranslationRotationScaleMtx(Mtx* mtx, f32 scaleX, f32 scaleY, f32 scaleZ, s32 angle, f32 axisX, f32 axisY, f32 axisZ,f32 translateX, f32 translateY, f32 translateZ);
+
// void func_80178750(void);
// void func_80178818(void);
void func_80178978(void);
diff --git a/spec b/spec
index d923922359..9a29b7fcfe 100644
--- a/spec
+++ b/spec
@@ -528,7 +528,8 @@ beginseg
include "build/src/code/z_rumble.o"
include "build/src/code/z_view.o"
include "build/src/code/z_vimode.o"
- include "build/data/code/z_vimode.data.o"
+ include "build/src/code/code_80140CE0.o"
+ include "build/data/code/code_80140CE0.data.o"
include "build/src/code/code_80140E80.o"
include "build/src/code/z_vismono.o"
include "build/src/code/code_801420C0.o"
@@ -580,6 +581,7 @@ beginseg
include "build/src/code/speed_meter.o"
include "build/data/code/speed_meter.data.o"
include "build/data/code/speed_meter.bss.o"
+ include "build/src/code/su_mtx.o"
include "build/data/code/sys_cfb.bss.o"
include "build/src/code/sys_cmpdma.o"
include "build/data/code/sys_cmpdma.bss.o"
diff --git a/src/code/code_80140CE0.c b/src/code/code_80140CE0.c
new file mode 100644
index 0000000000..f3d487fabc
--- /dev/null
+++ b/src/code/code_80140CE0.c
@@ -0,0 +1,7 @@
+#include "global.h"
+
+#pragma GLOBAL_ASM("asm/non_matchings/code/code_80140CE0/func_80140CE0.s")
+
+#pragma GLOBAL_ASM("asm/non_matchings/code/code_80140CE0/func_80140D04.s")
+
+#pragma GLOBAL_ASM("asm/non_matchings/code/code_80140CE0/func_80140D10.s")
diff --git a/src/code/speed_meter.c b/src/code/speed_meter.c
index 5ad1cdbe0e..780a02c62f 100644
--- a/src/code/speed_meter.c
+++ b/src/code/speed_meter.c
@@ -15,9 +15,3 @@
#pragma GLOBAL_ASM("asm/non_matchings/code/speed_meter/func_80177AC8.s")
#pragma GLOBAL_ASM("asm/non_matchings/code/speed_meter/SpeedMeter_DrawAllocEntries.s")
-
-#pragma GLOBAL_ASM("asm/non_matchings/code/speed_meter/func_801780F0.s")
-
-#pragma GLOBAL_ASM("asm/non_matchings/code/speed_meter/func_801781EC.s")
-
-#pragma GLOBAL_ASM("asm/non_matchings/code/speed_meter/func_8017842C.s")
diff --git a/src/code/su_mtx.c b/src/code/su_mtx.c
new file mode 100644
index 0000000000..09cbda90e2
--- /dev/null
+++ b/src/code/su_mtx.c
@@ -0,0 +1,361 @@
+/**
+ * @file su_mtx.c
+ * @brief "Fast" functions for constructing RSP-compatible matrices directly
+ *
+ * The three functions in this file construct scaling, rotation, and translation matrices, and combinations thereof. The
+ * intention appears to be to exploit the peculiar structure of the RSP's fixed-point matrix format, which we recall is
+ *
+ * typedef long int Mtx_t[4][4];
+ * typedef union {
+ * Mtx_t m;
+ * struct {
+ * u16 intPart[4][4];
+ * u16 fracPart[4][4];
+ * };
+ * long long int forc_structure_alignment;
+ * } Mtx; // size = 0x40
+ *
+ * This means it can be written to as either words or two sets of shorts; the latter is the correct format for
+ * interpreting it the way the RSP does, but the first is likely faster to access and write to. The functions save
+ * writing halves by
+ *
+ * - writing a word 0 if two adjacent elements are 0 in a convenient place, or 1 if they are 0 and 1,
+ * - writing a word using a shift and overwriting the dirty half afterwards.
+ *
+ * Examples of both of these are seen in Mtx_SetTranslateScaleMtx(); we explain there in full detail due to the
+ * function's relative simplicity. The other two functions we merely point out the interesting parts rather than
+ * explaining the whole process.
+ *
+ * We use the following notation throughout: round brackets denote the word-sized elements, square the pairs they cover.
+ * The integer and fractional parts of a fixed-point number are denoted by i and f respectively, and these letters are
+ * also used to represent which half each of the pairs belongs to.
+ *
+ * (00) [i00,i01], (01) [i02,i03]
+ * (02) [i10,i11], (03) [i12,i13]
+ * (10) [i20,i21], (11) [i22,i23]
+ * (12) [i30,i31], (13) [i32,i33]
+ * (20) [f00,f01], (21) [f02,f03]
+ * (22) [f10,f11], (23) [f12,f13]
+ * (30) [f20,f21], (31) [f22,f23]
+ * (32) [f30,f31], (33) [f32,f33]
+ *
+ * We keep the RSP's column-major convention; this file is confusing enough without attempting to transpose everything.
+ * Bear in mind therefore that we are acting on *row vectors* on the *right*.
+ *
+ * @warning The behaviour of the output of the functions in this file is undefined in C89, since both members of the
+ * union are used to set the mtxs. (C99+ allow type-punning, but the behaviour is still implementation-defined because
+ * it relies on the storage being big-endian.)
+ *
+ * @remark Name inferred from shared Animal Forest functions, meaning of "su" is unclear.
+ */
+
+#include "global.h"
+
+/**
+ * Constructs a matrix \$f ST \$f, i.e. a scaling \$f S \$f followed by a translation \$f T \$f.
+ *
+ * The final result is
+ *
+ * \f[
+ * \begin{bmatrix}
+ * s_x & 0 & 0 & 0 \\
+ * 0 & s_y & 0 & 0 \\
+ * 0 & 0 & s_z & 0 \\
+ * t_x & t_y & t_z & 1
+ * \end{bmatrix}
+ * \f]
+ *
+ * @param[out] mtx Fixed-point matrix pointer to output to
+ * @param[in] scaleX Scale in the X direction
+ * @param[in] scaleY Scale in the Y direction
+ * @param[in] scaleZ Scale in the Z direction
+ * @param[in] translateX X component of translation
+ * @param[in] translateY Y component of translation
+ * @param[in] translateZ Z component of translation
+ *
+ * @remark Original name: "suMtxMakeTS"
+ */
+void Mtx_SetTranslateScaleMtx(Mtx* mtx, f32 scaleX, f32 scaleY, f32 scaleZ, f32 translateX, f32 translateY,
+ f32 translateZ) {
+ Mtx* m = mtx;
+ s32 fixedPoint;
+
+ m->m[0][1] = 0; // [i02, i03] == [0, 0]
+ m->m[2][1] = 0; // [f02, f03] == [0, 0]
+ m->m[0][3] = 0; // [i12, i13] == [0, 0]
+ m->m[2][3] = 0; // [f22, f23] == [0, 0]
+ m->m[1][0] = 0; // [i20, i21] == [0, 0]
+ //
+ fixedPoint = scaleX * 0x10000; //
+ m->m[0][0] = fixedPoint; // [i00, i01] == [scaleX.i, scaleX.f]; i01 is now "dirty"
+ m->intPart[0][1] = 0; // Clean i01 by zeroing it: [i00, i01] == [scaleX.i, 0]
+ m->m[2][0] = (u32)fixedPoint << 16; // [f00, f01] == [scaleX.f, 0]
+ //
+ fixedPoint = scaleY * 0x10000; //
+ m->m[0][2] = (u32)fixedPoint >> 16; // [i10, i11] == [0, scaleY.i]
+ m->m[2][2] = fixedPoint & 0xFFFF; // [f10, f11] == [0, scaleY.f]
+ //
+ fixedPoint = scaleZ * 0x10000; //
+ m->m[1][1] = fixedPoint; // [i22, i23] == [scaleZ.i, scaleZ.f]
+ m->intPart[2][3] = 0; // [i22, i23] == [scaleZ.i, 0]
+ m->m[3][1] = (u32)fixedPoint << 16; // [f22, f23] == [scaleZ.f, 0]
+ //
+ m->m[3][0] = 0; // [f20, f21] == [0, 0]
+ //
+ fixedPoint = translateX * 0x10000; //
+ m->intPart[3][0] = ((u32)fixedPoint >> 16) & 0xFFFF; // [i30, i31] == [translateX.i, ?]
+ m->fracPart[3][0] = fixedPoint & 0xFFFF; // [f30, f31] == [translateX.f, ?]
+ //
+ fixedPoint = translateY * 0x10000; //
+ m->intPart[3][1] = ((u32)fixedPoint >> 16) & 0xFFFF; // [i30, i31] == [translateX.i, translateY.i]
+ m->fracPart[3][1] = fixedPoint & 0xFFFF; // [f30, f31] == [translateX.f, translateY.f]
+ //
+ fixedPoint = translateZ * 0x10000; //
+ m->intPart[3][2] = ((u32)fixedPoint >> 16) & 0xFFFF; // [i30, i31] == [translateZ.i, ?]
+ m->intPart[3][3] = 1; // [i32, i33] == [translateZ.i, 1]
+ m->m[3][3] = (u32)fixedPoint << 16; // [f32, f33] == [translateZ.f, 0]
+
+ // So we end up with
+ // [scaleX.i, 0], [0, 0],
+ // [0, scaleY.i], [0, 0],
+ // [0, 0], [scaleZ.i, 0],
+ // [translateX.i, translateY.i], [translateZ.i, 1]
+ //
+ // [scaleX.f, 0], [0, 0],
+ // [0, scaleY.f], [0, 0],
+ // [0, 0], [scaleZ.f, 0]
+ // [translateX.f, translateY.f], [translateZ.f, 0]
+}
+
+// Unused
+/**
+ * Should create an axis-angle rotation matrix.
+ *
+ * @note The axis vector is expected to be normalised.
+ *
+ * The result should be:
+ *
+ * \f[
+ * \begin{bmatrix}
+ * (1-a_x^2) \cos \theta + a_x^2 & a_x a_y (1 - \cos \theta) + a_z \sin \theta & a_z a_x (1 - \cos \theta) - a_y
+ * \sin \theta & 0 \\
+ * a_x a_y (1 - \cos \theta) - a_z \sin \theta & (1-a_y^2) \cos \theta + a_y^2 & a_y a_z (1 - \cos \theta) + a_x
+ * \sin \theta & 0 \\
+ * a_z a_x (1 - \cos \theta) + a_y \sin \theta & a_y a_z (1 - \cos \theta) - a_x \sin \theta & (1-a_z^2) \cos
+ * \theta
+ * + a_z^2 & 0 \\
+ * t_x & t_y & t_z & 1
+ * \end{bmatrix}
+ * \f]
+ *
+ * @warning There is a significant bug in this function, which means it does not actually produce this correct rotation
+ * matrix; see inline for details.
+ *
+ * @param[out] mtx Fixed-point matrix pointer to output to
+ * @param[in] angle angle to rotate about axis
+ * @param[in] axisX X component of axis to rotate about
+ * @param[in] axisY Y component of axis to rotate about
+ * @param[in] axisZ Z component of axis to rotate about
+ *
+ * @remark Original name: probably something like "suMtxMakeR" or "suMtxMakeRotateVector"
+ */
+void Mtx_SetRotationMtx(Mtx* mtx, s32 angle, f32 axisX, f32 axisY, f32 axisZ) {
+ //! FAKE? The somewhat peculiar distribution of temps in this function seems necessary to match?
+ f32 tempX;
+ f32 tempZ;
+ f32 tempY;
+ f32 sin = Math_SinS(angle);
+ f32 cos = Math_CosS(angle);
+ f32 tempXX;
+ f32 tempYY;
+ f32 tempZZ;
+
+ s32 fixedPoint;
+
+ mtx->m[1][2] = 0; // [i30, i31] == [0, 0]
+ mtx->m[1][3] = 1; // [i32, i33] == [0, 1]
+ mtx->m[3][2] = 0; // [f30, f31] == [0, 0]
+ mtx->m[3][3] = 0; // [f31, f32] == [0, 0]
+
+ tempXX = axisX * axisX;
+ tempX = (axisY * axisZ) * (1.0f - cos);
+
+ // i00 and f00
+ fixedPoint = (((1.0f - tempXX) * cos) + tempXX) * 0x10000;
+ mtx->intPart[0][0] = ((u32)fixedPoint >> 0x10) & 0xFFFF;
+ mtx->fracPart[0][0] = fixedPoint & 0xFFFF;
+
+ tempZ = ((axisX * axisY) * (1.0f - cos));
+ tempZZ = axisZ * axisZ;
+
+ // i21 and f21
+ fixedPoint = (tempX - (axisX * sin)) * 0x10000;
+ mtx->intPart[2][1] = ((u32)fixedPoint >> 0x10) & 0xFFFF;
+ mtx->fracPart[2][1] = fixedPoint & 0xFFFF;
+
+ tempY = (axisZ * axisX) * (1.0f - cos);
+ tempYY = axisY * axisY;
+
+ // [i12, i13] and [f12, f13]
+ fixedPoint = (tempX + (axisX * sin)) * 0x10000;
+ mtx->m[0][3] = fixedPoint; // i13 dirty
+ mtx->intPart[1][3] = 0; // clean i13
+ mtx->m[2][3] = fixedPoint << 0x10;
+
+ // i11 and f11
+ fixedPoint = ((((1.0f - tempYY) * cos) + tempYY) * 0x10000);
+ mtx->intPart[1][1] = ((u32)fixedPoint >> 0x10) & 0xFFFF;
+ mtx->fracPart[1][1] = fixedPoint & 0xFFFF;
+
+ //! @bug The sign of the `axisY * sin` term is wrong on the next two elements (i/f20 and i/f02), which causes the
+ //! matrix to not be a rotation matrix (and indeed not even necessarily invertible).
+ // i20 and f20
+ fixedPoint = (tempY - (axisY * sin)) * 0x10000;
+ mtx->intPart[2][0] = ((u32)fixedPoint >> 0x10) & 0xFFFF;
+ mtx->fracPart[2][0] = fixedPoint & 0xFFFF;
+
+ // [i02, i03] and [f02, f03]
+ fixedPoint = (tempY + (axisY * sin)) * 0x10000;
+ mtx->m[0][1] = fixedPoint; // i03 dirty
+ mtx->intPart[0][3] = 0; // clean i03
+ mtx->m[2][1] = fixedPoint << 0x10;
+
+ // [i22, i23] and [f22, f23]
+ fixedPoint = ((((1.0f - tempZZ) * cos) + tempZZ) * 0x10000);
+ mtx->m[1][1] = fixedPoint; // i23 dirty
+ mtx->intPart[2][3] = 0; // clean i23
+ mtx->m[3][1] = fixedPoint << 0x10;
+
+ // i10 and f10
+ fixedPoint = (tempZ - axisZ * sin) * 0x10000;
+ mtx->intPart[1][0] = ((u32)fixedPoint >> 0x10) & 0xFFFF;
+ mtx->fracPart[1][0] = fixedPoint & 0xFFFF;
+
+ // i01 and f01
+ fixedPoint = (tempZ + axisZ * sin) * 0x10000;
+ mtx->intPart[0][1] = ((u32)fixedPoint >> 0x10) & 0xFFFF;
+ mtx->fracPart[0][1] = fixedPoint & 0xFFFF;
+}
+
+/**
+ * Creates a general scale, rotation, translation matrix.
+ *
+ * @note The axis vector is expected to be normalised.
+ *
+ * The transformations are applied in the order scale, rotate, translate: \$f SRT \$f
+ *
+ * The result should be
+ * \f[
+ * \begin{bmatrix}
+ * s_x((1-a_x^2) \cos \theta + a_x^2) & s_x(a_x a_y (1 - \cos \theta) + a_z \sin \theta) & s_x(a_z a_x (1 - \cos
+ * \theta) - a_y \sin \theta & 0) \\
+ * s_y(a_x a_y (1 - \cos \theta) - a_z \sin \theta) & s_y((1-a_y^2) \cos \theta + a_y^2) & s_z(a_y a_z (1 - \cos
+ * \theta) + a_x \sin \theta) & 0 \\
+ * s_z(a_z a_x (1 - \cos \theta) + a_y \sin \theta) & s_z(a_y a_z (1 - \cos \theta) - a_x \sin \theta) &
+ * s_z((1-a_z^2) \cos \theta + a_z^2) & 0 \\
+ * 0 & 0 & 0 & 1
+ * \end{bmatrix} .
+ * \f]
+ *
+ * @warning There is a significant bug in this function, which means it does not actually produce a proper rotation
+ * matrix; see inline for details.
+ *
+ * @param[out] mtx Fixed-point matrix pointer to output to
+ * @param[in] scaleX Scale in the X direction
+ * @param[in] scaleY Scale in the Y direction
+ * @param[in] scaleZ Scale in the Z direction
+ * @param[in] angle angle to rotate about axis
+ * @param[in] axisX X component of axis to rotate about
+ * @param[in] axisY Y component of axis to rotate about
+ * @param[in] axisZ Z component of axis to rotate about
+ * @param[in] translateX X component of translation
+ * @param[in] translateY Y component of translation
+ * @param[in] translateZ Z component of translation
+ *
+ * @remark Original name: probably something like "suMtxMakeSRT", although Animal Forest function is a Tait-Bryan
+ * rotation rather than axis-angle.
+ */
+void Mtx_SetTranslationRotationScaleMtx(Mtx* mtx, f32 scaleX, f32 scaleY, f32 scaleZ, s32 angle, f32 axisX, f32 axisY,
+ f32 axisZ, f32 translateX, f32 translateY, f32 translateZ) {
+ f32 tempX;
+ f32 tempY;
+ f32 tempZ;
+ f32 sin = Math_SinS(angle);
+ f32 cos = Math_CosS(angle);
+ f32 tempXX = axisX * axisX;
+ f32 tempYY = axisY * axisY;
+ f32 tempZZ = axisZ * axisZ;
+ s32 fixedPoint;
+
+ tempX = axisY * axisZ * (1.0f - cos);
+
+ // i00 and f00
+ fixedPoint = (tempXX + (1.0f - tempXX) * cos) * scaleX * 0x10000;
+ mtx->intPart[0][0] = ((u32)fixedPoint >> 0x10) & 0xFFFF;
+ mtx->fracPart[0][0] = fixedPoint & 0xFFFF;
+
+ tempY = axisZ * axisX * (1.0f - cos);
+
+ // i21 and f21
+ fixedPoint = (tempX - axisX * sin) * scaleZ * 0x10000;
+ mtx->intPart[2][1] = ((u32)fixedPoint >> 0x10) & 0xFFFF;
+ mtx->fracPart[2][1] = fixedPoint & 0xFFFF;
+
+ tempZ = axisX * axisY * (1.0f - cos);
+
+ // [i12, i13] and [f12, f13]
+ fixedPoint = (tempX + axisX * sin) * scaleY * 0x10000;
+ mtx->m[0][3] = fixedPoint; // i13 dirty
+ mtx->intPart[1][3] = 0; // clean i13
+ mtx->m[2][3] = fixedPoint << 0x10;
+
+ // i11 and f11
+ fixedPoint = (tempYY + (1.0f - tempYY) * cos) * scaleY * 0x10000;
+ mtx->intPart[1][1] = ((u32)fixedPoint >> 0x10) & 0xFFFF;
+ mtx->fracPart[1][1] = fixedPoint & 0xFFFF;
+
+ //! @bug The sign of the `axisY * sin` term is wrong on the next two elements (i/f20 and i/f02), which causes the
+ //! matrix to not be a rotation matrix (and indeed not even necessarily invertible).
+ // i20, f20
+ fixedPoint = (tempY - axisY * sin) * scaleZ * 0x10000;
+ mtx->intPart[2][0] = ((u32)fixedPoint >> 0x10) & 0xFFFF;
+ mtx->fracPart[2][0] = fixedPoint & 0xFFFF;
+
+ // [i02, i03] and [f02, f03]
+ fixedPoint = (tempY + axisY * sin) * scaleX * 0x10000;
+ mtx->m[0][1] = fixedPoint; // [i03, i03], i03 dirty
+ mtx->intPart[0][3] = 0; // clean i03
+ mtx->m[2][1] = fixedPoint << 0x10; // [f02, f03]
+
+ // [i22, i23] and [f22, f23]
+ fixedPoint = (tempZZ + (1.0f - tempZZ) * cos) * scaleZ * 0x10000;
+ mtx->m[1][1] = fixedPoint;
+ mtx->intPart[2][3] = 0;
+ mtx->m[3][1] = fixedPoint << 0x10;
+
+ // i10 and f10
+ fixedPoint = (tempZ - axisZ * sin) * scaleY * 0x10000;
+ mtx->intPart[1][0] = ((u32)fixedPoint >> 0x10) & 0xFFFF;
+ mtx->fracPart[1][0] = fixedPoint & 0xFFFF;
+
+ // i01 and f01
+ fixedPoint = (tempZ + axisZ * sin) * scaleX * 0x10000;
+ mtx->intPart[0][1] = ((u32)fixedPoint >> 0x10) & 0xFFFF;
+ mtx->fracPart[0][1] = fixedPoint & 0xFFFF;
+
+ // [i30, i31] and f30
+ fixedPoint = translateX * 0x10000;
+ mtx->m[1][2] = fixedPoint; // [i30, i31], i31 dirty
+ mtx->fracPart[3][0] = fixedPoint & 0xFFFF; // f30
+
+ // i31 and f31
+ fixedPoint = translateY * 0x10000;
+ mtx->intPart[3][1] = ((u32)fixedPoint >> 0x10) & 0xFFFF; // overwrite i31
+ mtx->fracPart[3][1] = fixedPoint & 0xFFFF; // overwrite f31
+
+ // [i32, i33] and [f32, f33]
+ fixedPoint = translateZ * 0x10000;
+ mtx->m[1][3] = fixedPoint; // [i32, i33]
+ mtx->intPart[3][3] = 1; // clean i33
+ mtx->m[3][3] = (fixedPoint << 0x10); // [f32, f33]
+}
diff --git a/src/code/z_actor.c b/src/code/z_actor.c
index 0483d097e9..919752d748 100644
--- a/src/code/z_actor.c
+++ b/src/code/z_actor.c
@@ -2353,10 +2353,8 @@ Actor* Actor_UpdateActor(UpdateActor_Params* params) {
if (!Object_IsLoaded(&play->objectCtx, actor->objBankIndex)) {
Actor_Kill(actor);
} else {
- s32 tmp = (params->unk_08 == 0);
-
if (((params->unk_08) && !(actor->flags & params->unk_08)) ||
- ((tmp = (params->unk_08 == 0)) &&
+ ((((!params->unk_08) != 0)) &&
(!(actor->flags & ACTOR_FLAG_100000) ||
((actor->category == ACTORCAT_EXPLOSIVES) && (params->player->stateFlags1 & PLAYER_STATE1_200))) &&
(params->unkC != 0) && (actor != params->unk10) && ((actor != params->player->heldActor)) &&
diff --git a/src/code/z_lifemeter.c b/src/code/z_lifemeter.c
index 669aa338ab..b123baab69 100644
--- a/src/code/z_lifemeter.c
+++ b/src/code/z_lifemeter.c
@@ -235,6 +235,7 @@ void LifeMeter_Draw(PlayState* play) {
if ((gSaveContext.save.playerData.health % 0x10) == 0) {
fullHeartCount--;
}
+
offsetY = 0.0f;
offsetX = 0.0f;
curColorSet = -1;
@@ -381,12 +382,13 @@ void LifeMeter_Draw(PlayState* play) {
}
}
mtx = GRAPH_ALLOC(gfxCtx, sizeof(Mtx));
- func_801780F0(mtx, 1.0f - (0.32f * lifesize), 1.0f - (0.32f * lifesize), 1.0f - (0.32f * lifesize),
- -130.0f + offsetX, 94.5f - offsetY, 0.0f);
+ Mtx_SetTranslateScaleMtx(mtx, 1.0f - (0.32f * lifesize), 1.0f - (0.32f * lifesize),
+ 1.0f - (0.32f * lifesize), -130.0f + offsetX, 94.5f - offsetY, 0.0f);
gSPMatrix(OVERLAY_DISP++, mtx, G_MTX_LOAD | G_MTX_MODELVIEW);
gSPVertex(OVERLAY_DISP++, beatingHeartVtx, 4, 0);
gSP1Quadrangle(OVERLAY_DISP++, 0, 2, 3, 1, 0);
}
+
offsetX += 10.0f;
if (i == 9) {
offsetY += 10.0f;
@@ -423,10 +425,8 @@ u32 LifeMeter_IsCritical(void) {
if (gSaveContext.save.playerData.healthCapacity <= 0x50) {
criticalThreshold = 0x10;
-
} else if (gSaveContext.save.playerData.healthCapacity <= 0xA0) {
criticalThreshold = 0x18;
-
} else if (gSaveContext.save.playerData.healthCapacity <= 0xF0) {
criticalThreshold = 0x20;
} else {
diff --git a/src/code/z_vimode.c b/src/code/z_vimode.c
index 76479a0287..b7d2dea64a 100644
--- a/src/code/z_vimode.c
+++ b/src/code/z_vimode.c
@@ -15,9 +15,3 @@
#pragma GLOBAL_ASM("asm/non_matchings/code/z_vimode/func_8014090C.s")
#pragma GLOBAL_ASM("asm/non_matchings/code/z_vimode/func_80140970.s")
-
-#pragma GLOBAL_ASM("asm/non_matchings/code/z_vimode/func_80140CE0.s")
-
-#pragma GLOBAL_ASM("asm/non_matchings/code/z_vimode/func_80140D04.s")
-
-#pragma GLOBAL_ASM("asm/non_matchings/code/z_vimode/func_80140D10.s")
diff --git a/src/overlays/actors/ovl_Dm_Bal/z_dm_bal.c b/src/overlays/actors/ovl_Dm_Bal/z_dm_bal.c
index e2dc0daa71..b9c4cb33c0 100644
--- a/src/overlays/actors/ovl_Dm_Bal/z_dm_bal.c
+++ b/src/overlays/actors/ovl_Dm_Bal/z_dm_bal.c
@@ -15,8 +15,8 @@ void DmBal_Destroy(Actor* thisx, PlayState* play);
void DmBal_Update(Actor* thisx, PlayState* play);
void DmBal_Draw(Actor* thisx, PlayState* play);
-void func_80C1EAC4(DmBal* this);
-void func_80C1EAD8(DmBal* this, PlayState* play);
+void DmBal_SetupDoNothing(DmBal* this);
+void DmBal_DoNothing(DmBal* this, PlayState* play);
ActorInit Dm_Bal_InitVars = {
ACTOR_DM_BAL,
@@ -59,18 +59,18 @@ void DmBal_Init(Actor* thisx, PlayState* play) {
Actor_UpdateBgCheckInfo(play, &this->actor, 0.0f, 0.0f, 0.0f, 4);
this->timer = 60;
this->eyeIndex = 0;
- this->unk_336 = 0;
- func_80C1EAC4(this);
+ this->keepEyesShut = false;
+ DmBal_SetupDoNothing(this);
}
void DmBal_Destroy(Actor* thisx, PlayState* play) {
}
-void func_80C1EAC4(DmBal* this) {
- this->actionFunc = func_80C1EAD8;
+void DmBal_SetupDoNothing(DmBal* this) {
+ this->actionFunc = DmBal_DoNothing;
}
-void func_80C1EAD8(DmBal* this, PlayState* play) {
+void DmBal_DoNothing(DmBal* this, PlayState* play) {
}
void func_80C1EAE8(DmBal* this, PlayState* play) {
@@ -84,12 +84,12 @@ void func_80C1EAE8(DmBal* this, PlayState* play) {
D_80C1F2C0 = play->csCtx.actorActions[actionIndex]->action;
switch (play->csCtx.actorActions[actionIndex]->action) {
case 1:
- this->unk_336 = 0;
+ this->keepEyesShut = false;
this->eyeIndex = 0;
Actor_ChangeAnimationByInfo(&this->skelAnime, sAnimationInfo, 0);
break;
case 2:
- this->unk_336 = 1;
+ this->keepEyesShut = true;
Actor_ChangeAnimationByInfo(&this->skelAnime, sAnimationInfo, 12);
break;
case 3:
@@ -98,34 +98,34 @@ void func_80C1EAE8(DmBal* this, PlayState* play) {
}
} else if (D_80C1F2C0 == 3) {
if (Animation_OnFrame(&this->skelAnime, 0.0f)) {
- this->unk_336 = 1;
+ this->keepEyesShut = true;
} else if (Animation_OnFrame(&this->skelAnime, 29.0f)) {
- this->unk_336 = 0;
+ this->keepEyesShut = false;
this->eyeIndex = 0;
}
}
Cutscene_ActorTranslateAndYaw(&this->actor, play, actionIndex);
this->actor.home.pos = this->actor.world.pos;
} else {
- this->unk_336 = 0;
+ this->keepEyesShut = false;
this->eyeIndex = 0;
D_80C1F2C0 = 0x63;
}
}
void func_80C1EC60(DmBal* this, PlayState* play) {
- f32 temp_fv1_2;
+ f32 scaleX;
this->unk_338 += 0x320;
this->unk_33A += 0x3E8;
this->scale.y = this->scale.z = Math_CosS(this->unk_338) * 0.1f + 1.0f;
- temp_fv1_2 = (Math_SinS(this->unk_338) * 0.1f) + 1.0f;
- this->scale.x = SQ(temp_fv1_2);
+ scaleX = (Math_SinS(this->unk_338) * 0.1f) + 1.0f;
+ this->scale.x = SQ(scaleX);
this->actor.world.pos.y = this->actor.home.pos.y + (Math_SinS(this->unk_338) * 25.0f);
}
-void func_80C1ED0C(DmBal* this) {
- if (this->unk_336 == 1) {
+void DmBal_UpdateEyes(DmBal* this) {
+ if (this->keepEyesShut == true) {
this->eyeIndex = 1;
} else if (this->timer >= 4) {
this->timer--;
@@ -138,38 +138,36 @@ void func_80C1ED0C(DmBal* this) {
}
}
-void func_80C1ED64(DmBal* this, PlayState* play, Vec3f* arg2, Vec3f* arg3, f32 arg4) {
- Actor* paper = Actor_Spawn(&play->actorCtx, play, ACTOR_EN_PAPER, arg2->x, arg2->y, arg2->z, 0, 0, 0, 0);
+void DmBal_SpawnPaper(DmBal* this, PlayState* play, Vec3f* pos, Vec3f* vel, f32 gravity) {
+ Actor* paper = Actor_Spawn(&play->actorCtx, play, ACTOR_EN_PAPER, pos->x, pos->y, pos->z, 0, 0, 0, 0);
if (paper != NULL) {
- paper->velocity = *arg3;
- paper->gravity = arg4;
+ paper->velocity = *vel;
+ paper->gravity = gravity;
}
}
-Vec3f D_80C1F2C4 = { 0.0f, 9.0f, 0.0f };
-
void DmBal_Update(Actor* thisx, PlayState* play) {
s32 pad;
DmBal* this = THIS;
- Vec3f sp3C;
- Vec3f sp30;
+ // Throw confetti
if (Animation_OnFrame(&this->skelAnime, 29.0f) && (this->skelAnime.animation == &object_bal_Anim_001804)) {
- sp3C = this->actor.world.pos;
- sp30 = D_80C1F2C4;
- sp3C.x += 7.0f * Math_SinS(this->actor.shape.rot.y);
- sp3C.y += 2.5f;
- sp3C.z += 7.0f * Math_CosS(this->actor.shape.rot.y);
- sp30.x = Math_SinS(this->actor.shape.rot.y) * 5.0f;
- sp30.z = Math_CosS(this->actor.shape.rot.y) * 5.0f;
- func_80C1ED64(this, play, &sp3C, &sp30, -0.4f);
- func_80C1ED64(this, play, &sp3C, &sp30, -0.5f);
+ Vec3f pos = this->actor.world.pos;
+ Vec3f vel = { 0.0f, 9.0f, 0.0f };
+
+ pos.x += 7.0f * Math_SinS(this->actor.shape.rot.y);
+ pos.y += 2.5f;
+ pos.z += 7.0f * Math_CosS(this->actor.shape.rot.y);
+ vel.x = Math_SinS(this->actor.shape.rot.y) * 5.0f;
+ vel.z = Math_CosS(this->actor.shape.rot.y) * 5.0f;
+ DmBal_SpawnPaper(this, play, &pos, &vel, -0.4f);
+ DmBal_SpawnPaper(this, play, &pos, &vel, -0.5f);
}
this->actionFunc(this, play);
func_80C1EAE8(this, play);
func_80C1EC60(this, play);
- func_80C1ED0C(this);
+ DmBal_UpdateEyes(this);
SkelAnime_Update(&this->skelAnime);
}
@@ -178,8 +176,8 @@ s32 DmBal_OverrideLimbDraw(PlayState* play, s32 limbIndex, Gfx** dList, Vec3f* p
Vec3s rots;
if (limbIndex == 6) {
- rots.x = Math_SinS(this->unk_33A) * 3640.0f;
- rots.z = Math_CosS(this->unk_33A) * 3640.0f;
+ rots.x = Math_SinS(this->unk_33A) * (0x10000 / 18);
+ rots.z = Math_CosS(this->unk_33A) * (0x10000 / 18);
Matrix_RotateZYX(rots.x, 0, rots.z, MTXMODE_APPLY);
Matrix_Scale(this->scale.x, this->scale.y, this->scale.z, MTXMODE_APPLY);
Matrix_RotateZS(-rots.z, MTXMODE_APPLY);
diff --git a/src/overlays/actors/ovl_Dm_Bal/z_dm_bal.h b/src/overlays/actors/ovl_Dm_Bal/z_dm_bal.h
index 7b38356538..01f10d4c92 100644
--- a/src/overlays/actors/ovl_Dm_Bal/z_dm_bal.h
+++ b/src/overlays/actors/ovl_Dm_Bal/z_dm_bal.h
@@ -18,7 +18,7 @@ typedef struct DmBal {
/* 0x19C */ Vec3s morphTable[OBJECT_BAL_LIMB_MAX];
/* 0x328 */ UNK_TYPE1 pad_328[12];
/* 0x334 */ s16 timer;
- /* 0x336 */ s16 unk_336;
+ /* 0x336 */ s16 keepEyesShut;
/* 0x338 */ s16 unk_338;
/* 0x33A */ s16 unk_33A;
} DmBal; /* size = 0x33C */
diff --git a/src/overlays/actors/ovl_En_Paper/z_en_paper.c b/src/overlays/actors/ovl_En_Paper/z_en_paper.c
index 47d97e1c2f..a3f158cbdc 100644
--- a/src/overlays/actors/ovl_En_Paper/z_en_paper.c
+++ b/src/overlays/actors/ovl_En_Paper/z_en_paper.c
@@ -1,7 +1,11 @@
-/*
- * File: z_en_paper.c
+/**
+ * @file z_en_paper.c
* Overlay: ovl_En_Paper
- * Description: Tingle Confetti
+ * Description: Tingle confetti group
+ *
+ * Complex actor for its length, that creates and manages a group of confetti effects. Each is a triangle of a
+ * particular colour, that rotates around a fixed axis at a randomly-chosen fixed velocity, and is subject to gravity,
+ * drag and wind forces.
*/
#include "z_en_paper.h"
@@ -16,12 +20,12 @@ void EnPaper_Destroy(Actor* thisx, PlayState* play);
void EnPaper_Update(Actor* thisx, PlayState* play);
void EnPaper_Draw(Actor* thisx, PlayState* play);
-void func_80C1F480(EnPaper* this, PlayState* play);
-void func_80C1F4FC(EnPaper* this, PlayState* play);
-void func_80C1F46C(EnPaper* this);
-void func_80C1F4E8(EnPaper* this);
-void func_80C1F55C(EnPaper* this, EnPaperStruct* arg1);
-void func_80C1F6E0(EnPaper* this, EnPaperStruct* arg1);
+void EnPaper_SetupSpreadConfettiGroup(EnPaper* this);
+void EnPaper_SpreadConfettiGroup(EnPaper* this, PlayState* play);
+void EnPaper_SetupFlyConfettiGroup(EnPaper* this);
+void EnPaper_FlyConfettiGroup(EnPaper* this, PlayState* play);
+void EnPaper_InitConfettiPiece(EnPaper* this, EnPaperConfetto* piece);
+void EnPaper_FlyConfettiPiece(EnPaper* this, EnPaperConfetto* piece);
ActorInit En_Paper_InitVars = {
ACTOR_EN_PAPER,
@@ -35,114 +39,155 @@ ActorInit En_Paper_InitVars = {
(ActorFunc)EnPaper_Draw,
};
-Vec3f D_80C1FC60 = { 0.0f, 0.0f, 1.0f };
+static Vec3f sUnitVecZ = { 0.0f, 0.0f, 1.0f };
void EnPaper_Init(Actor* thisx, PlayState* play) {
EnPaper* this = THIS;
Actor_SetScale(&this->actor, 0.01f);
this->timer = 70;
- this->unk_D78 = D_80C1FC60;
+ this->windForce = sUnitVecZ;
Actor_UpdateBgCheckInfo(play, &this->actor, 0.0f, 0.0f, 0.0f, 4);
- func_80C1F46C(this);
+ EnPaper_SetupSpreadConfettiGroup(this);
}
void EnPaper_Destroy(Actor* thisx, PlayState* play) {
}
-void func_80C1F46C(EnPaper* this) {
- this->actionFunc = func_80C1F480;
+void EnPaper_SetupSpreadConfettiGroup(EnPaper* this) {
+ this->actionFunc = EnPaper_SpreadConfettiGroup;
}
-void func_80C1F480(EnPaper* this, PlayState* play) {
+/**
+ * Set up the pieces of confetti.
+ */
+void EnPaper_SpreadConfettiGroup(EnPaper* this, PlayState* play) {
s32 i;
- for (i = 0; i < ARRAY_COUNT(this->unk_148); i++) {
- func_80C1F55C(this, &this->unk_148[i]);
+ for (i = 0; i < ARRAY_COUNT(this->pieces); i++) {
+ EnPaper_InitConfettiPiece(this, &this->pieces[i]);
}
- func_80C1F4E8(this);
+ EnPaper_SetupFlyConfettiGroup(this);
}
-void func_80C1F4E8(EnPaper* this) {
- this->actionFunc = func_80C1F4FC;
+void EnPaper_SetupFlyConfettiGroup(EnPaper* this) {
+ this->actionFunc = EnPaper_FlyConfettiGroup;
}
-void func_80C1F4FC(EnPaper* this, PlayState* play) {
+/**
+ * Main action function for the group.
+ */
+void EnPaper_FlyConfettiGroup(EnPaper* this, PlayState* play) {
s32 i;
- for (i = 0; i < ARRAY_COUNT(this->unk_148); i++) {
- func_80C1F6E0(this, &this->unk_148[i]);
+ for (i = 0; i < ARRAY_COUNT(this->pieces); i++) {
+ EnPaper_FlyConfettiPiece(this, &this->pieces[i]);
}
}
-void func_80C1F55C(EnPaper* this, EnPaperStruct* arg1) {
+void EnPaper_InitConfettiPiece(EnPaper* this, EnPaperConfetto* piece) {
+ // Pick rotation axis randomly (significantly biased towards the z = 0 plane)
Matrix_RotateZYX(Rand_Next(), Rand_Next(), Rand_Next(), MTXMODE_NEW);
- Matrix_MultVec3f(&D_80C1FC60, &arg1->unk_0C);
+ Matrix_MultVec3f(&sUnitVecZ, &piece->rotAxis);
- arg1->unk_18 = this->actor.world.pos;
+ // copy actor position and distribute uniformly in a cube of side 2 around it
+ piece->pos = this->actor.world.pos;
+ piece->pos.x += Rand_Centered() * 4.0f;
+ piece->pos.y += Rand_Centered() * 4.0f;
+ piece->pos.z += Rand_Centered() * 4.0f;
- arg1->unk_18.x += Rand_Centered() * 4.0f;
- arg1->unk_18.y += Rand_Centered() * 4.0f;
- arg1->unk_18.z += Rand_Centered() * 4.0f;
+ // copy actor velocity and distrbute uniformly in a cuboid with sides 9, 6, 9 with actor.velocity in the middle
+ // of the base.
+ piece->vel = this->actor.velocity;
+ piece->vel.x += Rand_Centered() * 9.0f;
+ piece->vel.y += Rand_ZeroOne() * 6.0f;
+ piece->vel.z += Rand_Centered() * 9.0f;
- arg1->unk_24 = this->actor.velocity;
+ // Choose random starting angle and angular velocity
+ piece->angle = Rand_Next();
+ piece->angVel = (Rand_Next() >> 4) + (0x10000 / 180);
- arg1->unk_24.x += Rand_Centered() * 9.0f;
- arg1->unk_24.y += Rand_ZeroOne() * 6.0f;
- arg1->unk_24.z += Rand_Centered() * 9.0f;
-
- arg1->unk_30 = Rand_Next();
- arg1->unk_32 = (Rand_Next() >> 4) + 0x16C;
-
- Matrix_RotateAxisS(arg1->unk_30, &arg1->unk_0C, MTXMODE_NEW);
- Matrix_MultVec3f(&D_80C1FC60, &arg1->unk_00);
+ // Rotate the unit Z-vector by the random starting axis and angle
+ Matrix_RotateAxisS(piece->angle, &piece->rotAxis, MTXMODE_NEW);
+ Matrix_MultVec3f(&sUnitVecZ, &piece->normal);
}
-void func_80C1F6E0(EnPaper* this, EnPaperStruct* arg1) {
- f32 sp1C = Math_CosS(arg1->unk_30);
- f32 sp18 = Math_SinS(arg1->unk_30);
- f32 temp_ft4 = 1.0f - sp1C;
+/**
+ * Main falling function for a single piece, handles dynamics.
+ *
+ * Position \f$ \mathbf{x} \f$ essentially satisfies discretised version of the equation
+ *
+ * \f[
+ * \ddot{\mathbf{x}} = -0.2 \mathbf{n} (\dot{\mathbf{x}} + \mathbf{W}) + \mathbf{g} ,
+ * \f]
+ *
+ * where
+ *
+ * - \f$ \mathbf{n} \f$ is the unit normal to the confetti triangle's plane
+ * - \f$ \mathbf{W} \f$ is the random wind pressure
+ * - \f$ \mathbf{g} \f$ is gravity
+ *
+ * and the product is componentwise: the normal is being used as a cross-sectional area measure rather than vectorially.
+ */
+void EnPaper_FlyConfettiPiece(EnPaper* this, EnPaperConfetto* piece) {
+ f32 cos = Math_CosS(piece->angle);
+ f32 sin = Math_SinS(piece->angle);
+ f32 versin = 1.0f - cos;
- if (arg1->unk_18.y < (this->actor.floorHeight - 40.0f)) {
+ if (piece->pos.y < (this->actor.floorHeight - 40.0f)) {
return;
}
- arg1->unk_24.y += this->actor.gravity;
+ // acceleration due to gravity
+ piece->vel.y += this->actor.gravity;
- arg1->unk_24.x -= 0.2f * fabsf(arg1->unk_00.x) * (arg1->unk_24.x + this->unk_D78.x);
- arg1->unk_24.y -= 0.2f * fabsf(arg1->unk_00.y) * (arg1->unk_24.y + this->unk_D78.y);
- arg1->unk_24.z -= 0.2f * fabsf(arg1->unk_00.z) * (arg1->unk_24.z + this->unk_D78.z);
+ // drag and wind force: normal is used to simulate cross-section size of piece, although
+ piece->vel.x -= 0.2f * fabsf(piece->normal.x) * (piece->vel.x + this->windForce.x);
+ piece->vel.y -= 0.2f * fabsf(piece->normal.y) * (piece->vel.y + this->windForce.y);
+ piece->vel.z -= 0.2f * fabsf(piece->normal.z) * (piece->vel.z + this->windForce.z);
- arg1->unk_30 += arg1->unk_32;
+ // rotate around axis
+ piece->angle += piece->angVel;
- arg1->unk_18.x += arg1->unk_24.x;
- arg1->unk_18.y += arg1->unk_24.y;
- arg1->unk_18.z += arg1->unk_24.z;
+ // move
+ piece->pos.x += piece->vel.x;
+ piece->pos.y += piece->vel.y;
+ piece->pos.z += piece->vel.z;
- arg1->unk_00.x = (arg1->unk_0C.x * temp_ft4 * arg1->unk_0C.z) - (arg1->unk_0C.y * sp18);
- arg1->unk_00.y = (arg1->unk_0C.y * temp_ft4 * arg1->unk_0C.z) + (arg1->unk_0C.x * sp18);
- arg1->unk_00.z = (arg1->unk_0C.z * temp_ft4 * arg1->unk_0C.z) + sp1C;
+ // Rotate unit Z vector about `axis` by `angle` to get forward direction. This is the same calculation as at the
+ // bottom of EnPaper_InitConfettiPiece(), but done manually instead of using any matrix functions.
+ piece->normal.x = (versin * piece->rotAxis.x * piece->rotAxis.z) - (piece->rotAxis.y * sin);
+ piece->normal.y = (versin * piece->rotAxis.y * piece->rotAxis.z) + (piece->rotAxis.x * sin);
+ piece->normal.z = (versin * piece->rotAxis.z * piece->rotAxis.z) + cos;
}
-void func_80C1F87C(EnPaper* this) {
- f32 sp2C = (Rand_Centered() * 4.0f) + 6.0f;
- f32 sp28;
+#define WIND_PITCH_BOUND (0x10000 / 12)
- this->unk_D78.y = Math_SinS(this->actor.shape.rot.x) * -sp2C;
+/**
+ * Sets the wind force for the whole group, using the shape.rot from the previous frame, and picks a new random one
+ * for the next frame.
+ */
+void EnPaper_UpdateWind(EnPaper* this) {
+ f32 strength = (Rand_Centered() * 4.0f) + 6.0f;
+ f32 cosX;
- sp28 = Math_CosS(this->actor.shape.rot.x) * -sp2C;
- this->unk_D78.x = Math_SinS(this->actor.shape.rot.y) * sp28;
- this->unk_D78.z = Math_CosS(this->actor.shape.rot.y) * sp28;
+ this->windForce.y = Math_SinS(this->actor.shape.rot.x) * -strength;
+ cosX = Math_CosS(this->actor.shape.rot.x) * -strength;
+ this->windForce.x = Math_SinS(this->actor.shape.rot.y) * cosX;
+ this->windForce.z = Math_CosS(this->actor.shape.rot.y) * cosX;
+ // New random wind direction. A uniform distribution of the angles in spherical coordinates is not uniformly
+ // distributed on the sphere, so this is biased more towards up and down.
this->actor.shape.rot.x += (s16)(Rand_Next() >> 8);
this->actor.shape.rot.y += (s16)(Rand_Next() >> 6);
- if (ABS_ALT(this->actor.shape.rot.x) > 0x1555) {
+ // Essentially a clamp
+ if (ABS_ALT(this->actor.shape.rot.x) > WIND_PITCH_BOUND) {
if (this->actor.shape.rot.x > 0) {
- this->actor.shape.rot.x = 0x1555;
+ this->actor.shape.rot.x = WIND_PITCH_BOUND;
} else {
- this->actor.shape.rot.x = -0x1555;
+ this->actor.shape.rot.x = -WIND_PITCH_BOUND;
}
}
}
@@ -151,8 +196,8 @@ void EnPaper_Update(Actor* thisx, PlayState* play) {
EnPaper* this = THIS;
this->actionFunc(this, play);
+ EnPaper_UpdateWind(this);
- func_80C1F87C(this);
if (this->timer == 0) {
Actor_Kill(&this->actor);
return;
@@ -163,7 +208,7 @@ void EnPaper_Update(Actor* thisx, PlayState* play) {
void EnPaper_Draw(Actor* thisx, PlayState* play) {
EnPaper* this = THIS;
- EnPaperStruct* ptr = this->unk_148;
+ EnPaperConfetto* piece = this->pieces;
s32 i;
OPEN_DISPS(play->state.gfxCtx);
@@ -174,7 +219,8 @@ void EnPaper_Draw(Actor* thisx, PlayState* play) {
gDPSetCombineLERP(POLY_OPA_DISP++, 0, 0, 0, SHADE, 0, 0, 0, SHADE, PRIMITIVE, 0, COMBINED, 0, 0, 0, 0, COMBINED);
gSPLoadGeometryMode(POLY_OPA_DISP++, G_ZBUFFER | G_SHADE | G_CULL_BACK | G_FOG | G_LIGHTING | G_SHADING_SMOOTH);
- for (i = 0; i < ARRAY_COUNT(this->unk_148); i++, ptr++) {
+ for (i = 0; i < ARRAY_COUNT(this->pieces); i++, piece++) {
+ // colour the first 30 white and 10 each of the other 3 colours
if (i == 0) {
gDPSetPrimColor(POLY_OPA_DISP++, 0, 0, 255, 255, 255, 255);
} else if (i == 30) {
@@ -185,14 +231,15 @@ void EnPaper_Draw(Actor* thisx, PlayState* play) {
gDPSetPrimColor(POLY_OPA_DISP++, 0, 0, 64, 255, 128, 255);
}
- if ((this->actor.floorHeight - 40.0f) < ptr->unk_18.y) {
- MtxF* mf = GRAPH_ALLOC(play->state.gfxCtx, sizeof(MtxF));
+ if ((this->actor.floorHeight - 40.0f) < piece->pos.y) {
+ Mtx* mtx = GRAPH_ALLOC(play->state.gfxCtx, sizeof(Mtx));
- func_8017842C(mf, this->actor.scale.x, this->actor.scale.y, this->actor.scale.z, ptr->unk_30, ptr->unk_0C.x,
- ptr->unk_0C.y, ptr->unk_0C.z, ptr->unk_18.x, ptr->unk_18.y, ptr->unk_18.z);
+ Mtx_SetTranslationRotationScaleMtx(mtx, this->actor.scale.x, this->actor.scale.y, this->actor.scale.z,
+ piece->angle, piece->rotAxis.x, piece->rotAxis.y, piece->rotAxis.z,
+ piece->pos.x, piece->pos.y, piece->pos.z);
- gSPMatrix(POLY_OPA_DISP++, mf, G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW);
- gSPDisplayList(POLY_OPA_DISP++, object_bal_DL_00D5A0);
+ gSPMatrix(POLY_OPA_DISP++, mtx, G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW);
+ gSPDisplayList(POLY_OPA_DISP++, gTingleConfettiDL);
}
}
diff --git a/src/overlays/actors/ovl_En_Paper/z_en_paper.h b/src/overlays/actors/ovl_En_Paper/z_en_paper.h
index 5e8450722a..a48c6ae881 100644
--- a/src/overlays/actors/ovl_En_Paper/z_en_paper.h
+++ b/src/overlays/actors/ovl_En_Paper/z_en_paper.h
@@ -7,20 +7,22 @@ struct EnPaper;
typedef void (*EnPaperActionFunc)(struct EnPaper*, PlayState*);
+#define ENPAPER_PIECES_COUNT 60
+
typedef struct {
- /* 0x00 */ Vec3f unk_00;
- /* 0x0C */ Vec3f unk_0C;
- /* 0x18 */ Vec3f unk_18;
- /* 0x24 */ Vec3f unk_24;
- /* 0x30 */ s16 unk_30;
- /* 0x32 */ s16 unk_32;
-} EnPaperStruct; // size = 0x34
+ /* 0x00 */ Vec3f normal; // normal to the plane of the confetti piece
+ /* 0x0C */ Vec3f rotAxis; // axis to spin around
+ /* 0x18 */ Vec3f pos;
+ /* 0x24 */ Vec3f vel;
+ /* 0x30 */ s16 angle; // angle to rotate around `rotAxis`
+ /* 0x32 */ s16 angVel; // angular velocity of `angle`
+} EnPaperConfetto; // size = 0x34
typedef struct EnPaper {
/* 0x000 */ Actor actor;
/* 0x144 */ EnPaperActionFunc actionFunc;
- /* 0x148 */ EnPaperStruct unk_148[60];
- /* 0xD78 */ Vec3f unk_D78;
+ /* 0x148 */ EnPaperConfetto pieces[ENPAPER_PIECES_COUNT];
+ /* 0xD78 */ Vec3f windForce;
/* 0xD84 */ s16 timer;
} EnPaper; // size = 0xD88
diff --git a/tools/disasm/files.txt b/tools/disasm/files.txt
index c546c1f69b..3f2d082775 100644
--- a/tools/disasm/files.txt
+++ b/tools/disasm/files.txt
@@ -428,6 +428,7 @@
0x8013EC10 : "z_rumble",
0x8013EE60 : "z_view",
0x80140260 : "z_vimode",
+ 0x80140CE0 : "code_80140CE0",
0x80140E80 : "code_80140E80",
0x801418B0 : "z_vismono",
0x801420C0 : "code_801420C0",
@@ -464,6 +465,7 @@
0x80174F10 : "padmgr",
0x80176280 : "sched",
0x80177390 : "speed_meter",
+ 0x801780F0 : "su_mtx",
0x80178750 : "sys_cmpdma",
0x80178F30 : "sys_initial_check",
0x80179300 : "sys_math",
@@ -558,7 +560,7 @@
0x801C5CB0 : "z_skelanime",
0x801C5CD0 : "z_skin_matrix",
0x801C5D10 : "z_sub_s",
- 0x801C5DD0 : "z_vimode",
+ 0x801C5DD0 : "code_80140CE0",
0x801C5E30 : "z_vr_box",
0x801C5FC0 : "z_sram_NES",
0x801C6A70 : "z_message",
diff --git a/tools/disasm/functions.txt b/tools/disasm/functions.txt
index ac39602196..988fbde6bb 100644
--- a/tools/disasm/functions.txt
+++ b/tools/disasm/functions.txt
@@ -3222,9 +3222,9 @@
0x80177A84:("func_80177A84",),
0x80177AC8:("func_80177AC8",),
0x80177E58:("SpeedMeter_DrawAllocEntries",),
- 0x801780F0:("func_801780F0",),
- 0x801781EC:("func_801781EC",),
- 0x8017842C:("func_8017842C",),
+ 0x801780F0:("Mtx_SetTranslateScaleMtx",),
+ 0x801781EC:("Mtx_SetRotationMtx",),
+ 0x8017842C:("Mtx_SetTranslationRotationScaleMtx",),
0x80178750:("func_80178750",),
0x80178818:("func_80178818",),
0x80178978:("func_80178978",),
@@ -16990,25 +16990,25 @@
0x80C1E84C:("EnEndingHero_Draw",),
0x80C1E9E0:("DmBal_Init",),
0x80C1EAB4:("DmBal_Destroy",),
- 0x80C1EAC4:("func_80C1EAC4",),
- 0x80C1EAD8:("func_80C1EAD8",),
+ 0x80C1EAC4:("DmBal_SetupDoNothing",),
+ 0x80C1EAD8:("DmBal_DoNothing",),
0x80C1EAE8:("func_80C1EAE8",),
0x80C1EC60:("func_80C1EC60",),
- 0x80C1ED0C:("func_80C1ED0C",),
- 0x80C1ED64:("func_80C1ED64",),
+ 0x80C1ED0C:("DmBal_UpdateEyes",),
+ 0x80C1ED64:("DmBal_SpawnPaper",),
0x80C1EDE4:("DmBal_Update",),
0x80C1EF80:("DmBal_OverrideLimbDraw",),
0x80C1F060:("DmBal_PostLimbDraw",),
0x80C1F078:("DmBal_Draw",),
0x80C1F3D0:("EnPaper_Init",),
0x80C1F45C:("EnPaper_Destroy",),
- 0x80C1F46C:("func_80C1F46C",),
- 0x80C1F480:("func_80C1F480",),
- 0x80C1F4E8:("func_80C1F4E8",),
- 0x80C1F4FC:("func_80C1F4FC",),
- 0x80C1F55C:("func_80C1F55C",),
- 0x80C1F6E0:("func_80C1F6E0",),
- 0x80C1F87C:("func_80C1F87C",),
+ 0x80C1F46C:("EnPaper_SetupSpreadConfettiGroup",),
+ 0x80C1F480:("EnPaper_SpreadConfettiGroup",),
+ 0x80C1F4E8:("EnPaper_SetupFlyConfettiGroup",),
+ 0x80C1F4FC:("EnPaper_FlyConfettiGroup",),
+ 0x80C1F55C:("EnPaper_InitConfettiPiece",),
+ 0x80C1F6E0:("EnPaper_FlyConfettiPiece",),
+ 0x80C1F87C:("EnPaper_UpdateWind",),
0x80C1F97C:("EnPaper_Update",),
0x80C1F9D0:("EnPaper_Draw",),
0x80C1FCF0:("EnHintSkb_Init",),