diff --git a/assets/xml/objects/gameplay_field_keep.xml b/assets/xml/objects/gameplay_field_keep.xml
index eac855669..32427cfe8 100644
--- a/assets/xml/objects/gameplay_field_keep.xml
+++ b/assets/xml/objects/gameplay_field_keep.xml
@@ -42,9 +42,11 @@
-
-
-
+
+
+
+
+
diff --git a/assets/xml/objects/gameplay_keep.xml b/assets/xml/objects/gameplay_keep.xml
index 37fb3fc61..3253e718c 100644
--- a/assets/xml/objects/gameplay_keep.xml
+++ b/assets/xml/objects/gameplay_keep.xml
@@ -1230,7 +1230,7 @@
-
+
@@ -1347,7 +1347,7 @@
-
+
diff --git a/assets/xml/objects/object_boss01.xml b/assets/xml/objects/object_boss01.xml
index 08730f034..ff37d2bc9 100644
--- a/assets/xml/objects/object_boss01.xml
+++ b/assets/xml/objects/object_boss01.xml
@@ -142,10 +142,10 @@
-
+
-
+
diff --git a/assets/xml/objects/object_boss05.xml b/assets/xml/objects/object_boss05.xml
index 9490dd878..54dcd0e95 100644
--- a/assets/xml/objects/object_boss05.xml
+++ b/assets/xml/objects/object_boss05.xml
@@ -1,81 +1,110 @@
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/assets/xml/objects/object_takaraya_objects.xml b/assets/xml/objects/object_takaraya_objects.xml
index 4ce448c86..ef1735575 100644
--- a/assets/xml/objects/object_takaraya_objects.xml
+++ b/assets/xml/objects/object_takaraya_objects.xml
@@ -1,9 +1,10 @@
+
-
-
-
-
-
+
+
+
+
+
diff --git a/include/functions.h b/include/functions.h
index 0ef814bf0..cc69faebc 100644
--- a/include/functions.h
+++ b/include/functions.h
@@ -537,7 +537,7 @@ void Lib_PlaySfx(u16 sfxId);
void Lib_PlaySfx_2(u16 sfxId);
void Lib_PlaySfx_AtPos(Vec3f* pos, u16 sfxId);
void Lib_Vec3f_TranslateAndRotateY(Vec3f* translation, s16 rotAngle, Vec3f* src, Vec3f* dst);
-void Color_RGB8_Lerp(Color_RGB8* a, Color_RGB8* b, f32 t, Color_RGB8* dst);
+void Color_RGB8_Lerp(Color_RGB8* from, Color_RGB8* to, f32 lerp, Color_RGB8* dst);
void Lib_Nop801004FC(void);
void* Lib_SegmentedToVirtual(void* ptr);
void* Lib_SegmentedToVirtualNull(void* ptr);
diff --git a/include/regs.h b/include/regs.h
index 2d113ea48..354408378 100644
--- a/include/regs.h
+++ b/include/regs.h
@@ -61,6 +61,7 @@ extern RegEditor* gRegEditor;
/* TODO: There are still a few OoT defines here that need confirmation */
+#define R_ENV_DISABLE_DBG REG(9)
#define R_TIME_SPEED REG(15)
#define R_RUN_SPEED_LIMIT REG(45)
@@ -148,6 +149,9 @@ extern RegEditor* gRegEditor;
#define R_REVERSE_FLOOR_INDEX XREG(94)
#define R_MINIMAP_DISABLED XREG(95)
+#define R_ENV_LIGHT1_DIR(i) cREG(3 + (i))
+#define R_ENV_LIGHT2_DIR(i) cREG(6 + (i))
+
#define R_TRANS_FADE_FLASH_ALPHA_STEP iREG(50) // Set to a negative number to start the flash
#define R_ROOM_CULL_DEBUG_MODE iREG(86)
#define R_ROOM_CULL_NUM_ENTRIES iREG(87)
diff --git a/include/variables.h b/include/variables.h
index 3ee072344..a7a3f082a 100644
--- a/include/variables.h
+++ b/include/variables.h
@@ -32,7 +32,7 @@ extern u8* sYaz0MaxPtr;
extern void* gYaz0DecompressDstEnd;
// extern UNK_TYPE4 D_8009CD10;
-extern u32 sRandFloat;
+extern u32 gRandFloat;
// extern UNK_TYPE4 sArenaLockMsg;
extern DmaEntry dmadata[1568];
@@ -1679,20 +1679,7 @@ extern SaveContext gSaveContext;
// extern UNK_TYPE4 D_801F6B00;
// extern UNK_TYPE4 D_801F6B04;
// extern UNK_TYPE4 D_801F6B08;
-// extern UNK_TYPE1 D_801F6B0C;
-// extern UNK_TYPE1 D_801F6B0E;
-// extern UNK_TYPE1 D_801F6B10;
-// extern UNK_TYPE1 D_801F6B12;
-// extern UNK_TYPE1 D_801F6B14;
-// extern UNK_TYPE1 D_801F6B16;
-// extern UNK_TYPE1 D_801F6B18;
-// extern UNK_TYPE1 D_801F6B1A;
-// extern UNK_TYPE1 D_801F6B1C;
-// extern UNK_TYPE1 D_801F6B1E;
-// extern UNK_TYPE1 D_801F6B20;
-// extern UNK_TYPE1 D_801F6B22;
-// extern UNK_TYPE4 sCurCsCamera;
-// extern UNK_TYPE1 D_801F6B58;
+
extern void (*sKaleidoScopeUpdateFunc)(PlayState* play);
extern void (*sKaleidoScopeDrawFunc)(PlayState* play);
diff --git a/include/z64actor.h b/include/z64actor.h
index 9ecbb75db..e89267690 100644
--- a/include/z64actor.h
+++ b/include/z64actor.h
@@ -649,12 +649,12 @@ typedef enum {
/* 0x5B */ TATL_HINT_ID_SKULLFISH,
/* 0x5C */ TATL_HINT_ID_DESBREKO,
/* 0x5D */ TATL_HINT_ID_GREEN_CHUCHU,
- /* 0x5E */ TATL_HINT_ID_ODOLWA_1,
+ /* 0x5E */ TATL_HINT_ID_ODOLWA_PHASE_ONE, // 799 or fewer frames have passed, says Odolwa is dangerous to get close to
/* 0x5F */ TATL_HINT_ID_GEKKO_GIANT_SLIME,
/* 0x60 */ TATL_HINT_ID_BAD_BAT,
/* 0x61 */ TATL_HINT_ID_REAL_BOMBCHU,
- /* 0x62 */ TATL_HINT_ID_ODOLWA_2,
- /* 0x63 */ TATL_HINT_ID_ODOLWA_3,
+ /* 0x62 */ TATL_HINT_ID_ODOLWA_CLOSE_TO_PHASE_TWO, // 800 frames have passed, warns that Odolwa will attack after dancing
+ /* 0x63 */ TATL_HINT_ID_ODOLWA_PHASE_TWO, // 1000 or more frames have passed, explains that the bugs are drawn to fire
/* 0x64 */ TATL_HINT_ID_MUSHROOM,
/* 0xFF */ TATL_HINT_ID_NONE = 0xFF
} TatlHintId;
diff --git a/include/z64collision_check.h b/include/z64collision_check.h
index 07f4939b8..02289deec 100644
--- a/include/z64collision_check.h
+++ b/include/z64collision_check.h
@@ -394,7 +394,8 @@ typedef enum {
#define DMG_ENTRY(damage, effect) ((damage) | ((effect) << 4))
-// These flags are not to be used in code until we figure out how we want to format them. They are only here for reference
+// Don't use combinations of these flags in code until we figure out how we want to format them.
+// It's okay to use these flags if the code is only checking a single flag, though.
#define DMG_DEKU_NUT (1 << 0x00)
#define DMG_DEKU_STICK (1 << 0x01)
#define DMG_HORSE_TRAMPLE (1 << 0x02)
diff --git a/include/z64cutscene.h b/include/z64cutscene.h
index 8cdd01a66..52a6cf1bd 100644
--- a/include/z64cutscene.h
+++ b/include/z64cutscene.h
@@ -737,18 +737,21 @@ typedef struct {
/* 0xA */ s16 relativeTo; // see `CutsceneCamRelativeTo`
} CsCmdCamPoint; // size = 0xC
-typedef enum {
- /* 0 */ CS_CAM_INTERP_0,
- /* 1 */ CS_CAM_INTERP_1,
- /* 2 */ CS_CAM_INTERP_2,
- /* 3 */ CS_CAM_INTERP_3,
- /* 4 */ CS_CAM_INTERP_4,
- /* 5 */ CS_CAM_INTERP_5,
- /* 6 */ CS_CAM_INTERP_6,
- /* 7 */ CS_CAM_INTERP_7
+typedef enum CutsceneCamInterpType {
+ /* 0 */ CS_CAM_INTERP_NONE, // values do not change.
+ // values 1-3 only uses a single point from the cmd
+ /* 1 */ CS_CAM_INTERP_SET, // values immediately set to cmd values.
+ /* 2 */ CS_CAM_INTERP_LINEAR, // Lerp to the target position
+ /* 3 */ CS_CAM_INTERP_SCALE, // Step to the target position in increments scaled by the remaining distance
+ // values 4-5 uses multiple points from the cmd
+ /* 4 */ CS_CAM_INTERP_MP_CUBIC, // cubic multi-point (identical to SM64/OoT)
+ /* 5 */ CS_CAM_INTERP_MP_QUAD, // quadratic multi-point
+ // value 6 only uses a single point from the cmd
+ /* 6 */ CS_CAM_INTERP_GEO, // does VecGeo calculations using fov
+ /* 7 */ CS_CAM_INTERP_OFF // interpolation is not processed.
} CutsceneCamInterpType;
-typedef enum {
+typedef enum CutsceneCamRelativeTo {
/* 0 */ CS_CAM_REL_0,
/* 1 */ CS_CAM_REL_1,
/* 2 */ CS_CAM_REL_2,
@@ -759,26 +762,25 @@ typedef enum {
// Roll and Fov Data
-typedef struct {
- /* 0x0 */ s16 unused0; // unused
+typedef struct CsCmdCamMisc {
+ /* 0x0 */ s16 unused0; // used only in the unused interp function
/* 0x2 */ s16 roll;
/* 0x4 */ s16 fov;
/* 0x6 */ s16 unused1; // unused
} CsCmdCamMisc; // size = 0x8
-typedef struct {
- /* 0x00 */ Vec3f unk_00;
- /* 0x0C */ Vec3f unk_0C;
- /* 0x18 */ f32 unk_18;
- /* 0x1C */ f32 unk_1C;
- /* 0x2A */ f32 unk_20;
- /* 0x24 */ s16 unk_24;
- /* 0x26 */ s16 unk_26;
- /* 0x28 */ s16 unk_28;
+typedef struct CutsceneCameraInterp {
+ /* 0x00 */ Vec3f curPos;
+ /* 0x0C */ Vec3f initPos;
+ /* 0x18 */ f32 initFov;
+ /* 0x1C */ f32 initRoll;
+ /* 0x2A */ f32 unk_20; // position adjustment based on fov?
+ /* 0x24 */ s16 curFrame;
+ /* 0x26 */ s16 waypoint;
+ /* 0x28 */ s16 duration;
/* 0x2A */ s16 numEntries;
/* 0x1E */ u8 curPoint;
- /* 0x2D */ u8 unk_2D;
- /* 0x2E */ UNK_TYPE1 unk_2E[2];
+ /* 0x2D */ u8 type; // See `CutsceneCamInterpType`
} CutsceneCameraInterp; // size = 0x30
typedef struct CutsceneCamera {
@@ -798,11 +800,11 @@ typedef struct CutsceneCamera {
} CutsceneCamera; // size = 0x80
typedef enum {
- /* 0 */ CS_CAM_STATE_UPDATE_ALL, // Update spline and next spline timer
- /* 0 */ CS_CAM_STATE_UPDATE_SPLINE, // Update spline, do not advance next spline timer
- /* 0 */ CS_CAM_STATE_PAUSE, // No updates
- /* 0 */ CS_CAM_STATE_DONE_SPLINE, // Finished the current spline, ready for the next one
- /* 0 */ CS_CAM_STATE_DONE = 999 // Finished all the splines.
+ /* 0 */ CS_CAM_STATE_UPDATE_ALL, // Update spline and next spline timer
+ /* 1 */ CS_CAM_STATE_UPDATE_SPLINE, // Update spline, do not advance next spline timer
+ /* 2 */ CS_CAM_STATE_PAUSE, // No updates
+ /* 3 */ CS_CAM_STATE_DONE_SPLINE, // Finished the current spline, ready for the next one
+ /* 999 */ CS_CAM_STATE_DONE = 999 // Finished all the splines.
} CutsceneCameraState;
// OoT Remnant
diff --git a/include/z64math.h b/include/z64math.h
index c0f20bc6d..2c457d8bf 100644
--- a/include/z64math.h
+++ b/include/z64math.h
@@ -131,7 +131,10 @@ typedef union {
} MtxF; // size = 0x40
#define LERPIMP(v0, v1, t) ((v0) + (((v1) - (v0)) * (t)))
+#define LERPIMP_ALT(v0, v1, t) (((v1) - (v0)) * (t) + (v0))
+#define S16_LERP(v0, v1, t) ((s16)(((v1) - (v0)) * (t)) + (v0))
#define F32_LERP(v0, v1, t) ((1.0f - (t)) * (f32)(v0) + (t) * (f32)(v1))
+#define F32_LERP_ALT(v0, v1, t) ((f32)(v0) * (1.0f - (t)) + (t) * (f32)(v1))
#define F32_LERPIMP(v0, v1, t) ((f32)(v0) + (((f32)(v1) - (f32)(v0)) * (t)))
#define F32_LERPIMPINV(v0, v1, t) ((f32)(v0) + (((f32)(v1) - (f32)(v0)) / (t)))
#define BINANG_LERPIMP(v0, v1, t) ((v0) + (s16)(BINANG_SUB((v1), (v0)) * (t)))
diff --git a/include/z64save.h b/include/z64save.h
index 25ce107f8..5fffb73c3 100644
--- a/include/z64save.h
+++ b/include/z64save.h
@@ -240,7 +240,7 @@ typedef struct PermanentSceneFlags {
/* 0x08 */ u32 switch1;
/* 0x0C */ u32 clearedRoom;
/* 0x10 */ u32 collectible;
- /* 0x14 */ u32 unk_14; // varies based on scene. For dungeons, floors visited.
+ /* 0x14 */ u32 unk_14; // varies based on scene. For dungeons, floors visited.
/* 0x18 */ u32 rooms;
} PermanentSceneFlags; // size = 0x1C
@@ -1598,9 +1598,8 @@ typedef enum {
#define EVENTINF_THREEDAYRESET_LOST_STICK_AMMO 0x73
#define EVENTINF_THREEDAYRESET_LOST_ARROW_AMMO 0x74
-#define EVENTINF_75 0x75
-#define EVENTINF_76 0x76
-#define EVENTINF_77 0x77
+// Checked in Kankyo as one to store the day: gSaveContext.eventInf[7] & 0xE0
+// EVENTINF_75 to EVENTINF_77
#define CHECK_EVENTINF(flag) (gSaveContext.eventInf[(flag) >> 4] & (1 << ((flag) & 0xF)))
#define SET_EVENTINF(flag) (gSaveContext.eventInf[(flag) >> 4] |= (1 << ((flag) & 0xF)))
@@ -1628,6 +1627,13 @@ typedef enum {
#define SET_EVENTINF_DOG_RACE_RACE_STANDING(raceStanding) \
(gSaveContext.eventInf[0] = (gSaveContext.eventInf[0] & EVENTINF_DOG_RACE_STATE_MASK) | (raceStanding << 3))
+#define EVENTINF_GET_7_E0 ((gSaveContext.eventInf[7] & 0xE0) >> 5)
+
+#define EVENTINF_SET_7_E0(day, temp) \
+ (temp) = gSaveContext.eventInf[7] & (u8)~0xE0; \
+ (temp) |= (u8)((day) << 5); \
+ gSaveContext.eventInf[7] = (temp)
+
typedef enum {
/* 0 */ DUNGEON_INDEX_WOODFALL_TEMPLE,
/* 1 */ DUNGEON_INDEX_SNOWHEAD_TEMPLE,
diff --git a/spec b/spec
index e00ce0937..15a41193c 100644
--- a/spec
+++ b/spec
@@ -531,7 +531,6 @@ beginseg
include "build/src/code/z_player_call.o"
include "build/src/code/z_shrink_window.o"
include "build/src/code/cutscene_camera.o"
- include "build/data/code/cutscene_camera.bss.o"
include "build/src/code/z_kaleido_manager.o"
include "build/src/code/z_kaleido_scope_call.o"
include "build/src/code/z_fbdemo_dlftbls.o"
@@ -2296,9 +2295,7 @@ beginseg
name "ovl_Boss_01"
compress
include "build/src/overlays/actors/ovl_Boss_01/z_boss_01.o"
- include "build/data/ovl_Boss_01/ovl_Boss_01.data.o"
- include "build/data/ovl_Boss_01/ovl_Boss_01.bss.o"
- include "build/data/ovl_Boss_01/ovl_Boss_01.reloc.o"
+ include "build/src/overlays/actors/ovl_Boss_01/ovl_Boss_01_reloc.o"
endseg
beginseg
@@ -3290,9 +3287,7 @@ beginseg
name "ovl_Obj_Takaraya_Wall"
compress
include "build/src/overlays/actors/ovl_Obj_Takaraya_Wall/z_obj_takaraya_wall.o"
- include "build/data/ovl_Obj_Takaraya_Wall/ovl_Obj_Takaraya_Wall.data.o"
- include "build/data/ovl_Obj_Takaraya_Wall/ovl_Obj_Takaraya_Wall.bss.o"
- include "build/data/ovl_Obj_Takaraya_Wall/ovl_Obj_Takaraya_Wall.reloc.o"
+ include "build/src/overlays/actors/ovl_Obj_Takaraya_Wall/ovl_Obj_Takaraya_Wall_reloc.o"
endseg
beginseg
diff --git a/src/boot/O2/rand.c b/src/boot/O2/rand.c
index 2db64e9d5..9f3051717 100644
--- a/src/boot/O2/rand.c
+++ b/src/boot/O2/rand.c
@@ -5,7 +5,7 @@ static u32 sRandInt = 1;
//! Space to store a value to be re-interpreted as a float.
//! This can't be static because it is used in z_kankyo.
-u32 sRandFloat;
+u32 gRandFloat;
//! These values are recommended by the algorithms book *Numerical Recipes in C. The Art of Scientific Computing*, 2nd
//! Edition, 1992, ISBN 0-521-43108-5. (p. 284):
@@ -37,8 +37,8 @@ void Rand_Seed(u32 seed) {
*/
f32 Rand_ZeroOne(void) {
sRandInt = (sRandInt * RAND_MULTIPLIER) + RAND_INCREMENT;
- sRandFloat = ((sRandInt >> 9) | 0x3F800000);
- return *((f32*)&sRandFloat) - 1.0f;
+ gRandFloat = ((sRandInt >> 9) | 0x3F800000);
+ return *((f32*)&gRandFloat) - 1.0f;
}
/**
@@ -46,8 +46,8 @@ f32 Rand_ZeroOne(void) {
*/
f32 Rand_Centered(void) {
sRandInt = (sRandInt * RAND_MULTIPLIER) + RAND_INCREMENT;
- sRandFloat = ((sRandInt >> 9) | 0x3F800000);
- return *((f32*)&sRandFloat) - 1.5f;
+ gRandFloat = ((sRandInt >> 9) | 0x3F800000);
+ return *((f32*)&gRandFloat) - 1.5f;
}
//! All functions below are unused variants of the above four, that use a provided random number variable instead of the
@@ -79,8 +79,8 @@ u32 Rand_Next_Variable(u32* rndNum) {
f32 Rand_ZeroOne_Variable(u32* rndNum) {
u32 next = (*rndNum * RAND_MULTIPLIER) + RAND_INCREMENT;
- sRandFloat = ((*rndNum = next) >> 9) | 0x3F800000;
- return *((f32*)&sRandFloat) - 1.0f;
+ gRandFloat = ((*rndNum = next) >> 9) | 0x3F800000;
+ return *((f32*)&gRandFloat) - 1.0f;
}
/**
@@ -91,6 +91,6 @@ f32 Rand_ZeroOne_Variable(u32* rndNum) {
f32 Rand_Centered_Variable(u32* rndNum) {
u32 next = (*rndNum * RAND_MULTIPLIER) + RAND_INCREMENT;
- sRandFloat = ((*rndNum = next) >> 9) | 0x3F800000;
- return *((f32*)&sRandFloat) - 1.5f;
+ gRandFloat = ((*rndNum = next) >> 9) | 0x3F800000;
+ return *((f32*)&gRandFloat) - 1.5f;
}
diff --git a/src/code/cutscene_camera.c b/src/code/cutscene_camera.c
index d92d119dd..24f6fcc18 100644
--- a/src/code/cutscene_camera.c
+++ b/src/code/cutscene_camera.c
@@ -1,31 +1,34 @@
#include "global.h"
-extern CutsceneCamera* sCurCsCamera;
+static CutsceneCamera* sCurCsCamera;
typedef s16 (*CsCamInterpolateCallback)(Vec3f*, f32*, s16*, CsCmdCamPoint*, CsCmdCamMisc*, CutsceneCameraInterp*);
-// function declarations
-s16 func_80161180(Vec3f* pos, f32* fov, s16* roll, CsCmdCamPoint* point, CsCmdCamMisc* misc,
- CutsceneCameraInterp* interp);
-s16 func_8016237C(Vec3f* pos, f32* fov, s16* roll, CsCmdCamPoint* point, CsCmdCamMisc* misc,
- CutsceneCameraInterp* interp);
-s16 func_8016253C(Vec3f* pos, f32* fov, s16* roll, CsCmdCamPoint* point, CsCmdCamMisc* misc,
- CutsceneCameraInterp* interp);
-s16 func_80162A50(Vec3f* pos, f32* fov, s16* roll, CsCmdCamPoint* point, CsCmdCamMisc* misc,
- CutsceneCameraInterp* interp);
-s16 func_801623E4(Vec3f* pos, f32* fov, s16* roll, CsCmdCamPoint* point, CsCmdCamMisc* misc,
- CutsceneCameraInterp* interp);
-s16 func_80161C20(Vec3f* pos, f32* fov, s16* roll, CsCmdCamPoint* point, CsCmdCamMisc* misc,
- CutsceneCameraInterp* interp);
-s16 func_80161E4C(Vec3f* pos, f32* fov, s16* roll, CsCmdCamPoint* point, CsCmdCamMisc* misc,
- CutsceneCameraInterp* interp);
-s16 func_801620CC(Vec3f* pos, f32* fov, s16* roll, CsCmdCamPoint* point, CsCmdCamMisc* misc,
- CutsceneCameraInterp* interp);
-s16 func_80163334(Vec3f* pos, f32* fov, s16* roll, CsCmdCamPoint* point, CsCmdCamMisc* misc,
- CutsceneCameraInterp* interp);
+s16 CutsceneCamera_Interp_Off(Vec3f* camPos, f32* camFov, s16* camRoll, CsCmdCamPoint* pointCmd, CsCmdCamMisc* miscCmd,
+ CutsceneCameraInterp* interpState);
+s16 CutsceneCamera_Interp_None(Vec3f* camPos, f32* camFov, s16* camRoll, CsCmdCamPoint* pointCmd, CsCmdCamMisc* miscCmd,
+ CutsceneCameraInterp* interpState);
+s16 CutsceneCamera_Interp_MultiPointQuadratic(Vec3f* camPos, f32* camFov, s16* camRoll, CsCmdCamPoint* pointCmd,
+ CsCmdCamMisc* miscCmd, CutsceneCameraInterp* interpState);
+s16 CutsceneCamera_Interp_MultiPointCubic(Vec3f* camPos, f32* camFov, s16* camRoll, CsCmdCamPoint* pointCmd,
+ CsCmdCamMisc* miscCmd, CutsceneCameraInterp* interpState);
+s16 CutsceneCamera_Interp_Set(Vec3f* camPos, f32* camFov, s16* camRoll, CsCmdCamPoint* pointCmd, CsCmdCamMisc* miscCmd,
+ CutsceneCameraInterp* interpState);
+s16 CutsceneCamera_Interp_Linear(Vec3f* camPos, f32* camFov, s16* camRoll, CsCmdCamPoint* pointCmd,
+ CsCmdCamMisc* miscCmd, CutsceneCameraInterp* interpState);
+s16 CutsceneCamera_Interp_Scale(Vec3f* camPos, f32* camFov, s16* camRoll, CsCmdCamPoint* pointCmd,
+ CsCmdCamMisc* miscCmd, CutsceneCameraInterp* interpState);
+s16 CutsceneCamera_Interp_Geo(Vec3f* camPos, f32* camFov, s16* camRoll, CsCmdCamPoint* pointCmd, CsCmdCamMisc* miscCmd,
+ CutsceneCameraInterp* interpState);
+s16 CutsceneCamera_Interp_Unused(Vec3f* camPos, f32* camFov, s16* camRoll, CsCmdCamPoint* pointCmd,
+ CsCmdCamMisc* miscCmd, CutsceneCameraInterp* interpState);
+
f32 func_80163660(Actor* actor);
-#pragma GLOBAL_ASM("asm/non_matchings/code/cutscene_camera/func_80161180.s")
+s16 CutsceneCamera_Interp_Off(Vec3f* camPos, f32* camFov, s16* camRoll, CsCmdCamPoint* pointCmd, CsCmdCamMisc* miscCmd,
+ CutsceneCameraInterp* interpState) {
+ return 0;
+}
/**
* Initializes Cutscene Camera Info
@@ -44,44 +47,44 @@ s32 CutsceneCamera_Init(Camera* camera, CutsceneCamera* csCamera) {
__osMemset(&csCamera->eyeInterp, 0, sizeof(CutsceneCameraInterp));
__osMemset(&csCamera->atInterp, 0, sizeof(CutsceneCameraInterp));
- csCamera->eyeInterp.unk_2D = csCamera->atInterp.unk_2D = 7;
+ csCamera->eyeInterp.type = csCamera->atInterp.type = CS_CAM_INTERP_OFF;
return 1;
}
CsCamInterpolateCallback CutsceneCamera_Interpolate(u8 interpType) {
switch (interpType) {
- case CS_CAM_INTERP_7:
+ case CS_CAM_INTERP_OFF:
default:
- return func_80161180;
+ return CutsceneCamera_Interp_Off;
- case CS_CAM_INTERP_0:
- return func_8016237C;
+ case CS_CAM_INTERP_NONE:
+ return CutsceneCamera_Interp_None;
- case CS_CAM_INTERP_5:
- return func_8016253C;
+ case CS_CAM_INTERP_MP_QUAD:
+ return CutsceneCamera_Interp_MultiPointQuadratic;
- case CS_CAM_INTERP_4:
- return func_80162A50;
+ case CS_CAM_INTERP_MP_CUBIC:
+ return CutsceneCamera_Interp_MultiPointCubic;
- case CS_CAM_INTERP_1:
- return func_801623E4;
+ case CS_CAM_INTERP_SET:
+ return CutsceneCamera_Interp_Set;
- case CS_CAM_INTERP_2:
- return func_80161C20;
+ case CS_CAM_INTERP_LINEAR:
+ return CutsceneCamera_Interp_Linear;
- case CS_CAM_INTERP_3:
- return func_80161E4C;
+ case CS_CAM_INTERP_SCALE:
+ return CutsceneCamera_Interp_Scale;
- case CS_CAM_INTERP_6:
- return func_801620CC;
+ case CS_CAM_INTERP_GEO:
+ return CutsceneCamera_Interp_Geo;
}
}
u8 CutsceneCamera_ProcessSpline(CutsceneCamera* csCamera) {
s32 sp5C;
- f32* fov;
- s16* roll;
+ f32* camFov;
+ s16* camRoll;
CsCamInterpolateCallback interpHandler;
Player* player;
Actor* target;
@@ -100,19 +103,19 @@ u8 CutsceneCamera_ProcessSpline(CutsceneCamera* csCamera) {
sp5C = false;
}
- csCamera->eyeInterp.unk_00 = csCamera->camera->eye;
- csCamera->atInterp.unk_00 = csCamera->camera->at;
+ csCamera->eyeInterp.curPos = csCamera->camera->eye;
+ csCamera->atInterp.curPos = csCamera->camera->at;
if (sp5C) {
- fov = NULL;
+ camFov = NULL;
} else {
- fov = &csCamera->camera->fov;
+ camFov = &csCamera->camera->fov;
}
if (sp5C) {
- roll = NULL;
+ camRoll = NULL;
} else {
- roll = &csCamera->camera->roll;
+ camRoll = &csCamera->camera->roll;
}
interpHandler = CutsceneCamera_Interpolate(csCamera->atCmd[csCamera->eyeInterp.curPoint].interpType);
@@ -142,7 +145,7 @@ u8 CutsceneCamera_ProcessSpline(CutsceneCamera* csCamera) {
break;
}
- numPoints = interpHandler(&csCamera->camera->at, fov, roll, &csCamera->atCmd[csCamera->eyeInterp.curPoint],
+ numPoints = interpHandler(&csCamera->camera->at, camFov, camRoll, &csCamera->atCmd[csCamera->eyeInterp.curPoint],
&csCamera->miscCmd[csCamera->eyeInterp.curPoint], &csCamera->eyeInterp);
switch (csCamera->atCmd[csCamera->eyeInterp.curPoint].relativeTo) {
@@ -174,15 +177,15 @@ u8 CutsceneCamera_ProcessSpline(CutsceneCamera* csCamera) {
csCamera->eyeInterp.curPoint += numPoints;
if (sp5C) {
- fov = &csCamera->camera->fov;
+ camFov = &csCamera->camera->fov;
} else {
- fov = NULL;
+ camFov = NULL;
}
if (sp5C) {
- roll = &csCamera->camera->roll;
+ camRoll = &csCamera->camera->roll;
} else {
- roll = NULL;
+ camRoll = NULL;
}
interpHandler = CutsceneCamera_Interpolate(csCamera->eyeCmd[csCamera->atInterp.curPoint].interpType);
@@ -212,7 +215,7 @@ u8 CutsceneCamera_ProcessSpline(CutsceneCamera* csCamera) {
break;
}
- numPoints = interpHandler(&csCamera->camera->eye, fov, roll, &csCamera->eyeCmd[csCamera->atInterp.curPoint],
+ numPoints = interpHandler(&csCamera->camera->eye, camFov, camRoll, &csCamera->eyeCmd[csCamera->atInterp.curPoint],
&csCamera->miscCmd[csCamera->atInterp.curPoint], &csCamera->atInterp);
switch (csCamera->eyeCmd[csCamera->atInterp.curPoint].relativeTo) {
@@ -308,7 +311,7 @@ s32 CutsceneCamera_UpdateSplines(u8* script, CutsceneCamera* csCamera) {
csCamera->splineIndex = (csCamera->splineIndex & 0xFFFF) + 1;
csCamera->state = CS_CAM_STATE_UPDATE_ALL;
csCamera->nextSplineTimer = csCamera->updateSplineTimer = 0;
- csCamera->eyeInterp.unk_2D = csCamera->atInterp.unk_2D = 7;
+ csCamera->eyeInterp.type = csCamera->atInterp.type = CS_CAM_INTERP_OFF;
}
csCamera->nextSplineTimer++;
@@ -353,28 +356,578 @@ void CutsceneCamera_Reset(void) {
sCurCsCamera->state = CS_CAM_STATE_UPDATE_ALL;
}
-#pragma GLOBAL_ASM("asm/non_matchings/code/cutscene_camera/func_80161C20.s")
+// Linear interpolation from initial values to cmd values. Set weight to 100 to go all the way to the cmd value.
+s16 CutsceneCamera_Interp_Linear(Vec3f* camPos, f32* camFov, s16* camRoll, CsCmdCamPoint* pointCmd,
+ CsCmdCamMisc* miscCmd, CutsceneCameraInterp* interpState) {
+ f32 lerp;
-#pragma GLOBAL_ASM("asm/non_matchings/code/cutscene_camera/func_80161E4C.s")
+ if (interpState->type != CS_CAM_INTERP_LINEAR) {
+ // Initialize
+ interpState->type = CS_CAM_INTERP_LINEAR;
+ interpState->waypoint = 0;
+ interpState->curFrame = 0;
+ interpState->duration = 1;
+ if (camPos != NULL) {
+ interpState->initPos.x = camPos->x;
+ interpState->initPos.y = camPos->y;
+ interpState->initPos.z = camPos->z;
+ }
-#pragma GLOBAL_ASM("asm/non_matchings/code/cutscene_camera/func_801620CC.s")
+ if (camFov != NULL) {
+ interpState->initFov = *camFov;
+ }
-#pragma GLOBAL_ASM("asm/non_matchings/code/cutscene_camera/func_8016237C.s")
+ if (camRoll != NULL) {
+ interpState->initRoll = *camRoll;
+ }
+ }
-#pragma GLOBAL_ASM("asm/non_matchings/code/cutscene_camera/func_801623E4.s")
+ interpState->curFrame++;
-#pragma GLOBAL_ASM("asm/non_matchings/code/cutscene_camera/func_801624EC.s")
+ lerp = ((f32)interpState->curFrame / pointCmd->duration) * (pointCmd->weight / 100.0f);
-#pragma GLOBAL_ASM("asm/non_matchings/code/cutscene_camera/func_8016253C.s")
+ if (camPos != NULL) {
+ VEC3F_LERPIMPDST(camPos, &interpState->initPos, &pointCmd->pos, lerp);
+ }
-#pragma GLOBAL_ASM("asm/non_matchings/code/cutscene_camera/func_801629BC.s")
+ if (camFov != NULL) {
+ *camFov = LERPIMP(interpState->initFov, miscCmd->fov, lerp);
+ }
-#pragma GLOBAL_ASM("asm/non_matchings/code/cutscene_camera/func_80162A50.s")
+ if (camRoll != NULL) {
+ s16 targetRoll;
+ s16 rollDiffToTarget;
-#pragma GLOBAL_ASM("asm/non_matchings/code/cutscene_camera/func_80162FF8.s")
+ targetRoll = CAM_DEG_TO_BINANG(miscCmd->roll);
-#pragma GLOBAL_ASM("asm/non_matchings/code/cutscene_camera/func_801631DC.s")
+ rollDiffToTarget = (s16)(targetRoll - (s16)interpState->initRoll);
-#pragma GLOBAL_ASM("asm/non_matchings/code/cutscene_camera/func_80163334.s")
+ *camRoll = (s16)interpState->initRoll + (s16)(rollDiffToTarget * lerp);
+ }
-#pragma GLOBAL_ASM("asm/non_matchings/code/cutscene_camera/func_80163660.s")
+ if (interpState->curFrame >= pointCmd->duration) {
+ // Finished
+ interpState->type = CS_CAM_INTERP_OFF;
+ return 1;
+ }
+
+ return 0;
+}
+
+s16 CutsceneCamera_Interp_Scale(Vec3f* camPos, f32* camFov, s16* camRoll, CsCmdCamPoint* pointCmd,
+ CsCmdCamMisc* miscCmd, CutsceneCameraInterp* interpState) {
+ f32 lerp;
+ f32 tmp1;
+ f32 tmp2;
+
+ if (interpState->type != CS_CAM_INTERP_SCALE) {
+ // Initialize
+ interpState->type = CS_CAM_INTERP_SCALE;
+ interpState->waypoint = 0;
+ interpState->curFrame = 0;
+ interpState->duration = 1;
+ if (camPos != NULL) {
+ interpState->initPos.x = camPos->x;
+ interpState->initPos.y = camPos->y;
+ interpState->initPos.z = camPos->z;
+ }
+ if (camFov != NULL) {
+ interpState->initFov = *camFov;
+ }
+ if (camRoll != NULL) {
+ interpState->initRoll = *camRoll;
+ }
+ }
+
+ tmp2 = (((pointCmd->weight + 100) * (pointCmd->duration / 2)) +
+ (((pointCmd->weight + 100) / 2) * (pointCmd->duration & 1)));
+ if (pointCmd->duration < 2) {
+ lerp = 1.0f;
+ } else {
+ tmp1 = (f32)(pointCmd->weight - 100) / (pointCmd->duration - 1);
+ lerp = ((interpState->curFrame * tmp1) + 100.0f) / tmp2;
+ }
+
+ interpState->curFrame++;
+
+ if (camPos != NULL) {
+ camPos->x += (pointCmd->pos.x - interpState->initPos.x) * lerp;
+ camPos->y += (pointCmd->pos.y - interpState->initPos.y) * lerp;
+ camPos->z += (pointCmd->pos.z - interpState->initPos.z) * lerp;
+ }
+
+ if (camFov != NULL) {
+ *camFov += (miscCmd->fov - interpState->initFov) * lerp;
+ }
+
+ if (camRoll != NULL) {
+ s16 targetRoll;
+ s16 rollDiffToTarget;
+
+ targetRoll = CAM_DEG_TO_BINANG(miscCmd->roll);
+
+ rollDiffToTarget = (s16)(targetRoll - (s16)interpState->initRoll);
+
+ *camRoll += (s16)(rollDiffToTarget * lerp);
+ }
+
+ if (interpState->curFrame >= pointCmd->duration) {
+ // Finished
+ interpState->type = CS_CAM_INTERP_OFF;
+ return 1;
+ }
+
+ return 0;
+}
+
+s16 CutsceneCamera_Interp_Geo(Vec3f* camPos, f32* camFov, s16* camRoll, CsCmdCamPoint* pointCmd, CsCmdCamMisc* miscCmd,
+ CutsceneCameraInterp* interpState) {
+ VecGeo sp40;
+ f32 lerp;
+ f32 tmp1;
+ f32 tmp2;
+
+ if (interpState->type != CS_CAM_INTERP_GEO) {
+ // Initialize
+ interpState->type = CS_CAM_INTERP_GEO;
+ interpState->waypoint = 0;
+ interpState->curFrame = 0;
+ interpState->duration = 1;
+ if (camPos != NULL) {
+ interpState->unk_20 = OLib_Vec3fDist(&interpState->curPos, camPos) * Math_FTanF(DEG_TO_RAD(*camFov));
+ }
+ if (camFov != NULL) {
+ interpState->initFov = *camFov;
+ }
+ if (camRoll != NULL) {
+ interpState->initRoll = *camRoll;
+ }
+ }
+
+ tmp2 = (((pointCmd->weight + 100) * (pointCmd->duration / 2)) +
+ (((pointCmd->weight + 100) / 2) * (pointCmd->duration & 1)));
+ if (pointCmd->duration < 2) {
+ lerp = 1.0f;
+ } else {
+ tmp1 = (f32)(pointCmd->weight - 100) / (pointCmd->duration - 1);
+ lerp = ((interpState->curFrame * tmp1) + 100.0f) / tmp2;
+ }
+
+ interpState->curFrame++;
+
+ if (camPos != NULL) {
+ OLib_Vec3fDiffToVecGeo(&sp40, &interpState->curPos, camPos);
+ sp40.r = interpState->unk_20 / Math_FTanF(DEG_TO_RAD(*camFov));
+ OLib_AddVecGeoToVec3f(camPos, &interpState->curPos, &sp40);
+ }
+
+ if (camFov != NULL) {
+ *camFov += (miscCmd->fov - interpState->initFov) * lerp;
+ }
+
+ if (camRoll != NULL) {
+ s16 targetRoll;
+ s16 rollDiffToTarget;
+
+ targetRoll = CAM_DEG_TO_BINANG(miscCmd->roll);
+
+ rollDiffToTarget = (s16)(targetRoll - (s16)interpState->initRoll);
+
+ *camRoll += (s16)(rollDiffToTarget * lerp);
+ }
+
+ if (interpState->curFrame >= pointCmd->duration) {
+ // Finished
+ interpState->type = CS_CAM_INTERP_OFF;
+ return 1;
+ }
+
+ return 0;
+}
+
+// Updates the interpolation state but does not change the pos/fov/roll values
+s16 CutsceneCamera_Interp_None(Vec3f* camPos, f32* camFov, s16* camRoll, CsCmdCamPoint* pointCmd, CsCmdCamMisc* miscCmd,
+ CutsceneCameraInterp* interpState) {
+ if (interpState->type != CS_CAM_INTERP_NONE) {
+ // Initialize
+ interpState->type = CS_CAM_INTERP_NONE;
+ interpState->waypoint = 0;
+ interpState->curFrame = 0;
+ interpState->duration = 1;
+ }
+
+ interpState->curFrame++;
+
+ if (interpState->curFrame >= pointCmd->duration) {
+ // Finishes
+ interpState->type = CS_CAM_INTERP_OFF;
+ return 1;
+ }
+
+ return 0;
+}
+
+// Immediately sets the values
+s16 CutsceneCamera_Interp_Set(Vec3f* camPos, f32* camFov, s16* camRoll, CsCmdCamPoint* pointCmd, CsCmdCamMisc* miscCmd,
+ CutsceneCameraInterp* interpState) {
+ s16 pad;
+
+ if (interpState->type != CS_CAM_INTERP_SET) {
+ // Initialize
+ interpState->type = CS_CAM_INTERP_SET;
+ interpState->waypoint = 0;
+ interpState->curFrame = 0;
+ interpState->duration = 1;
+ if (camFov != NULL) {
+ *camFov = miscCmd->fov;
+ }
+ if (camRoll != NULL) {
+ *camRoll = CAM_DEG_TO_BINANG(miscCmd->roll);
+ }
+ }
+
+ if (camPos != NULL) {
+ camPos->x = pointCmd->pos.x;
+ camPos->y = pointCmd->pos.y;
+ camPos->z = pointCmd->pos.z;
+ }
+
+ interpState->curFrame++;
+
+ if (interpState->curFrame >= pointCmd->duration) {
+ // Finished
+ interpState->type = CS_CAM_INTERP_OFF;
+ return 1;
+ }
+
+ return 0;
+}
+
+void func_801624EC(f32 u, f32* coeff) {
+ f32 u1 = 1.0f - u;
+
+ coeff[0] = u1 * u1 * 0.5f;
+ coeff[1] = u * u1 + 0.5f;
+ coeff[2] = u * u * 0.5f;
+}
+
+s16 CutsceneCamera_Interp_MultiPointQuadratic(Vec3f* camPos, f32* camFov, s16* camRoll, CsCmdCamPoint* pointCmd,
+ CsCmdCamMisc* miscCmd, CutsceneCameraInterp* interpState) {
+ f32 new_var;
+ f32 coeff[3];
+ s32 waypoints[3];
+
+ if (interpState->type != CS_CAM_INTERP_MP_QUAD) {
+ // Initialize
+ interpState->type = CS_CAM_INTERP_MP_QUAD;
+ interpState->waypoint = 0;
+ interpState->duration = pointCmd->duration;
+ interpState->curFrame = 0;
+ }
+
+ new_var = (f32)interpState->curFrame / pointCmd[interpState->waypoint + 1].duration;
+
+ if (interpState->waypoint < (interpState->duration - 1)) {
+ waypoints[0] = interpState->waypoint;
+ } else {
+ waypoints[0] = interpState->duration - 1;
+ }
+
+ if ((interpState->waypoint + 1) < (interpState->duration - 1)) {
+ waypoints[1] = interpState->waypoint + 1;
+ } else {
+ waypoints[1] = interpState->duration - 1;
+ }
+
+ if ((interpState->waypoint + 2) < (interpState->duration - 1)) {
+ waypoints[2] = interpState->waypoint + 2;
+ } else {
+ waypoints[2] = interpState->duration - 1;
+ }
+
+ func_801624EC(new_var, coeff);
+
+ if (camPos != NULL) {
+ camPos->x = (coeff[0] * pointCmd[waypoints[0]].pos.x) + (coeff[1] * pointCmd[waypoints[1]].pos.x) +
+ (coeff[2] * pointCmd[waypoints[2]].pos.x);
+ camPos->y = (coeff[0] * pointCmd[waypoints[0]].pos.y) + (coeff[1] * pointCmd[waypoints[1]].pos.y) +
+ (coeff[2] * pointCmd[waypoints[2]].pos.y);
+ camPos->z = (coeff[0] * pointCmd[waypoints[0]].pos.z) + (coeff[1] * pointCmd[waypoints[1]].pos.z) +
+ (coeff[2] * pointCmd[waypoints[2]].pos.z);
+ }
+
+ if (camFov != NULL) {
+ *camFov = (coeff[0] * miscCmd[waypoints[0]].fov) + (coeff[1] * miscCmd[waypoints[1]].fov) +
+ (coeff[2] * miscCmd[waypoints[2]].fov);
+ }
+
+ if (camRoll != NULL) {
+ s16 targetRolls[3];
+ s32 sp28[2];
+ s32 rollDiffToTarget;
+
+ targetRolls[0] = CAM_DEG_TO_BINANG(miscCmd[waypoints[0]].roll);
+ targetRolls[1] = CAM_DEG_TO_BINANG(miscCmd[waypoints[1]].roll);
+ targetRolls[2] = CAM_DEG_TO_BINANG(miscCmd[waypoints[2]].roll);
+
+ sp28[0] = (s16)(targetRolls[1] - targetRolls[0]);
+ sp28[1] = sp28[0] + (s16)(targetRolls[2] - targetRolls[1]);
+
+ rollDiffToTarget = ((coeff[1] * sp28[0]) + (coeff[2] * sp28[1]));
+ *camRoll = targetRolls[0] + rollDiffToTarget;
+ }
+
+ interpState->curFrame++;
+
+ if (interpState->curFrame == pointCmd[interpState->waypoint + 1].duration) {
+ interpState->waypoint++;
+ interpState->curFrame = 0;
+ if (interpState->waypoint >= (interpState->duration - 2)) {
+ interpState->type = CS_CAM_INTERP_OFF;
+ return interpState->duration;
+ }
+ }
+ return 0;
+}
+
+/**
+ * This code is very similar to the spline system in OoT's func_800BB0A0
+ * in that it is based on the Super Mario 64 cutscene camera movement
+ */
+void func_801629BC(f32 u, f32* coeff) {
+ coeff[0] = (1.0f - u) * (1.0f - u) * (1.0f - u) * (1.0f / 6.0f);
+ coeff[1] = ((u * u * u * 0.5f) - u * u) + (2.0f / 3.0f);
+ coeff[2] = (u * u * u * -0.5f) + (u * u * 0.5f) + (u * 0.5f) + (1.0f / 6.0f);
+ coeff[3] = u * u * u * (1.0f / 6.0f);
+}
+
+s16 CutsceneCamera_Interp_MultiPointCubic(Vec3f* camPos, f32* camFov, s16* camRoll, CsCmdCamPoint* pointCmd,
+ CsCmdCamMisc* miscCmd, CutsceneCameraInterp* interpState) {
+ f32 new_var;
+ f32 coeff[4];
+ s32 waypoints[4];
+
+ if (interpState->type != CS_CAM_INTERP_MP_CUBIC) {
+ // Initialize
+ interpState->type = CS_CAM_INTERP_MP_CUBIC;
+ interpState->waypoint = 0;
+ interpState->duration = pointCmd->duration;
+ interpState->curFrame = 0;
+ }
+
+ new_var = (f32)interpState->curFrame / pointCmd[interpState->waypoint + 1].duration;
+
+ if (interpState->waypoint < (interpState->duration - 1)) {
+ waypoints[0] = interpState->waypoint;
+ } else {
+ waypoints[0] = interpState->duration - 1;
+ }
+
+ if ((interpState->waypoint + 1) < (interpState->duration - 1)) {
+ waypoints[1] = interpState->waypoint + 1;
+ } else {
+ waypoints[1] = interpState->duration - 1;
+ }
+
+ if ((interpState->waypoint + 2) < (interpState->duration - 1)) {
+ waypoints[2] = interpState->waypoint + 2;
+ } else {
+ waypoints[2] = interpState->duration - 1;
+ }
+
+ if ((interpState->waypoint + 3) < (interpState->duration - 1)) {
+ waypoints[3] = interpState->waypoint + 3;
+ } else {
+ waypoints[3] = interpState->duration - 1;
+ }
+
+ func_801629BC(new_var, coeff);
+
+ if (camPos != NULL) {
+ camPos->x = (coeff[0] * pointCmd[waypoints[0]].pos.x) + (coeff[1] * pointCmd[waypoints[1]].pos.x) +
+ (coeff[2] * pointCmd[waypoints[2]].pos.x) + (coeff[3] * pointCmd[waypoints[3]].pos.x);
+ camPos->y = (coeff[0] * pointCmd[waypoints[0]].pos.y) + (coeff[1] * pointCmd[waypoints[1]].pos.y) +
+ (coeff[2] * pointCmd[waypoints[2]].pos.y) + (coeff[3] * pointCmd[waypoints[3]].pos.y);
+ camPos->z = (coeff[0] * pointCmd[waypoints[0]].pos.z) + (coeff[1] * pointCmd[waypoints[1]].pos.z) +
+ (coeff[2] * pointCmd[waypoints[2]].pos.z) + (coeff[3] * pointCmd[waypoints[3]].pos.z);
+ }
+
+ if (camFov != NULL) {
+ *camFov = (coeff[0] * miscCmd[waypoints[0]].fov) + (coeff[1] * miscCmd[waypoints[1]].fov) +
+ (coeff[2] * miscCmd[waypoints[2]].fov) + (coeff[3] * miscCmd[waypoints[3]].fov);
+ }
+
+ if (camRoll != NULL) {
+ s16 targetRolls[4];
+ s32 sp2C[3];
+ s32 rollDiffToTarget;
+
+ targetRolls[0] = CAM_DEG_TO_BINANG(miscCmd[waypoints[0]].roll);
+ targetRolls[1] = CAM_DEG_TO_BINANG(miscCmd[waypoints[1]].roll);
+ targetRolls[2] = CAM_DEG_TO_BINANG(miscCmd[waypoints[2]].roll);
+ targetRolls[3] = CAM_DEG_TO_BINANG(miscCmd[waypoints[3]].roll);
+
+ sp2C[0] = (s16)(targetRolls[1] - targetRolls[0]);
+ sp2C[1] = sp2C[0] + (s16)(targetRolls[2] - targetRolls[1]);
+ sp2C[2] = sp2C[1] + (s16)(targetRolls[3] - targetRolls[2]);
+
+ rollDiffToTarget = ((coeff[1] * sp2C[0]) + (coeff[2] * sp2C[1]) + (coeff[3] * sp2C[2]));
+ *camRoll = targetRolls[0] + rollDiffToTarget;
+ }
+
+ interpState->curFrame++;
+
+ if (interpState->curFrame == pointCmd[interpState->waypoint + 1].duration) {
+ interpState->curFrame = 0;
+ interpState->waypoint++;
+ if (interpState->waypoint >= (interpState->duration - 3)) {
+ interpState->type = CS_CAM_INTERP_OFF;
+ return interpState->duration;
+ }
+ }
+ return 0;
+}
+
+static f32 sKnots[38];
+
+// Only used by unused CutsceneCamera_Interp_Unused
+void func_80162FF8(s16 arg0) {
+ f32 val = 0.0f;
+ s32 i;
+
+ sKnots[0] = 0.0f;
+ sKnots[1] = 0.0f;
+ sKnots[2] = 0.0f;
+
+ for (i = 3; i < arg0; i++) {
+ if (i == 3) {
+ val += 0.9f;
+ } else if ((i == 4) || (i == (arg0 - 1))) {
+ val += 0.6f;
+ } else {
+ val += 0.3f;
+ }
+
+ sKnots[i] = val;
+ }
+
+ val += 0.9f;
+ sKnots[i++] = val;
+ sKnots[i++] = val;
+ sKnots[i++] = val;
+}
+
+#define FUNC_801631DC_ORDER 3
+
+// Only used by unused CutsceneCamera_Interp_Unused
+void func_801631DC(f32 progress, s32 arg2, f32* coeff) {
+ f32 coeffTemp[4][4];
+ s32 i;
+ s32 j;
+ s32 k;
+
+ for (i = 0; i < FUNC_801631DC_ORDER + 1; i++) {
+ for (j = 0; j < FUNC_801631DC_ORDER + 1; j++) {
+ coeffTemp[i][j] = 0.0f;
+ }
+ }
+
+ coeffTemp[0][FUNC_801631DC_ORDER - 1] = 1.0f;
+
+ for (i = 1; i < FUNC_801631DC_ORDER; i++) {
+ for (j = arg2 - i, k = (FUNC_801631DC_ORDER - 1) - i; j <= arg2; j++, k++) {
+ if (sKnots[j + i] != sKnots[j]) {
+ coeffTemp[i][k] = ((progress - sKnots[j]) / (sKnots[j + i] - sKnots[j])) * coeffTemp[i - 1][k];
+ } else {
+ coeffTemp[i][k] = 0.0f;
+ }
+
+ if (sKnots[j + i + 1] != sKnots[j + 1]) {
+ coeffTemp[i][k] +=
+ ((sKnots[j + i + 1] - progress) / (sKnots[j + i + 1] - sKnots[j + 1])) * coeffTemp[i - 1][k + 1];
+ }
+ }
+ }
+ for (j = 0; j < FUNC_801631DC_ORDER; j++) {
+ coeff[j] = coeffTemp[FUNC_801631DC_ORDER - 1][j];
+ }
+}
+
+s16 CutsceneCamera_Interp_Unused(Vec3f* camPos, f32* camFov, s16* camRoll, CsCmdCamPoint* pointCmd,
+ CsCmdCamMisc* miscCmd, CutsceneCameraInterp* interpState) {
+ s32 index;
+ f32 coeff[3];
+
+ if (interpState->type != CS_CAM_INTERP_MP_CUBIC) {
+ // Initialize
+ interpState->type = CS_CAM_INTERP_MP_CUBIC;
+ interpState->waypoint = 0;
+ interpState->duration = pointCmd->duration;
+ func_80162FF8(interpState->duration);
+ interpState->curFrame = 0;
+ }
+
+ index = interpState->waypoint + 2;
+ func_801631DC(F32_LERPIMP(sKnots[index], sKnots[index + 1],
+ (f32)interpState->curFrame / miscCmd[interpState->waypoint].unused0),
+ index, coeff);
+
+ if (camPos != NULL) {
+ camPos->x = (coeff[0] * pointCmd[interpState->waypoint].pos.x) +
+ (coeff[1] * pointCmd[interpState->waypoint + 1].pos.x) +
+ (coeff[2] * pointCmd[interpState->waypoint + 2].pos.x);
+ camPos->y = (coeff[0] * pointCmd[interpState->waypoint].pos.y) +
+ (coeff[1] * pointCmd[interpState->waypoint + 1].pos.y) +
+ (coeff[2] * pointCmd[interpState->waypoint + 2].pos.y);
+ camPos->z = (coeff[0] * pointCmd[interpState->waypoint].pos.z) +
+ (coeff[1] * pointCmd[interpState->waypoint + 1].pos.z) +
+ (coeff[2] * pointCmd[interpState->waypoint + 2].pos.z);
+ }
+
+ if (camFov != NULL) {
+ *camFov = (coeff[0] * miscCmd[interpState->waypoint].fov) +
+ (coeff[1] * miscCmd[interpState->waypoint + 1].fov) +
+ (coeff[2] * miscCmd[interpState->waypoint + 2].fov);
+ }
+
+ if (camRoll != NULL) {
+ *camRoll = CAM_DEG_TO_BINANG((coeff[0] * miscCmd[interpState->waypoint].roll) +
+ (coeff[1] * miscCmd[interpState->waypoint + 1].roll) +
+ (coeff[2] * miscCmd[interpState->waypoint + 2].roll));
+ }
+
+ interpState->curFrame++;
+
+ if (interpState->curFrame >= miscCmd[interpState->waypoint].unused0) {
+ interpState->waypoint++;
+ interpState->curFrame = 0;
+ if (interpState->waypoint >= (interpState->duration - 2)) {
+ interpState->type = CS_CAM_INTERP_OFF;
+ return interpState->duration;
+ }
+ }
+ return 0;
+}
+
+f32 func_80163660(Actor* actor) {
+ if (actor->category != ACTORCAT_PLAYER) {
+ return 38.0f - (actor->focus.pos.y - actor->world.pos.y);
+ }
+
+ switch (((Player*)actor)->transformation) {
+ case PLAYER_FORM_DEKU:
+ return -8.0f;
+
+ case PLAYER_FORM_GORON:
+ return 23.0f;
+
+ case PLAYER_FORM_ZORA:
+ return 27.0f;
+
+ case PLAYER_FORM_FIERCE_DEITY:
+ return 17.0f;
+
+ default:
+ return 0.0f;
+ }
+}
diff --git a/src/code/z_kankyo.c b/src/code/z_kankyo.c
index 6dda9090d..0326dcd24 100644
--- a/src/code/z_kankyo.c
+++ b/src/code/z_kankyo.c
@@ -2,6 +2,13 @@
#include "z64light.h"
#include "z64math.h"
+typedef enum {
+ /* 0x00 */ LIGHTNING_BOLT_START,
+ /* 0x01 */ LIGHTNING_BOLT_WAIT,
+ /* 0x02 */ LIGHTNING_BOLT_DRAW,
+ /* 0xFF */ LIGHTNING_BOLT_INACTIVE = 0xFF
+} LightningBoltState;
+
typedef struct {
/* 0x00 */ u8 state;
/* 0x04 */ Vec3f offset;
@@ -56,6 +63,8 @@ Gfx* sSkyboxStarsDList;
#include "global.h"
#include "sys_cfb.h"
#include "objects/gameplay_keep/gameplay_keep.h"
+#include "objects/gameplay_field_keep/gameplay_field_keep.h"
+#include "overlays/kaleido_scope/ovl_kaleido_scope/z_kaleido_scope.h"
// Data
f32 sSandstormLerpScale = 0.0f;
@@ -64,7 +73,7 @@ s32 sEnvSkyboxNumStars = 0;
u16 sTimeJump = 0;
u8 sSandstormColorIndex = 0;
u8 sNextSandstormColorIndex = 0;
-u8 D_801BDBA8 = 0;
+u8 D_801BDBA8 = false;
u8 D_801BDBAC = 0;
u8 gWeatherMode = WEATHER_MODE_CLEAR;
u8 gLightConfigAfterUnderwater = 0;
@@ -474,6 +483,12 @@ SkyboxFile sNormalSkyFiles[] = {
},
};
+void Environment_UpdatePostmanEvents(PlayState* play);
+void Environment_UpdateRain(PlayState* play);
+void Environment_UpdateTimeBasedSequence(PlayState* play);
+u8 func_800FE5D0(struct PlayState* play);
+void Environment_JumpForwardInTime(void);
+
void Environment_GraphCallback(GraphicsContext* gfxCtx, void* arg) {
PlayState* play = (PlayState*)arg;
@@ -487,8 +502,8 @@ typedef struct {
} ZBufValConversionEntry; // size = 0x8
ZBufValConversionEntry sZBufValConversionTable[1 << 3] = {
- { 6, 0x00000 }, { 5, 0x20000 }, { 4, 0x30000 }, { 3, 0x38000 },
- { 2, 0x3C000 }, { 1, 0x3E000 }, { 0, 0x3F000 }, { 0, 0x3F800 },
+ { 6, 0x0000 << 3 }, { 5, 0x4000 << 3 }, { 4, 0x6000 << 3 }, { 3, 0x7000 << 3 },
+ { 2, 0x7800 << 3 }, { 1, 0x7C00 << 3 }, { 0, 0x7E00 << 3 }, { 0, 0x7F00 << 3 },
};
#define ZBUFVAL_EXPONENT(v) (((v) >> 15) & 7)
@@ -510,7 +525,305 @@ s32 Environment_ZBufValToFixedPoint(s32 zBufferVal) {
return ret;
}
+#ifdef NON_MATCHING
+// The 2 constant from the `gWeatherMode` switch branch is reused when checking `CURRENT_DAY`
+// A near identical issue is present in the non-matching `func_809F24C8` from `z_boss_06.c`
+void Environment_Init(PlayState* play2, EnvironmentContext* envCtx, s32 arg2) {
+ PlayState* play = play2;
+ f32 temp_ft4;
+ u8 var_a0;
+ u8 temp;
+ s16 i;
+
+ CREG(1) = 0;
+
+ gSaveContext.sunsSongState = SUNSSONG_INACTIVE;
+
+ gSaveContext.skyboxTime = ((void)0, gSaveContext.save.time);
+
+ Environment_JumpForwardInTime();
+
+ if ((((void)0, gSaveContext.save.time) >= CLOCK_TIME(18, 0)) ||
+ (((void)0, gSaveContext.save.time) < CLOCK_TIME(6, 0))) {
+ gSaveContext.save.isNight = true;
+ } else {
+ gSaveContext.save.isNight = false;
+ }
+
+ play->state.gfxCtx->callback = Environment_GraphCallback;
+ play->state.gfxCtx->callbackArg = play;
+
+ Lights_DirectionalSetInfo(&envCtx->dirLight1, 80, 80, 80, 80, 80, 80);
+ LightContext_InsertLight(play, &play->lightCtx, &envCtx->dirLight1);
+
+ Lights_DirectionalSetInfo(&envCtx->dirLight2, 80, 80, 80, 80, 80, 80);
+ LightContext_InsertLight(play, &play->lightCtx, &envCtx->dirLight2);
+
+ envCtx->changeSkyboxState = CHANGE_SKYBOX_INACTIVE;
+ envCtx->changeSkyboxTimer = 0;
+ envCtx->changeLightEnabled = false;
+ envCtx->changeLightTimer = 0;
+ envCtx->skyboxDmaState = 0;
+ envCtx->skybox1Index = 99;
+ envCtx->skybox2Index = 99;
+
+ envCtx->glareAlpha = 0.0f;
+ envCtx->lensFlareAlphaScale = 0.0f;
+
+ if ((play->sceneId == SCENE_00KEIKOKU) && (gSaveContext.sceneLayer == 8)) {
+ gSaveContext.save.day = 1;
+ }
+
+ switch (gSaveContext.save.day) {
+ default:
+ case 0:
+ case 1:
+ envCtx->lightConfig = 0;
+ envCtx->changeLightNextConfig = 0;
+ break;
+
+ case 2:
+ envCtx->lightConfig = 3;
+ envCtx->changeLightNextConfig = 3;
+ break;
+
+ case 3:
+ envCtx->lightConfig = 4;
+ envCtx->changeLightNextConfig = 4;
+ break;
+ }
+
+ envCtx->lightSetting = 0;
+ envCtx->prevLightSetting = 0;
+ envCtx->lightBlendOverride = LIGHT_BLEND_OVERRIDE_NONE;
+ envCtx->stormRequest = STORM_REQUEST_NONE;
+ envCtx->stormState = STORM_STATE_OFF;
+ envCtx->lightningState = LIGHTNING_OFF;
+ envCtx->timeSeqState = TIMESEQ_DAY_BGM;
+ envCtx->fillScreen = 0;
+ envCtx->screenFillColor[0] = 0;
+ envCtx->screenFillColor[1] = 0;
+ envCtx->screenFillColor[2] = 0;
+ envCtx->screenFillColor[3] = 0;
+ envCtx->customSkyboxFilter = false;
+ envCtx->skyboxFilterColor[0] = 0;
+ envCtx->skyboxFilterColor[1] = 0;
+ envCtx->skyboxFilterColor[2] = 0;
+ envCtx->skyboxFilterColor[3] = 0;
+ envCtx->sandstormState = SANDSTORM_OFF;
+ envCtx->sandstormPrimA = 0;
+ envCtx->sandstormEnvA = 0;
+ envCtx->lightBlend = 1.0f;
+
+ gLightningStrike.state = LIGHTNING_STRIKE_WAIT;
+ gLightningStrike.flashRed = 0;
+ gLightningStrike.flashGreen = 0;
+ gLightningStrike.flashBlue = 0;
+
+ sLightningFlashAlpha = 0;
+
+ D_801F4F30 = 0xFF;
+ D_801F4F31 = 0;
+ D_801F4E30 = 0;
+ D_801F4F33 = 0;
+
+ gSaveContext.cutsceneTransitionControl = 0;
+
+ envCtx->adjLightSettings.ambientColor[0] = 0;
+ envCtx->adjLightSettings.ambientColor[1] = 0;
+ envCtx->adjLightSettings.ambientColor[2] = 0;
+ envCtx->adjLightSettings.light1Color[0] = 0;
+ envCtx->adjLightSettings.light1Color[1] = 0;
+ envCtx->adjLightSettings.light1Color[2] = 0;
+ envCtx->adjLightSettings.light2Color[0] = 0;
+ envCtx->adjLightSettings.light2Color[1] = 0;
+ envCtx->adjLightSettings.light2Color[2] = 0;
+ envCtx->adjLightSettings.fogColor[0] = 0;
+ envCtx->adjLightSettings.fogColor[1] = 0;
+ envCtx->adjLightSettings.fogColor[2] = 0;
+ envCtx->adjLightSettings.fogNear = 0;
+ envCtx->adjLightSettings.zFar = 0;
+
+ envCtx->sunPos.x = -(Math_SinS(((void)0, gSaveContext.save.time) - CLOCK_TIME(12, 0)) * 120.0f) * 25.0f;
+ envCtx->sunPos.y = (Math_CosS(((void)0, gSaveContext.save.time) - CLOCK_TIME(12, 0)) * 120.0f) * 25.0f;
+ envCtx->sunPos.z = (Math_CosS(((void)0, gSaveContext.save.time) - CLOCK_TIME(12, 0)) * 20.0f) * 25.0f;
+
+ envCtx->windDirection.x = 80;
+ envCtx->windDirection.y = 80;
+ envCtx->windDirection.z = 80;
+ envCtx->windSpeed = 20.0f;
+
+ envCtx->lightBlendEnabled = false;
+ envCtx->lightSettingOverride = LIGHT_SETTING_OVERRIDE_NONE;
+ envCtx->lightBlendRateOverride = LIGHT_BLENDRATE_OVERRIDE_NONE;
+
+ R_TIME_SPEED = envCtx->sceneTimeSpeed = 0;
+ R_TIME_SPEED = R_TIME_SPEED;
+ R_ENV_DISABLE_DBG = false;
+
+ CREG(64) = 0;
+
+ play->envCtx.precipitation[PRECIP_RAIN_MAX] = 0;
+ play->envCtx.precipitation[PRECIP_SNOW_CUR] = 0;
+ play->envCtx.precipitation[PRECIP_SNOW_MAX] = 0;
+ play->envCtx.precipitation[PRECIP_SOS_MAX] = 0;
+
+ D_801F4E31 = envCtx->skyboxConfig;
+
+ var_a0 = 0;
+ if (((void)0, gSaveContext.save.day) != 0) {
+ //! FAKE: temp
+ var_a0 = ((void)0, temp = gSaveContext.save.day) - 1;
+ }
+ envCtx->skyboxConfig = var_a0 + (D_801F4E31 * 3);
+ envCtx->changeSkyboxNextConfig = envCtx->skyboxConfig;
+
+ if (D_801F4E31 == 4) {
+ envCtx->skyboxConfig = 0xE;
+ envCtx->changeSkyboxNextConfig = envCtx->skyboxConfig;
+ } else if (D_801F4E31 == 5) {
+ envCtx->skyboxConfig = 0x10;
+ envCtx->changeSkyboxNextConfig = envCtx->skyboxConfig;
+ } else if (D_801F4E31 == 6) {
+ envCtx->skyboxConfig = 0x11;
+ envCtx->changeSkyboxNextConfig = envCtx->skyboxConfig;
+ } else if (D_801F4E31 == 7) {
+ envCtx->skyboxConfig = var_a0 + 0x12;
+ envCtx->changeSkyboxNextConfig = envCtx->skyboxConfig;
+ } else if (D_801F4E31 == 8) {
+ envCtx->skyboxConfig = var_a0 + 0x15;
+ envCtx->changeSkyboxNextConfig = envCtx->skyboxConfig;
+ } else if (D_801F4E31 == 9) {
+ envCtx->skyboxConfig = 0x18;
+ envCtx->changeSkyboxNextConfig = envCtx->skyboxConfig;
+ } else if (D_801F4E31 == 0xA) {
+ envCtx->skyboxConfig = var_a0 + 0x19;
+ envCtx->changeSkyboxNextConfig = envCtx->skyboxConfig;
+ }
+
+ if (var_a0 >= 3) {
+ envCtx->skyboxConfig = 0xD;
+ envCtx->changeSkyboxNextConfig = 0xD;
+ }
+
+ if (envCtx->skyboxConfig >= 0x1C) {
+ envCtx->skyboxConfig = 0;
+ envCtx->changeSkyboxNextConfig = 0;
+ }
+
+ D_801F4E74 = 0.0f;
+
+ if ((play->sceneId == SCENE_IKANA) &&
+ ((((void)0, gSaveContext.sceneLayer) == 0) || (((void)0, gSaveContext.sceneLayer) == 1)) &&
+ !CHECK_WEEKEVENTREG(WEEKEVENTREG_CLEARED_STONE_TOWER_TEMPLE)) {
+ play->skyboxId = SKYBOX_3;
+ envCtx->lightConfig = 5;
+ envCtx->changeLightNextConfig = 5;
+ D_801F4E74 = 1.0f;
+ }
+
+ if (gSaveContext.retainWeatherMode || (gSaveContext.respawnFlag != 0)) {
+ // if (gWeatherMode == WEATHER_MODE_2) {
+ // if (1) {}
+ // if (!CHECK_WEEKEVENTREG(WEEKEVENTREG_CLEARED_STONE_TOWER_TEMPLE)) {
+ // play->skyboxId = SKYBOX_3;
+ // envCtx->lightConfig = 5;
+ // envCtx->changeLightNextConfig = 5;
+ // D_801F4E74 = 1.0f;
+ // } else {
+ // gWeatherMode = WEATHER_MODE_CLEAR;
+ // }
+ // }
+
+ switch (gWeatherMode) {
+ case WEATHER_MODE_2:
+ if (!CHECK_WEEKEVENTREG(WEEKEVENTREG_CLEARED_STONE_TOWER_TEMPLE)) {
+ play->skyboxId = SKYBOX_3;
+ envCtx->lightConfig = 5;
+ envCtx->changeLightNextConfig = 5;
+ D_801F4E74 = 1.0f;
+ } else {
+ gWeatherMode = WEATHER_MODE_CLEAR;
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ play->envCtx.precipitation[PRECIP_SNOW_CUR] = 0;
+ play->envCtx.precipitation[PRECIP_SNOW_MAX] = 0;
+
+ if (gWeatherMode == WEATHER_MODE_1) {
+ if ((CURRENT_DAY == 2) && (((void)0, gSaveContext.save.time) >= CLOCK_TIME(7, 0)) &&
+ (((void)0, gSaveContext.save.time) < CLOCK_TIME(17, 30))) {
+ if (Environment_GetStormState(play) != STORM_STATE_OFF) {
+ play->envCtx.precipitation[PRECIP_RAIN_MAX] = 60;
+ }
+ play->envCtx.precipitation[PRECIP_RAIN_CUR] = 60;
+ } else {
+ gWeatherMode = WEATHER_MODE_CLEAR;
+ Environment_StopStormNatureAmbience(play);
+ }
+ } else if (gWeatherMode == WEATHER_MODE_SNOW) {
+ play->envCtx.precipitation[PRECIP_SNOW_CUR] = 128;
+ play->envCtx.precipitation[PRECIP_SNOW_MAX] = 128;
+ Environment_StopStormNatureAmbience(play);
+ } else {
+ Environment_StopStormNatureAmbience(play);
+ }
+ } else {
+ gWeatherMode = WEATHER_MODE_CLEAR;
+ Environment_StopStormNatureAmbience(play);
+ }
+
+ gInterruptSongOfStorms = false;
+ gLightConfigAfterUnderwater = 0;
+ sSkyboxIsChanging = false;
+ gSaveContext.retainWeatherMode = false;
+
+ R_ENV_LIGHT1_DIR(0) = 80;
+ R_ENV_LIGHT1_DIR(1) = 80;
+ R_ENV_LIGHT1_DIR(2) = 80;
+
+ R_ENV_LIGHT2_DIR(0) = -80;
+ R_ENV_LIGHT2_DIR(1) = -80;
+ R_ENV_LIGHT2_DIR(2) = -80;
+
+ cREG(9) = 10;
+ cREG(10) = 0;
+ cREG(11) = 0;
+ cREG(12) = 0;
+ cREG(13) = 0;
+ cREG(14) = 0;
+ D_801F4DDC = 1;
+
+ for (i = 0; i < ARRAY_COUNT(sLightningBolts); i++) {
+ sLightningBolts[i].state = LIGHTNING_BOLT_INACTIVE;
+ }
+
+ play->roomCtx.unk7A[0] = 0;
+ play->roomCtx.unk7A[1] = 0;
+
+ for (i = 0; i < ARRAY_COUNT(play->csCtx.actorCues); i++) {
+ play->csCtx.actorCues[i] = NULL;
+ }
+
+ gCustomLensFlare1On = false;
+ gCustomLensFlare2On = false;
+ Rumble_StateReset();
+
+ sEnvSkyboxNumStars = 0;
+ gSkyboxNumStars = 0;
+ D_801BDBA8 = false;
+ sEnvIsTimeStopped = false;
+ sSunPrimAlpha = 255.0f;
+
+ Environment_UpdatePostmanEvents(play);
+}
+#else
#pragma GLOBAL_ASM("asm/non_matchings/code/z_kankyo/Environment_Init.s")
+#endif
u8 Environment_SmoothStepToU8(u8* pvalue, u8 target, u8 scale, u8 step, u8 minStep) {
s16 stepSize = 0;
@@ -667,11 +980,170 @@ Color_RGBA8 sSkyboxEnvColors[] = {
{ 60, 0, 10, 0 }, { 75, 45, 0, 0 }, { 20, 20, 89, 0 }, { 0, 7, 17, 0 },
};
-#pragma GLOBAL_ASM("asm/non_matchings/code/z_kankyo/Environment_UpdateSkybox.s")
+void Environment_UpdateSkybox(u8 skyboxId, EnvironmentContext* envCtx, SkyboxContext* skyboxCtx) {
+ size_t size;
+ s32 i;
+ u8 skybox1Index = 255;
+ u8 skybox2Index = 255;
+ u8 color1Index = 255;
+ u8 color2Index = 255;
+ u8 skyboxBlend = 0;
+ f32 colorWeight = 0.0f;
-#pragma GLOBAL_ASM("asm/non_matchings/code/z_kankyo/Environment_EnableUnderwaterLights.s")
+ if (envCtx->skyboxConfig >= 0x1C) {
+ envCtx->skyboxConfig = 0;
+ }
-#pragma GLOBAL_ASM("asm/non_matchings/code/z_kankyo/Environment_DisableUnderwaterLights.s")
+ if ((skyboxId == SKYBOX_NORMAL_SKY) || ((skyboxId == SKYBOX_3) && (D_801F4E74 < 1.0f))) {
+ if (envCtx->skyboxDisabled) {
+ return;
+ }
+
+ for (i = 0; i < ARRAY_COUNT(sTimeBasedSkyboxConfigs[envCtx->skyboxConfig]); i++) {
+ if ((gSaveContext.skyboxTime >= sTimeBasedSkyboxConfigs[envCtx->skyboxConfig][i].startTime) &&
+ ((gSaveContext.skyboxTime < sTimeBasedSkyboxConfigs[envCtx->skyboxConfig][i].endTime) ||
+ (sTimeBasedSkyboxConfigs[envCtx->skyboxConfig][i].endTime == (CLOCK_TIME(24, 0) - 1)))) {
+
+ skybox1Index = sTimeBasedSkyboxConfigs[envCtx->skyboxConfig][i].skybox1Index;
+ skybox2Index = sTimeBasedSkyboxConfigs[envCtx->skyboxConfig][i].skybox2Index;
+ if (skybox1Index == skybox2Index) {
+ sSkyboxIsChanging = false;
+ } else {
+ sSkyboxIsChanging = true;
+ }
+
+ if (sSkyboxIsChanging) {
+ skyboxBlend = Environment_LerpWeight(sTimeBasedSkyboxConfigs[envCtx->skyboxConfig][i].endTime,
+ sTimeBasedSkyboxConfigs[envCtx->skyboxConfig][i].startTime,
+ ((void)0, gSaveContext.skyboxTime)) *
+ 255.0f;
+ } else {
+ skyboxBlend = Environment_LerpWeight(sTimeBasedSkyboxConfigs[envCtx->skyboxConfig][i].endTime,
+ sTimeBasedSkyboxConfigs[envCtx->skyboxConfig][i].startTime,
+ ((void)0, gSaveContext.skyboxTime)) *
+ 255.0f;
+
+ if (skyboxBlend < 128) {
+ skyboxBlend = 255;
+ } else {
+ skyboxBlend = 0;
+ }
+
+ if ((envCtx->changeSkyboxState != CHANGE_SKYBOX_INACTIVE) &&
+ (envCtx->changeSkyboxState < CHANGE_SKYBOX_ACTIVE)) {
+ envCtx->changeSkyboxState++;
+ skyboxBlend = 0;
+ }
+ }
+ color1Index = sTimeBasedSkyboxConfigs[envCtx->skyboxConfig][i].color1Index;
+ color2Index = sTimeBasedSkyboxConfigs[envCtx->skyboxConfig][i].color2Index;
+ colorWeight = Environment_LerpWeight(sTimeBasedSkyboxConfigs[envCtx->skyboxConfig][i].endTime,
+ sTimeBasedSkyboxConfigs[envCtx->skyboxConfig][i].startTime,
+ gSaveContext.skyboxTime);
+ break;
+ }
+ }
+
+ if (CREG(64) != 0) {
+ skybox1Index = skybox2Index = CREG(71);
+ } else {
+ CREG(71) = skybox1Index;
+ }
+
+ if (envCtx->changeSkyboxState >= CHANGE_SKYBOX_ACTIVE) {
+ skybox1Index = sTimeBasedSkyboxConfigs[envCtx->skyboxConfig][i].skybox1Index;
+ skybox2Index = sTimeBasedSkyboxConfigs[envCtx->changeSkyboxNextConfig][i].skybox2Index;
+ color1Index = sTimeBasedSkyboxConfigs[envCtx->skyboxConfig][i].color1Index;
+ color2Index = sTimeBasedSkyboxConfigs[envCtx->changeSkyboxNextConfig][i].color2Index;
+ skyboxBlend = ((envCtx->changeDuration - (f32)envCtx->changeSkyboxTimer) / envCtx->changeDuration) * 255.0f;
+ colorWeight = (envCtx->changeDuration - (f32)envCtx->changeSkyboxTimer) / envCtx->changeDuration;
+ envCtx->changeSkyboxTimer--;
+ if (envCtx->changeSkyboxTimer <= 0) {
+ envCtx->changeSkyboxState = CHANGE_SKYBOX_INACTIVE;
+ envCtx->skyboxConfig = envCtx->changeSkyboxNextConfig;
+ }
+ }
+
+ if ((envCtx->skybox1Index != skybox1Index) && (envCtx->skyboxDmaState == SKYBOX_DMA_INACTIVE)) {
+ envCtx->skyboxDmaState = SKYBOX_DMA_TEXTURE1_START;
+ size = sNormalSkyFiles[skybox1Index].file.vromEnd - sNormalSkyFiles[skybox1Index].file.vromStart;
+ osCreateMesgQueue(&envCtx->loadQueue, envCtx->loadMsg, ARRAY_COUNT(envCtx->loadMsg));
+ DmaMgr_SendRequestImpl(&envCtx->dmaRequest, skyboxCtx->staticSegments[0],
+ sNormalSkyFiles[skybox1Index].file.vromStart, size, 0, &envCtx->loadQueue, NULL);
+ envCtx->skybox1Index = skybox1Index;
+ }
+
+ if ((envCtx->skybox2Index != skybox2Index) && (envCtx->skyboxDmaState == SKYBOX_DMA_INACTIVE)) {
+ envCtx->skyboxDmaState = SKYBOX_DMA_TEXTURE2_START;
+ size = sNormalSkyFiles[skybox2Index].file.vromEnd - sNormalSkyFiles[skybox2Index].file.vromStart;
+ osCreateMesgQueue(&envCtx->loadQueue, envCtx->loadMsg, ARRAY_COUNT(envCtx->loadMsg));
+ DmaMgr_SendRequestImpl(&envCtx->dmaRequest, skyboxCtx->staticSegments[1],
+ sNormalSkyFiles[skybox2Index].file.vromStart, size, 0, &envCtx->loadQueue, NULL);
+ envCtx->skybox2Index = skybox2Index;
+ }
+
+ if ((envCtx->skyboxDmaState == SKYBOX_DMA_TEXTURE1_START) ||
+ (envCtx->skyboxDmaState == SKYBOX_DMA_TEXTURE2_START)) {
+ if (osRecvMesg(&envCtx->loadQueue, NULL, 0) == 0) {
+ envCtx->skyboxDmaState = SKYBOX_DMA_INACTIVE;
+ }
+ }
+
+ envCtx->skyboxBlend = skyboxBlend;
+ Skybox_SetColors(skyboxCtx,
+ LERPIMP_ALT(sSkyboxPrimColors[color1Index].r, sSkyboxPrimColors[color2Index].r, colorWeight),
+ LERPIMP_ALT(sSkyboxPrimColors[color1Index].g, sSkyboxPrimColors[color2Index].g, colorWeight),
+ LERPIMP_ALT(sSkyboxPrimColors[color1Index].b, sSkyboxPrimColors[color2Index].b, colorWeight),
+ LERPIMP_ALT(sSkyboxEnvColors[color1Index].r, sSkyboxEnvColors[color2Index].r, colorWeight),
+ LERPIMP_ALT(sSkyboxEnvColors[color1Index].g, sSkyboxEnvColors[color2Index].g, colorWeight),
+ LERPIMP_ALT(sSkyboxEnvColors[color1Index].b, sSkyboxEnvColors[color2Index].b, colorWeight));
+ }
+}
+
+void Environment_EnableUnderwaterLights(PlayState* play, s32 waterLightsIndex) {
+ if (waterLightsIndex == 0x1F) {
+ waterLightsIndex = 0;
+ }
+
+ if (play->envCtx.lightMode == LIGHT_MODE_TIME) {
+ if (!D_801F4F31) {
+ D_801F4F31 = true;
+ gLightConfigAfterUnderwater = play->envCtx.changeLightNextConfig;
+ if (play->envCtx.lightConfig != waterLightsIndex) {
+ play->envCtx.lightConfig = waterLightsIndex;
+ play->envCtx.changeLightNextConfig = waterLightsIndex;
+ }
+ } else if ((play->envCtx.lightConfig != waterLightsIndex) && (play->envCtx.changeLightTimer == 0)) {
+ play->envCtx.changeLightEnabled = true;
+ play->envCtx.changeLightNextConfig = waterLightsIndex;
+ play->envCtx.changeDuration = 20;
+ play->envCtx.changeLightTimer = play->envCtx.changeDuration;
+ }
+ } else if (play->envCtx.lightSettingOverride == LIGHT_SETTING_OVERRIDE_NONE) {
+ if (!D_801F4F31) {
+ D_801F4F31 = true;
+ gLightConfigAfterUnderwater = play->envCtx.lightSetting;
+ }
+ play->envCtx.lightBlendEnabled = false; // instantly switch to water lights
+ play->envCtx.lightSettingOverride = waterLightsIndex;
+ }
+}
+
+void Environment_DisableUnderwaterLights(PlayState* play) {
+ if (play->envCtx.lightMode == LIGHT_MODE_TIME) {
+ if (D_801F4F31) {
+ D_801F4F31 = false;
+ play->envCtx.lightConfig = gLightConfigAfterUnderwater;
+ play->envCtx.changeLightNextConfig = gLightConfigAfterUnderwater;
+ }
+ } else if (D_801F4F31) {
+ D_801F4F31 = false;
+ play->envCtx.lightSetting = gLightConfigAfterUnderwater;
+ play->envCtx.lightBlendEnabled = false; // instantly switch to previous lights
+ play->envCtx.lightSettingOverride = LIGHT_SETTING_OVERRIDE_NONE;
+ play->envCtx.lightBlend = 1.0f;
+ }
+}
void Environment_WipeRumbleRequests(void) {
if ((gSaveContext.gameMode != GAMEMODE_NORMAL) && (gSaveContext.gameMode != GAMEMODE_END_CREDITS)) {
@@ -679,15 +1151,118 @@ void Environment_WipeRumbleRequests(void) {
}
}
-#pragma GLOBAL_ASM("asm/non_matchings/code/z_kankyo/Environment_UpdateSkyboxRotY.s")
+void Environment_UpdateSkyboxRotY(PlayState* play) {
+ if ((play->pauseCtx.state == PAUSE_STATE_OFF) && (play->pauseCtx.debugEditor == DEBUG_EDITOR_NONE) &&
+ ((play->skyboxId == SKYBOX_NORMAL_SKY) || (play->skyboxId == SKYBOX_3))) {
+ play->skyboxCtx.rot.y -= R_TIME_SPEED * 1.0e-4f;
+ }
+}
-#pragma GLOBAL_ASM("asm/non_matchings/code/z_kankyo/Environment_UpdateNextDayTime.s")
+void Environment_UpdateNextDayTime(void) {
+ if ((gSaveContext.nextDayTime >= 0xFF00) && (gSaveContext.nextDayTime != NEXT_TIME_NONE)) {
+ gSaveContext.nextDayTime -= 0x10;
-#pragma GLOBAL_ASM("asm/non_matchings/code/z_kankyo/Environment_UpdateTime.s")
+ // nextDayTime is used as both a time of day value and a timer to delay sfx when changing days.
+ // When Sun's Song is played, nextDayTime is set to 0x8000 or 0 for day and night respectively.
+ // These values will actually get used as a time of day value.
+ // After this, nextDayTime is assigned magic values of 0xFFFE or 0xFFFD for day and night respectively.
+ // From here, 0x10 is decremented from nextDayTime until it reaches either 0xFF0E or 0xFF0D, effectively
+ // delaying the chicken crow or dog howl sfx by 15 frames when loading the new area.
-#pragma GLOBAL_ASM("asm/non_matchings/code/z_kankyo/func_800F6CEC.s")
+ if (gSaveContext.nextDayTime == (NEXT_TIME_DAY_SET - (15 * 0x10))) {
+ Audio_PlaySfx(NA_SE_EV_CHICKEN_CRY_M);
+ gSaveContext.nextDayTime = NEXT_TIME_NONE;
+ } else if (gSaveContext.nextDayTime == (NEXT_TIME_NIGHT_SET - (15 * 0x10))) {
+ Audio_PlaySfx_2(NA_SE_EV_DOG_CRY_EVENING);
+ gSaveContext.nextDayTime = NEXT_TIME_NONE;
+ }
+ }
+}
-#pragma GLOBAL_ASM("asm/non_matchings/code/z_kankyo/func_800F6EA4.s")
+void Environment_UpdateTime(PlayState* play, EnvironmentContext* envCtx, PauseContext* pauseCtx, MessageContext* msgCtx,
+ GameOverContext* gameOverCtx) {
+ u16 time;
+
+ if (!sEnvIsTimeStopped && (pauseCtx->state == PAUSE_STATE_OFF) && (gameOverCtx->state == GAMEOVER_INACTIVE)) {
+ if ((msgCtx->msgMode == MSGMODE_NONE) || (msgCtx->currentTextId == 0xF7) || (msgCtx->currentTextId == 0x20D2) ||
+ (msgCtx->currentTextId == 0x140C) ||
+ ((msgCtx->currentTextId >= 0x100) && (msgCtx->currentTextId <= 0x200)) ||
+ (gSaveContext.gameMode == GAMEMODE_END_CREDITS)) {
+ if (!FrameAdvance_IsEnabled(&play->state) &&
+ ((play->transitionMode == TRANS_MODE_OFF) || (gSaveContext.gameMode != GAMEMODE_NORMAL))) {
+ if (play->transitionTrigger == TRANS_TRIGGER_OFF) {
+ if ((CutsceneManager_GetCurrentCsId() == CS_ID_NONE) && !Play_InCsMode(play)) {
+ gSaveContext.save.time = ((void)0, gSaveContext.save.time) + (u16)R_TIME_SPEED;
+ if (R_TIME_SPEED != 0) {
+ gSaveContext.save.time =
+ ((void)0, gSaveContext.save.time) + (u16)((void)0, gSaveContext.save.timeSpeedOffset);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if ((((void)0, gSaveContext.skyboxTime) >= CLOCK_TIME(6, 0)) ||
+ (((void)0, gSaveContext.save.time) < CLOCK_TIME(6, 0)) ||
+ (((void)0, gSaveContext.save.time) >= (CLOCK_TIME(6, 0) + 0x10))) {
+ gSaveContext.skyboxTime = gSaveContext.save.time;
+ }
+
+ time = ((void)0, gSaveContext.save.time);
+
+ if ((time >= CLOCK_TIME(18, 0)) || (time < CLOCK_TIME(6, 0))) {
+ gSaveContext.save.isNight = true;
+ } else {
+ gSaveContext.save.isNight = false;
+ }
+}
+
+void func_800F6CEC(PlayState* play, u8 arg1, AdjLightSettings* adjLightSettings, EnvLightSettings* lightSettings) {
+ s32 phi_t1;
+ s32 temp_v1_2;
+ s32 temp_v1 = (arg1 % 4);
+
+ if ((((void)0, gSaveContext.save.day) >= 2) && (arg1 >= 4) && (arg1 < 8)) {
+ temp_v1_2 = (((void)0, gSaveContext.save.day) + 1) * 4;
+ for (phi_t1 = 0; phi_t1 != 3; phi_t1++) {
+ adjLightSettings->ambientColor[phi_t1] =
+ lightSettings[temp_v1_2 + temp_v1].ambientColor[phi_t1] - lightSettings[temp_v1].ambientColor[phi_t1];
+ adjLightSettings->light1Color[phi_t1] =
+ lightSettings[temp_v1_2 + temp_v1].light1Color[phi_t1] - lightSettings[temp_v1].light1Color[phi_t1];
+ adjLightSettings->light2Color[phi_t1] =
+ lightSettings[temp_v1_2 + temp_v1].light2Color[phi_t1] - lightSettings[temp_v1].light2Color[phi_t1];
+ adjLightSettings->fogColor[phi_t1] =
+ lightSettings[temp_v1_2 + temp_v1].fogColor[phi_t1] - lightSettings[temp_v1].fogColor[phi_t1];
+ }
+ adjLightSettings->fogNear =
+ lightSettings[temp_v1_2 + temp_v1].blendRateAndFogNear - lightSettings[temp_v1].blendRateAndFogNear;
+ }
+
+ if ((arg1 >= 4) && (arg1 < 8) && (gWeatherMode == WEATHER_MODE_1)) {
+ adjLightSettings->ambientColor[0] = -50;
+ adjLightSettings->ambientColor[1] = -100;
+ adjLightSettings->ambientColor[2] = -100;
+ adjLightSettings->light1Color[0] = -100;
+ adjLightSettings->light1Color[1] = -100;
+ adjLightSettings->light1Color[2] = -100;
+ adjLightSettings->light2Color[0] = -100;
+ adjLightSettings->light2Color[1] = -100;
+ adjLightSettings->light2Color[2] = -100;
+
+ temp_v1 = arg1;
+ adjLightSettings->fogColor[0] = -lightSettings[temp_v1].fogColor[0] + 30;
+ adjLightSettings->fogColor[1] = -lightSettings[temp_v1].fogColor[1] + 30;
+ adjLightSettings->fogColor[2] = -lightSettings[temp_v1].fogColor[2] + 45;
+ }
+}
+
+u8 Environment_LerpColor(f32 to, f32 from, f32 lerp) {
+ to = CLAMP(to, 0.0f, 255.0f);
+ from = CLAMP(from, 0.0f, 255.0f);
+
+ return ((to - from) * lerp) + from;
+}
s32 Environment_IsSceneUpsideDown(PlayState* play) {
s32 ret = false;
@@ -698,12 +1273,399 @@ s32 Environment_IsSceneUpsideDown(PlayState* play) {
return ret;
}
+#ifdef NON_MATCHING
+// Something is happening with the indexing of:
+// `lightSettingsList[(s32)envCtx->lightSetting]` and `lightSettingsList[(s32)envCtx->prevLightSetting]`
+void Environment_UpdateLights(PlayState* play, EnvironmentContext* envCtx, LightContext* lightCtx) {
+ EnvLightSettings* lightSettingsList;
+ f32 var_fs3;
+ s32 i;
+ s32 j;
+ f32 temp_fv0;
+ s32 adjustment;
+ u16 temp_s0_2;
+ u16 var_v0;
+ AdjLightSettings spA4[4];
+
+ var_fs3 = 0.0f;
+ __osMemset(spA4, 0, sizeof(AdjLightSettings) * ARRAY_COUNT(spA4));
+ lightSettingsList = play->envCtx.lightSettingsList;
+
+ if ((envCtx->lightSettingOverride != LIGHT_SETTING_OVERRIDE_NONE) &&
+ (envCtx->lightBlendOverride != LIGHT_BLEND_OVERRIDE_FULL_CONTROL) &&
+ (envCtx->lightSetting != envCtx->lightSettingOverride) && (envCtx->lightBlend >= 1.0f) &&
+ (envCtx->lightSettingOverride <= LIGHT_SETTING_MAX)) {
+
+ envCtx->lightBlend = 0.0f;
+ envCtx->prevLightSetting = envCtx->lightSetting;
+ envCtx->lightSetting = envCtx->lightSettingOverride;
+ }
+
+ if (envCtx->lightSettingOverride != LIGHT_SETTING_OVERRIDE_FULL_CONTROL) {
+ if ((envCtx->lightMode == LIGHT_MODE_TIME) && (envCtx->lightSettingOverride == LIGHT_SETTING_OVERRIDE_NONE)) {
+ for (i = 0; i < ARRAY_COUNT(sTimeBasedLightConfigs[envCtx->lightConfig]); i++) {
+ f32 arg0;
+ f32 arg1;
+ u16 startTime;
+ u16 endTime;
+ u8 sp97;
+ u8 sp96;
+ u8 sp95;
+ u8 sp94;
+
+ startTime = sTimeBasedLightConfigs[envCtx->lightConfig][i].startTime;
+ endTime = sTimeBasedLightConfigs[envCtx->lightConfig][i].endTime;
+
+ sp97 = sTimeBasedLightConfigs[envCtx->lightConfig][i].lightSetting;
+ sp95 = sTimeBasedLightConfigs[envCtx->lightConfig][i].nextLightSetting;
+ sp96 = sTimeBasedLightConfigs[envCtx->changeLightNextConfig][i].lightSetting;
+ sp94 = sTimeBasedLightConfigs[envCtx->changeLightNextConfig][i].nextLightSetting;
+
+ if ((gSaveContext.skyboxTime >= startTime) &&
+ ((gSaveContext.skyboxTime < endTime) || (endTime == 0xFFFF))) {
+ u8 blend8[2]; // sp90
+ s16 blend16[2]; // sp8C
+
+ func_800F6CEC(play, sp97, &spA4[0], lightSettingsList);
+ func_800F6CEC(play, sp95, &spA4[1], lightSettingsList);
+ func_800F6CEC(play, sp96, &spA4[2], lightSettingsList);
+ func_800F6CEC(play, sp94, &spA4[3], lightSettingsList);
+
+ if ((sp94 >= envCtx->numLightSettings) && !D_801BDBA8) {
+ D_801BDBA8 = true;
+ }
+
+ if ((sp97 >= envCtx->numLightSettings) || (sp95 >= envCtx->numLightSettings) ||
+ (sp96 >= envCtx->numLightSettings) || (sp94 >= envCtx->numLightSettings)) {
+ sp97 = 0;
+ sp95 = 0;
+ sp96 = 0;
+ sp94 = 0;
+ }
+
+ temp_fv0 = Environment_LerpWeight(endTime, startTime, ((void)0, gSaveContext.skyboxTime));
+
+ sSandstormColorIndex = sp97 & 3;
+ sNextSandstormColorIndex = sp95 & 3;
+ sSandstormLerpScale = temp_fv0;
+
+ if (envCtx->changeLightEnabled) {
+ var_fs3 = ((f32)envCtx->changeDuration - envCtx->changeLightTimer) / envCtx->changeDuration;
+ envCtx->changeLightTimer--;
+
+ if (envCtx->changeLightTimer <= 0) {
+ envCtx->changeLightEnabled = false;
+ envCtx->lightConfig = envCtx->changeLightNextConfig;
+ }
+ }
+
+ for (j = 0; j < 3; j++) {
+ arg0 = lightSettingsList[(s32)sp95].ambientColor[j] + spA4[1].ambientColor[j];
+ arg1 = lightSettingsList[(s32)sp97].ambientColor[j] + spA4[0].ambientColor[j];
+ blend8[0] = Environment_LerpColor(arg0, arg1, temp_fv0);
+
+ arg0 = lightSettingsList[(s32)sp94].ambientColor[j] + spA4[3].ambientColor[j];
+ arg1 = lightSettingsList[(s32)sp96].ambientColor[j] + spA4[2].ambientColor[j];
+ blend8[1] = Environment_LerpColor(arg0, arg1, temp_fv0);
+
+ envCtx->lightSettings.ambientColor[j] = LERPIMP_ALT(blend8[0], blend8[1], var_fs3);
+ }
+
+ if (Environment_IsSceneUpsideDown(play)) {
+ var_v0 = ((void)0, gSaveContext.save.time) + CLOCK_TIME(12, 0);
+ } else {
+ var_v0 = ((void)0, gSaveContext.save.time);
+ }
+ temp_s0_2 = var_v0 - CLOCK_TIME(12, 0);
+
+ envCtx->lightSettings.light1Dir[0] = -(Math_SinS(temp_s0_2) * 120.0f);
+ envCtx->lightSettings.light1Dir[1] = Math_CosS(temp_s0_2) * 120.0f;
+ envCtx->lightSettings.light1Dir[2] = Math_CosS(temp_s0_2) * 20.0f;
+
+ envCtx->lightSettings.light2Dir[0] = -envCtx->lightSettings.light1Dir[0];
+ envCtx->lightSettings.light2Dir[1] = -envCtx->lightSettings.light1Dir[1];
+ envCtx->lightSettings.light2Dir[2] = -envCtx->lightSettings.light1Dir[2];
+
+ for (j = 0; j < 3; j++) {
+ arg0 = lightSettingsList[(s32)sp95].light1Color[j] + spA4[1].light1Color[j];
+ arg1 = lightSettingsList[(s32)sp97].light1Color[j] + spA4[0].light1Color[j];
+ blend8[0] = Environment_LerpColor(arg0, arg1, temp_fv0);
+
+ arg0 = lightSettingsList[(s32)sp94].light1Color[j] + spA4[3].light1Color[j];
+ arg1 = lightSettingsList[(s32)sp96].light1Color[j] + spA4[2].light1Color[j];
+ blend8[1] = Environment_LerpColor(arg0, arg1, temp_fv0);
+
+ envCtx->lightSettings.light1Color[j] = LERPIMP_ALT(blend8[0], blend8[1], var_fs3);
+
+ arg0 = lightSettingsList[(s32)sp95].light2Color[j] + spA4[1].light2Color[j];
+ arg1 = lightSettingsList[(s32)sp97].light2Color[j] + spA4[0].light2Color[j];
+ blend8[0] = Environment_LerpColor(arg0, arg1, temp_fv0);
+
+ arg0 = lightSettingsList[(s32)sp94].light2Color[j] + spA4[3].light2Color[j];
+ arg1 = lightSettingsList[(s32)sp96].light2Color[j] + spA4[2].light2Color[j];
+ blend8[1] = Environment_LerpColor(arg0, arg1, temp_fv0);
+
+ envCtx->lightSettings.light2Color[j] = LERPIMP_ALT(blend8[0], blend8[1], var_fs3);
+ }
+
+ for (j = 0; j < 3; j++) {
+ arg0 = lightSettingsList[(s32)sp95].fogColor[j] + spA4[1].fogColor[j];
+ arg1 = lightSettingsList[(s32)sp97].fogColor[j] + spA4[0].fogColor[j];
+ blend8[0] = Environment_LerpColor(arg0, arg1, temp_fv0);
+
+ arg0 = lightSettingsList[(s32)sp94].fogColor[j] + spA4[3].fogColor[j];
+ arg1 = lightSettingsList[(s32)sp96].fogColor[j] + spA4[2].fogColor[j];
+ blend8[1] = Environment_LerpColor(arg0, arg1, temp_fv0);
+
+ envCtx->lightSettings.fogColor[j] = LERPIMP_ALT(blend8[0], blend8[1], var_fs3);
+ }
+
+ blend16[0] = S16_LERP(ENV_LIGHT_SETTINGS_FOG_NEAR(lightSettingsList[(s32)sp97].blendRateAndFogNear),
+ ENV_LIGHT_SETTINGS_FOG_NEAR(lightSettingsList[(s32)sp95].blendRateAndFogNear),
+ temp_fv0);
+ blend16[1] = S16_LERP(ENV_LIGHT_SETTINGS_FOG_NEAR(lightSettingsList[(s32)sp96].blendRateAndFogNear),
+ ENV_LIGHT_SETTINGS_FOG_NEAR(lightSettingsList[(s32)sp94].blendRateAndFogNear),
+ temp_fv0);
+ envCtx->lightSettings.fogNear = LERPIMP_ALT(blend16[0], blend16[1], var_fs3);
+
+ blend16[0] =
+ S16_LERP(lightSettingsList[(s32)sp97].zFar, lightSettingsList[(s32)sp95].zFar, temp_fv0);
+ blend16[1] =
+ S16_LERP(lightSettingsList[(s32)sp96].zFar, lightSettingsList[(s32)sp94].zFar, temp_fv0);
+ envCtx->lightSettings.zFar = LERPIMP_ALT(blend16[0], blend16[1], var_fs3);
+
+ break;
+ }
+ }
+ } else {
+ if ((envCtx->lightSetting >= envCtx->numLightSettings) && !D_801BDBA8) {
+ D_801BDBA8 = true;
+ }
+
+ if (!envCtx->lightBlendEnabled) {
+ for (i = 0; i < 3; i++) {
+ envCtx->lightSettings.ambientColor[i] =
+ lightSettingsList[(s32)envCtx->lightSetting].ambientColor[i];
+ envCtx->lightSettings.light1Dir[i] = lightSettingsList[(s32)envCtx->lightSetting].light1Dir[i];
+ envCtx->lightSettings.light1Color[i] = lightSettingsList[(s32)envCtx->lightSetting].light1Color[i];
+ envCtx->lightSettings.light2Dir[i] = lightSettingsList[(s32)envCtx->lightSetting].light2Dir[i];
+ envCtx->lightSettings.light2Color[i] = lightSettingsList[(s32)envCtx->lightSetting].light2Color[i];
+ envCtx->lightSettings.fogColor[i] = lightSettingsList[(s32)envCtx->lightSetting].fogColor[i];
+ }
+ envCtx->lightSettings.fogNear =
+ ENV_LIGHT_SETTINGS_FOG_NEAR(lightSettingsList[(s32)envCtx->lightSetting].blendRateAndFogNear);
+ envCtx->lightSettings.zFar = lightSettingsList[(s32)envCtx->lightSetting].zFar;
+ envCtx->lightBlend = 1.0f;
+ } else {
+ u8 var_v0_3;
+
+ //! FAKE:
+ if (1) {}
+
+ var_v0_3 =
+ ENV_LIGHT_SETTINGS_BLEND_RATE_U8(lightSettingsList[(s32)envCtx->lightSetting].blendRateAndFogNear);
+
+ if (var_v0_3 == 0) {
+ var_v0_3++;
+ }
+
+ if (envCtx->lightBlendRateOverride != LIGHT_BLENDRATE_OVERRIDE_NONE) {
+ var_v0_3 = envCtx->lightBlendRateOverride;
+ }
+
+ if (envCtx->lightBlendOverride == LIGHT_BLEND_OVERRIDE_NONE) {
+ envCtx->lightBlend += var_v0_3 / 255.0f;
+ }
+
+ if (envCtx->lightBlend > 1.0f) {
+ envCtx->lightBlend = 1.0f;
+ }
+
+ for (i = 0; i < 3; i++) {
+ envCtx->lightSettings.ambientColor[i] =
+ LERPIMP_ALT(lightSettingsList[(s32)envCtx->prevLightSetting].ambientColor[i],
+ lightSettingsList[(s32)envCtx->lightSetting].ambientColor[i], envCtx->lightBlend);
+ envCtx->lightSettings.light1Dir[i] =
+ LERPIMP_ALT(lightSettingsList[(s32)envCtx->prevLightSetting].light1Dir[i],
+ lightSettingsList[(s32)envCtx->lightSetting].light1Dir[i], envCtx->lightBlend);
+ envCtx->lightSettings.light1Color[i] =
+ LERPIMP_ALT(lightSettingsList[(s32)envCtx->prevLightSetting].light1Color[i],
+ lightSettingsList[(s32)envCtx->lightSetting].light1Color[i], envCtx->lightBlend);
+ envCtx->lightSettings.light2Dir[i] =
+ LERPIMP_ALT(lightSettingsList[(s32)envCtx->prevLightSetting].light2Dir[i],
+ lightSettingsList[(s32)envCtx->lightSetting].light2Dir[i], envCtx->lightBlend);
+ envCtx->lightSettings.light2Color[i] =
+ LERPIMP_ALT(lightSettingsList[(s32)envCtx->prevLightSetting].light2Color[i],
+ lightSettingsList[(s32)envCtx->lightSetting].light2Color[i], envCtx->lightBlend);
+ envCtx->lightSettings.fogColor[i] =
+ LERPIMP_ALT(lightSettingsList[(s32)envCtx->prevLightSetting].fogColor[i],
+ lightSettingsList[(s32)envCtx->lightSetting].fogColor[i], envCtx->lightBlend);
+ }
+
+ envCtx->lightSettings.fogNear = LERPIMP_ALT(
+ ENV_LIGHT_SETTINGS_FOG_NEAR(lightSettingsList[(s32)envCtx->prevLightSetting].blendRateAndFogNear),
+ ENV_LIGHT_SETTINGS_FOG_NEAR(lightSettingsList[(s32)envCtx->lightSetting].blendRateAndFogNear),
+ envCtx->lightBlend);
+
+ envCtx->lightSettings.zFar =
+ LERPIMP_ALT(lightSettingsList[(s32)envCtx->prevLightSetting].zFar,
+ lightSettingsList[(s32)envCtx->lightSetting].zFar, envCtx->lightBlend);
+ }
+ }
+ }
+
+ envCtx->lightBlendEnabled = true;
+
+ for (i = 0; i < 3; i++) {
+ if ((s16)(envCtx->lightSettings.ambientColor[i] + envCtx->adjLightSettings.ambientColor[i]) > 255) {
+ lightCtx->ambientColor[i] = 255;
+ } else if ((s16)(envCtx->lightSettings.ambientColor[i] + envCtx->adjLightSettings.ambientColor[i]) < 0) {
+ lightCtx->ambientColor[i] = 0;
+ } else {
+ lightCtx->ambientColor[i] =
+ (s16)(envCtx->lightSettings.ambientColor[i] + envCtx->adjLightSettings.ambientColor[i]);
+ }
+
+ if ((s16)(envCtx->lightSettings.light1Color[i] + envCtx->adjLightSettings.light1Color[i]) > 255) {
+ envCtx->dirLight1.params.dir.color[i] = 255;
+ } else if ((s16)(envCtx->lightSettings.light1Color[i] + envCtx->adjLightSettings.light1Color[i]) < 0) {
+ envCtx->dirLight1.params.dir.color[i] = 0;
+ } else {
+ envCtx->dirLight1.params.dir.color[i] =
+ (s16)(envCtx->lightSettings.light1Color[i] + envCtx->adjLightSettings.light1Color[i]);
+ }
+
+ if ((s16)(envCtx->lightSettings.light2Color[i] + envCtx->adjLightSettings.light2Color[i]) > 255) {
+ envCtx->dirLight2.params.dir.color[i] = 255;
+ } else if ((s16)(envCtx->lightSettings.light2Color[i] + envCtx->adjLightSettings.light2Color[i]) < 0) {
+ envCtx->dirLight2.params.dir.color[i] = 0;
+ } else {
+ envCtx->dirLight2.params.dir.color[i] =
+ (s16)(envCtx->lightSettings.light2Color[i] + envCtx->adjLightSettings.light2Color[i]);
+ }
+
+ if ((s16)(envCtx->lightSettings.fogColor[i] + envCtx->adjLightSettings.fogColor[i]) > 255) {
+ lightCtx->fogColor[i] = 255;
+ } else if ((s16)(envCtx->lightSettings.fogColor[i] + envCtx->adjLightSettings.fogColor[i]) < 0) {
+ lightCtx->fogColor[i] = 0;
+ } else {
+ lightCtx->fogColor[i] = (s16)(envCtx->lightSettings.fogColor[i] + envCtx->adjLightSettings.fogColor[i]);
+ }
+ }
+
+ // Set both directional light directions
+ envCtx->dirLight1.params.dir.x = envCtx->lightSettings.light1Dir[0];
+ envCtx->dirLight1.params.dir.y = envCtx->lightSettings.light1Dir[1];
+ envCtx->dirLight1.params.dir.z = envCtx->lightSettings.light1Dir[2];
+
+ envCtx->dirLight2.params.dir.x = envCtx->lightSettings.light2Dir[0];
+ envCtx->dirLight2.params.dir.y = envCtx->lightSettings.light2Dir[1];
+ envCtx->dirLight2.params.dir.z = envCtx->lightSettings.light2Dir[2];
+
+ // Adjust fog near and far if necessary
+ adjustment = envCtx->lightSettings.fogNear + envCtx->adjLightSettings.fogNear;
+
+ if (adjustment <= ENV_FOGNEAR_MAX) {
+ lightCtx->fogNear = adjustment;
+ } else {
+ lightCtx->fogNear = ENV_FOGNEAR_MAX;
+ }
+
+ adjustment = envCtx->lightSettings.zFar + envCtx->adjLightSettings.zFar;
+
+ if (adjustment <= ENV_ZFAR_MAX) {
+ lightCtx->zFar = adjustment;
+ } else {
+ lightCtx->zFar = ENV_ZFAR_MAX;
+ }
+
+ if ((envCtx->dirLight1.params.dir.x == 0) && (envCtx->dirLight1.params.dir.y == 0) &&
+ (envCtx->dirLight1.params.dir.z == 0)) {
+ envCtx->dirLight1.params.dir.x = 1;
+ }
+ if ((envCtx->dirLight2.params.dir.x == 0) && (envCtx->dirLight2.params.dir.y == 0) &&
+ (envCtx->dirLight2.params.dir.z == 0)) {
+ envCtx->dirLight2.params.dir.x = 1;
+ }
+}
+#else
+void Environment_UpdateLights(PlayState* play, EnvironmentContext* envCtx, LightContext* lightCtx);
#pragma GLOBAL_ASM("asm/non_matchings/code/z_kankyo/Environment_UpdateLights.s")
+#endif
-#pragma GLOBAL_ASM("asm/non_matchings/code/z_kankyo/Environment_UpdateSun.s")
+void Environment_UpdateSun(PlayState* play) {
+ f32 temp_f0;
+ s16 temp_a0;
+ u16 phi_v0;
-void func_800F88C4(u16 weekEventFlag);
-#pragma GLOBAL_ASM("asm/non_matchings/code/z_kankyo/func_800F88C4.s")
+ if (!play->envCtx.sunDisabled) {
+ if (play->envCtx.precipitation[PRECIP_RAIN_CUR] != 0) {
+ Math_SmoothStepToF(&sSunPrimAlpha, 0.0f, 0.5f, 4.0f, 0.01f);
+ } else {
+ Math_SmoothStepToF(&sSunPrimAlpha, 255.0f, 0.5f, 4.0f, 0.01f);
+ }
+
+ if (Environment_IsSceneUpsideDown(play)) {
+ phi_v0 = ((void)0, gSaveContext.save.time) + CLOCK_TIME(12, 0);
+ } else {
+ phi_v0 = ((void)0, gSaveContext.save.time);
+ }
+
+ temp_a0 = phi_v0 - CLOCK_TIME(12, 0);
+
+ if (play->csCtx.state != CS_STATE_IDLE) {
+ Math_SmoothStepToF(&play->envCtx.sunPos.x, -(Math_SinS(temp_a0) * 120.0f) * 25.0f, 1.0f, 0.8f, 0.8f);
+ Math_SmoothStepToF(&play->envCtx.sunPos.y, Math_CosS(temp_a0) * 120.0f * 25.0f, 1.0f, 0.8f, 0.8f);
+ //! @bug This should be z.
+ Math_SmoothStepToF(&play->envCtx.sunPos.y, Math_CosS(temp_a0) * 20.0f * 25.0f, 1.0f, 0.8f, 0.8f);
+ } else {
+ play->envCtx.sunPos.x = -(Math_SinS(temp_a0) * 120.0f) * 25.0f;
+ play->envCtx.sunPos.y = Math_CosS(temp_a0) * 120.0f * 25.0f;
+ play->envCtx.sunPos.z = Math_CosS(temp_a0) * 20.0f * 25.0f;
+ }
+
+ temp_f0 = play->envCtx.sunPos.y / 25.0f;
+ if (Environment_IsSceneUpsideDown(play)) {
+ temp_f0 = -temp_f0;
+ }
+
+ sSunEnvAlpha = temp_f0 / 80.0f * 255.0f;
+ if (sSunEnvAlpha < 0.0f) {
+ sSunEnvAlpha = 0.0f;
+ }
+ if (sSunEnvAlpha > 255.0f) {
+ sSunEnvAlpha = 255.0f;
+ }
+ sSunEnvAlpha = 255.0f - sSunEnvAlpha;
+
+ sSunColor = temp_f0 / 80.0f;
+ if (sSunColor < 0.0f) {
+ sSunColor = 0.0f;
+ }
+ if (sSunColor > 1.0f) {
+ sSunColor = 1.0f;
+ }
+
+ sSunScale = (2.0f * sSunColor) + 12.0f;
+ }
+}
+
+void func_800F88C4(u16 weekEventFlag) {
+ if (!CHECK_WEEKEVENTREG(WEEKEVENTREG_28_08) && !CHECK_WEEKEVENTREG(WEEKEVENTREG_28_10) &&
+ CHECK_WEEKEVENTREG(weekEventFlag)) {
+ if (CHECK_WEEKEVENTREG(WEEKEVENTREG_91_04)) {
+ SET_WEEKEVENTREG(WEEKEVENTREG_28_08);
+ SET_WEEKEVENTREG(WEEKEVENTREG_90_08);
+ SET_WEEKEVENTREG(WEEKEVENTREG_51_02);
+ }
+
+ if (CHECK_WEEKEVENTREG(WEEKEVENTREG_91_08)) {
+ SET_WEEKEVENTREG(WEEKEVENTREG_28_10);
+ SET_WEEKEVENTREG(WEEKEVENTREG_90_08);
+ SET_WEEKEVENTREG(WEEKEVENTREG_51_02);
+ }
+ }
+}
void func_800F8970(void) {
if (!CHECK_WEEKEVENTREG(WEEKEVENTREG_27_40) && (SCHEDULE_TIME_NOW >= SCHEDULE_TIME(9, 31))) {
@@ -732,13 +1694,122 @@ void func_800F8970(void) {
}
}
-#pragma GLOBAL_ASM("asm/non_matchings/code/z_kankyo/Environment_UpdatePostmanEvents.s")
+void Environment_UpdatePostmanEvents(PlayState* play) {
+ u16 temp_a2_2;
+ u8 eventInfBits;
+ u8 day = EVENTINF_GET_7_E0;
-#pragma GLOBAL_ASM("asm/non_matchings/code/z_kankyo/Environment_Update.s")
+ if (day != (u8)((void)0, gSaveContext.save.day)) {
+ day = (u8)((void)0, gSaveContext.save.day);
-#pragma GLOBAL_ASM("asm/non_matchings/code/z_kankyo/Environment_DrawSun.s")
+ EVENTINF_SET_7_E0(day, eventInfBits);
-#pragma GLOBAL_ASM("asm/non_matchings/code/z_kankyo/Environment_DrawSunLensFlare.s")
+ SET_WEEKEVENTREG(WEEKEVENTREG_27_40);
+ SET_WEEKEVENTREG(WEEKEVENTREG_27_80);
+ SET_WEEKEVENTREG(WEEKEVENTREG_28_01);
+ SET_WEEKEVENTREG(WEEKEVENTREG_28_02);
+ SET_WEEKEVENTREG(WEEKEVENTREG_28_04);
+
+ func_800F88C4(WEEKEVENTREG_DEPOSITED_LETTER_TO_KAFEI_SOUTH_UPPER_CLOCKTOWN);
+ func_800F88C4(WEEKEVENTREG_DEPOSITED_LETTER_TO_KAFEI_NORTH_CLOCKTOWN);
+ func_800F88C4(WEEKEVENTREG_DEPOSITED_LETTER_TO_KAFEI_EAST_UPPER_CLOCKTOWN);
+ func_800F88C4(WEEKEVENTREG_DEPOSITED_LETTER_TO_KAFEI_EAST_LOWER_CLOCKTOWN);
+ func_800F88C4(WEEKEVENTREG_DEPOSITED_LETTER_TO_KAFEI_SOUTH_LOWER_CLOCKTOWN);
+
+ CLEAR_WEEKEVENTREG(WEEKEVENTREG_27_40);
+ CLEAR_WEEKEVENTREG(WEEKEVENTREG_27_80);
+ CLEAR_WEEKEVENTREG(WEEKEVENTREG_28_01);
+ CLEAR_WEEKEVENTREG(WEEKEVENTREG_28_02);
+ CLEAR_WEEKEVENTREG(WEEKEVENTREG_28_04);
+ }
+
+ if ((SCHEDULE_TIME_NOW < SCHEDULE_TIME(9, 0)) || (SCHEDULE_TIME_NOW > SCHEDULE_TIME(12, 0))) {
+ CLEAR_WEEKEVENTREG(WEEKEVENTREG_90_08);
+ }
+
+ func_800F8970();
+
+ if (!CHECK_WEEKEVENTREG(WEEKEVENTREG_RECEIVED_ROOM_KEY) && !CHECK_WEEKEVENTREG(WEEKEVENTREG_55_02)) {
+ if (((void)0, gSaveContext.save.day) >= 2) {
+ SET_WEEKEVENTREG(WEEKEVENTREG_55_02);
+ } else if ((((void)0, gSaveContext.save.day) == 1) && (SCHEDULE_TIME_NOW >= SCHEDULE_TIME(16, 30))) {
+ SET_WEEKEVENTREG(WEEKEVENTREG_55_02);
+ }
+ }
+
+ if (!CHECK_WEEKEVENTREG(WEEKEVENTREG_90_01)) {
+ temp_a2_2 = ((void)0, gSaveContext.save.time) - D_801F4E78;
+ if (CHECK_WEEKEVENTREG(WEEKEVENTREG_89_40) && ((u16)SCHEDULE_TIME_NOW >= (u16)SCHEDULE_TIME(5, 0))) {
+ SET_WEEKEVENTREG(WEEKEVENTREG_90_01);
+ } else if (CHECK_WEEKEVENTREG(WEEKEVENTREG_89_08) && (temp_a2_2 >= CLOCK_TIME(0, 23))) {
+ SET_WEEKEVENTREG(WEEKEVENTREG_89_40);
+ D_801F4E78 = 0;
+ } else if (CHECK_WEEKEVENTREG(WEEKEVENTREG_85_80) && (temp_a2_2 >= (CLOCK_TIME(1, 39) - 1))) {
+ SET_WEEKEVENTREG(WEEKEVENTREG_89_08);
+ D_801F4E78 = gSaveContext.save.time;
+ } else if (CHECK_WEEKEVENTREG(WEEKEVENTREG_86_01) && (temp_a2_2 >= (CLOCK_TIME(1, 9) - 1))) {
+ SET_WEEKEVENTREG(WEEKEVENTREG_85_80);
+ }
+ }
+}
+
+void Environment_Update(PlayState* play, EnvironmentContext* envCtx, LightContext* lightCtx, PauseContext* pauseCtx,
+ MessageContext* msgCtx, GameOverContext* gameOverCtx, GraphicsContext* gfxCtx) {
+ D_801BDBC0 = 0;
+ D_801BDBC4 = 0;
+
+ Environment_WipeRumbleRequests();
+
+ if (pauseCtx->state == PAUSE_STATE_OFF) {
+ Environment_UpdateSkyboxRotY(play);
+ Environment_UpdateRain(play);
+ Environment_UpdateTimeBasedSequence(play);
+ Environment_UpdateNextDayTime();
+ Environment_UpdateTime(play, envCtx, pauseCtx, msgCtx, gameOverCtx);
+ Environment_UpdateSun(play);
+ Environment_UpdateLights(play, envCtx, lightCtx);
+ Environment_UpdatePostmanEvents(play);
+ }
+}
+
+void Environment_DrawSun(PlayState* play) {
+ if (!play->envCtx.sunDisabled) {
+ OPEN_DISPS(play->state.gfxCtx);
+
+ if ((play->envCtx.sunPos.y > -800.0f) || Environment_IsSceneUpsideDown(play)) {
+ Matrix_Translate(play->view.eye.x + play->envCtx.sunPos.x, play->view.eye.y + play->envCtx.sunPos.y,
+ play->view.eye.z + play->envCtx.sunPos.z, MTXMODE_NEW);
+ if (((void)0, gSaveContext.save.time) < CLOCK_TIME(12, 0)) {
+ gDPSetPrimColor(POLY_OPA_DISP++, 0, 0, 255, (u8)((u8)(sSunColor * 52.0f) + 203),
+ (u8)((u8)(sSunColor * 19.0f) + 181), (u8)sSunPrimAlpha);
+ gDPSetEnvColor(POLY_OPA_DISP++, (u8)(-(u8)(sSunColor * 5.0f) + 175),
+ (u8)((u8)(sSunColor * 135.0f) + 120), (u8)((u8)(sSunColor * 100.0f) + 100),
+ sSunEnvAlpha);
+ } else {
+ gDPSetPrimColor(POLY_OPA_DISP++, 0, 0, 255, (u8)((u8)(sSunColor * 75.0f) + 180),
+ (u8)((u8)(sSunColor * 100.0f) + 100), (u8)sSunPrimAlpha);
+ gDPSetEnvColor(POLY_OPA_DISP++, 180, (u8)(sSunColor * 255.0f), (u8)(sSunColor * 200.0f), sSunEnvAlpha);
+ }
+ Matrix_Scale(sSunScale, sSunScale, sSunScale, MTXMODE_APPLY);
+ gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(play->state.gfxCtx), G_MTX_LOAD);
+ Gfx_SetupDL54_Opa(play->state.gfxCtx);
+ gSPDisplayList(POLY_OPA_DISP++, gSunDL);
+ }
+
+ CLOSE_DISPS(play->state.gfxCtx);
+ }
+}
+
+void Environment_DrawSunLensFlare(PlayState* play, EnvironmentContext* envCtx, View* view, GraphicsContext* gfxCtx,
+ Vec3f vec) {
+ if ((play->envCtx.precipitation[PRECIP_RAIN_CUR] == 0) &&
+ !(GET_ACTIVE_CAM(play)->stateFlags & CAM_STATE_UNDERWATER) && (play->skyboxId == SKYBOX_NORMAL_SKY)) {
+ f32 v0 = Math_CosS(((void)0, gSaveContext.save.time) - CLOCK_TIME(12, 0));
+
+ Environment_DrawLensFlare(play, &play->envCtx, &play->view, play->state.gfxCtx, vec, 370.0f, v0 * 120.0f, 0x190,
+ true);
+ }
+}
typedef enum {
/* 0 */ LENS_FLARE_CIRCLE0,
@@ -769,46 +1840,871 @@ static u32 sLensFlareTypes[] = {
LENS_FLARE_CIRCLE1, LENS_FLARE_CIRCLE1, LENS_FLARE_CIRCLE1, LENS_FLARE_CIRCLE1, LENS_FLARE_CIRCLE1,
};
-#pragma GLOBAL_ASM("asm/non_matchings/code/z_kankyo/Environment_DrawLensFlare.s")
+void Environment_DrawLensFlare(PlayState* play, EnvironmentContext* envCtx, View* view, GraphicsContext* gfxCtx,
+ Vec3f pos, f32 scale, f32 colorIntensity, s16 glareStrength, u8 isSun) {
+ s16 i;
+ f32 tempX;
+ f32 tempY;
+ f32 tempZ;
+ f32 lookDirX;
+ f32 lookDirY;
+ f32 lookDirZ;
+ f32 tempX2;
+ f32 tempY2;
+ f32 tempZ2;
+ f32 posDirX;
+ f32 posDirY;
+ f32 posDirZ;
+ f32 length;
+ f32 dist;
+ f32 halfPosX;
+ f32 halfPosY;
+ f32 halfPosZ;
+ f32 cosAngle;
+ s32 pad;
+ f32 lensFlareAlphaScaleTarget;
+ f32 alpha;
+ f32 adjScale;
+ f32 fogInfluence;
+ Vec3f screenPos;
+ u8 isOffScreen = false;
+ f32 weight = 0.0f;
+ f32 glareAlphaScale;
+ Mtx* mtx;
-#pragma GLOBAL_ASM("asm/non_matchings/code/z_kankyo/Environment_RandCentered.s")
+ OPEN_DISPS(gfxCtx);
-#pragma GLOBAL_ASM("asm/non_matchings/code/z_kankyo/Environment_DrawRainImpl.s")
+ dist = Math3D_Distance(&pos, &view->eye) / 12.0f;
-#pragma GLOBAL_ASM("asm/non_matchings/code/z_kankyo/Environment_DrawRain.s")
+ // compute a unit vector in the look direction
+ tempX = view->at.x - view->eye.x;
+ tempY = view->at.y - view->eye.y;
+ tempZ = view->at.z - view->eye.z;
-#pragma GLOBAL_ASM("asm/non_matchings/code/z_kankyo/Environment_ChangeLightSetting.s")
+ length = sqrtf(SQ(tempX) + SQ(tempY) + SQ(tempZ));
-#pragma GLOBAL_ASM("asm/non_matchings/code/z_kankyo/Environment_DrawSkyboxFilters.s")
+ lookDirX = tempX / length;
+ lookDirY = tempY / length;
+ lookDirZ = tempZ / length;
-#pragma GLOBAL_ASM("asm/non_matchings/code/z_kankyo/Environment_DrawLightningFlash.s")
+ // compute a position along the look vector half as far as pos
+ halfPosX = view->eye.x + lookDirX * (dist * 6.0f);
+ halfPosY = view->eye.y + lookDirY * (dist * 6.0f);
+ halfPosZ = view->eye.z + lookDirZ * (dist * 6.0f);
-#pragma GLOBAL_ASM("asm/non_matchings/code/z_kankyo/Environment_UpdateLightningStrike.s")
+ // compute a unit vector in the direction from halfPos to pos
+ tempX2 = pos.x - halfPosX;
+ tempY2 = pos.y - halfPosY;
+ tempZ2 = pos.z - halfPosZ;
-#pragma GLOBAL_ASM("asm/non_matchings/code/z_kankyo/Environment_AddLightningBolts.s")
+ length = sqrtf(SQ(tempX2) + SQ(tempY2) + SQ(tempZ2));
+
+ posDirX = tempX2 / length;
+ posDirY = tempY2 / length;
+ posDirZ = tempZ2 / length;
+
+ // compute the cosine of the angle between lookDir and posDir
+ cosAngle = (lookDirX * posDirX + lookDirY * posDirY + lookDirZ * posDirZ) /
+ sqrtf((SQ(lookDirX) + SQ(lookDirY) + SQ(lookDirZ)) * (SQ(posDirX) + SQ(posDirY) + SQ(posDirZ)));
+
+ lensFlareAlphaScaleTarget = cosAngle * 3.5f;
+ lensFlareAlphaScaleTarget = CLAMP_MAX(lensFlareAlphaScaleTarget, 1.0f);
+
+ if (!isSun) {
+ lensFlareAlphaScaleTarget = cosAngle;
+ }
+
+ if (cosAngle < 0.0f) {
+ // don't draw lens flare
+ } else {
+ if (isSun) {
+ Play_GetScreenPos(play, &pos, &screenPos);
+ sSunDepthTestX = screenPos.x;
+ sSunDepthTestY = screenPos.y - 5.0f;
+ if (sSunDepthTestY < 0) {
+ sSunDepthTestY = 0;
+ }
+ if (sSunScreenDepth != GPACK_ZDZ(G_MAXFBZ, 0) || screenPos.x < 0.0f || screenPos.y < 0.0f ||
+ screenPos.x > SCREEN_WIDTH || screenPos.y > SCREEN_HEIGHT) {
+ isOffScreen = true;
+ }
+ }
+
+ for (i = 0; i < ARRAY_COUNT(sLensFlareTypes); i++) {
+ Matrix_Translate(pos.x, pos.y, pos.z, MTXMODE_NEW);
+
+ if (isSun) {
+ weight = Environment_LerpWeight(60, 15, play->view.fovy);
+ }
+
+ Matrix_Translate(-posDirX * i * dist, -posDirY * i * dist, -posDirZ * i * dist, MTXMODE_APPLY);
+ adjScale = sLensFlareScales[i] * cosAngle;
+
+ if (isSun) {
+ adjScale *= 0.001f * (scale + 630.0f * weight);
+ } else {
+ adjScale *= 0.0001f * scale * (2.0f * dist);
+ }
+
+ Matrix_Scale(adjScale, adjScale, adjScale, MTXMODE_APPLY);
+
+ alpha = colorIntensity / 10.0f;
+ alpha = CLAMP_MAX(alpha, 1.0f);
+ alpha = alpha * sLensFlareAlphas[i];
+ alpha = CLAMP_MIN(alpha, 0.0f);
+
+ if (isSun) {
+ fogInfluence = (ENV_FOGNEAR_MAX - play->lightCtx.fogNear) / 50.0f;
+
+ fogInfluence = CLAMP_MAX(fogInfluence, 1.0f);
+
+ alpha *= 1.0f - fogInfluence;
+ }
+
+ if (isOffScreen == false) {
+ Math_SmoothStepToF(&envCtx->lensFlareAlphaScale, lensFlareAlphaScaleTarget, 0.5f, 0.05f, 0.001f);
+ } else {
+ Math_SmoothStepToF(&envCtx->lensFlareAlphaScale, 0.0f, 0.5f, 0.05f, 0.001f);
+ }
+
+ POLY_XLU_DISP = Gfx_SetupDL65_NoCD(POLY_XLU_DISP++);
+ gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, sLensFlareColors[i].r, sLensFlareColors[i].g, sLensFlareColors[i].b,
+ alpha * envCtx->lensFlareAlphaScale);
+ mtx = Matrix_NewMtx(gfxCtx);
+ if (mtx != NULL) {
+ gSPMatrix(POLY_XLU_DISP++, mtx, G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW);
+ gDPSetCombineLERP(POLY_XLU_DISP++, 0, 0, 0, PRIMITIVE, TEXEL0, 0, PRIMITIVE, 0, 0, 0, 0, PRIMITIVE,
+ TEXEL0, 0, PRIMITIVE, 0);
+ gDPSetAlphaDither(POLY_XLU_DISP++, G_AD_DISABLE);
+ gDPSetColorDither(POLY_XLU_DISP++, G_CD_DISABLE);
+ gSPMatrix(POLY_XLU_DISP++, &D_01000000, G_MTX_NOPUSH | G_MTX_MUL | G_MTX_MODELVIEW);
+
+ switch (sLensFlareTypes[i]) {
+ case LENS_FLARE_CIRCLE0:
+ case LENS_FLARE_CIRCLE1:
+ gSPDisplayList(POLY_XLU_DISP++, gLensFlareCircleDL);
+ break;
+
+ case LENS_FLARE_RING:
+ gSPDisplayList(POLY_XLU_DISP++, gLensFlareRingDL);
+ break;
+
+ default:
+ break;
+ }
+ }
+ }
+
+ glareAlphaScale = cosAngle - (1.5f - cosAngle);
+
+ if (glareStrength != 0) {
+ if (glareAlphaScale > 0.0f) {
+ POLY_XLU_DISP = Gfx_SetupDL57(POLY_XLU_DISP);
+ alpha = colorIntensity / 10.0f;
+ alpha = CLAMP_MAX(alpha, 1.0f);
+ alpha = alpha * glareStrength;
+ alpha = CLAMP_MIN(alpha, 0.0f);
+
+ fogInfluence = (ENV_FOGNEAR_MAX - play->lightCtx.fogNear) / 50.0f;
+
+ fogInfluence = CLAMP_MAX(fogInfluence, 1.0f);
+
+ alpha *= 1.0f - fogInfluence;
+
+ gDPSetAlphaDither(POLY_XLU_DISP++, G_AD_DISABLE);
+ gDPSetColorDither(POLY_XLU_DISP++, G_CD_DISABLE);
+
+ //! FAKE:
+ if (i) {}
+
+ if (isOffScreen == false) {
+ Math_SmoothStepToF(&envCtx->glareAlpha, alpha * glareAlphaScale, 0.5f, 50.0f, 0.1f);
+ } else {
+ Math_SmoothStepToF(&envCtx->glareAlpha, 0.0f, 0.5f, 50.0f, 0.1f);
+ }
+
+ weight = colorIntensity / 120.0f;
+ weight = CLAMP_MIN(weight, 0.0f);
+
+ gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 255, (u8)(weight * 75.0f) + 180, (u8)(weight * 155.0f) + 100,
+ (u8)envCtx->glareAlpha);
+ gSPDisplayList(POLY_XLU_DISP++, D_0E000000.clearFillRect);
+ } else {
+ envCtx->glareAlpha = 0.0f;
+ }
+ }
+ }
+
+ CLOSE_DISPS(gfxCtx);
+}
+
+f32 Environment_RandCentered(void) {
+ return Rand_ZeroOne() - 0.5f;
+}
+
+void Environment_DrawRainImpl(PlayState* play, View* view, GraphicsContext* gfxCtx) {
+ Player* player = GET_PLAYER(play);
+ s32 i;
+ u8 precip;
+ Vec3f temp;
+ f32 length;
+ f32 precipScale;
+ f32 windScale;
+ f32 spF0;
+ f32 spEC;
+ f32 spE8;
+ f32 spE4;
+ f32 spE0;
+ Vec3f spD4;
+ u8 materialApplied;
+ f32 temp_fs1;
+ f32 temp_fs2;
+ s16 pitch;
+ s16 yaw;
+ f32 scale;
+
+ if (play->envCtx.precipitation[PRECIP_SOS_MAX] != 0) {
+ precip = play->envCtx.precipitation[PRECIP_RAIN_CUR];
+ } else {
+ precipScale = func_80173B48(&play->state) / 3e7f;
+ precipScale = CLAMP(precipScale, 0.0f, 1.0f);
+ precip = play->envCtx.precipitation[PRECIP_RAIN_CUR] * precipScale;
+ if (precip < 5) {
+ precip = 5;
+ }
+ }
+
+ OPEN_DISPS(gfxCtx);
+
+ temp.x = view->at.x - view->eye.x;
+ temp.y = view->at.y - view->eye.y;
+ temp.z = view->at.z - view->eye.z;
+ length = sqrtf(SQ(temp.x) + SQ(temp.y) + SQ(temp.z));
+
+ temp_fs1 = temp.x / length;
+ temp_fs2 = temp.z / length;
+
+ spF0 = view->eye.x + (temp_fs1 * 50.0f);
+ spEC = view->eye.y + ((temp.y / length) * 120.0f);
+ spE8 = view->eye.z + (temp_fs2 * 50.0f);
+
+ spE4 = view->eye.x + (temp_fs1 * 220.0f);
+ spE0 = view->eye.z + (temp_fs2 * 220.0f);
+
+ if ((u32)precip != 0) {
+ gDPPipeSync(POLY_XLU_DISP++);
+ gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 150, 255, 255, 25);
+ POLY_XLU_DISP = Gfx_SetupDL(POLY_XLU_DISP, SETUPDL_20);
+ }
+
+ windScale = play->envCtx.windSpeed / 60.0f;
+ windScale = CLAMP(windScale, 0.0f, 1.0f);
+
+ spD4.x = play->envCtx.windDirection.x * windScale;
+ spD4.y = play->envCtx.windDirection.y + 100.0f;
+ spD4.z = play->envCtx.windDirection.z * windScale;
+
+ pitch = 0x4000 - Math_Vec3f_Pitch(&gZeroVec3f, &spD4);
+ yaw = Math_Vec3f_Yaw(&gZeroVec3f, &spD4) + 0x8000;
+
+ for (i = 0; i < precip; i++) {
+ Matrix_Translate(((Rand_ZeroOne() - 0.7f) * 100.0f) + spF0, ((Rand_ZeroOne() - 0.7f) * 100.0f) + spEC,
+ ((Rand_ZeroOne() - 0.7f) * 100.0f) + spE8, MTXMODE_NEW);
+ gSPMatrix(POLY_XLU_DISP++, &D_01000000, G_MTX_NOPUSH | G_MTX_MUL | G_MTX_MODELVIEW);
+ Matrix_RotateYS(yaw + (s16)(i << 5), MTXMODE_APPLY);
+ Matrix_RotateXS(pitch + (s16)(i << 5), MTXMODE_APPLY);
+ Matrix_Scale(0.3f, 1.0f, 0.3f, MTXMODE_APPLY);
+ gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(gfxCtx), G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW);
+ gSPDisplayList(POLY_XLU_DISP++, gFallingRainDropDL);
+ }
+
+ if (player->actor.floorHeight < view->eye.y) {
+ materialApplied = false;
+ for (i = 0; i < precip; i++) {
+ if (!materialApplied) {
+ Gfx_SetupDL25_Xlu(gfxCtx);
+ gDPSetEnvColor(POLY_XLU_DISP++, 255, 255, 255, 255);
+ gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 255, 255, 255, 100);
+ materialApplied++;
+ }
+ Matrix_Translate((Environment_RandCentered() * 220.0f) + spE4, player->actor.floorHeight + 2.0f,
+ (Environment_RandCentered() * 220.0f) + spE0, MTXMODE_NEW);
+ scale = (Rand_ZeroOne() * 0.05f) + 0.05f;
+ Matrix_Scale(scale, scale, scale, MTXMODE_APPLY);
+ gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(gfxCtx), G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW);
+ gSPDisplayList(POLY_XLU_DISP++, gEffShockwaveDL);
+ }
+ }
+
+ CLOSE_DISPS(gfxCtx);
+}
+
+void Environment_DrawRain(PlayState* play, View* view, GraphicsContext* gfxCtx) {
+ if (!(GET_ACTIVE_CAM(play)->stateFlags & CAM_STATE_UNDERWATER) &&
+ (play->envCtx.precipitation[PRECIP_SNOW_CUR] == 0)) {
+ if (play->envCtx.precipitation[PRECIP_SOS_MAX] != 0) {
+ if (play->envCtx.precipitation[PRECIP_SNOW_CUR] == 0) {
+ Environment_DrawRainImpl(play, view, gfxCtx);
+ }
+ } else if (!(GET_ACTIVE_CAM(play)->stateFlags & CAM_STATE_UNDERWATER)) {
+ if ((Environment_GetStormState(play) != STORM_STATE_OFF) &&
+ (play->envCtx.precipitation[PRECIP_SNOW_CUR] == 0)) {
+ Environment_DrawRainImpl(play, view, gfxCtx);
+ }
+ }
+ }
+}
+
+void Environment_ChangeLightSetting(PlayState* play, u8 lightSetting) {
+ if (play->envCtx.lightMode == LIGHT_MODE_TIME) {
+ if ((lightSetting == LIGHT_SETTING_MAX) || D_801F4F33) {
+ if (D_801F4F30 != 0xFF) {
+ play->envCtx.changeLightEnabled = true;
+ play->envCtx.changeLightNextConfig = D_801F4F30;
+ play->envCtx.changeDuration = 20;
+ play->envCtx.changeLightTimer = play->envCtx.changeDuration;
+ D_801F4F30 = 0xFF;
+ }
+ } else if (play->envCtx.changeLightNextConfig != lightSetting) {
+ D_801F4F30 = play->envCtx.lightConfig;
+ play->envCtx.changeLightEnabled = true;
+ play->envCtx.changeLightNextConfig = lightSetting;
+ play->envCtx.changeDuration = 20;
+ play->envCtx.changeLightTimer = play->envCtx.changeDuration;
+ }
+ } else if ((play->envCtx.lightSetting != lightSetting) && (play->envCtx.lightBlend >= 1.0f) &&
+ (play->envCtx.lightSettingOverride == LIGHT_SETTING_OVERRIDE_NONE)) {
+ if (lightSetting >= LIGHT_SETTING_MAX) {
+ lightSetting = 0;
+ }
+
+ play->envCtx.lightBlend = 0.0f;
+ play->envCtx.prevLightSetting = play->envCtx.lightSetting;
+ play->envCtx.lightSetting = lightSetting;
+ }
+}
+
+void Environment_DrawSkyboxFilters(PlayState* play) {
+ if ((((play->skyboxId != SKYBOX_NONE) && (play->lightCtx.fogNear < 980)) || (play->skyboxId >= SKYBOX_2)) &&
+ ((play->skyboxId != SKYBOX_3) || (D_801F4E74 != 0.0f))) {
+ f32 alpha;
+
+ OPEN_DISPS(play->state.gfxCtx);
+
+ Gfx_SetupDL57_Opa(play->state.gfxCtx);
+
+ alpha = (1000 - play->lightCtx.fogNear) * 0.02f;
+
+ if (play->skyboxId == SKYBOX_2) {
+ alpha = 1.0f;
+ }
+
+ if (alpha > 1.0f) {
+ alpha = 1.0f;
+ }
+
+ if (play->skyboxId != SKYBOX_3) {
+ gDPSetPrimColor(POLY_OPA_DISP++, 0, 0, play->lightCtx.fogColor[0], play->lightCtx.fogColor[1],
+ play->lightCtx.fogColor[2], 255.0f * alpha);
+ } else {
+ gDPSetPrimColor(POLY_OPA_DISP++, 0, 0, play->lightCtx.fogColor[0] + 16, play->lightCtx.fogColor[1] + 16,
+ play->lightCtx.fogColor[2] + 16, 255.0f * D_801F4E74);
+ }
+ gSPDisplayList(POLY_OPA_DISP++, D_0E000000.clearFillRect);
+
+ CLOSE_DISPS(play->state.gfxCtx);
+ }
+
+ if (play->envCtx.customSkyboxFilter) {
+ OPEN_DISPS(play->state.gfxCtx);
+
+ Gfx_SetupDL57_Opa(play->state.gfxCtx);
+ gDPSetPrimColor(POLY_OPA_DISP++, 0, 0, play->envCtx.skyboxFilterColor[0], play->envCtx.skyboxFilterColor[1],
+ play->envCtx.skyboxFilterColor[2], play->envCtx.skyboxFilterColor[3]);
+ gSPDisplayList(POLY_OPA_DISP++, D_0E000000.clearFillRect);
+
+ CLOSE_DISPS(play->state.gfxCtx);
+ }
+}
+
+void Environment_DrawLightningFlash(PlayState* play, u8 red, u8 green, u8 blue, u8 alpha) {
+ OPEN_DISPS(play->state.gfxCtx);
+
+ Gfx_SetupDL57_Opa(play->state.gfxCtx);
+ gDPSetPrimColor(POLY_OPA_DISP++, 0, 0, red, green, blue, alpha);
+ gSPDisplayList(POLY_OPA_DISP++, D_0E000000.clearFillRect);
+
+ CLOSE_DISPS(play->state.gfxCtx);
+}
+
+void Environment_UpdateLightningStrike(PlayState* play) {
+ if (play->envCtx.lightningState != LIGHTNING_OFF) {
+ switch (gLightningStrike.state) {
+ case LIGHTNING_STRIKE_WAIT:
+ // every frame theres a 10% chance of the timer advancing 10 units
+ if (Rand_ZeroOne() < 0.1f) {
+ gLightningStrike.delayTimer += 10.0f;
+ }
+
+ gLightningStrike.delayTimer += Rand_ZeroOne();
+
+ if (gLightningStrike.delayTimer > 500.0f) {
+ gLightningStrike.flashRed = 200;
+ gLightningStrike.flashGreen = 200;
+ gLightningStrike.flashBlue = 255;
+ gLightningStrike.flashAlphaTarget = 200;
+
+ gLightningStrike.delayTimer = 0.0f;
+ Environment_AddLightningBolts(
+ play, (u8)((Rand_ZeroOne() * (ARRAY_COUNT(sLightningBolts) - 0.1f)) + 1.0f));
+ sLightningFlashAlpha = 0;
+ gLightningStrike.state++;
+ }
+ break;
+
+ case LIGHTNING_STRIKE_START:
+ gLightningStrike.flashRed = 200;
+ gLightningStrike.flashGreen = 200;
+ gLightningStrike.flashBlue = 255;
+
+ play->envCtx.adjLightSettings.ambientColor[0] += 80;
+ play->envCtx.adjLightSettings.ambientColor[1] += 80;
+ play->envCtx.adjLightSettings.ambientColor[2] += 100;
+
+ sLightningFlashAlpha += 100;
+
+ if (sLightningFlashAlpha >= gLightningStrike.flashAlphaTarget) {
+ Audio_SetAmbienceChannelIO(AMBIENCE_CHANNEL_LIGHTNING, CHANNEL_IO_PORT_0, 0);
+ gLightningStrike.state++;
+ gLightningStrike.flashAlphaTarget = 0;
+ }
+ break;
+
+ case LIGHTNING_STRIKE_END:
+ if (play->envCtx.adjLightSettings.ambientColor[0] > 0) {
+ play->envCtx.adjLightSettings.ambientColor[0] -= 10;
+ play->envCtx.adjLightSettings.ambientColor[1] -= 10;
+ }
+
+ if (play->envCtx.adjLightSettings.ambientColor[2] > 0) {
+ play->envCtx.adjLightSettings.ambientColor[2] -= 10;
+ }
+
+ sLightningFlashAlpha -= 10;
+
+ if (sLightningFlashAlpha <= gLightningStrike.flashAlphaTarget) {
+ play->envCtx.adjLightSettings.ambientColor[0] = 0;
+ play->envCtx.adjLightSettings.ambientColor[1] = 0;
+ play->envCtx.adjLightSettings.ambientColor[2] = 0;
+
+ gLightningStrike.state = LIGHTNING_STRIKE_WAIT;
+
+ if (play->envCtx.lightningState == LIGHTNING_LAST) {
+ play->envCtx.lightningState = LIGHTNING_OFF;
+ }
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ if (gLightningStrike.state != LIGHTNING_STRIKE_WAIT) {
+ Environment_DrawLightningFlash(play, gLightningStrike.flashRed, gLightningStrike.flashGreen,
+ gLightningStrike.flashBlue, sLightningFlashAlpha);
+ }
+}
+
+void Environment_AddLightningBolts(PlayState* play, u8 num) {
+ s16 boltsAdded = 0;
+ s16 i;
+
+ for (i = 0; i < ARRAY_COUNT(sLightningBolts); i++) {
+ if (sLightningBolts[i].state == LIGHTNING_BOLT_INACTIVE) {
+ sLightningBolts[i].state = LIGHTNING_BOLT_START;
+ boltsAdded++;
+
+ if (boltsAdded >= num) {
+ break;
+ }
+ }
+ }
+}
static TexturePtr sLightningTextures[] = {
gEffLightning1Tex, gEffLightning2Tex, gEffLightning3Tex, gEffLightning4Tex,
gEffLightning5Tex, gEffLightning6Tex, gEffLightning7Tex, gEffLightning8Tex,
};
-#pragma GLOBAL_ASM("asm/non_matchings/code/z_kankyo/Environment_DrawLightning.s")
+/**
+ * Draw any active lightning bolt entries contained in `sLightningBolts`
+ */
+void Environment_DrawLightning(PlayState* play, s32 unused) {
+ s16 i;
+ f32 dx;
+ f32 dz;
+ f32 x;
+ f32 z;
+ s32 pad[2];
-#pragma GLOBAL_ASM("asm/non_matchings/code/z_kankyo/Environment_PlaySceneSequence.s")
+ OPEN_DISPS(play->state.gfxCtx);
-#pragma GLOBAL_ASM("asm/non_matchings/code/z_kankyo/Environment_UpdateTimeBasedSequence.s")
+ for (i = 0; i < ARRAY_COUNT(sLightningBolts); i++) {
+ switch (sLightningBolts[i].state) {
+ case LIGHTNING_BOLT_START:
+ dx = play->view.at.x - play->view.eye.x;
+ dz = play->view.at.z - play->view.eye.z;
-#pragma GLOBAL_ASM("asm/non_matchings/code/z_kankyo/Environment_DrawCustomLensFlare.s")
+ x = dx / sqrtf(SQ(dx) + SQ(dz));
+ z = dz / sqrtf(SQ(dx) + SQ(dz));
-#pragma GLOBAL_ASM("asm/non_matchings/code/z_kankyo/Environment_InitGameOverLights.s")
+ sLightningBolts[i].pos.x = play->view.eye.x + x * 9500.0f;
+ sLightningBolts[i].pos.y = Rand_ZeroOne() * 1000.0f + 4000.0f;
+ sLightningBolts[i].pos.z = play->view.eye.z + z * 9500.0f;
-#pragma GLOBAL_ASM("asm/non_matchings/code/z_kankyo/Environment_FadeInGameOverLights.s")
+ sLightningBolts[i].offset.x = (Rand_ZeroOne() - 0.5f) * 5000.0f;
+ sLightningBolts[i].offset.y = 0.0f;
+ sLightningBolts[i].offset.z = (Rand_ZeroOne() - 0.5f) * 5000.0f;
-#pragma GLOBAL_ASM("asm/non_matchings/code/z_kankyo/Environment_FadeOutGameOverLights.s")
+ sLightningBolts[i].textureIndex = 0;
+ sLightningBolts[i].pitch = (Rand_ZeroOne() - 0.5f) * 40.0f;
+ sLightningBolts[i].roll = (Rand_ZeroOne() - 0.5f) * 40.0f;
+ sLightningBolts[i].delayTimer = 3 * (i + 1);
+ sLightningBolts[i].state++;
+ break;
-#pragma GLOBAL_ASM("asm/non_matchings/code/z_kankyo/Environment_UpdateRain.s")
+ case LIGHTNING_BOLT_WAIT:
+ sLightningBolts[i].delayTimer--;
-#pragma GLOBAL_ASM("asm/non_matchings/code/z_kankyo/Environment_FillScreen.s")
+ if (sLightningBolts[i].delayTimer <= 0) {
+ sLightningBolts[i].state++;
+ }
+ break;
+
+ case LIGHTNING_BOLT_DRAW:
+ if (sLightningBolts[i].textureIndex < 7) {
+ sLightningBolts[i].textureIndex++;
+ } else {
+ sLightningBolts[i].state = LIGHTNING_BOLT_INACTIVE;
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ if (sLightningBolts[i].state == LIGHTNING_BOLT_DRAW) {
+ Matrix_Translate(sLightningBolts[i].pos.x + sLightningBolts[i].offset.x,
+ sLightningBolts[i].pos.y + sLightningBolts[i].offset.y,
+ sLightningBolts[i].pos.z + sLightningBolts[i].offset.z, MTXMODE_NEW);
+ Matrix_RotateXFApply(DEG_TO_RAD(sLightningBolts[i].pitch));
+ Matrix_RotateZF(DEG_TO_RAD(sLightningBolts[i].roll), MTXMODE_APPLY);
+ Matrix_Scale(22.0f, 100.0f, 22.0f, MTXMODE_APPLY);
+ gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 255, 255, 255, 128);
+ gDPSetEnvColor(POLY_XLU_DISP++, 0, 255, 255, 128);
+ gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(play->state.gfxCtx), G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW);
+ gSPSegment(POLY_XLU_DISP++, 0x08,
+ Lib_SegmentedToVirtual(sLightningTextures[sLightningBolts[i].textureIndex]));
+ Gfx_SetupDL61_Xlu(play->state.gfxCtx);
+ gSPMatrix(POLY_XLU_DISP++, &D_01000000, G_MTX_NOPUSH | G_MTX_MUL | G_MTX_MODELVIEW);
+ gSPDisplayList(POLY_XLU_DISP++, gEffLightningDL);
+ }
+ }
+
+ CLOSE_DISPS(play->state.gfxCtx);
+}
+
+void Environment_PlaySceneSequence(PlayState* play) {
+ u8 dayMinusOne = ((void)0, gSaveContext.save.day) - 1;
+
+ if (dayMinusOne >= 3) {
+ dayMinusOne = 0;
+ }
+
+ if (play->envCtx.timeSeqState != TIMESEQ_REQUEST) {
+ play->envCtx.timeSeqState = TIMESEQ_DISABLED;
+ if (((void)0, gSaveContext.forcedSeqId) != NA_BGM_GENERAL_SFX) {
+ play->envCtx.timeSeqState = TIMESEQ_REQUEST;
+ SEQCMD_PLAY_SEQUENCE(SEQ_PLAYER_BGM_MAIN, 0, ((void)0, gSaveContext.forcedSeqId) + SEQ_FLAG_ASYNC);
+ gSaveContext.forcedSeqId = NA_BGM_GENERAL_SFX;
+ } else if (!Environment_IsFinalHours(play) || func_800FE5D0(play) ||
+ (AudioSeq_GetActiveSeqId(SEQ_PLAYER_BGM_MAIN) != NA_BGM_FINAL_HOURS)) {
+ if (play->sequenceCtx.seqId == NA_BGM_NO_MUSIC) {
+ if (play->sequenceCtx.ambienceId == AMBIENCE_ID_13) {
+ return;
+ }
+ if (play->sequenceCtx.ambienceId != ((void)0, gSaveContext.ambienceId)) {
+ Audio_PlayAmbience(play->sequenceCtx.ambienceId);
+ }
+ } else if (play->sequenceCtx.ambienceId == AMBIENCE_ID_13) {
+ if (play->sequenceCtx.seqId != ((void)0, gSaveContext.seqId)) {
+ Audio_PlaySceneSequence(play->sequenceCtx.seqId, dayMinusOne);
+ }
+ } else if ((((void)0, gSaveContext.save.time) >= CLOCK_TIME(6, 0)) &&
+ (((void)0, gSaveContext.save.time) <= CLOCK_TIME(17, 10))) {
+ if (gSceneSeqState != SCENESEQ_DEFAULT) {
+ Audio_PlayMorningSceneSequence(play->sequenceCtx.seqId, dayMinusOne);
+ } else if ((((void)0, gSaveContext.save.time) >= CLOCK_TIME(6, 1)) &&
+ (play->sequenceCtx.seqId != ((void)0, gSaveContext.seqId))) {
+ Audio_PlaySceneSequence(play->sequenceCtx.seqId, dayMinusOne);
+ }
+ play->envCtx.timeSeqState = TIMESEQ_FADE_DAY_BGM;
+ } else {
+ if (play->sequenceCtx.ambienceId != ((void)0, gSaveContext.ambienceId)) {
+ Audio_PlayAmbience(play->sequenceCtx.ambienceId);
+ }
+ if ((((void)0, gSaveContext.save.time) > CLOCK_TIME(17, 10)) &&
+ (((void)0, gSaveContext.save.time) < CLOCK_TIME(19, 0))) {
+ play->envCtx.timeSeqState = TIMESEQ_EARLY_NIGHT_CRITTERS;
+ } else if ((((void)0, gSaveContext.save.time) > CLOCK_TIME(19, 0)) ||
+ (((void)0, gSaveContext.save.time) < CLOCK_TIME(5, 0))) {
+ play->envCtx.timeSeqState = TIMESEQ_NIGHT_CRITTERS;
+ } else {
+ play->envCtx.timeSeqState = TIMESEQ_MORNING_CRITTERS;
+ }
+ }
+ }
+ Audio_SetEnvReverb(play->roomCtx.curRoom.echo);
+ gSceneSeqState = SCENESEQ_DEFAULT;
+ }
+}
+
+void Environment_UpdateTimeBasedSequence(PlayState* play) {
+ s32 pad;
+
+ //! FAKE:
+ if (!gSaveContext.sceneLayer) {}
+
+ if ((play->csCtx.state == 0) && !(play->actorCtx.flags & ACTORCTX_FLAG_1)) {
+ switch (play->envCtx.timeSeqState) {
+ case TIMESEQ_DAY_BGM:
+ break;
+
+ case TIMESEQ_FADE_DAY_BGM:
+ if (((void)0, gSaveContext.save.time) > CLOCK_TIME(17, 10)) {
+ SEQCMD_STOP_SEQUENCE(SEQ_PLAYER_BGM_MAIN, 240);
+ play->envCtx.timeSeqState++;
+ }
+ break;
+
+ case TIMESEQ_NIGHT_BEGIN_SFX:
+ if (((void)0, gSaveContext.save.time) >= CLOCK_TIME(18, 0)) {
+ play->envCtx.timeSeqState++;
+ }
+ break;
+
+ case TIMESEQ_EARLY_NIGHT_CRITTERS:
+ if (play->envCtx.precipitation[PRECIP_RAIN_CUR] < 9) {
+ Audio_PlayAmbience(play->sequenceCtx.ambienceId);
+ Audio_SetAmbienceChannelIO(AMBIENCE_CHANNEL_CRITTER_0, 1, 1);
+ }
+ play->envCtx.timeSeqState++;
+ break;
+
+ case TIMESEQ_NIGHT_DELAY:
+ if (((void)0, gSaveContext.save.time) >= CLOCK_TIME(19, 0)) {
+ play->envCtx.timeSeqState++;
+ }
+ break;
+
+ case TIMESEQ_NIGHT_CRITTERS:
+ Audio_SetAmbienceChannelIO(AMBIENCE_CHANNEL_CRITTER_0, 1, 0);
+ Audio_SetAmbienceChannelIO(AMBIENCE_CHANNEL_CRITTER_1 << 4 | AMBIENCE_CHANNEL_CRITTER_3, 1, 1);
+ play->envCtx.timeSeqState++;
+ break;
+
+ case TIMESEQ_DAY_BEGIN_SFX:
+ if ((((void)0, gSaveContext.save.time) < CLOCK_TIME(19, 0)) &&
+ (((void)0, gSaveContext.save.time) >= CLOCK_TIME(5, 0))) {
+ play->envCtx.timeSeqState++;
+ }
+ break;
+
+ case TIMESEQ_MORNING_CRITTERS:
+ Audio_SetAmbienceChannelIO(AMBIENCE_CHANNEL_CRITTER_1 << 4 | AMBIENCE_CHANNEL_CRITTER_3, 1, 0);
+ Audio_SetAmbienceChannelIO(AMBIENCE_CHANNEL_CRITTER_4 << 4 | AMBIENCE_CHANNEL_CRITTER_5, 1, 1);
+ play->envCtx.timeSeqState++;
+ break;
+
+ case TIMESEQ_DAY_DELAY:
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ if ((play->envCtx.timeSeqState != TIMESEQ_REQUEST) && (((void)0, gSaveContext.save.day) == 3) &&
+ (((void)0, gSaveContext.save.time) < CLOCK_TIME(6, 0)) && !func_800FE5D0(play) &&
+ (play->transitionTrigger == TRANS_TRIGGER_OFF) && (play->transitionMode == TRANS_MODE_OFF) &&
+ (play->csCtx.state == 0) && ((play->sceneId != SCENE_00KEIKOKU) || (((void)0, gSaveContext.sceneLayer) != 1)) &&
+ (CutsceneManager_GetCurrentCsId() == CS_ID_NONE) &&
+ (AudioSeq_GetActiveSeqId(SEQ_PLAYER_BGM_MAIN) != NA_BGM_FINAL_HOURS) &&
+ (AudioSeq_GetActiveSeqId(SEQ_PLAYER_BGM_MAIN) != NA_BGM_SONG_OF_SOARING)) {
+ SEQCMD_STOP_SEQUENCE(SEQ_PLAYER_AMBIENCE, 0);
+ Audio_PlaySceneSequence(NA_BGM_FINAL_HOURS, 3 - 1);
+ }
+}
+
+void Environment_DrawCustomLensFlare(PlayState* play) {
+ Vec3f pos;
+
+ if (gCustomLensFlare1On) {
+ pos.x = gCustomLensFlare1Pos.x;
+ pos.y = gCustomLensFlare1Pos.y;
+ pos.z = gCustomLensFlare1Pos.z;
+ Environment_DrawLensFlare(play, &play->envCtx, &play->view, play->state.gfxCtx, pos, D_801F4E44, D_801F4E48,
+ D_801F4E4C, false);
+ }
+
+ if (gCustomLensFlare2On) {
+ pos.x = gCustomLensFlare2Pos.x;
+ pos.y = gCustomLensFlare2Pos.y;
+ pos.z = gCustomLensFlare2Pos.z;
+ Environment_DrawLensFlare(play, &play->envCtx, &play->view, play->state.gfxCtx, pos, D_801F4E5C, D_801F4E60,
+ D_801F4E64, false);
+ }
+}
+
+void Environment_InitGameOverLights(PlayState* play) {
+ s32 pad;
+ Player* player = GET_PLAYER(play);
+
+ sGameOverLightsIntensity = 0;
+
+ Lights_PointNoGlowSetInfo(&sNGameOverLightInfo, player->actor.world.pos.x - 10.0f,
+ player->actor.world.pos.y + 10.0f, player->actor.world.pos.z - 10.0f, 0, 0, 0, 255);
+ sNGameOverLightNode = LightContext_InsertLight(play, &play->lightCtx, &sNGameOverLightInfo);
+
+ Lights_PointNoGlowSetInfo(&sSGameOverLightInfo, player->actor.world.pos.x + 10.0f,
+ player->actor.world.pos.y + 10.0f, player->actor.world.pos.z + 10.0f, 0, 0, 0, 255);
+
+ sSGameOverLightNode = LightContext_InsertLight(play, &play->lightCtx, &sSGameOverLightInfo);
+}
+
+void Environment_FadeInGameOverLights(PlayState* play) {
+ Player* player = GET_PLAYER(play);
+ s16 i;
+
+ Lights_PointNoGlowSetInfo(&sNGameOverLightInfo, player->actor.world.pos.x - 10.0f,
+ player->actor.world.pos.y + 10.0f, player->actor.world.pos.z - 10.0f,
+ sGameOverLightsIntensity, sGameOverLightsIntensity, sGameOverLightsIntensity, 255);
+ Lights_PointNoGlowSetInfo(&sSGameOverLightInfo, player->actor.world.pos.x + 10.0f,
+ player->actor.world.pos.y + 10.0f, player->actor.world.pos.z + 10.0f,
+ sGameOverLightsIntensity, sGameOverLightsIntensity, sGameOverLightsIntensity, 255);
+
+ if (sGameOverLightsIntensity < 254) {
+ sGameOverLightsIntensity += 2;
+ }
+
+ if (Play_CamIsNotFixed(&play->state)) {
+ for (i = 0; i < 3; i++) {
+ if (play->envCtx.adjLightSettings.ambientColor[i] > -255) {
+ play->envCtx.adjLightSettings.ambientColor[i] -= 12;
+ play->envCtx.adjLightSettings.light1Color[i] -= 12;
+ }
+ play->envCtx.adjLightSettings.fogColor[i] = -255;
+ }
+
+ if (play->envCtx.lightSettings.zFar + play->envCtx.adjLightSettings.zFar > 900) {
+ play->envCtx.adjLightSettings.zFar -= 100;
+ }
+
+ if (play->envCtx.lightSettings.fogNear + play->envCtx.adjLightSettings.fogNear > 950) {
+ play->envCtx.adjLightSettings.fogNear -= 10;
+ }
+ } else {
+ play->envCtx.fillScreen = true;
+ play->envCtx.screenFillColor[0] = 0;
+ play->envCtx.screenFillColor[1] = 0;
+ play->envCtx.screenFillColor[2] = 0;
+ play->envCtx.screenFillColor[3] = sGameOverLightsIntensity;
+ }
+}
+
+void Environment_FadeOutGameOverLights(PlayState* play) {
+ Player* player = GET_PLAYER(play);
+ s32 i;
+
+ if (sGameOverLightsIntensity >= 3) {
+ sGameOverLightsIntensity -= 3;
+ } else {
+ sGameOverLightsIntensity = 0;
+ }
+
+ if (sGameOverLightsIntensity == 1) {
+ LightContext_RemoveLight(play, &play->lightCtx, sNGameOverLightNode);
+ LightContext_RemoveLight(play, &play->lightCtx, sSGameOverLightNode);
+ } else if (sGameOverLightsIntensity >= 2) {
+ Lights_PointNoGlowSetInfo(&sNGameOverLightInfo, player->actor.world.pos.x - 10.0f,
+ player->actor.world.pos.y + 10.0f, player->actor.world.pos.z - 10.0f,
+ sGameOverLightsIntensity, sGameOverLightsIntensity, sGameOverLightsIntensity, 255);
+ Lights_PointNoGlowSetInfo(&sSGameOverLightInfo, player->actor.world.pos.x + 10.0f,
+ player->actor.world.pos.y + 10.0f, player->actor.world.pos.z + 10.0f,
+ sGameOverLightsIntensity, sGameOverLightsIntensity, sGameOverLightsIntensity, 255);
+ }
+
+ if (Play_CamIsNotFixed(&play->state)) {
+ for (i = 0; i < 3; i++) {
+ Math_SmoothStepToS(&play->envCtx.adjLightSettings.ambientColor[i], 0, 5, 12, 1);
+ Math_SmoothStepToS(&play->envCtx.adjLightSettings.light1Color[i], 0, 5, 12, 1);
+ play->envCtx.adjLightSettings.fogColor[i] = 0;
+ }
+ play->envCtx.adjLightSettings.zFar = 0;
+ play->envCtx.adjLightSettings.fogNear = 0;
+ } else {
+ play->envCtx.fillScreen = true;
+ play->envCtx.screenFillColor[0] = 0;
+ play->envCtx.screenFillColor[1] = 0;
+ play->envCtx.screenFillColor[2] = 0;
+ play->envCtx.screenFillColor[3] = sGameOverLightsIntensity;
+ if (sGameOverLightsIntensity == 0) {
+ play->envCtx.fillScreen = false;
+ }
+ }
+}
+
+void Environment_UpdateRain(PlayState* play) {
+ u8 max = MAX(play->envCtx.precipitation[PRECIP_RAIN_MAX], play->envCtx.precipitation[PRECIP_SOS_MAX]);
+
+ if ((play->envCtx.precipitation[PRECIP_RAIN_CUR] != max) && ((play->state.frames % 8) == 0)) {
+ if (play->envCtx.precipitation[PRECIP_RAIN_CUR] < max) {
+ play->envCtx.precipitation[PRECIP_RAIN_CUR] += 2;
+ } else {
+ play->envCtx.precipitation[PRECIP_RAIN_CUR] -= 2;
+ }
+ }
+}
+
+void Environment_FillScreen(GraphicsContext* gfxCtx, u8 red, u8 green, u8 blue, u8 alpha, u8 drawFlags) {
+ if (alpha != 0) {
+ OPEN_DISPS(gfxCtx);
+
+ if (drawFlags & FILL_SCREEN_OPA) {
+ POLY_OPA_DISP = Gfx_SetupDL57(POLY_OPA_DISP);
+ gDPSetPrimColor(POLY_OPA_DISP++, 0, 0, red, green, blue, alpha);
+ gDPSetAlphaDither(POLY_OPA_DISP++, G_AD_DISABLE);
+ gDPSetColorDither(POLY_OPA_DISP++, G_CD_DISABLE);
+ gSPDisplayList(POLY_OPA_DISP++, D_0E000000.clearFillRect);
+ }
+
+ if (drawFlags & FILL_SCREEN_XLU) {
+ POLY_XLU_DISP = Gfx_SetupDL57(POLY_XLU_DISP);
+ gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, red, green, blue, alpha);
+
+ if ((u32)alpha == 255) {
+ gDPSetRenderMode(POLY_XLU_DISP++, G_RM_OPA_SURF, G_RM_OPA_SURF2);
+ }
+
+ gDPSetAlphaDither(POLY_XLU_DISP++, G_AD_DISABLE);
+ gDPSetColorDither(POLY_XLU_DISP++, G_CD_DISABLE);
+ gSPDisplayList(POLY_XLU_DISP++, D_0E000000.clearFillRect);
+ }
+
+ CLOSE_DISPS(gfxCtx);
+ }
+}
u8 sSandstormPrimColors[] = {
255, 255, 255, //
@@ -832,9 +2728,267 @@ u8 sSandstormEnvColors[] = {
50, 40, 0, //
};
-#pragma GLOBAL_ASM("asm/non_matchings/code/z_kankyo/Environment_DrawSandstorm.s")
+void Environment_DrawSandstorm(PlayState* play, u8 sandstormState) {
+ s32 primA1 = 0;
+ s32 envA1 = 0;
+ s32 primA = play->envCtx.sandstormPrimA;
+ s32 envA = play->envCtx.sandstormEnvA;
+ s32 step = 9;
+ u16 sp96;
+ u16 sp94;
+ u16 sp92;
+ Color_RGBA8 primColor;
+ Color_RGBA8 envColor;
+ s32 index;
+ f32 sp98;
+ s32 pad;
-#pragma GLOBAL_ASM("asm/non_matchings/code/z_kankyo/Environment_AdjustLights.s")
+ switch (sandstormState) {
+ case SANDSTORM_ACTIVE:
+ primA1 = play->state.frames % 128;
+ if (primA1 > 128) {
+ primA1 = 255 - primA1;
+ }
+ primA1 += 73;
+ envA1 = 255;
+ break;
+
+ case SANDSTORM_FILL:
+ primA1 = 255;
+ envA1 = (play->envCtx.sandstormPrimA >= 255) ? 255 : 128;
+ break;
+
+ case SANDSTORM_UNFILL:
+ case SANDSTORM_5:
+ envA1 = 128;
+ if (play->envCtx.sandstormEnvA > 128) {
+ primA1 = 255;
+ } else {
+ primA1 = play->state.frames % 128;
+ if (primA1 > 64) {
+ primA1 = 128 - primA1;
+ }
+ primA1 += 73;
+ }
+ if ((primA1 >= primA) && (primA1 != 255)) {
+ play->envCtx.sandstormState++;
+ }
+ break;
+
+ case SANDSTORM_DISSIPATE:
+ primA1 = (play->envCtx.sandstormEnvA > 128) ? 255 : play->envCtx.sandstormEnvA >> 1;
+
+ if (primA == 0) {
+ play->envCtx.sandstormState = SANDSTORM_OFF;
+ }
+ break;
+
+ case SANDSTORM_6:
+ case SANDSTORM_8:
+ primA1 = D_801F4E30;
+ envA1 = D_801F4E30;
+ step = 1;
+ if (sandstormState == SANDSTORM_8) {
+ step = 10;
+ }
+ break;
+
+ case SANDSTORM_7:
+ case SANDSTORM_9:
+ step = 1;
+ if (primA == 0) {
+ play->envCtx.sandstormState = SANDSTORM_OFF;
+ }
+ if (sandstormState == SANDSTORM_9) {
+ step = 10;
+ }
+ break;
+
+ case SANDSTORM_A:
+ step = 255;
+ primA1 = D_801F4E30;
+ envA1 = D_801F4E30;
+ if (primA == 0) {
+ play->envCtx.sandstormState = SANDSTORM_OFF;
+ }
+ break;
+
+ case SANDSTORM_B:
+ primA1 = play->state.frames & 0x7F;
+ if (primA1 > 64) {
+ primA1 = 128 - primA1;
+ }
+ primA1 += 73;
+ envA1 = 128;
+ break;
+
+ case SANDSTORM_C:
+ if (primA == 0) {
+ play->envCtx.sandstormState = SANDSTORM_OFF;
+ }
+ break;
+
+ case SANDSTORM_D:
+ primA1 = D_801F4E30;
+ envA1 = D_801F4E30;
+ step = 10;
+ break;
+
+ default:
+ break;
+ }
+
+ if (ABS_ALT(primA - primA1) < step) {
+ primA = primA1;
+ } else if (primA1 < primA) {
+ primA -= step;
+ } else {
+ primA += step;
+ }
+
+ if (ABS_ALT(envA - envA1) < step) {
+ envA = envA1;
+ } else if (envA1 < envA) {
+ envA -= step;
+ } else {
+ envA += step;
+ }
+
+ play->envCtx.sandstormPrimA = primA;
+ play->envCtx.sandstormEnvA = envA;
+
+ sp98 = (512.0f - (primA + envA)) * (3.0f / 128.0f);
+
+ if (sp98 > 6.0f) {
+ sp98 = 6.0f;
+ }
+
+ if (play->envCtx.sandstormPrimA != 0) {
+ index = 0;
+ if (sandstormState >= SANDSTORM_B) {
+ index = 4 * 3;
+ }
+
+ if ((play->envCtx.lightMode != LIGHT_MODE_TIME) ||
+ (play->envCtx.lightSettingOverride != LIGHT_SETTING_OVERRIDE_NONE)) {
+ primColor.r = sSandstormPrimColors[index + 1 * 3 + 0];
+ primColor.g = sSandstormPrimColors[index + 1 * 3 + 1];
+ primColor.b = sSandstormPrimColors[index + 1 * 3 + 2];
+ envColor.r = sSandstormEnvColors[index + 1 * 3 + 0];
+ envColor.g = sSandstormEnvColors[index + 1 * 3 + 1];
+ envColor.b = sSandstormEnvColors[index + 1 * 3 + 2];
+ } else if (sSandstormColorIndex == sNextSandstormColorIndex) {
+ primColor.r = sSandstormPrimColors[sSandstormColorIndex * 3 + index + 0];
+ primColor.g = sSandstormPrimColors[sSandstormColorIndex * 3 + index + 1];
+ primColor.b = sSandstormPrimColors[sSandstormColorIndex * 3 + index + 2];
+ envColor.r = sSandstormEnvColors[sSandstormColorIndex * 3 + index + 0];
+ envColor.g = sSandstormEnvColors[sSandstormColorIndex * 3 + index + 1];
+ envColor.b = sSandstormEnvColors[sSandstormColorIndex * 3 + index + 2];
+ } else {
+ primColor.r =
+ (s32)F32_LERP_ALT(sSandstormPrimColors[sSandstormColorIndex * 3 + index + 0],
+ sSandstormPrimColors[sNextSandstormColorIndex * 3 + index + 0], sSandstormLerpScale);
+ primColor.g =
+ (s32)F32_LERP_ALT(sSandstormPrimColors[sSandstormColorIndex * 3 + index + 1],
+ sSandstormPrimColors[sNextSandstormColorIndex * 3 + index + 1], sSandstormLerpScale);
+ primColor.b =
+ (s32)F32_LERP_ALT(sSandstormPrimColors[sSandstormColorIndex * 3 + index + 2],
+ sSandstormPrimColors[sNextSandstormColorIndex * 3 + index + 2], sSandstormLerpScale);
+ envColor.r =
+ (s32)F32_LERP_ALT(sSandstormEnvColors[sSandstormColorIndex * 3 + index + 0],
+ sSandstormEnvColors[sNextSandstormColorIndex * 3 + index + 0], sSandstormLerpScale);
+ envColor.g =
+ (s32)F32_LERP_ALT(sSandstormEnvColors[sSandstormColorIndex * 3 + index + 1],
+ sSandstormEnvColors[sNextSandstormColorIndex * 3 + index + 1], sSandstormLerpScale);
+ envColor.b =
+ (s32)F32_LERP_ALT(sSandstormEnvColors[sSandstormColorIndex * 3 + index + 2],
+ sSandstormEnvColors[sNextSandstormColorIndex * 3 + index + 2], sSandstormLerpScale);
+ }
+
+ envColor.r = ((envColor.r * sp98) + ((6.0f - sp98) * primColor.r)) * (1.0f / 6.0f);
+ envColor.g = ((envColor.g * sp98) + ((6.0f - sp98) * primColor.g)) * (1.0f / 6.0f);
+ envColor.b = ((envColor.b * sp98) + ((6.0f - sp98) * primColor.b)) * (1.0f / 6.0f);
+
+ sp96 = (sSandstormScroll * (11.0f / 6.0f));
+ sp94 = (sSandstormScroll * (9.0f / 6.0f));
+ sp92 = (sSandstormScroll * (6.0f / 6.0f));
+
+ OPEN_DISPS(play->state.gfxCtx);
+
+ POLY_XLU_DISP = Gfx_SetupDL64(POLY_XLU_DISP);
+
+ gDPSetAlphaDither(POLY_XLU_DISP++, G_AD_NOISE);
+ gDPSetColorDither(POLY_XLU_DISP++, G_CD_NOISE);
+ gDPSetPrimColor(POLY_XLU_DISP++, 0, 0x80, primColor.r, primColor.g, primColor.b, play->envCtx.sandstormPrimA);
+ gDPSetEnvColor(POLY_XLU_DISP++, envColor.r, envColor.g, envColor.b, play->envCtx.sandstormEnvA);
+ gSPSegment(POLY_XLU_DISP++, 0x08,
+ Gfx_TwoTexScroll(play->state.gfxCtx, G_TX_RENDERTILE, (u32)sp96 % 4096, 0, 512, 32, 1,
+ (u32)sp94 % 4096, 4095 - ((u32)sp92 % 4096), 256, 64));
+ gDPSetTextureLUT(POLY_XLU_DISP++, G_TT_NONE);
+ gSPDisplayList(POLY_XLU_DISP++, gFieldSandstormDL);
+
+ CLOSE_DISPS(play->state.gfxCtx);
+ }
+
+ sSandstormScroll += (s32)sp98;
+}
+
+s32 Environment_AdjustLights(PlayState* play, f32 arg1, f32 arg2, f32 arg3, f32 arg4) {
+ f32 temp;
+ s32 i;
+ Player* player = GET_PLAYER(play);
+
+ if (play->roomCtx.curRoom.behaviorType1 == ROOM_BEHAVIOR_TYPE1_5) {
+ return 0;
+ }
+
+ if (!Play_CamIsNotFixed(&play->state)) {
+ return 0;
+ }
+ if (play->unk_18880) {
+ return 0;
+ }
+ if ((player != NULL) && (player->stateFlags1 & PLAYER_STATE1_2)) {
+ return 0;
+ }
+
+ arg1 = CLAMP_MIN(arg1, 0.0f);
+ arg1 = CLAMP_MAX(arg1, 1.0f);
+
+ temp = arg1 - arg3;
+
+ if (arg1 < arg3) {
+ temp = 0.0f;
+ }
+
+ play->envCtx.adjLightSettings.fogNear = (arg2 - play->envCtx.lightSettings.fogNear) * temp;
+
+ if (arg1 == 0.0f) {
+ for (i = 0; i < 3; i++) {
+ play->envCtx.adjLightSettings.fogColor[i] = 0;
+ }
+ } else {
+ temp = arg1 * 5.0f;
+ temp = CLAMP_MAX(temp, 1.0f);
+
+ for (i = 0; i < 3; i++) {
+ play->envCtx.adjLightSettings.fogColor[i] = -(f32)play->envCtx.lightSettings.fogColor[i] * temp;
+ }
+ }
+
+ if (arg4 <= 0.0f) {
+ return 1;
+ }
+
+ arg1 *= arg4;
+
+ for (i = 0; i < 3; i++) {
+ play->envCtx.adjLightSettings.ambientColor[i] = -(f32)play->envCtx.lightSettings.ambientColor[i] * arg1;
+ play->envCtx.adjLightSettings.light1Color[i] = -(f32)play->envCtx.lightSettings.light1Color[i] * arg1;
+ }
+
+ return 1;
+}
void Environment_LerpRGB8(u8* from, Color_RGB8* to, f32 lerp, s16* dst) {
Color_RGB8 result;
@@ -879,13 +3033,49 @@ u32 Environment_GetTotalDays(void) {
return gSaveContext.save.day;
}
-#pragma GLOBAL_ASM("asm/non_matchings/code/z_kankyo/Environment_ForcePlaySequence.s")
+void Environment_ForcePlaySequence(u16 seqId) {
+ gSaveContext.forcedSeqId = seqId;
+}
-#pragma GLOBAL_ASM("asm/non_matchings/code/z_kankyo/Environment_IsForcedSequenceDisabled.s")
+s32 Environment_IsForcedSequenceDisabled(void) {
+ s32 isDisabled = false;
-#pragma GLOBAL_ASM("asm/non_matchings/code/z_kankyo/Environment_PlayStormNatureAmbience.s")
+ if (gSaveContext.forcedSeqId != NA_BGM_GENERAL_SFX) {
+ isDisabled = true;
+ }
-#pragma GLOBAL_ASM("asm/non_matchings/code/z_kankyo/Environment_StopStormNatureAmbience.s")
+ return isDisabled;
+}
+
+void Environment_PlayStormNatureAmbience(PlayState* play) {
+ if (((play->sequenceCtx.seqId != NA_BGM_NO_MUSIC) && (play->sequenceCtx.ambienceId == AMBIENCE_ID_13)) ||
+ (AudioSeq_GetActiveSeqId(SEQ_PLAYER_BGM_MAIN) == NA_BGM_FINAL_HOURS)) {
+ Audio_PlayAmbience(AMBIENCE_ID_07);
+ } else if ((play->sequenceCtx.seqId != NA_BGM_NO_MUSIC) && (play->sequenceCtx.ambienceId != AMBIENCE_ID_13)) {
+ if ((((void)0, gSaveContext.save.time) >= CLOCK_TIME(6, 0)) &&
+ (((void)0, gSaveContext.save.time) < CLOCK_TIME(18, 0))) {
+ Audio_PlayAmbience(play->sequenceCtx.ambienceId);
+ }
+ }
+
+ Audio_SetAmbienceChannelIO(AMBIENCE_CHANNEL_RAIN, CHANNEL_IO_PORT_1, 1);
+ Audio_SetAmbienceChannelIO(AMBIENCE_CHANNEL_LIGHTNING, CHANNEL_IO_PORT_1, 1);
+}
+
+void Environment_StopStormNatureAmbience(PlayState* play) {
+ if (((play->sequenceCtx.seqId != NA_BGM_NO_MUSIC) && (play->sequenceCtx.ambienceId == AMBIENCE_ID_13)) ||
+ (AudioSeq_GetActiveSeqId(SEQ_PLAYER_BGM_MAIN) == NA_BGM_FINAL_HOURS)) {
+ SEQCMD_STOP_SEQUENCE(SEQ_PLAYER_AMBIENCE, 20);
+ } else if ((play->sequenceCtx.seqId != NA_BGM_NO_MUSIC) && (play->sequenceCtx.ambienceId != AMBIENCE_ID_13)) {
+ if ((((void)0, gSaveContext.save.time) >= CLOCK_TIME(6, 0)) &&
+ (((void)0, gSaveContext.save.time) < CLOCK_TIME(18, 0))) {
+ SEQCMD_STOP_SEQUENCE(SEQ_PLAYER_AMBIENCE, 20);
+ }
+ }
+
+ Audio_SetAmbienceChannelIO(AMBIENCE_CHANNEL_RAIN, CHANNEL_IO_PORT_1, 0);
+ Audio_SetAmbienceChannelIO(AMBIENCE_CHANNEL_LIGHTNING, CHANNEL_IO_PORT_1, 0);
+}
void Environment_WarpSongLeave(PlayState* play) {
gWeatherMode = WEATHER_MODE_CLEAR;
@@ -897,15 +3087,249 @@ void Environment_WarpSongLeave(PlayState* play) {
gSaveContext.nextTransitionType = TRANS_TYPE_FADE_WHITE;
}
-#pragma GLOBAL_ASM("asm/non_matchings/code/z_kankyo/Environment_SetupSkyboxStars.s")
+void Environment_SetupSkyboxStars(PlayState* play) {
+ f32 phi_f0;
-#pragma GLOBAL_ASM("asm/non_matchings/code/z_kankyo/Environment_DrawSkyboxStar.s")
+ if ((play->envCtx.skybox1Index == 0) && (play->skyboxId == SKYBOX_NORMAL_SKY)) {
+ if ((((void)0, gSaveContext.save.time) >= CLOCK_TIME(21, 0)) ||
+ (((void)0, gSaveContext.save.time) < CLOCK_TIME(2, 0))) {
+ phi_f0 = 1.0f;
+ } else if (((void)0, gSaveContext.save.time) > CLOCK_TIME(19, 0)) {
+ phi_f0 = 1.0f - ((CLOCK_TIME(21, 0) - ((void)0, gSaveContext.save.time)) * (1.0f / (CLOCK_TIME(2, 0) + 1)));
+ } else if (((void)0, gSaveContext.save.time) < CLOCK_TIME(3, 0)) {
+ phi_f0 = (CLOCK_TIME(3, 0) - ((void)0, gSaveContext.save.time)) * (1.0f / (CLOCK_TIME(1, 0) + 1));
+ } else {
+ phi_f0 = 0.0f;
+ }
+ phi_f0 = (play->envCtx.skyboxConfig == 0x18) ? 1.0f : phi_f0;
+
+ D_801F4F28 = phi_f0;
+ sEnvSkyboxNumStars = gSkyboxNumStars;
+ } else {
+ D_801F4F28 = 0.0f;
+ sEnvSkyboxNumStars = 0;
+ }
+
+ if ((sEnvSkyboxNumStars != 0) && (D_801F4F28 != 0.0f)) {
+ OPEN_DISPS(play->state.gfxCtx);
+
+ sSkyboxStarsDList = POLY_OPA_DISP;
+
+ gSPNoOp(POLY_OPA_DISP++);
+
+ CLOSE_DISPS(play->state.gfxCtx);
+ } else {
+ sSkyboxStarsDList = NULL;
+ }
+}
+
+void Environment_DrawSkyboxStar(Gfx** gfxp, f32 x, f32 y, s32 width, s32 height) {
+ Gfx* gfx = *gfxp;
+ u32 xl = x * 4.0f;
+ u32 yl = y * 4.0f;
+ u32 xd = width;
+ u32 yd = height;
+
+ gSPTextureRectangle(gfx++, xl, yl, xl + xd, yl + yd, 0, 0, 0, 0, 0);
+
+ *gfxp = gfx;
+}
+
+#ifdef NON_MATCHING
+// `D_801DD900`is loading unaligned but storing aligned
+// Also small float regalloc at the gRandFloat section
+// https://decomp.me/scratch/3zFop
+void Environment_DrawSkyboxStarsImpl(PlayState* play, Gfx** gfxP) {
+ static const Vec3s D_801DD880[] = {
+ { 0x0384, 0x2328, 0xD508 }, { 0x09C4, 0x2328, 0xDA1C }, { 0x0E74, 0x22D8, 0xDA1C }, { 0x1450, 0x2468, 0xD8F0 },
+ { 0x1C84, 0x28A0, 0xCBA8 }, { 0x1F40, 0x2134, 0xD8F0 }, { 0x1F40, 0x28A0, 0xDAE4 }, { 0xE4A8, 0x4A38, 0x4A38 },
+ { 0xD058, 0x4C2C, 0x3A98 }, { 0xD8F0, 0x36B0, 0x47E0 }, { 0xD954, 0x3264, 0x3E1C }, { 0xD8F0, 0x3070, 0x37DC },
+ { 0xD8F0, 0x1F40, 0x5208 }, { 0xD760, 0x1838, 0x27D8 }, { 0x0000, 0x4E20, 0x4A38 }, { 0x076C, 0x2328, 0xDCD8 },
+ };
+ // Possibly Color_RGBA8_u32
+ static const u32 D_801DD8E0[] = {
+ 0x41A4FFFF, 0x83A4E6FF, 0x62CDFFFF, 0x5252FFFF, 0x7BA4A4FF, 0x62CDFFFF, 0x62A4E6FF, 0xFF5A00FF,
+ };
+ static const u32 D_801DD900[] = {
+ 0x405070FF, 0x606080FF, 0x807090FF, 0xA080A0FF, 0xC090A8FF, 0xE0A0B0FF, 0xE0A0B0FF, 0x686888FF,
+ 0x887898FF, 0xA888A8FF, 0xC898B8FF, 0xE8A8B8FF, 0xE0B0B8FF, 0xF0C0C0FF, 0xE8B8C0FF, 0xF8C8C0FF,
+ };
+ Vec3f pos;
+ s32 pad1;
+ f32 imgY; // spF4
+ f32 imgX; // spF0
+ Gfx* gfx;
+ s32 phi_v1; // spE8
+ s32 negateY; // spE4
+ f32 invScale;
+ f32 temp_f20;
+ Gfx* gfxTemp; // spD8
+ f32 scale;
+ s32 i;
+ u32 randInt;
+ u32 imgWidth;
+ f32* imgXPtr;
+ f32* imgYPtr;
+ Vec3f* posPtr;
+ s32 pad[2];
+ f32(*viewProjectionMtxF)[4];
+
+ gfx = *gfxP;
+ negateY = Environment_IsSceneUpsideDown(play);
+
+ Matrix_MtxToMtxF(play->view.viewingPtr, &play->billboardMtxF);
+ Matrix_MtxToMtxF(&play->view.projection, &play->viewProjectionMtxF);
+ SkinMatrix_MtxFMtxFMult(&play->viewProjectionMtxF, &play->billboardMtxF, &play->viewProjectionMtxF);
+
+ phi_v1 = 0;
+
+ gDPPipeSync(gfx++);
+ gDPSetEnvColor(gfx++, 255, 255, 255, 255.0f * D_801F4F28);
+ gDPSetCombineLERP(gfx++, PRIMITIVE, 0, ENVIRONMENT, 0, PRIMITIVE, 0, ENVIRONMENT, 0, PRIMITIVE, 0, ENVIRONMENT, 0,
+ PRIMITIVE, 0, ENVIRONMENT, 0);
+ gDPSetOtherMode(gfx++,
+ G_AD_DISABLE | G_CD_DISABLE | G_CK_NONE | G_TC_FILT | G_TF_POINT | G_TT_NONE | G_TL_TILE |
+ G_TD_CLAMP | G_TP_NONE | G_CYC_1CYCLE | G_PM_NPRIMITIVE,
+ G_AC_NONE | G_ZS_PRIM | G_RM_AA_XLU_LINE | G_RM_AA_XLU_LINE2);
+
+ randInt = ((u32)gSaveContext.save.saveInfo.playerData.playerName[0] << 0x18) ^
+ ((u32)gSaveContext.save.saveInfo.playerData.playerName[1] << 0x14) ^
+ ((u32)gSaveContext.save.saveInfo.playerData.playerName[2] << 0x10) ^
+ ((u32)gSaveContext.save.saveInfo.playerData.playerName[3] << 0xC) ^
+ ((u32)gSaveContext.save.saveInfo.playerData.playerName[4] << 8) ^
+ ((u32)gSaveContext.save.saveInfo.playerData.playerName[5] << 4) ^
+ ((u32)gSaveContext.save.saveInfo.playerData.playerName[6] << 0) ^
+ ((u32)gSaveContext.save.saveInfo.playerData.playerName[7] >> 4) ^
+ ((u32)gSaveContext.save.saveInfo.playerData.playerName[7] << 0x1C);
+
+ //! FAKE:
+ if (play->view.viewingPtr && play->view.viewingPtr && play->view.viewingPtr) {}
+
+ for (i = 0; i < sEnvSkyboxNumStars; i++) {
+ if (i < 16) {
+ pos.x = play->view.eye.x + (s32)D_801DD880[i].x;
+ pos.y = play->view.eye.y + (s32)D_801DD880[i].y;
+ pos.z = play->view.eye.z + (s32)D_801DD880[i].z;
+ imgWidth = 8;
+ } else {
+ f32 temp_f22;
+ f32 temp_f4;
+ f32 temp_f2;
+
+ // temp_f4 = Rand_ZeroOne_Variable(&randInt);
+ randInt = (randInt * 1664525) + 1013904223;
+ gRandFloat = (randInt >> 9) | 0x3F800000;
+ temp_f4 = *((f32*)&gRandFloat) - 1.0f;
+
+ // temp_f20 = Rand_ZeroOne_Variable(&randInt);
+ randInt = (randInt * 1664525) + 1013904223;
+ gRandFloat = (randInt >> 9) | 0x3F800000;
+ temp_f20 = ((*((f32*)&gRandFloat) - 1.0f) + temp_f4) * 0.5f;
+
+ // randInt = Rand_Next_Variable(&randInt);
+ randInt = (randInt * 1664525) + 1013904223;
+
+ // Set random position
+ pos.y = play->view.eye.y + (SQ(temp_f20) * SQ(128.0f)) - 1000.0f;
+ pos.x = play->view.eye.x + (Math_SinS(randInt) * (1.2f - temp_f20) * SQ(128.0f));
+ pos.z = play->view.eye.z + (Math_CosS(randInt) * (1.2f - temp_f20) * SQ(128.0f));
+
+ // temp_f2 = Rand_ZeroOne_Variable(&randInt);
+ randInt = (randInt * 1664525) + 1013904223;
+ gRandFloat = ((randInt >> 9) | 0x3F800000);
+ temp_f2 = *((f32*)&gRandFloat) - 1.0f;
+
+ // Set random width
+ imgWidth = (u32)((SQ(temp_f2) * 8.0f) + 2.0f);
+ }
+
+ if (negateY) {
+ pos.y = -pos.y;
+ }
+
+ if ((i < 15) || ((i == 15) && ((((void)0, gSaveContext.save.day) % 7) == 0))) {
+ gDPSetColor(gfx++, G_SETPRIMCOLOR, D_801DD8E0[i % ARRAY_COUNTU(D_801DD8E0)]);
+ } else if (((i & 0x3F) == 0) || (i == 16)) {
+ gDPSetColor(gfx++, G_SETPRIMCOLOR, D_801DD900[phi_v1 % ARRAY_COUNTU(D_801DD900)]);
+ phi_v1++;
+ }
+
+ posPtr = &pos;
+ imgXPtr = &imgX;
+ imgYPtr = &imgY;
+ viewProjectionMtxF = play->viewProjectionMtxF.mf;
+
+ if (imgWidth >= 2) {
+ // w component
+ scale = pos.x * play->viewProjectionMtxF.mf[0][3] + pos.y * play->viewProjectionMtxF.mf[1][3] +
+ pos.z * play->viewProjectionMtxF.mf[2][3] + play->viewProjectionMtxF.mf[3][3];
+ if (scale >= 1.0f) {
+ invScale = 1.0f / scale;
+ // x component
+ imgX = (pos.x * viewProjectionMtxF[0][0] + pos.y * viewProjectionMtxF[1][0] +
+ pos.z * viewProjectionMtxF[2][0] + viewProjectionMtxF[3][0]) *
+ invScale;
+ // y component
+ imgY = (((pos.x * viewProjectionMtxF[0][1]) + (pos.y * viewProjectionMtxF[1][1]) +
+ (pos.z * viewProjectionMtxF[2][1])) +
+ viewProjectionMtxF[3][1]) *
+ invScale;
+ }
+
+ if ((scale >= 1.0f) && (imgX > -1.0f) && (imgX < 1.0f) && (imgY > -1.0f) && (imgY < 1.0f)) {
+ imgX = (imgX * (SCREEN_WIDTH / 2)) + (SCREEN_WIDTH / 2);
+ imgY = (imgY * -(SCREEN_HEIGHT / 2)) + (SCREEN_HEIGHT / 2);
+
+ gfxTemp = gfx;
+ Environment_DrawSkyboxStar(&gfxTemp, imgX, imgY, imgWidth, 4);
+ gfx = gfxTemp;
+ }
+ //! FAKE:
+ if (viewProjectionMtxF) {}
+ }
+ }
+
+ gDPPipeSync(gfx++);
+ *gfxP = gfx;
+}
+#else
+void Environment_DrawSkyboxStarsImpl(PlayState* play, Gfx** gfxP);
#pragma GLOBAL_ASM("asm/non_matchings/code/z_kankyo/Environment_DrawSkyboxStarsImpl.s")
+#endif
-#pragma GLOBAL_ASM("asm/non_matchings/code/z_kankyo/Environment_Draw.s")
+void Environment_Draw(PlayState* play) {
+ Environment_SetupSkyboxStars(play);
+ Environment_DrawSun(play);
+ Environment_UpdateLightningStrike(play);
+ Environment_DrawLightning(play, 0);
+ Environment_DrawSkyboxFilters(play);
+}
-#pragma GLOBAL_ASM("asm/non_matchings/code/z_kankyo/Environment_DrawSkyboxStars.s")
+void Environment_DrawSkyboxStars(PlayState* play) {
+ Gfx* nextOpa;
+ Gfx* opa;
+
+ if (sSkyboxStarsDList != NULL) {
+ OPEN_DISPS(play->state.gfxCtx);
+
+ opa = POLY_OPA_DISP;
+ nextOpa = Graph_GfxPlusOne(opa);
+
+ gSPDisplayList(sSkyboxStarsDList, nextOpa);
+
+ Environment_DrawSkyboxStarsImpl(play, &nextOpa);
+
+ gSPEndDisplayList(nextOpa++);
+
+ Graph_BranchDlist(opa, nextOpa);
+
+ POLY_OPA_DISP = nextOpa;
+ sSkyboxStarsDList = NULL;
+
+ CLOSE_DISPS(play->state.gfxCtx);
+ }
+}
void Environment_StopTime(void) {
sEnvIsTimeStopped = true;
@@ -992,21 +3416,101 @@ void Environment_SetTimeJump(f32 minutes) {
sTimeJump = CLOCK_TIME_F(0, minutes);
}
-#pragma GLOBAL_ASM("asm/non_matchings/code/z_kankyo/func_800FE6F8.s")
+u8 func_800FE6F8(PlayState* play, s16 timeAdvanceScaling, s16 nextTimeLimit) {
+ u16 nextTime;
+ u8 ret = false;
-#pragma GLOBAL_ASM("asm/non_matchings/code/z_kankyo/Environment_GetSandstormColorIndex.s")
+ if (R_TIME_SPEED != 0) {
+ nextTime = ((void)0, gSaveContext.save.time) +
+ (R_TIME_SPEED + ((void)0, gSaveContext.save.timeSpeedOffset)) * timeAdvanceScaling * 20;
+ if (nextTime > nextTimeLimit) {
+ ret = true;
+ }
+ }
-#pragma GLOBAL_ASM("asm/non_matchings/code/z_kankyo/Environment_GetNextSandstormColorIndex.s")
+ return ret;
+}
-#pragma GLOBAL_ASM("asm/non_matchings/code/z_kankyo/Environment_GetSandstormLerpScale.s")
+u8 Environment_GetSandstormColorIndex(void) {
+ return sSandstormColorIndex;
+}
-#pragma GLOBAL_ASM("asm/non_matchings/code/z_kankyo/Environment_LerpSandstormColors.s")
+u8 Environment_GetNextSandstormColorIndex(void) {
+ return sNextSandstormColorIndex;
+}
-#pragma GLOBAL_ASM("asm/non_matchings/code/z_kankyo/func_800FE9B4.s")
+f32 Environment_GetSandstormLerpScale(void) {
+ return sSandstormLerpScale;
+}
-#pragma GLOBAL_ASM("asm/non_matchings/code/z_kankyo/func_800FEA50.s")
+void Environment_LerpSandstormColors(Color_RGBA8* colorSrc, Color_RGBA8* colorDst) {
+ f32 sandstormLerpScale = Environment_GetSandstormLerpScale();
+ s32 pad;
+ u32 sandstormColorIndex = Environment_GetSandstormColorIndex();
+ u32 nextSandstormColorIndex = Environment_GetNextSandstormColorIndex();
-#pragma GLOBAL_ASM("asm/non_matchings/code/z_kankyo/func_800FEAB0.s")
+ if (sandstormLerpScale <= 0.0f) {
+ colorDst->r = colorSrc[sandstormColorIndex].r;
+ colorDst->g = colorSrc[sandstormColorIndex].g;
+ colorDst->b = colorSrc[sandstormColorIndex].b;
+ colorDst->a = colorSrc[sandstormColorIndex].a;
+ } else {
+ colorDst->r =
+ (s32)F32_LERP(colorSrc[sandstormColorIndex].r, colorSrc[nextSandstormColorIndex].r, sandstormLerpScale);
+ colorDst->g =
+ (s32)F32_LERP(colorSrc[sandstormColorIndex].g, colorSrc[nextSandstormColorIndex].g, sandstormLerpScale);
+ colorDst->b =
+ (s32)F32_LERP(colorSrc[sandstormColorIndex].b, colorSrc[nextSandstormColorIndex].b, sandstormLerpScale);
+ colorDst->a =
+ (s32)F32_LERP(colorSrc[sandstormColorIndex].a, colorSrc[nextSandstormColorIndex].a, sandstormLerpScale);
+ }
+}
+
+// Related to soft soil
+u8 func_800FE9B4(PlayState* play) {
+ u8 ret = false;
+
+ if ((play->envCtx.precipitation[PRECIP_SOS_MAX] == 60) && (play->envCtx.precipitation[PRECIP_SNOW_CUR] == 0)) {
+ ret = true;
+ }
+
+ if ((play->envCtx.precipitation[PRECIP_RAIN_MAX] == 60) &&
+ !(GET_ACTIVE_CAM(play)->stateFlags & CAM_STATE_UNDERWATER)) {
+ if ((Environment_GetStormState(play) != STORM_STATE_OFF) &&
+ (play->envCtx.precipitation[PRECIP_SNOW_CUR] == 0)) {
+ ret = true;
+ }
+ }
+
+ return ret;
+}
+
+void func_800FEA50(PlayState* play) {
+ s8 phi_v0;
+
+ switch (gSaveContext.save.day) {
+ default:
+ case 0:
+ case 1:
+ phi_v0 = 0;
+ break;
+
+ case 2:
+ phi_v0 = 3;
+ break;
+
+ case 3:
+ phi_v0 = 4;
+ break;
+ }
+
+ D_801F4F30 = phi_v0;
+ D_801F4F33 = true;
+}
+
+void func_800FEAB0() {
+ D_801F4F33 = false;
+}
void Environment_JumpForwardInTime(void) {
if (sTimeJump != 0) {
@@ -1015,4 +3519,65 @@ void Environment_JumpForwardInTime(void) {
}
}
-#pragma GLOBAL_ASM("asm/non_matchings/code/z_kankyo/func_800FEAF4.s")
+void func_800FEAF4(EnvironmentContext* envCtx) {
+ u8 phi_v1 = 0;
+
+ if (((void)0, gSaveContext.save.day) != 0) {
+ phi_v1 = ((void)0, gSaveContext.save.day) - 1;
+ }
+
+ envCtx->skyboxConfig = phi_v1 + (D_801F4E31 * 3);
+ envCtx->changeSkyboxNextConfig = envCtx->skyboxConfig;
+
+ if (D_801F4E31 == 4) {
+ envCtx->skyboxConfig = 0xE;
+ envCtx->changeSkyboxNextConfig = envCtx->skyboxConfig;
+ } else if (D_801F4E31 == 5) {
+ envCtx->skyboxConfig = 0x10;
+ envCtx->changeSkyboxNextConfig = envCtx->skyboxConfig;
+ } else if (D_801F4E31 == 6) {
+ envCtx->skyboxConfig = 0x11;
+ envCtx->changeSkyboxNextConfig = envCtx->skyboxConfig;
+ } else if (D_801F4E31 == 7) {
+ envCtx->skyboxConfig = phi_v1 + 0x12;
+ envCtx->changeSkyboxNextConfig = envCtx->skyboxConfig;
+ } else if (D_801F4E31 == 8) {
+ envCtx->skyboxConfig = phi_v1 + 0x15;
+ envCtx->changeSkyboxNextConfig = envCtx->skyboxConfig;
+ } else if (D_801F4E31 == 9) {
+ envCtx->skyboxConfig = 0x18;
+ envCtx->changeSkyboxNextConfig = envCtx->skyboxConfig;
+ } else if (D_801F4E31 == 0xA) {
+ envCtx->skyboxConfig = phi_v1 + 0x19;
+ envCtx->changeSkyboxNextConfig = envCtx->skyboxConfig;
+ }
+
+ if (phi_v1 >= 3) {
+ envCtx->skyboxConfig = 0xD;
+ envCtx->changeSkyboxNextConfig = 0xD;
+ }
+
+ if (envCtx->skyboxConfig >= 0x1C) {
+ envCtx->skyboxConfig = 0;
+ envCtx->changeSkyboxNextConfig = 0;
+ }
+
+ switch (((void)0, gSaveContext.save.day)) {
+ default:
+ case 0:
+ case 1:
+ envCtx->lightConfig = 0;
+ envCtx->changeLightNextConfig = 0;
+ break;
+
+ case 2:
+ envCtx->lightConfig = 3;
+ envCtx->changeLightNextConfig = 3;
+ break;
+
+ case 3:
+ envCtx->lightConfig = 4;
+ envCtx->changeLightNextConfig = 4;
+ break;
+ }
+}
diff --git a/src/code/z_lib.c b/src/code/z_lib.c
index c520b00d5..cf5666ed4 100644
--- a/src/code/z_lib.c
+++ b/src/code/z_lib.c
@@ -663,15 +663,15 @@ void Lib_Vec3f_TranslateAndRotateY(Vec3f* translation, s16 rotAngle, Vec3f* src,
dst->z = translation->z + (src->z * cos - src->x * sin);
}
-void Color_RGB8_Lerp(Color_RGB8* a, Color_RGB8* b, f32 t, Color_RGB8* dst) {
+void Color_RGB8_Lerp(Color_RGB8* from, Color_RGB8* to, f32 lerp, Color_RGB8* dst) {
f32 aF;
- aF = a->r;
- dst->r = aF + (b->r - aF) * t;
- aF = a->g;
- dst->g = aF + (b->g - aF) * t;
- aF = a->b;
- dst->b = aF + (b->b - aF) * t;
+ aF = from->r;
+ dst->r = aF + (to->r - aF) * lerp;
+ aF = from->g;
+ dst->g = aF + (to->g - aF) * lerp;
+ aF = from->b;
+ dst->b = aF + (to->b - aF) * lerp;
}
f32 Math_Vec3f_StepTo(Vec3f* start, Vec3f* target, f32 speed) {
diff --git a/src/code/z_play.c b/src/code/z_play.c
index 144a05a2f..7f6f8659b 100644
--- a/src/code/z_play.c
+++ b/src/code/z_play.c
@@ -385,7 +385,7 @@ void Play_Destroy(GameState* thisx) {
BombersNotebook_Destroy(&sBombersNotebook);
this->state.gfxCtx->callback = NULL;
- this->state.gfxCtx->callbackArg = 0;
+ this->state.gfxCtx->callbackArg = NULL;
Play_DestroyMotionBlur();
if (R_PAUSE_BG_PRERENDER_STATE != PAUSE_BG_PRERENDER_OFF) {
@@ -1332,7 +1332,7 @@ void Play_DrawMain(PlayState* this) {
}
if (1) {
- Environment_FillScreen(gfxCtx, 0, 0, 0, this->bgCoverAlpha, 1);
+ Environment_FillScreen(gfxCtx, 0, 0, 0, this->bgCoverAlpha, FILL_SCREEN_OPA);
}
if (1) {
@@ -1353,13 +1353,14 @@ void Play_DrawMain(PlayState* this) {
if (1) {
if (R_PLAY_FILL_SCREEN_ON) {
Environment_FillScreen(gfxCtx, R_PLAY_FILL_SCREEN_R, R_PLAY_FILL_SCREEN_G, R_PLAY_FILL_SCREEN_B,
- R_PLAY_FILL_SCREEN_ALPHA, 3);
+ R_PLAY_FILL_SCREEN_ALPHA, FILL_SCREEN_OPA | FILL_SCREEN_XLU);
}
switch (this->envCtx.fillScreen) {
case 1:
Environment_FillScreen(gfxCtx, this->envCtx.screenFillColor[0], this->envCtx.screenFillColor[1],
- this->envCtx.screenFillColor[2], this->envCtx.screenFillColor[3], 3);
+ this->envCtx.screenFillColor[2], this->envCtx.screenFillColor[3],
+ FILL_SCREEN_OPA | FILL_SCREEN_XLU);
break;
default:
@@ -1374,7 +1375,7 @@ void Play_DrawMain(PlayState* this) {
}
if (this->worldCoverAlpha != 0) {
- Environment_FillScreen(gfxCtx, 0, 0, 0, this->worldCoverAlpha, 3);
+ Environment_FillScreen(gfxCtx, 0, 0, 0, this->worldCoverAlpha, FILL_SCREEN_OPA | FILL_SCREEN_XLU);
}
if (1) {
diff --git a/src/overlays/actors/ovl_Boss_01/z_boss_01.c b/src/overlays/actors/ovl_Boss_01/z_boss_01.c
index 826e126dc..6adf386d7 100644
--- a/src/overlays/actors/ovl_Boss_01/z_boss_01.c
+++ b/src/overlays/actors/ovl_Boss_01/z_boss_01.c
@@ -1,236 +1,638 @@
/*
* File: z_boss_01.c
* Overlay: ovl_Boss_01
- * Description: Odolwa
+ * Description: Odolwa, his bugs, and his afterimages.
+ *
+ * In addition to handling Odolwa, this actor is also responsible for handling the bugs that Odolwa can spawn as well as
+ * the afterimages that appear when Odolwa jumps or does a spin attack. The bugs are effectively their own actor with
+ * their own Update and Draw functions and a whole dedicated section of the file specifically for them. Afterimages are
+ * much simpler, and their code is more interleaved with Odolwa's code. This actor also handles the falling block and
+ * ring of fire effects that Odolwa can summon.
+ *
+ * Odolwa's behavior can primarily be divided into two states: waiting and attacking. He has a variety of wait actions
+ * and attack types that he can select between, and this can make it difficult to tell when he is waiting and when he is
+ * attacking. Two of his wait actions look like attacks, and when selecting an attack, he can randomly choose to dance
+ * for a bit before attacking. Additionally, Odolwa can attack the player via more indirect means while waiting by
+ * summoning bugs, by dropping falling blocks on them, or by summoning a ring of fire to surround them.
+ *
+ * Outside of waiting and attacking, most of Odolwa's behavior involves either dodging attacks (e.g., by jumping,
+ * running around, or guarding) or reacting to attacks. He also handles all his cutscenes (his intro cutscene, the
+ * cutscene where he summons bugs, and his death cutscene) manually via actionFuncs.
+ *
+ * Odolwa's fight can be divided roughly into two phases, but unlike most bosses, the second phase does not happen when
+ * his health reaches a certain point. Rather, the second phase starts when 1000 or more frames have passed since the
+ * fight began. At this point, he will play the bug summoning cutscene, and more wait actions will be available for him
+ * to randomly select; these new wait actions are what allow him to summon a ring of fire, drop falling blocks, etc.
*/
#include "z_boss_01.h"
#include "z64rumble.h"
#include "z64shrink_window.h"
+#include "assets/objects/gameplay_keep/gameplay_keep.h"
+#include "overlays/actors/ovl_Door_Warp1/z_door_warp1.h"
+#include "overlays/actors/ovl_En_Clear_Tag/z_en_clear_tag.h"
+#include "overlays/actors/ovl_En_Tanron1/z_en_tanron1.h"
+#include "overlays/actors/ovl_Item_B_Heart/z_item_b_heart.h"
#define FLAGS (ACTOR_FLAG_TARGETABLE | ACTOR_FLAG_UNFRIENDLY | ACTOR_FLAG_10 | ACTOR_FLAG_20)
#define THIS ((Boss01*)thisx)
+#define ODOLWA_EFFECT_COUNT 100
+
+// This actor has an array of timers in its instance, but it only ever uses the first entry.
+#define TIMER_CURRENT_ACTION 0
+#define TIMER_BUG_CURRENT_ACTION 0
+#define TIMER_AFTERIMAGE_DESPAWN 0
+
void Boss01_Init(Actor* thisx, PlayState* play);
void Boss01_Destroy(Actor* thisx, PlayState* play);
-void Boss01_Update(Actor* thisx, PlayState* play);
+void Boss01_Update(Actor* thisx, PlayState* play2);
void Boss01_Draw(Actor* thisx, PlayState* play);
-void func_809D12B4(Boss01* this, PlayState* play);
-void func_809D1B2C(Boss01* this, PlayState* play);
-void func_809D1E74(Boss01* this, PlayState* play);
-void func_809D20D0(Boss01* this, PlayState* play);
-void func_809D25E8(Boss01* this, PlayState* play);
-void func_809D26B8(Boss01* this, PlayState* play);
-void func_809D27D4(Boss01* this, PlayState* play);
-void func_809D2AA0(Boss01* this, PlayState* play);
-void func_809D2BCC(Boss01* this, PlayState* play);
-void func_809D2CDC(Boss01* this, PlayState* play);
-void func_809D2E4C(Boss01* this, PlayState* play);
-void func_809D30D0(Boss01* this, PlayState* play);
-void func_809D345C(Boss01* this, PlayState* play);
-void func_809D3530(Boss01* this, PlayState* play);
-void func_809D3ADC(Boss01* this, PlayState* play);
-void func_809D3CD0(Boss01* this, PlayState* play);
-void func_809D4464(Boss01* this, PlayState* play);
-void func_809D6314(Boss01* this, PlayState* play);
-void func_809D6488(Boss01* this, PlayState* play);
-void func_809D6588(Boss01* this, PlayState* play);
-void func_809D65E0(Boss01* this, PlayState* play);
+void Boss01_SetupIntroCutscene(Boss01* this, PlayState* play);
+void Boss01_IntroCutscene(Boss01* this, PlayState* play);
+void Boss01_SummonBugsCutscene(Boss01* this, PlayState* play);
+void Boss01_Afterimage_SetupWaitToDespawn(Boss01* this, PlayState* play);
+void Boss01_Afterimage_WaitToDespawn(Boss01* this, PlayState* play);
+void Boss01_SetupWait(Boss01* this, PlayState* play, u8 waitType);
+void Boss01_Wait(Boss01* this, PlayState* play);
+void Boss01_Dazed(Boss01* this, PlayState* play);
+void Boss01_SetupSpinAttack(Boss01* this, PlayState* play);
+void Boss01_SpinAttack(Boss01* this, PlayState* play);
+void Boss01_SetupDanceBeforeAttack(Boss01* this, PlayState* play);
+void Boss01_DanceBeforeAttack(Boss01* this, PlayState* play);
+void Boss01_SetupRun(Boss01* this, PlayState* play);
+void Boss01_Run(Boss01* this, PlayState* play);
+void Boss01_SetupJump(Boss01* this, PlayState* play, u8 shouldPerformFallingSlash);
+void Boss01_JumpSquat(Boss01* this, PlayState* play);
+void Boss01_Jump(Boss01* this, PlayState* play);
+void Boss01_JumpLand(Boss01* this, PlayState* play);
+void Boss01_SetupVerticalSlash(Boss01* this, PlayState* play);
+void Boss01_VerticalSlash(Boss01* this, PlayState* play);
+void Boss01_SetupHorizontalSlash(Boss01* this, PlayState* play);
+void Boss01_HorizontalSlash(Boss01* this, PlayState* play);
+void Boss01_Guard(Boss01* this, PlayState* play);
+void Boss01_SetupKick(Boss01* this, PlayState* play);
+void Boss01_Kick(Boss01* this, PlayState* play);
+void Boss01_SetupShieldBash(Boss01* this, PlayState* play);
+void Boss01_ShieldBash(Boss01* this, PlayState* play);
+void Boss01_Damaged(Boss01* this, PlayState* play);
+void Boss01_SetupSummonMoths(Boss01* this, PlayState* play);
+void Boss01_SummonMoths(Boss01* this, PlayState* play);
+void Boss01_SetupDeathCutscene(Boss01* this, PlayState* play);
+void Boss01_DeathCutscene(Boss01* this, PlayState* play);
+void Boss01_SetupStunned(Boss01* this, PlayState* play);
+void Boss01_Stunned(Boss01* this, PlayState* play);
+void Boss01_Afterimage_Draw(Actor* thisx, PlayState* play);
+void Boss01_GenShadowTex(u8* tex, Boss01* this, PlayState* play);
+void Boss01_DrawShadowTex(u8* tex, Boss01* this, PlayState* play);
+void Boss01_Bug_SetupCrawl(Boss01* this, PlayState* play);
+void Boss01_Bug_Crawl(Boss01* this, PlayState* play);
+void Boss01_Bug_Damaged(Boss01* this, PlayState* play);
+void Boss01_Bug_SetupDead(Boss01* this, PlayState* play);
+void Boss01_Bug_Stunned(Boss01* this, PlayState* play);
+void Boss01_Bug_Dead(Boss01* this, PlayState* play);
+void Boss01_Bug_Update(Actor* thisx, PlayState* play);
+void Boss01_Bug_Draw(Actor* thisx, PlayState* play);
+void Boss01_UpdateEffects(Boss01* this, PlayState* play);
+void Boss01_DrawEffects(PlayState* play);
-#if 0
-// static DamageTable sDamageTable = {
-static DamageTable D_809D7990 = {
- /* Deku Nut */ DMG_ENTRY(0, 0x1),
- /* Deku Stick */ DMG_ENTRY(1, 0xF),
- /* Horse trample */ DMG_ENTRY(0, 0x0),
- /* Explosives */ DMG_ENTRY(1, 0xE),
- /* Zora boomerang */ DMG_ENTRY(1, 0xF),
- /* Normal arrow */ DMG_ENTRY(1, 0xF),
- /* UNK_DMG_0x06 */ DMG_ENTRY(0, 0x0),
- /* Hookshot */ DMG_ENTRY(0, 0x0),
- /* Goron punch */ DMG_ENTRY(1, 0xE),
- /* Sword */ DMG_ENTRY(1, 0xE),
- /* Goron pound */ DMG_ENTRY(1, 0xF),
- /* Fire arrow */ DMG_ENTRY(2, 0x2),
- /* Ice arrow */ DMG_ENTRY(2, 0x3),
- /* Light arrow */ DMG_ENTRY(2, 0x4),
- /* Goron spikes */ DMG_ENTRY(1, 0xF),
- /* Deku spin */ DMG_ENTRY(1, 0xF),
- /* Deku bubble */ DMG_ENTRY(0, 0x1),
- /* Deku launch */ DMG_ENTRY(1, 0xE),
- /* UNK_DMG_0x12 */ DMG_ENTRY(0, 0x1),
- /* Zora barrier */ DMG_ENTRY(0, 0xB),
- /* Normal shield */ DMG_ENTRY(0, 0x0),
- /* Light ray */ DMG_ENTRY(0, 0x0),
- /* Thrown object */ DMG_ENTRY(1, 0xF),
- /* Zora punch */ DMG_ENTRY(1, 0xE),
- /* Spin attack */ DMG_ENTRY(1, 0xD),
- /* Sword beam */ DMG_ENTRY(2, 0xC),
- /* Normal Roll */ DMG_ENTRY(0, 0x0),
- /* UNK_DMG_0x1B */ DMG_ENTRY(0, 0x0),
- /* UNK_DMG_0x1C */ DMG_ENTRY(0, 0x0),
- /* Unblockable */ DMG_ENTRY(0, 0x0),
- /* UNK_DMG_0x1E */ DMG_ENTRY(0, 0x0),
- /* Powder Keg */ DMG_ENTRY(1, 0xE),
+typedef enum {
+ /* 0 */ ODOLWA_EFFECT_NONE,
+ /* 1 */ ODOLWA_EFFECT_FALLING_BLOCK, // The blocks that fall from the ceiling and their fragments
+ /* 3 */ ODOLWA_EFFECT_RING_OF_FIRE = 3 // The ring of fire that surrounds the player
+} OdolwaEffectType;
+
+typedef enum {
+ /* 0 */ ODOLWA_WAIT_READY,
+ /* 1 */ ODOLWA_WAIT_SPIN_SWORD,
+ /* 2 */ ODOLWA_WAIT_VERTICAL_HOP,
+ /* 3 */ ODOLWA_WAIT_SHAKE_DANCE,
+ /* 4 */ ODOLWA_WAIT_UP_AND_DOWN_DANCE,
+ /* 5 */ ODOLWA_WAIT_ARM_SWING_DANCE,
+ /* 6 */ ODOLWA_WAIT_THRUST_ATTACK,
+ /* 7 */ ODOLWA_WAIT_DOUBLE_SLASH,
+ /* 8 */ ODOLWA_WAIT_SIDE_TO_SIDE_HOP,
+ /* 9 */ ODOLWA_WAIT_SIDE_TO_SIDE_DANCE,
+ /* 10 */ ODOLWA_WAIT_SPIN_DANCE,
+ /* 11 */ ODOLWA_WAIT_JUMP_DANCE,
+ /* 12 */ ODOLWA_WAIT_MAX,
+
+ // This doesn't correspond to an actual wait action that Odolwa can perform, but it can be passed as a parameter to
+ // Boss01_SetupWait to randomly select between one of Odolwa's available wait types (as well as having a random
+ // chance to summon moths instead), assuming that the fight is in its second phase.
+ /* 100 */ ODOLWA_WAIT_RANDOM = 100
+} OdolwaWaitType;
+
+typedef enum {
+ // Waits for the player to walk forward, then starts the cutscene and transitions to the next state immediately.
+ // This state is also used to indiciate the cutscene ended.
+ /* 0 */ ODOLWA_INTRO_CS_STATE_WAITING_FOR_PLAYER_OR_DONE,
+
+ // Points the camera directly at the player, plays some of Odolwa's SFXs, and waits for 120 frames.
+ /* 1 */ ODOLWA_INTRO_CS_STATE_LOOK_AT_PLAYER,
+
+ // Pans up to watch Odolwa fall from the ceiling. Transitions to the next state once he's close to the ground.
+ /* 2 */ ODOLWA_INTRO_CS_STATE_ODOLWA_FALLING,
+
+ // Plays Odolwa's intro animation and shows his title card. Ends the cutscene after 140 frames.
+ /* 3 */ ODOLWA_INTRO_CS_STATE_ODOLWA_LANDED
+} OdolwaIntroCsState;
+
+typedef enum {
+ // Starts the cutscene and transitions to the next state immediately.
+ /* 0 */ ODOLWA_BUG_SUMMONING_CS_STATE_STARTED,
+
+ // This state lasts for the entire duration of the cutscene, and cutsceneState is left in this state when it ends.
+ /* 1 */ ODOLWA_BUG_SUMMONING_CS_STATE_PLAYING_OR_DONE
+} OdolwaBugSummoningCsState;
+
+typedef enum {
+ // Starts the cutscene and transitions to the next state immediately.
+ /* 0 */ ODOLWA_DEATH_CS_STATE_STARTED,
+
+ // Plays Odolwa's death animation and makes him fall face-first towards the ground. Ends after 71 frames.
+ /* 1 */ ODOLWA_DEATH_CS_STATE_PLAY_ANIM_AND_FALL_FORWARD,
+
+ // Surrounds Odolwa with fire and slowly shrinks him. After 180 frames, spawns the blue warp and ends the cutscene.
+ /* 2 */ ODOLWA_DEATH_CS_STATE_BURST_INTO_FLAMES_AND_SHRINK,
+
+ // Signals that the cutscene has ended. Also teleports Odolwa far above the ceiling.
+ /* 3 */ ODOLWA_DEATH_CS_STATE_DONE
+} OdolwaDeathCsState;
+
+typedef enum {
+ /* 0 */ ODOLWA_DRAW_DMGEFF_STATE_NONE,
+ /* 1 */ ODOLWA_DRAW_DMGEFF_STATE_FIRE_INIT,
+ /* 2 */ ODOLWA_DRAW_DMGEFF_STATE_FIRE_ACTIVE,
+ /* 10 */ ODOLWA_DRAW_DMGEFF_STATE_FROZEN_INIT = 10,
+ /* 11 */ ODOLWA_DRAW_DMGEFF_STATE_FROZEN_ACTIVE,
+ /* 20 */ ODOLWA_DRAW_DMGEFF_STATE_LIGHT_ORB_INIT = 20,
+ /* 21 */ ODOLWA_DRAW_DMGEFF_STATE_LIGHT_ORB_ACTIVE,
+ /* 30 */ ODOLWA_DRAW_DMGEFF_STATE_BLUE_LIGHT_ORB_INIT = 30,
+ /* 40 */ ODOLWA_DRAW_DMGEFF_STATE_ELECTRIC_SPARKS_INIT = 40,
+ /* 41 */ ODOLWA_DRAW_DMGEFF_STATE_ELECTRIC_SPARKS_ACTIVE
+} OdolwaDrawDmgEffState;
+
+typedef enum {
+ /* 0 */ ODOLWA_SHADOW_SIZE_MEDIUM,
+ /* 1 */ ODOLWA_SHADOW_SIZE_LARGE,
+ /* 2 */ ODOLWA_SHADOW_SIZE_EXTRA_LARGE,
+ /* 3 */ ODOLWA_SHADOW_SIZE_SMALL
+} OdolwaShadowSize;
+
+typedef enum {
+ // There are no AT colliders enabled; the player can pass through the sword without reacting or taking any damage.
+ /* 0 */ ODOLWA_SWORD_STATE_INACTIVE,
+
+ // The sword's two AT colliders are enabled; the player will be knocked back and take damage if they touch the
+ // sword. There is a third collider originating from Odolwa's pelvis that is also active, but it is offset very far
+ // out-of-bounds in this state, so the player can never touch it.
+ /* 1 */ ODOLWA_SWORD_STATE_ACTIVE,
+
+ // Similar to the previous state, but the pelvis collider is now placed on the floor in front of Odolwa.
+ /* 2 */ ODOLWA_SWORD_STATE_HORIZONTAL_SLASH
+} OdolwaSwordState;
+
+/**
+ * When falling blocks spawn, the sound effect of them falling always plays from this position, regardless of where they
+ * actually spawn.
+ */
+static Vec3f sFallingBlockSfxPos = { 0.0f, 1000.0f, 0.0f };
+
+/**
+ * Odolwa's sword trail is a circular arc, and this variable is used to determine the angular range of this arc (and
+ * thus the total size of the trail). The trail consists of 10 segments in an arc, and the angle of each of those
+ * segments is M_PI / sSwordTrailAngularRangeDivisor; in other words, as this variable decreases, Odolwa's sword trail
+ * covers a larger angular range, and as it increaes, the sword trail covers a smaller angular range.
+ */
+static f32 sSwordTrailAngularRangeDivisor = 10.0f;
+
+typedef enum {
+ // Named based on the fact that everything with this damage effect deals zero damage. If this effect is given to an
+ // attack that deals non-zero damage, it will behave exactly like ODOLWA_DMGEFF_DAMAGE.
+ /* 0x0 */ ODOLWA_DMGEFF_IMMUNE,
+
+ // Deals no damage, but turns Odolwa blue, stops all animations, and makes him wait in place for 40 frames.
+ /* 0x1 */ ODOLWA_DMGEFF_STUN,
+
+ // Deals damage and surrounds Odolwa with fire.
+ /* 0x2 */ ODOLWA_DMGEFF_FIRE,
+
+ // Behaves exactly like ODOLWA_DMGEFF_STUN, but also surrounds Odolwa with ice.
+ /* 0x3 */ ODOLWA_DMGEFF_FREEZE,
+
+ // Deals damage and surrounds Odolwa with yellow light orbs.
+ /* 0x4 */ ODOLWA_DMGEFF_LIGHT_ORB,
+
+ // Behaves exactly like ODOLWA_DMGEFF_STUN, but also surrounds Odolwa in electric sparks.
+ /* 0xB */ ODOLWA_DMGEFF_ELECTRIC_STUN = 0xB,
+
+ // Deals damage and surrounds Odolwa with blue light orbs.
+ /* 0xC */ ODOLWA_DMGEFF_BLUE_LIGHT_ORB,
+
+ // Deals damage and has no special effect.
+ /* 0xD */ ODOLWA_DMGEFF_DAMAGE,
+
+ // Deals damage and checks the timer that tracks how long Odolwa should be in his damaged state. If the timer is 7
+ // or more, it will reset the timer to 20 frames keep Odolwa in the damaged state for longer. If the timer is 6 or
+ // less, it will disable Odolwa's collision for 20 frames to ensure he can jump away without taking further damage.
+ /* 0xE */ ODOLWA_DMGEFF_DAMAGE_TIMER_CHECK,
+
+ // Deals no damage, but makes Odolwa play his dazed animation for 70 frames and be vulnerable to attacks.
+ /* 0xF */ ODOLWA_DMGEFF_DAZE
+} OdolwaDamageEffect;
+
+static DamageTable sOdolwaDamageTable = {
+ /* Deku Nut */ DMG_ENTRY(0, ODOLWA_DMGEFF_STUN),
+ /* Deku Stick */ DMG_ENTRY(1, ODOLWA_DMGEFF_DAZE),
+ /* Horse trample */ DMG_ENTRY(0, ODOLWA_DMGEFF_IMMUNE),
+ /* Explosives */ DMG_ENTRY(1, ODOLWA_DMGEFF_DAMAGE_TIMER_CHECK),
+ /* Zora boomerang */ DMG_ENTRY(1, ODOLWA_DMGEFF_DAZE),
+ /* Normal arrow */ DMG_ENTRY(1, ODOLWA_DMGEFF_DAZE),
+ /* UNK_DMG_0x06 */ DMG_ENTRY(0, ODOLWA_DMGEFF_IMMUNE),
+ /* Hookshot */ DMG_ENTRY(0, ODOLWA_DMGEFF_IMMUNE),
+ /* Goron punch */ DMG_ENTRY(1, ODOLWA_DMGEFF_DAMAGE_TIMER_CHECK),
+ /* Sword */ DMG_ENTRY(1, ODOLWA_DMGEFF_DAMAGE_TIMER_CHECK),
+ /* Goron pound */ DMG_ENTRY(1, ODOLWA_DMGEFF_DAZE),
+ /* Fire arrow */ DMG_ENTRY(2, ODOLWA_DMGEFF_FIRE),
+ /* Ice arrow */ DMG_ENTRY(2, ODOLWA_DMGEFF_FREEZE),
+ /* Light arrow */ DMG_ENTRY(2, ODOLWA_DMGEFF_LIGHT_ORB),
+ /* Goron spikes */ DMG_ENTRY(1, ODOLWA_DMGEFF_DAZE),
+ /* Deku spin */ DMG_ENTRY(1, ODOLWA_DMGEFF_DAZE),
+ /* Deku bubble */ DMG_ENTRY(0, ODOLWA_DMGEFF_STUN),
+ /* Deku launch */ DMG_ENTRY(1, ODOLWA_DMGEFF_DAMAGE_TIMER_CHECK),
+ /* UNK_DMG_0x12 */ DMG_ENTRY(0, ODOLWA_DMGEFF_STUN),
+ /* Zora barrier */ DMG_ENTRY(0, ODOLWA_DMGEFF_ELECTRIC_STUN),
+ /* Normal shield */ DMG_ENTRY(0, ODOLWA_DMGEFF_IMMUNE),
+ /* Light ray */ DMG_ENTRY(0, ODOLWA_DMGEFF_IMMUNE),
+ /* Thrown object */ DMG_ENTRY(1, ODOLWA_DMGEFF_DAZE),
+ /* Zora punch */ DMG_ENTRY(1, ODOLWA_DMGEFF_DAMAGE_TIMER_CHECK),
+ /* Spin attack */ DMG_ENTRY(1, ODOLWA_DMGEFF_DAMAGE),
+ /* Sword beam */ DMG_ENTRY(2, ODOLWA_DMGEFF_BLUE_LIGHT_ORB),
+ /* Normal Roll */ DMG_ENTRY(0, ODOLWA_DMGEFF_IMMUNE),
+ /* UNK_DMG_0x1B */ DMG_ENTRY(0, ODOLWA_DMGEFF_IMMUNE),
+ /* UNK_DMG_0x1C */ DMG_ENTRY(0, ODOLWA_DMGEFF_IMMUNE),
+ /* Unblockable */ DMG_ENTRY(0, ODOLWA_DMGEFF_IMMUNE),
+ /* UNK_DMG_0x1E */ DMG_ENTRY(0, ODOLWA_DMGEFF_IMMUNE),
+ /* Powder Keg */ DMG_ENTRY(1, ODOLWA_DMGEFF_DAMAGE_TIMER_CHECK),
};
-// static DamageTable sDamageTable = {
-static DamageTable D_809D79B0 = {
- /* Deku Nut */ DMG_ENTRY(0, 0x1),
- /* Deku Stick */ DMG_ENTRY(1, 0xF),
- /* Horse trample */ DMG_ENTRY(0, 0x0),
- /* Explosives */ DMG_ENTRY(2, 0xF),
- /* Zora boomerang */ DMG_ENTRY(0, 0x1),
- /* Normal arrow */ DMG_ENTRY(2, 0xF),
- /* UNK_DMG_0x06 */ DMG_ENTRY(0, 0x0),
- /* Hookshot */ DMG_ENTRY(0, 0x0),
- /* Goron punch */ DMG_ENTRY(1, 0xF),
- /* Sword */ DMG_ENTRY(1, 0xF),
- /* Goron pound */ DMG_ENTRY(1, 0xF),
- /* Fire arrow */ DMG_ENTRY(2, 0x2),
- /* Ice arrow */ DMG_ENTRY(2, 0x3),
- /* Light arrow */ DMG_ENTRY(2, 0x4),
- /* Goron spikes */ DMG_ENTRY(1, 0xF),
- /* Deku spin */ DMG_ENTRY(0, 0x1),
- /* Deku bubble */ DMG_ENTRY(0, 0x1),
- /* Deku launch */ DMG_ENTRY(1, 0xF),
- /* UNK_DMG_0x12 */ DMG_ENTRY(0, 0x1),
- /* Zora barrier */ DMG_ENTRY(0, 0x1),
- /* Normal shield */ DMG_ENTRY(0, 0x0),
- /* Light ray */ DMG_ENTRY(0, 0x0),
- /* Thrown object */ DMG_ENTRY(1, 0xF),
- /* Zora punch */ DMG_ENTRY(1, 0xE),
- /* Spin attack */ DMG_ENTRY(2, 0xD),
- /* Sword beam */ DMG_ENTRY(2, 0xD),
- /* Normal Roll */ DMG_ENTRY(0, 0x0),
- /* UNK_DMG_0x1B */ DMG_ENTRY(0, 0x0),
- /* UNK_DMG_0x1C */ DMG_ENTRY(0, 0x0),
- /* Unblockable */ DMG_ENTRY(0, 0x0),
- /* UNK_DMG_0x1E */ DMG_ENTRY(0, 0x0),
- /* Powder Keg */ DMG_ENTRY(1, 0xF),
+typedef enum {
+ // Named based on the fact that everything with this damage effect deals zero damage. If this effect is given to an
+ // attack that deals non-zero damage, it will behave exactly like BUG_DMGEFF_DAMAGE.
+ /* 0x0 */ BUG_DMGEFF_IMMUNE,
+
+ // Deals no damage, but turns the bug blue, stops all animations, and makes it wait in place for 40 frames.
+ /* 0x1 */ BUG_DMGEFF_STUN,
+
+ // Named after the only attack that uses it. Behaves exactly like BUG_DMGEFF_DAMAGE.
+ /* 0x2 */ BUG_DMGEFF_FIRE_ARROW,
+
+ // Named after the only attack that uses it. Behaves exactly like BUG_DMGEFF_DAMAGE.
+ /* 0x3 */ BUG_DMGEFF_ICE_ARROW,
+
+ // Named after the only attack that uses it. Behaves exactly like BUG_DMGEFF_DAMAGE.
+ /* 0x4 */ BUG_DMGEFF_LIGHT_ARROW,
+
+ // Named after the only two attacks that use it. Behaves exactly like BUG_DMGEFF_DAMAGE.
+ /* 0xD */ BUG_DMGEFF_SPIN_ATTACK_AND_SWORD_BEAM = 0xD,
+
+ // Named after the only attack that uses it. Behaves exactly like BUG_DMGEFF_DAMAGE.
+ /* 0xE */ BUG_DMGEFF_ZORA_PUNCH,
+
+ // Deals damage and has no special effect.
+ /* 0xF */ BUG_DMGEFF_DAMAGE
+} BugDamageEffect;
+
+static DamageTable sBugDamageTable = {
+ /* Deku Nut */ DMG_ENTRY(0, BUG_DMGEFF_STUN),
+ /* Deku Stick */ DMG_ENTRY(1, BUG_DMGEFF_DAMAGE),
+ /* Horse trample */ DMG_ENTRY(0, BUG_DMGEFF_IMMUNE),
+ /* Explosives */ DMG_ENTRY(2, BUG_DMGEFF_DAMAGE),
+ /* Zora boomerang */ DMG_ENTRY(0, BUG_DMGEFF_STUN),
+ /* Normal arrow */ DMG_ENTRY(2, BUG_DMGEFF_DAMAGE),
+ /* UNK_DMG_0x06 */ DMG_ENTRY(0, BUG_DMGEFF_IMMUNE),
+ /* Hookshot */ DMG_ENTRY(0, BUG_DMGEFF_IMMUNE),
+ /* Goron punch */ DMG_ENTRY(1, BUG_DMGEFF_DAMAGE),
+ /* Sword */ DMG_ENTRY(1, BUG_DMGEFF_DAMAGE),
+ /* Goron pound */ DMG_ENTRY(1, BUG_DMGEFF_DAMAGE),
+ /* Fire arrow */ DMG_ENTRY(2, BUG_DMGEFF_FIRE_ARROW),
+ /* Ice arrow */ DMG_ENTRY(2, BUG_DMGEFF_ICE_ARROW),
+ /* Light arrow */ DMG_ENTRY(2, BUG_DMGEFF_LIGHT_ARROW),
+ /* Goron spikes */ DMG_ENTRY(1, BUG_DMGEFF_DAMAGE),
+ /* Deku spin */ DMG_ENTRY(0, BUG_DMGEFF_STUN),
+ /* Deku bubble */ DMG_ENTRY(0, BUG_DMGEFF_STUN),
+ /* Deku launch */ DMG_ENTRY(1, BUG_DMGEFF_DAMAGE),
+ /* UNK_DMG_0x12 */ DMG_ENTRY(0, BUG_DMGEFF_STUN),
+ /* Zora barrier */ DMG_ENTRY(0, BUG_DMGEFF_STUN),
+ /* Normal shield */ DMG_ENTRY(0, BUG_DMGEFF_IMMUNE),
+ /* Light ray */ DMG_ENTRY(0, BUG_DMGEFF_IMMUNE),
+ /* Thrown object */ DMG_ENTRY(1, BUG_DMGEFF_DAMAGE),
+ /* Zora punch */ DMG_ENTRY(1, BUG_DMGEFF_ZORA_PUNCH),
+ /* Spin attack */ DMG_ENTRY(2, BUG_DMGEFF_SPIN_ATTACK_AND_SWORD_BEAM),
+ /* Sword beam */ DMG_ENTRY(2, BUG_DMGEFF_SPIN_ATTACK_AND_SWORD_BEAM),
+ /* Normal Roll */ DMG_ENTRY(0, BUG_DMGEFF_IMMUNE),
+ /* UNK_DMG_0x1B */ DMG_ENTRY(0, BUG_DMGEFF_IMMUNE),
+ /* UNK_DMG_0x1C */ DMG_ENTRY(0, BUG_DMGEFF_IMMUNE),
+ /* Unblockable */ DMG_ENTRY(0, BUG_DMGEFF_IMMUNE),
+ /* UNK_DMG_0x1E */ DMG_ENTRY(0, BUG_DMGEFF_IMMUNE),
+ /* Powder Keg */ DMG_ENTRY(1, BUG_DMGEFF_DAMAGE),
};
-// static ColliderJntSphElementInit sJntSphElementsInit[3] = {
-static ColliderJntSphElementInit D_809D79D0[3] = {
+// The limbs referenced here are not used. The spheres are positioned manually by Boss01_PostLimbDraw.
+static ColliderJntSphElementInit sSwordColliderJntSphElementsInit[3] = {
{
- { ELEMTYPE_UNK2, { 0xF7CFFFFF, 0x04, 0x04 }, { 0xF7CFFFFF, 0x00, 0x00 }, TOUCH_ON | TOUCH_SFX_NORMAL, BUMP_ON, OCELEM_ON, },
- { 0, { { 0, 0, 0 }, 35 }, 100 },
+ {
+ ELEMTYPE_UNK2,
+ { 0xF7CFFFFF, 0x04, 0x04 },
+ { 0xF7CFFFFF, 0x00, 0x00 },
+ TOUCH_ON | TOUCH_SFX_NORMAL,
+ BUMP_ON,
+ OCELEM_ON,
+ },
+ { ODOLWA_LIMB_NONE, { { 0, 0, 0 }, 35 }, 100 },
},
{
- { ELEMTYPE_UNK2, { 0xF7CFFFFF, 0x04, 0x04 }, { 0xF7CFFFFF, 0x00, 0x00 }, TOUCH_ON | TOUCH_SFX_NORMAL, BUMP_ON, OCELEM_ON, },
- { 0, { { 0, 0, 0 }, 35 }, 100 },
+ {
+ ELEMTYPE_UNK2,
+ { 0xF7CFFFFF, 0x04, 0x04 },
+ { 0xF7CFFFFF, 0x00, 0x00 },
+ TOUCH_ON | TOUCH_SFX_NORMAL,
+ BUMP_ON,
+ OCELEM_ON,
+ },
+ { ODOLWA_LIMB_NONE, { { 0, 0, 0 }, 35 }, 100 },
},
{
- { ELEMTYPE_UNK2, { 0xF7CFFFFF, 0x04, 0x04 }, { 0xF7CFFFFF, 0x00, 0x00 }, TOUCH_ON | TOUCH_SFX_NORMAL, BUMP_ON, OCELEM_ON, },
- { 0, { { 0, 0, 0 }, 70 }, 100 },
+ {
+ ELEMTYPE_UNK2,
+ { 0xF7CFFFFF, 0x04, 0x04 },
+ { 0xF7CFFFFF, 0x00, 0x00 },
+ TOUCH_ON | TOUCH_SFX_NORMAL,
+ BUMP_ON,
+ OCELEM_ON,
+ },
+ { ODOLWA_LIMB_NONE, { { 0, 0, 0 }, 70 }, 100 },
},
};
-// static ColliderJntSphInit sJntSphInit = {
-static ColliderJntSphInit D_809D7A3C = {
- { COLTYPE_METAL, AT_ON | AT_TYPE_ENEMY, AC_ON | AC_HARD | AC_TYPE_PLAYER, OC1_ON | OC1_TYPE_PLAYER, OC2_TYPE_1, COLSHAPE_JNTSPH, },
- ARRAY_COUNT(sJntSphElementsInit), D_809D79D0, // sJntSphElementsInit,
+static ColliderJntSphInit sSwordColliderJntSphInit = {
+ {
+ COLTYPE_METAL,
+ AT_ON | AT_TYPE_ENEMY,
+ AC_ON | AC_HARD | AC_TYPE_PLAYER,
+ OC1_ON | OC1_TYPE_PLAYER,
+ OC2_TYPE_1,
+ COLSHAPE_JNTSPH,
+ },
+ ARRAY_COUNT(sSwordColliderJntSphElementsInit),
+ sSwordColliderJntSphElementsInit,
};
-// static ColliderJntSphElementInit sJntSphElementsInit[1] = {
-static ColliderJntSphElementInit D_809D7A4C[1] = {
+// The limbs referenced here are not used. The spheres are positioned manually by Boss01_PostLimbDraw.
+static ColliderJntSphElementInit sShieldColliderJntSphElementsInit[1] = {
{
- { ELEMTYPE_UNK2, { 0xF7CFFFFF, 0x00, 0x04 }, { 0xF7CFFFFF, 0x00, 0x00 }, TOUCH_ON | TOUCH_SFX_NORMAL, BUMP_ON, OCELEM_ON, },
- { 0, { { 0, 0, 0 }, 36 }, 100 },
+ {
+ ELEMTYPE_UNK2,
+ { 0xF7CFFFFF, 0x00, 0x04 },
+ { 0xF7CFFFFF, 0x00, 0x00 },
+ TOUCH_ON | TOUCH_SFX_NORMAL,
+ BUMP_ON,
+ OCELEM_ON,
+ },
+ { ODOLWA_LIMB_NONE, { { 0, 0, 0 }, 36 }, 100 },
},
};
-// static ColliderJntSphInit sJntSphInit = {
-static ColliderJntSphInit D_809D7A70 = {
- { COLTYPE_METAL, AT_ON | AT_TYPE_ENEMY, AC_ON | AC_HARD | AC_TYPE_PLAYER, OC1_ON | OC1_TYPE_PLAYER, OC2_TYPE_1, COLSHAPE_JNTSPH, },
- ARRAY_COUNT(sJntSphElementsInit), D_809D7A4C, // sJntSphElementsInit,
+static ColliderJntSphInit sShieldColliderJntSphInit = {
+ {
+ COLTYPE_METAL,
+ AT_ON | AT_TYPE_ENEMY,
+ AC_ON | AC_HARD | AC_TYPE_PLAYER,
+ OC1_ON | OC1_TYPE_PLAYER,
+ OC2_TYPE_1,
+ COLSHAPE_JNTSPH,
+ },
+ ARRAY_COUNT(sShieldColliderJntSphElementsInit),
+ sShieldColliderJntSphElementsInit,
};
-// static ColliderJntSphElementInit sJntSphElementsInit[11] = {
-static ColliderJntSphElementInit D_809D7A80[11] = {
+// The limbs referenced here are not used. The spheres are positioned manually by Boss01_PostLimbDraw.
+static ColliderJntSphElementInit sBodyColliderJntSphElementsInit[11] = {
{
- { ELEMTYPE_UNK3, { 0xF7CFFFFF, 0x00, 0x04 }, { 0xF7CFFFFF, 0x00, 0x00 }, TOUCH_ON | TOUCH_SFX_NORMAL, BUMP_ON, OCELEM_ON, },
- { 0, { { 0, 0, 0 }, 20 }, 100 },
+ {
+ ELEMTYPE_UNK3,
+ { 0xF7CFFFFF, 0x00, 0x04 },
+ { 0xF7CFFFFF, 0x00, 0x00 },
+ TOUCH_ON | TOUCH_SFX_NORMAL,
+ BUMP_ON,
+ OCELEM_ON,
+ },
+ { ODOLWA_LIMB_NONE, { { 0, 0, 0 }, 20 }, 100 },
},
{
- { ELEMTYPE_UNK3, { 0xF7CFFFFF, 0x00, 0x04 }, { 0xF7CFFFFF, 0x00, 0x00 }, TOUCH_ON | TOUCH_SFX_NORMAL, BUMP_ON, OCELEM_ON, },
- { 1, { { 0, 0, 0 }, 30 }, 100 },
+ {
+ ELEMTYPE_UNK3,
+ { 0xF7CFFFFF, 0x00, 0x04 },
+ { 0xF7CFFFFF, 0x00, 0x00 },
+ TOUCH_ON | TOUCH_SFX_NORMAL,
+ BUMP_ON,
+ OCELEM_ON,
+ },
+ { ODOLWA_LIMB_ROOT, { { 0, 0, 0 }, 30 }, 100 },
},
{
- { ELEMTYPE_UNK3, { 0xF7CFFFFF, 0x00, 0x04 }, { 0xF7CFFFFF, 0x00, 0x00 }, TOUCH_ON | TOUCH_SFX_NORMAL, BUMP_ON, OCELEM_ON, },
- { 1, { { 0, 0, 0 }, 25 }, 100 },
+ {
+ ELEMTYPE_UNK3,
+ { 0xF7CFFFFF, 0x00, 0x04 },
+ { 0xF7CFFFFF, 0x00, 0x00 },
+ TOUCH_ON | TOUCH_SFX_NORMAL,
+ BUMP_ON,
+ OCELEM_ON,
+ },
+ { ODOLWA_LIMB_ROOT, { { 0, 0, 0 }, 25 }, 100 },
},
{
- { ELEMTYPE_UNK3, { 0xF7CFFFFF, 0x00, 0x04 }, { 0xF7CFFFFF, 0x00, 0x00 }, TOUCH_ON | TOUCH_SFX_NORMAL, BUMP_ON, OCELEM_ON, },
- { 1, { { 0, 0, 0 }, 15 }, 100 },
+ {
+ ELEMTYPE_UNK3,
+ { 0xF7CFFFFF, 0x00, 0x04 },
+ { 0xF7CFFFFF, 0x00, 0x00 },
+ TOUCH_ON | TOUCH_SFX_NORMAL,
+ BUMP_ON,
+ OCELEM_ON,
+ },
+ { ODOLWA_LIMB_ROOT, { { 0, 0, 0 }, 15 }, 100 },
},
{
- { ELEMTYPE_UNK3, { 0xF7CFFFFF, 0x00, 0x04 }, { 0xF7CFFFFF, 0x00, 0x00 }, TOUCH_ON | TOUCH_SFX_NORMAL, BUMP_ON, OCELEM_ON, },
- { 1, { { 0, 0, 0 }, 15 }, 100 },
+ {
+ ELEMTYPE_UNK3,
+ { 0xF7CFFFFF, 0x00, 0x04 },
+ { 0xF7CFFFFF, 0x00, 0x00 },
+ TOUCH_ON | TOUCH_SFX_NORMAL,
+ BUMP_ON,
+ OCELEM_ON,
+ },
+ { ODOLWA_LIMB_ROOT, { { 0, 0, 0 }, 15 }, 100 },
},
{
- { ELEMTYPE_UNK3, { 0xF7CFFFFF, 0x00, 0x04 }, { 0xF7CFFFFF, 0x00, 0x00 }, TOUCH_ON | TOUCH_SFX_NORMAL, BUMP_ON, OCELEM_ON, },
- { 1, { { 0, 0, 0 }, 15 }, 100 },
+ {
+ ELEMTYPE_UNK3,
+ { 0xF7CFFFFF, 0x00, 0x04 },
+ { 0xF7CFFFFF, 0x00, 0x00 },
+ TOUCH_ON | TOUCH_SFX_NORMAL,
+ BUMP_ON,
+ OCELEM_ON,
+ },
+ { ODOLWA_LIMB_ROOT, { { 0, 0, 0 }, 15 }, 100 },
},
{
- { ELEMTYPE_UNK3, { 0xF7CFFFFF, 0x00, 0x04 }, { 0xF7CFFFFF, 0x00, 0x00 }, TOUCH_ON | TOUCH_SFX_NORMAL, BUMP_ON, OCELEM_ON, },
- { 1, { { 0, 0, 0 }, 15 }, 100 },
+ {
+ ELEMTYPE_UNK3,
+ { 0xF7CFFFFF, 0x00, 0x04 },
+ { 0xF7CFFFFF, 0x00, 0x00 },
+ TOUCH_ON | TOUCH_SFX_NORMAL,
+ BUMP_ON,
+ OCELEM_ON,
+ },
+ { ODOLWA_LIMB_ROOT, { { 0, 0, 0 }, 15 }, 100 },
},
{
- { ELEMTYPE_UNK3, { 0xF7CFFFFF, 0x00, 0x04 }, { 0xF7CFFFFF, 0x00, 0x00 }, TOUCH_ON | TOUCH_SFX_NORMAL, BUMP_ON, OCELEM_ON, },
- { 1, { { 0, 0, 0 }, 15 }, 100 },
+ {
+ ELEMTYPE_UNK3,
+ { 0xF7CFFFFF, 0x00, 0x04 },
+ { 0xF7CFFFFF, 0x00, 0x00 },
+ TOUCH_ON | TOUCH_SFX_NORMAL,
+ BUMP_ON,
+ OCELEM_ON,
+ },
+ { ODOLWA_LIMB_ROOT, { { 0, 0, 0 }, 15 }, 100 },
},
{
- { ELEMTYPE_UNK3, { 0xF7CFFFFF, 0x00, 0x04 }, { 0xF7CFFFFF, 0x00, 0x00 }, TOUCH_ON | TOUCH_SFX_NORMAL, BUMP_ON, OCELEM_ON, },
- { 1, { { 0, 0, 0 }, 15 }, 100 },
+ {
+ ELEMTYPE_UNK3,
+ { 0xF7CFFFFF, 0x00, 0x04 },
+ { 0xF7CFFFFF, 0x00, 0x00 },
+ TOUCH_ON | TOUCH_SFX_NORMAL,
+ BUMP_ON,
+ OCELEM_ON,
+ },
+ { ODOLWA_LIMB_ROOT, { { 0, 0, 0 }, 15 }, 100 },
},
{
- { ELEMTYPE_UNK3, { 0xF7CFFFFF, 0x00, 0x04 }, { 0xF7CFFFFF, 0x00, 0x00 }, TOUCH_ON | TOUCH_SFX_NORMAL, BUMP_ON, OCELEM_ON, },
- { 1, { { 0, 0, 0 }, 15 }, 100 },
+ {
+ ELEMTYPE_UNK3,
+ { 0xF7CFFFFF, 0x00, 0x04 },
+ { 0xF7CFFFFF, 0x00, 0x00 },
+ TOUCH_ON | TOUCH_SFX_NORMAL,
+ BUMP_ON,
+ OCELEM_ON,
+ },
+ { ODOLWA_LIMB_ROOT, { { 0, 0, 0 }, 15 }, 100 },
},
{
- { ELEMTYPE_UNK3, { 0xF7CFFFFF, 0x00, 0x04 }, { 0xF7CFFFFF, 0x00, 0x00 }, TOUCH_ON | TOUCH_SFX_NORMAL, BUMP_ON, OCELEM_ON, },
- { 1, { { 0, 0, 0 }, 15 }, 100 },
+ {
+ ELEMTYPE_UNK3,
+ { 0xF7CFFFFF, 0x00, 0x04 },
+ { 0xF7CFFFFF, 0x00, 0x00 },
+ TOUCH_ON | TOUCH_SFX_NORMAL,
+ BUMP_ON,
+ OCELEM_ON,
+ },
+ { ODOLWA_LIMB_ROOT, { { 0, 0, 0 }, 15 }, 100 },
},
};
-// static ColliderJntSphInit sJntSphInit = {
-static ColliderJntSphInit D_809D7C0C = {
- { COLTYPE_HIT3, AT_ON | AT_TYPE_ENEMY, AC_ON | AC_TYPE_PLAYER, OC1_ON | OC1_TYPE_PLAYER, OC2_TYPE_1, COLSHAPE_JNTSPH, },
- ARRAY_COUNT(sJntSphElementsInit), D_809D7A80, // sJntSphElementsInit,
+static ColliderJntSphInit sBodyColliderJntSphInit = {
+ {
+ COLTYPE_HIT3,
+ AT_ON | AT_TYPE_ENEMY,
+ AC_ON | AC_TYPE_PLAYER,
+ OC1_ON | OC1_TYPE_PLAYER,
+ OC2_TYPE_1,
+ COLSHAPE_JNTSPH,
+ },
+ ARRAY_COUNT(sBodyColliderJntSphElementsInit),
+ sBodyColliderJntSphElementsInit,
};
-// static ColliderJntSphElementInit sJntSphElementsInit[2] = {
-static ColliderJntSphElementInit D_809D7C1C[2] = {
+// The limbs referenced here are not used. The spheres are positioned manually by Boss01_PostLimbDraw.
+static ColliderJntSphElementInit sKickAndShieldBashColliderJntSphElementsInit[2] = {
{
- { ELEMTYPE_UNK3, { 0xF7CFFFFF, 0x04, 0x04 }, { 0xF7CFFFFF, 0x00, 0x00 }, TOUCH_ON | TOUCH_SFX_HARD, BUMP_ON, OCELEM_ON, },
- { 0, { { 0, 0, 0 }, 36 }, 100 },
+ {
+ ELEMTYPE_UNK3,
+ { 0xF7CFFFFF, 0x04, 0x04 },
+ { 0xF7CFFFFF, 0x00, 0x00 },
+ TOUCH_ON | TOUCH_SFX_HARD,
+ BUMP_ON,
+ OCELEM_ON,
+ },
+ { ODOLWA_LIMB_NONE, { { 0, 0, 0 }, 36 }, 100 },
},
{
- { ELEMTYPE_UNK3, { 0xF7CFFFFF, 0x04, 0x04 }, { 0xF7CFFFFF, 0x00, 0x00 }, TOUCH_ON | TOUCH_SFX_HARD, BUMP_ON, OCELEM_ON, },
- { 0, { { 0, 0, 0 }, 36 }, 100 },
+ {
+ ELEMTYPE_UNK3,
+ { 0xF7CFFFFF, 0x04, 0x04 },
+ { 0xF7CFFFFF, 0x00, 0x00 },
+ TOUCH_ON | TOUCH_SFX_HARD,
+ BUMP_ON,
+ OCELEM_ON,
+ },
+ { ODOLWA_LIMB_NONE, { { 0, 0, 0 }, 36 }, 100 },
},
};
-// static ColliderJntSphInit sJntSphInit = {
-static ColliderJntSphInit D_809D7C64 = {
- { COLTYPE_HIT3, AT_ON | AT_TYPE_ENEMY, AC_ON | AC_TYPE_PLAYER, OC1_ON | OC1_TYPE_PLAYER, OC2_TYPE_1, COLSHAPE_JNTSPH, },
- ARRAY_COUNT(sJntSphElementsInit), D_809D7C1C, // sJntSphElementsInit,
+static ColliderJntSphInit sKickAndShieldBashColliderJntSphInit = {
+ {
+ COLTYPE_HIT3,
+ AT_ON | AT_TYPE_ENEMY,
+ AC_ON | AC_TYPE_PLAYER,
+ OC1_ON | OC1_TYPE_PLAYER,
+ OC2_TYPE_1,
+ COLSHAPE_JNTSPH,
+ },
+ ARRAY_COUNT(sKickAndShieldBashColliderJntSphElementsInit),
+ sKickAndShieldBashColliderJntSphElementsInit,
};
-// static ColliderCylinderInit sCylinderInit = {
-static ColliderCylinderInit D_809D7C74 = {
- { COLTYPE_HIT3, AT_ON | AT_TYPE_ENEMY, AC_ON | AC_TYPE_PLAYER, OC1_ON | OC1_TYPE_ALL, OC2_TYPE_1, COLSHAPE_CYLINDER, },
- { ELEMTYPE_UNK3, { 0xF7CFFFFF, 0x00, 0x04 }, { 0xF7FFFFFF, 0x00, 0x00 }, TOUCH_ON | TOUCH_SFX_NORMAL, BUMP_ON, OCELEM_ON, },
+static ColliderCylinderInit sBugACColliderCylinderInit = {
+ {
+ COLTYPE_HIT3,
+ AT_ON | AT_TYPE_ENEMY,
+ AC_ON | AC_TYPE_PLAYER,
+ OC1_ON | OC1_TYPE_ALL,
+ OC2_TYPE_1,
+ COLSHAPE_CYLINDER,
+ },
+ {
+ ELEMTYPE_UNK3,
+ { 0xF7CFFFFF, 0x00, 0x04 },
+ { 0xF7FFFFFF, 0x00, 0x00 },
+ TOUCH_ON | TOUCH_SFX_NORMAL,
+ BUMP_ON,
+ OCELEM_ON,
+ },
{ 15, 15, 10, { 0, 0, 0 } },
};
-// static ColliderCylinderInit sCylinderInit = {
-static ColliderCylinderInit D_809D7CA0 = {
- { COLTYPE_HIT3, AT_ON | AT_TYPE_ENEMY, AC_ON | AC_TYPE_PLAYER, OC1_ON | OC1_TYPE_ALL, OC2_TYPE_1, COLSHAPE_CYLINDER, },
- { ELEMTYPE_UNK3, { 0xF7CFFFFF, 0x00, 0x04 }, { 0xF7FFFFFF, 0x00, 0x00 }, TOUCH_ON | TOUCH_SFX_NORMAL, BUMP_ON, OCELEM_ON, },
+static ColliderCylinderInit sBugATColliderCylinderInit = {
+ {
+ COLTYPE_HIT3,
+ AT_ON | AT_TYPE_ENEMY,
+ AC_ON | AC_TYPE_PLAYER,
+ OC1_ON | OC1_TYPE_ALL,
+ OC2_TYPE_1,
+ COLSHAPE_CYLINDER,
+ },
+ {
+ ELEMTYPE_UNK3,
+ { 0xF7CFFFFF, 0x00, 0x04 },
+ { 0xF7FFFFFF, 0x00, 0x00 },
+ TOUCH_ON | TOUCH_SFX_NORMAL,
+ BUMP_ON,
+ OCELEM_ON,
+ },
{ 8, 15, 10, { 0, 0, 0 } },
};
@@ -246,191 +648,2943 @@ ActorInit Boss_01_InitVars = {
/**/ Boss01_Draw,
};
-#endif
-
-extern DamageTable D_809D7990;
-extern DamageTable D_809D79B0;
-extern ColliderJntSphElementInit D_809D79D0[3];
-extern ColliderJntSphInit D_809D7A3C;
-extern ColliderJntSphElementInit D_809D7A4C[1];
-extern ColliderJntSphInit D_809D7A70;
-extern ColliderJntSphElementInit D_809D7A80[11];
-extern ColliderJntSphInit D_809D7C0C;
-extern ColliderJntSphElementInit D_809D7C1C[2];
-extern ColliderJntSphInit D_809D7C64;
-extern ColliderCylinderInit D_809D7C74;
-extern ColliderCylinderInit D_809D7CA0;
-
-extern UNK_TYPE D_06000C44;
-extern UNK_TYPE D_06001884;
-extern UNK_TYPE D_0600C338;
-extern UNK_TYPE D_0600C5E0;
-extern UNK_TYPE D_0600C7A8;
-extern UNK_TYPE D_0600E3E8;
-extern UNK_TYPE D_0600FDEC;
-extern UNK_TYPE D_0600FF94;
-extern UNK_TYPE D_06010980;
-extern UNK_TYPE D_060124CC;
-extern UNK_TYPE D_06012B70;
-extern UNK_TYPE D_06012D10;
-extern UNK_TYPE D_06012EBC;
-extern UNK_TYPE D_06013480;
-extern UNK_TYPE D_0601407C;
-extern UNK_TYPE D_06014F14;
-extern UNK_TYPE D_06015A30;
-extern UNK_TYPE D_06016168;
-extern UNK_TYPE D_060164CC;
-extern UNK_TYPE D_0601F6A4;
-extern UNK_TYPE D_060204AC;
-extern UNK_TYPE D_060213A8;
-extern UNK_TYPE D_06022550;
-
-#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Boss_01/func_809D0530.s")
-
-#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Boss_01/func_809D0550.s")
-
-#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Boss_01/func_809D0678.s")
-
-#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Boss_01/func_809D082C.s")
-
-#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Boss_01/func_809D089C.s")
-
-#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Boss_01/func_809D092C.s")
-
-#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Boss_01/func_809D0AA4.s")
-
-#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Boss_01/Boss01_Init.s")
-
-#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Boss_01/Boss01_Destroy.s")
-
-#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Boss_01/func_809D119C.s")
-
-#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Boss_01/func_809D1258.s")
-
-#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Boss_01/func_809D12B4.s")
-
-#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Boss_01/func_809D1AB8.s")
-
-#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Boss_01/func_809D1B2C.s")
-
-#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Boss_01/func_809D1E5C.s")
-
-#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Boss_01/func_809D1E74.s")
-
-#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Boss_01/func_809D1EA4.s")
-
-#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Boss_01/func_809D20D0.s")
-
-#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Boss_01/func_809D2588.s")
-
-#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Boss_01/func_809D25E8.s")
-
-#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Boss_01/func_809D2664.s")
-
-#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Boss_01/func_809D26B8.s")
-
-#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Boss_01/func_809D2780.s")
-
-#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Boss_01/func_809D27D4.s")
-
-#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Boss_01/func_809D2858.s")
-
-#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Boss_01/func_809D2914.s")
-
-#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Boss_01/func_809D2A44.s")
-
-#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Boss_01/func_809D2AA0.s")
-
-#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Boss_01/func_809D2BCC.s")
-
-#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Boss_01/func_809D2CDC.s")
-
-#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Boss_01/func_809D2DE8.s")
-
-#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Boss_01/func_809D2E4C.s")
-
-#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Boss_01/func_809D3074.s")
-
-#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Boss_01/func_809D30D0.s")
-
-#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Boss_01/func_809D32B4.s")
-
-#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Boss_01/func_809D3374.s")
-
-#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Boss_01/func_809D3400.s")
-
-#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Boss_01/func_809D345C.s")
-
-#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Boss_01/func_809D34D4.s")
-
-#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Boss_01/func_809D3530.s")
-
-#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Boss_01/func_809D35A8.s")
-
-#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Boss_01/func_809D365C.s")
-
-#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Boss_01/func_809D370C.s")
-
-#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Boss_01/func_809D3A7C.s")
-
-#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Boss_01/func_809D3ADC.s")
-
-#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Boss_01/func_809D3C10.s")
-
-#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Boss_01/func_809D3CD0.s")
-
-#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Boss_01/func_809D441C.s")
-
-#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Boss_01/func_809D4464.s")
-
-#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Boss_01/func_809D44C0.s")
-
-#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Boss_01/func_809D4668.s")
-
-#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Boss_01/Boss01_Update.s")
-
-#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Boss_01/func_809D519C.s")
-
-#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Boss_01/func_809D5584.s")
-
-#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Boss_01/func_809D55CC.s")
-
-#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Boss_01/Boss01_Draw.s")
-
-#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Boss_01/func_809D5B0C.s")
-
-#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Boss_01/func_809D5BC4.s")
-
-#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Boss_01/func_809D5FB4.s")
-
-#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Boss_01/func_809D606C.s")
-
-#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Boss_01/func_809D62D4.s")
-
-#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Boss_01/func_809D6314.s")
-
-#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Boss_01/func_809D6424.s")
-
-#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Boss_01/func_809D6488.s")
-
-#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Boss_01/func_809D64E0.s")
-
-#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Boss_01/func_809D6540.s")
-
-#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Boss_01/func_809D6588.s")
-
-#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Boss_01/func_809D65E0.s")
-
-#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Boss_01/func_809D670C.s")
-
-#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Boss_01/func_809D694C.s")
-
-#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Boss_01/func_809D6B08.s")
-
-#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Boss_01/func_809D6BB4.s")
-
-#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Boss_01/func_809D6C98.s")
-
-#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Boss_01/func_809D6E7C.s")
-
-#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Boss_01/func_809D73D4.s")
+static Color_RGBA8 sDustPrimColor = { 60, 50, 20, 255 };
+
+static Color_RGBA8 sDustEnvColor = { 40, 30, 30, 255 };
+
+typedef struct {
+ /* 0x00 */ Vec3f pos;
+ /* 0x0C */ Vec3f velocity;
+ /* 0x18 */ f32 gravity;
+ /* 0x1C */ s16 rotX;
+ /* 0x1E */ s16 rotY;
+ /* 0x20 */ UNK_TYPE1 unk_20[0x8];
+ /* 0x28 */ u8 type;
+ /* 0x2A */ s16 timer;
+ /* 0x2C */ s16 isFallingBlockFragment;
+ /* 0x2E */ s16 angularVelocityX;
+ /* 0x30 */ s16 angularVelocityY;
+ /* 0x34 */ f32 scale;
+ /* 0x38 */ f32 alpha;
+} OdolwaEffect; // size = 0x3C
+
+/**
+ * Keeps track of how many bugs are currently spawned and alive. When there are four or fewer bugs alive, Odolwa can
+ * summon more bugs or falling blocks.
+ */
+s16 sOdolwaBugCount;
+
+/**
+ * Pointer to the main Odolwa instance so that bugs and effects can check and manipulate his state.
+ */
+Boss01* sOdolwa;
+
+/**
+ * Pointer to the swarm of moths that Odolwa can summon.
+ */
+EnTanron1* sMothSwarm;
+
+/**
+ * Odolwa draws his sword trail manually by manipulating these variables during his horizontal and vertical slashes.
+ * Note that the position of the sword is offset from Odolwa's position.
+ */
+f32 sOdolwaSwordTrailPosX;
+f32 sOdolwaSwordTrailPosY;
+f32 sOdolwaSwordTrailPosZ;
+f32 sOdolwaSwordTrailRotX;
+f32 sOdolwaSwordTrailRotY;
+f32 sOdolwaSwordTrailRotZ;
+f32 sOdolwaSwordTrailAlpha;
+
+/**
+ * If the intro cutscene was skipped (because it was already watched), this timer will be used to determine when to
+ * start playing the boss theme.
+ */
+u8 sOdolwaMusicStartTimer;
+
+/**
+ * When Odolwa is damaged or dazed, the sound effect will play from this position. It is set to his projectedPos.
+ */
+Vec3f sOdolwaDamageSfxPos;
+
+s32 sOdolwaRandSeed1;
+s32 sOdolwaRandSeed2;
+s32 sOdolwaRandSeed3;
+
+/**
+ * Stores all of Odolwa's effects. Note that the first entry in this array is always reserved for the ring of fire, so
+ * falling blocks can only be placed from the second entry onwards.
+ */
+OdolwaEffect sOdolwaEffects[ODOLWA_EFFECT_COUNT];
+
+void Boss01_InitRand(s32 seedInit1, s32 seedInit2, s32 seedInit3) {
+ sOdolwaRandSeed1 = seedInit1;
+ sOdolwaRandSeed2 = seedInit2;
+ sOdolwaRandSeed3 = seedInit3;
+}
+
+f32 Boss01_RandZeroOne(void) {
+ // Wichmann-Hill algorithm
+ f32 randFloat;
+
+ sOdolwaRandSeed1 = (sOdolwaRandSeed1 * 171) % 30269;
+ sOdolwaRandSeed2 = (sOdolwaRandSeed2 * 172) % 30307;
+ sOdolwaRandSeed3 = (sOdolwaRandSeed3 * 170) % 30323;
+
+ randFloat = (sOdolwaRandSeed1 / 30269.0f) + (sOdolwaRandSeed2 / 30307.0f) + (sOdolwaRandSeed3 / 30323.0f);
+
+ while (randFloat >= 1.0f) {
+ randFloat -= 1.0f;
+ }
+
+ return fabsf(randFloat);
+}
+
+/**
+ * Spawns a falling block or a fragment of a block that has hit the ground at the specified position.
+ */
+void Boss01_SpawnEffectFallingBlock(OdolwaEffect* effect, Vec3f* pos, s16 isFragment) {
+ s16 i;
+
+ for (i = 1; i < ODOLWA_EFFECT_COUNT; i++, effect++) {
+ if (effect->type == ODOLWA_EFFECT_NONE) {
+ effect->type = ODOLWA_EFFECT_FALLING_BLOCK;
+ effect->pos = *pos;
+ effect->timer = 0;
+ effect->rotX = Rand_ZeroFloat(0x10000);
+ effect->rotY = Rand_ZeroFloat(0x10000);
+ effect->isFallingBlockFragment = isFragment;
+ effect->gravity = -1.0f;
+
+ if (!isFragment) {
+ effect->angularVelocityY = Rand_CenteredFloat(0x320);
+ effect->angularVelocityX = Rand_CenteredFloat(0x320);
+ effect->scale = 8.0f * 0.001f;
+ effect->velocity = gZeroVec3f;
+ } else {
+ effect->angularVelocityY = Rand_CenteredFloat(0x1F40);
+ effect->angularVelocityX = Rand_CenteredFloat(0x1F40);
+ effect->scale = Rand_CenteredFloat(1.6f * 0.001f) + (3.2f * 0.001f);
+ effect->velocity.x = Rand_CenteredFloat(13.0f);
+ effect->velocity.y = Rand_ZeroFloat(4.0f) + 7.0f;
+ effect->velocity.z = Rand_CenteredFloat(13.0f);
+ }
+
+ break;
+ }
+ }
+}
+
+/**
+ * Spawns the ring of fire at the specified position.
+ */
+void Boss01_SpawnEffectRingOfFire(OdolwaEffect* effect, Vec3f* pos) {
+ if (effect->type == ODOLWA_EFFECT_NONE) {
+ effect->type = ODOLWA_EFFECT_RING_OF_FIRE;
+ effect->pos = *pos;
+ effect->timer = 0;
+ effect->velocity = gZeroVec3f;
+ effect->gravity = 0.0f;
+ effect->scale = 0.0f;
+ effect->alpha = 230.0f;
+ }
+}
+
+/**
+ * Manually sets the position of a sphere collider to a specific position.
+ */
+void Boss01_SetColliderSphere(s32 index, ColliderJntSph* collider, Vec3f* sphereCenter) {
+ collider->elements[index].dim.worldSphere.center.x = sphereCenter->x;
+ collider->elements[index].dim.worldSphere.center.y = sphereCenter->y;
+ collider->elements[index].dim.worldSphere.center.z = sphereCenter->z;
+ collider->elements[index].dim.worldSphere.radius =
+ collider->elements[index].dim.modelSphere.radius * collider->elements[index].dim.scale;
+}
+
+/**
+ * For the most part, this function is responsible for selecting which attack Odolwa will do after he's done waiting.
+ * Which attack he chooses depends on Odolwa's health, his distance to the player, and random chance. However, this
+ * function can also make Odolwa do a specific dance before attacking; if the mustAttack parameter is false, then Odolwa
+ * has a 20% chance of choosing to dance instead of attacking. This function will also make Odolwa go back to waiting if
+ * the player is too far off the ground (e.g., by jumping out of the Deku Flower in the center of the arena).
+ */
+void Boss01_SelectAttack(Boss01* this, PlayState* play, u8 mustAttack) {
+ Player* player = GET_PLAYER(play);
+
+ if (player->actor.world.pos.y > 200.0f) {
+ Boss01_SetupWait(this, play, ODOLWA_WAIT_RANDOM);
+ } else if ((Rand_ZeroOne() < 0.2f) && !mustAttack) {
+ // When Odolwa is done dancing, this function calls Boss01_SelectAttack with mustAttack set to true, so he will
+ // be guaranteed to choose an attack later, so long as the player isn't too far off the ground.
+ Boss01_SetupDanceBeforeAttack(this, play);
+ } else if (this->actor.xzDistToPlayer <= 250.0f) {
+ if (this->actor.xzDistToPlayer <= 150.0f) {
+ if (Rand_ZeroOne() < 0.5f) {
+ Boss01_SetupKick(this, play);
+ } else {
+ Boss01_SetupShieldBash(this, play);
+ }
+ } else {
+ Boss01_SetupHorizontalSlash(this, play);
+ }
+ } else if (((s8)this->actor.colChkInfo.health < 8) && (Rand_ZeroOne() < 0.75f)) {
+ Boss01_SetupSpinAttack(this, play);
+ } else {
+ Boss01_SetupVerticalSlash(this, play);
+ }
+}
+
+/**
+ * Spawns dust at both of Odolwa's feet if *any* of the following conditions are true:
+ * - Odolwa's speed is greater than 1.0f
+ * - Odolwa's additional velocity (in either the X or Z direction) is greater than 1.0f
+ * - this->frameCounter & dustSpawnFrameMask is equal to 0
+ * - dustSpawnFrameMask is 0
+ */
+void Boss01_SpawnDustAtFeet(Boss01* this, PlayState* play, u8 dustSpawnFrameMask) {
+ u8 i;
+ Vec3f pos;
+ Vec3f velocity;
+ Vec3f accel;
+
+ if (((this->frameCounter & dustSpawnFrameMask) == 0) &&
+ ((this->additionalVelocityX > 1.0f) || (this->additionalVelocityZ > 1.0f) || (dustSpawnFrameMask == 0) ||
+ (this->actor.speed > 1.0f))) {
+ for (i = 0; i < ARRAY_COUNT(this->feetPos); i++) {
+ velocity.x = Rand_CenteredFloat(5.0f);
+ velocity.y = Rand_ZeroFloat(2.0f) + 1.0f;
+ velocity.z = Rand_CenteredFloat(5.0f);
+ accel.y = -0.1f;
+ accel.x = accel.z = 0.0f;
+ pos.x = this->feetPos[i].x + Rand_CenteredFloat(20.0f);
+ pos.y = Rand_ZeroFloat(10.0f) + 3.0f;
+ pos.z = this->feetPos[i].z + Rand_CenteredFloat(20.0f);
+ func_800B0EB0(play, &pos, &velocity, &accel, &sDustPrimColor, &sDustEnvColor,
+ Rand_ZeroFloat(150.0f) + 350.0f, 10, Rand_ZeroFloat(5.0f) + 14.0f);
+ }
+ }
+}
+
+void Boss01_Init(Actor* thisx, PlayState* play) {
+ Boss01* this = THIS;
+ s32 pad;
+ s16 i;
+
+ Actor_SetScale(&this->actor, 0.015f);
+ if (ODOLWA_GET_TYPE(&this->actor) == ODOLWA_TYPE_BUG) {
+ SkelAnime_InitFlex(play, &this->skelAnime, &gOdolwaBugSkel, &gOdolwaBugCrawlAnim, this->jointTable,
+ this->morphTable, ODOLWA_BUG_LIMB_MAX);
+ this->actor.update = Boss01_Bug_Update;
+ this->actor.draw = Boss01_Bug_Draw;
+ this->bugDrawDmgEffType = ACTOR_DRAW_DMGEFF_BLUE_FIRE;
+ Boss01_Bug_SetupCrawl(this, play);
+ this->actor.gravity = -2.0f;
+ Collider_InitAndSetCylinder(play, &this->bugACCollider, &this->actor, &sBugACColliderCylinderInit);
+ Collider_InitAndSetCylinder(play, &this->bugATCollider, &this->actor, &sBugATColliderCylinderInit);
+ ActorShape_Init(&this->actor.shape, 0.0f, ActorShadow_DrawCircle, 12.0f);
+ Actor_SetScale(&this->actor, 0.025f);
+ this->actor.colChkInfo.health = 2;
+ sOdolwaBugCount++;
+ Actor_PlaySfx(&this->actor, NA_SE_EV_ROCK_FALL);
+ this->actor.colChkInfo.damageTable = &sBugDamageTable;
+ sOdolwa->actor.hintId = TATL_HINT_ID_ODOLWA_PHASE_TWO;
+ } else if (ODOLWA_GET_TYPE(&this->actor) == ODOLWA_TYPE_AFTERIMAGE) {
+ SkelAnime_InitFlex(play, &this->skelAnime, &gOdolwaSkel, &gOdolwaReadyAnim, this->jointTable, this->morphTable,
+ ODOLWA_LIMB_MAX);
+ Boss01_Afterimage_SetupWaitToDespawn(this, play);
+ this->timers[TIMER_AFTERIMAGE_DESPAWN] = ODOLWA_GET_AFTERIMAGE_DESPAWN_TIMER(&this->actor);
+ this->actor.world.rot.z = 0;
+ this->actor.draw = Boss01_Afterimage_Draw;
+ this->actor.flags &= ~ACTOR_FLAG_TARGETABLE;
+ } else {
+ if (CHECK_WEEKEVENTREG(WEEKEVENTREG_CLEARED_WOODFALL_TEMPLE)) {
+ Actor_SpawnAsChild(&play->actorCtx, &this->actor, play, ACTOR_DOOR_WARP1, 0.0f, 0.0f, 0.0f, 0, 0, 0,
+ ENDOORWARP1_FF_1);
+ Actor_Spawn(&play->actorCtx, play, ACTOR_ITEM_B_HEART, 0.0f, 0.0f, 250.0f, 0, 0, 0, BHEART_PARAM_NORMAL);
+ Actor_Kill(&this->actor);
+ return;
+ }
+
+ play->envCtx.lightSettingOverride = 0;
+ play->envCtx.lightBlendOverride = LIGHT_BLEND_OVERRIDE_FULL_CONTROL;
+ play->envCtx.lightBlend = 0.0f;
+ sOdolwa = this;
+ sOdolwaBugCount = 0;
+ play->specialEffects = sOdolwaEffects;
+
+ for (i = 0; i < ODOLWA_EFFECT_COUNT; i++) {
+ sOdolwaEffects[i].type = ODOLWA_EFFECT_NONE;
+ }
+
+ this->actor.hintId = TATL_HINT_ID_ODOLWA_PHASE_ONE;
+ this->actor.targetMode = TARGET_MODE_5;
+ this->actor.colChkInfo.mass = MASS_HEAVY;
+ this->actor.colChkInfo.damageTable = &sOdolwaDamageTable;
+ this->actor.colChkInfo.health = 20;
+
+ ActorShape_Init(&this->actor.shape, 0.0f, NULL, 0.0f);
+ Collider_InitAndSetJntSph(play, &this->swordCollider, &this->actor, &sSwordColliderJntSphInit,
+ this->swordColliderElements);
+ Collider_InitAndSetJntSph(play, &this->shieldCollider, &this->actor, &sShieldColliderJntSphInit,
+ this->shieldColliderElements);
+ Collider_InitAndSetJntSph(play, &this->bodyCollider, &this->actor, &sBodyColliderJntSphInit,
+ this->bodyColliderElements);
+ Collider_InitAndSetJntSph(play, &this->kickAndShieldBashCollider, &this->actor,
+ &sKickAndShieldBashColliderJntSphInit, this->kickAndShieldBashColliderElements);
+ SkelAnime_InitFlex(play, &this->skelAnime, &gOdolwaSkel, &gOdolwaReadyAnim, this->jointTable, this->morphTable,
+ ODOLWA_LIMB_MAX);
+
+ if ((KREG(64) != 0) || CHECK_EVENTINF(EVENTINF_54)) {
+ Boss01_SetupWait(this, play, ODOLWA_WAIT_READY);
+ this->actor.gravity = -2.5f;
+ sOdolwaMusicStartTimer = KREG(15) + 20;
+ } else {
+ Boss01_SetupIntroCutscene(this, play);
+ }
+
+ sMothSwarm = (EnTanron1*)Actor_SpawnAsChild(&play->actorCtx, &this->actor, play, ACTOR_EN_TANRON1, 0.0f, 437.0f,
+ -750.0f, 0, 0, 0, 0x100);
+ }
+
+ this->animMorphFrames1 = -2.0f;
+ this->animMorphFrames2 = -4.0f;
+}
+
+void Boss01_Destroy(Actor* thisx, PlayState* play) {
+}
+
+/**
+ * Checks every explosive actor to see if Odolwa is close enough to any of them. If he is, then he'll jump.
+ */
+void Boss01_JumpAwayFromExplosive(Boss01* this, PlayState* play) {
+ Actor* explosive = play->actorCtx.actorLists[ACTORCAT_EXPLOSIVES].first;
+
+ while (explosive != NULL) {
+ if (sqrtf(SQ(explosive->world.pos.x - this->actor.world.pos.x) +
+ SQ(explosive->world.pos.y - this->actor.world.pos.y) +
+ SQ(explosive->world.pos.z - this->actor.world.pos.z)) < 150.0f) {
+ Boss01_SetupJump(this, play, false);
+ }
+
+ explosive = explosive->next;
+ }
+}
+
+/**
+ * Note that this function will move Odolwa to (0, 2400, 0), regardless of where he was originally spawned. This is so
+ * he can fall from the ceiling in his intro cutscene, and this is also why he's not visible until the cutscene starts.
+ */
+void Boss01_SetupIntroCutscene(Boss01* this, PlayState* play) {
+ this->actionFunc = Boss01_IntroCutscene;
+ this->actor.world.pos.x = 0.0f;
+ this->actor.world.pos.z = 0.0f;
+ this->actor.gravity = 0.0f;
+ this->actor.world.pos.y = 2400.0f;
+ Animation_MorphToLoop(&this->skelAnime, &gOdolwaCrouchAnim, 0.0f);
+}
+
+/**
+ * Handles the entirety of Odolwa's intro cutscene, including manipulating the camera, starting the boss theme, showing
+ * Odolwa's title card, etc. It also handles waiting for the player to move forward from the door before actually
+ * starting the cutscene.
+ */
+void Boss01_IntroCutscene(Boss01* this, PlayState* play) {
+ Player* player = GET_PLAYER(play);
+
+ this->cutsceneTimer++;
+ SkelAnime_Update(&this->skelAnime);
+
+ switch (this->cutsceneState) {
+ case ODOLWA_INTRO_CS_STATE_WAITING_FOR_PLAYER_OR_DONE:
+ if ((CutsceneManager_GetCurrentCsId() != -1) || !(player->actor.world.pos.z < 590.0f)) {
+ break;
+ }
+
+ Cutscene_StartManual(play, &play->csCtx);
+ func_800B7298(play, &this->actor, PLAYER_CSACTION_WAIT);
+ this->subCamId = Play_CreateSubCamera(play);
+ Play_ChangeCameraStatus(play, CAM_ID_MAIN, CAM_STATUS_WAIT);
+ Play_ChangeCameraStatus(play, this->subCamId, CAM_STATUS_ACTIVE);
+ this->actor.flags &= ~ACTOR_FLAG_TARGETABLE;
+ this->cutsceneTimer = 0;
+ this->cutsceneState = ODOLWA_INTRO_CS_STATE_LOOK_AT_PLAYER;
+ this->subCamUp.x = 0.0f;
+ this->subCamUp.y = 1.0f;
+ this->subCamUp.z = 0.0f;
+ // fallthrough
+ case ODOLWA_INTRO_CS_STATE_LOOK_AT_PLAYER:
+ player->actor.world.rot.y = -0x8000;
+ player->actor.shape.rot.y = -0x8000;
+ player->actor.world.pos.x = -9.0f;
+ player->actor.world.pos.z = 587.0f;
+
+ this->subCamEye.x = -9.0f;
+ this->subCamEye.y = (Player_GetHeight(player) + player->actor.world.pos.y) - 24.0f;
+ this->subCamEye.z = (player->actor.world.pos.z - 200.0f) + 110.0f;
+
+ this->subCamAt.x = player->actor.world.pos.x;
+ this->subCamAt.y = (Player_GetHeight(player) + player->actor.world.pos.y) - 14.0f;
+ this->subCamAt.z = player->actor.world.pos.z;
+
+ if (player->transformation == PLAYER_FORM_FIERCE_DEITY) {
+ this->subCamEye.y -= 30.0f + BREG(16);
+ this->subCamAt.y -= 30.0f + BREG(17);
+ }
+
+ if (this->cutsceneTimer >= 20) {
+ Actor_PlaySfx(&this->actor, NA_SE_EN_MIBOSS_RHYTHM_OLD - SFX_FLAG);
+ }
+
+ if (this->cutsceneTimer == 40) {
+ func_800B7298(play, &this->actor, PLAYER_CSACTION_21);
+ }
+
+ if (this->cutsceneTimer == 100) {
+ func_800B7298(play, &this->actor, PLAYER_CSACTION_4);
+ }
+
+ if (this->cutsceneTimer >= 90) {
+ Audio_PlaySfx(NA_SE_EN_MIBOSS_FALL_OLD_OLD - SFX_FLAG);
+ }
+
+ if (this->cutsceneTimer == 120) {
+ this->actor.world.pos.y = 1500.0f;
+ this->actor.gravity = -2.5f;
+ this->cutsceneState = ODOLWA_INTRO_CS_STATE_ODOLWA_FALLING;
+ this->cutsceneTimer = 0;
+ Animation_MorphToPlayOnce(&this->skelAnime, &gOdolwaJumpAnim, 0.0f);
+ this->subCamEye.x = this->actor.world.pos.x;
+ this->subCamAt.x = this->actor.world.pos.x;
+ this->subCamAt.y = 80.0f;
+ this->subCamEye.y = 30.0f;
+ this->subCamEye.z = (this->actor.world.pos.z + 200.0f + 50.0f) - 150.0f;
+ this->subCamAt.z = this->actor.world.pos.z;
+ this->subCamUp.x = 2.0f;
+ }
+ break;
+
+ case ODOLWA_INTRO_CS_STATE_ODOLWA_FALLING:
+ Actor_PlaySfx(&this->actor, NA_SE_EN_MIBOSS_RHYTHM_OLD - SFX_FLAG);
+ Math_ApproachZeroF(&this->subCamUp.x, 0.1f, 0.1f);
+ Audio_PlaySfx(NA_SE_EN_MIBOSS_FALL_OLD_OLD - SFX_FLAG);
+ this->afterimageSpawnFrameMask = 2;
+ Math_ApproachF(&this->subCamAt.y, this->actor.world.pos.y + 80.0f, 0.25f, 30.0f);
+ if (this->actor.world.pos.y < 40.0f) {
+ Animation_MorphToPlayOnce(&this->skelAnime, &gOdolwaCrouchAnim, -2.0f);
+ this->cutsceneState = ODOLWA_INTRO_CS_STATE_ODOLWA_LANDED;
+ this->cutsceneTimer = 0;
+ }
+ break;
+
+ case ODOLWA_INTRO_CS_STATE_ODOLWA_LANDED:
+ if (this->cutsceneTimer < 51) {
+ Actor_PlaySfx(&this->actor, NA_SE_EN_MIBOSS_RHYTHM_OLD - SFX_FLAG);
+ }
+
+ if ((this->cutsceneTimer == 1) || (this->cutsceneTimer == 3)) {
+ u8 i;
+
+ for (i = 0; i < 10; i++) {
+ Boss01_SpawnDustAtFeet(this, play, 0);
+ }
+
+ Audio_PlaySfx(NA_SE_EN_MIBOSS_GND1_OLD);
+ Rumble_Override(0.0f, 200, 20, 20);
+ this->screenShakeMagnitude = 10.0f;
+ }
+
+ if (this->cutsceneTimer == 5) {
+ Animation_MorphToLoop(&this->skelAnime, &gOdolwaIntroSlashAnim, -20.0f);
+ }
+
+ if ((this->cutsceneTimer >= 6) &&
+ (Animation_OnFrame(&this->skelAnime, 30.0f) || Animation_OnFrame(&this->skelAnime, 54.0f))) {
+ Actor_PlaySfx(&this->actor, NA_SE_EN_MIBOSS_SWORD_OLD);
+ }
+
+ if (this->cutsceneTimer < 30) {
+ Math_ApproachF(&this->subCamAt.y, this->actor.world.pos.y + 80.0f, 0.25f, 1000.0f);
+ } else {
+ Math_ApproachF(&this->subCamEye.x, this->actor.world.pos.x + 70.0f, 0.05f,
+ this->subCamVelocity * 70.0f);
+ Math_ApproachF(&this->subCamEye.y, 200.0f, 0.05f, this->subCamVelocity * 170.0f);
+ Math_ApproachF(&this->subCamEye.z, (this->actor.world.pos.z + 200.0f + 50.0f) - 30.0f, 0.05f,
+ this->subCamVelocity * 120.0f);
+ Math_ApproachF(&this->subCamAt.y, this->actor.world.pos.y + 80.0f + 20.0f, 0.05f,
+ this->subCamVelocity * 20.0f);
+ Math_ApproachF(&this->subCamVelocity, 1.0f, 1.0f, 0.001f);
+ }
+
+ if (this->cutsceneTimer == 20) {
+ SEQCMD_PLAY_SEQUENCE(SEQ_PLAYER_BGM_MAIN, 0, NA_BGM_BOSS | SEQ_FLAG_ASYNC);
+ }
+
+ if (this->cutsceneTimer == 50) {
+ TitleCard_InitBossName(&play->state, &play->actorCtx.titleCtxt,
+ Lib_SegmentedToVirtual(&gOdolwaTitleCardTex), 160, 180, 128, 40);
+ }
+
+ if (this->cutsceneTimer == 140) {
+ Camera* mainCam = Play_GetCamera(play, CAM_ID_MAIN);
+
+ this->cutsceneState = ODOLWA_INTRO_CS_STATE_WAITING_FOR_PLAYER_OR_DONE;
+ this->phaseFrameCounter = 0;
+ Boss01_SetupWait(this, play, ODOLWA_WAIT_READY);
+ mainCam->eye = this->subCamEye;
+ mainCam->eyeNext = this->subCamEye;
+ mainCam->at = this->subCamAt;
+ func_80169AFC(play, this->subCamId, 0);
+ this->subCamId = SUB_CAM_ID_DONE;
+ Cutscene_StopManual(play, &play->csCtx);
+ func_800B7298(play, &this->actor, PLAYER_CSACTION_END);
+ this->actor.flags |= ACTOR_FLAG_TARGETABLE;
+ SET_EVENTINF(EVENTINF_54);
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ if (this->subCamId != SUB_CAM_ID_DONE) {
+ Vec3f at;
+
+ ShrinkWindow_Letterbox_SetSizeTarget(27);
+ this->screenShakeOffsetY = Math_CosS(play->gameplayFrames * 0x8000) * this->screenShakeMagnitude;
+ Math_ApproachZeroF(&this->screenShakeMagnitude, 1.0f, 0.75f);
+ at = this->subCamAt;
+ at.y += this->screenShakeOffsetY;
+ Play_SetCameraAtEyeUp(play, this->subCamId, &at, &this->subCamEye, &this->subCamUp);
+ }
+}
+
+void Boss01_SetupSummonBugsCutscene(Boss01* this, PlayState* play) {
+ this->actionFunc = Boss01_SummonBugsCutscene;
+ Animation_MorphToLoop(&this->skelAnime, &gOdolwaSideToSideDanceAnim, -10.0f);
+ this->cutsceneState = ODOLWA_BUG_SUMMONING_CS_STATE_STARTED;
+ this->cutsceneTimer = 0;
+ this->hasPlayedSummonBugCs++;
+ this->disableCollisionTimer = 30;
+ this->actor.speed = 0.0f;
+ this->additionalVelocityZ = 0.0f;
+ this->additionalVelocityX = 0.0f;
+}
+
+/**
+ * Handles everything involving the bug summoning cutscene, including manipulating the camera, spawning the bugs, etc.
+ */
+void Boss01_SummonBugsCutscene(Boss01* this, PlayState* play) {
+ Player* player = GET_PLAYER(play);
+ Vec3f offset;
+ Vec3f pos;
+
+ this->cutsceneTimer++;
+ this->disableCollisionTimer = 30;
+ SkelAnime_Update(&this->skelAnime);
+
+ switch (this->cutsceneState) {
+ case ODOLWA_BUG_SUMMONING_CS_STATE_STARTED:
+ if (CutsceneManager_GetCurrentCsId() != -1) {
+ break;
+ }
+
+ Cutscene_StartManual(play, &play->csCtx);
+ func_800B7298(play, &this->actor, PLAYER_CSACTION_WAIT);
+ this->subCamId = Play_CreateSubCamera(play);
+ Play_ChangeCameraStatus(play, CAM_ID_MAIN, CAM_STATUS_WAIT);
+ Play_ChangeCameraStatus(play, this->subCamId, CAM_STATUS_ACTIVE);
+ this->actor.flags &= ~ACTOR_FLAG_TARGETABLE;
+ this->cutsceneState = ODOLWA_BUG_SUMMONING_CS_STATE_PLAYING_OR_DONE;
+ this->actor.shape.rot.y = 0;
+ this->actor.world.pos.z = 0.0f;
+ this->actor.world.pos.x = 0.0f;
+ this->subCamVelocity = 0.0f;
+ this->subCamEyeNext.y = 100.0f;
+ // fallthrough
+ case ODOLWA_BUG_SUMMONING_CS_STATE_PLAYING_OR_DONE:
+ Actor_PlaySfx(&this->actor, NA_SE_EN_MIBOSS_VOICE1_OLD - SFX_FLAG);
+ Matrix_RotateYS(this->actor.shape.rot.y, MTXMODE_NEW);
+ Matrix_MultVecZ(250.0f, &offset);
+
+ this->subCamEye.x = this->actor.world.pos.x + offset.x;
+ this->subCamEye.y = this->actor.world.pos.y + offset.y + 40.0f;
+ this->subCamEye.z = this->actor.world.pos.z + offset.z;
+
+ this->subCamAt.x = this->actor.world.pos.x;
+ this->subCamAt.y = this->actor.world.pos.y + this->subCamEyeNext.y;
+ this->subCamAt.z = this->actor.world.pos.z;
+
+ if (this->cutsceneTimer > 40) {
+ Math_ApproachF(&this->subCamEyeNext.y, 800.0f, 0.1f, this->subCamVelocity);
+ Math_ApproachF(&this->subCamVelocity, 100.0f, 1.0f, 1.0f);
+ if ((this->cutsceneTimer > 80) && ((this->cutsceneTimer % 16) == 0)) {
+ Matrix_MultVecZ(100.0f, &offset);
+ pos.x = Rand_CenteredFloat(200.0f) + (this->actor.world.pos.x + offset.x);
+ pos.z = Rand_CenteredFloat(200.0f) + (this->actor.world.pos.z + offset.z);
+ Audio_PlaySfx(NA_SE_PL_DEKUNUTS_DROP_BOMB);
+ Actor_Spawn(&play->actorCtx, play, ACTOR_BOSS_01, pos.x, 1200.0f, pos.z, 0, Rand_ZeroFloat(0x10000),
+ 0, ODOLWA_TYPE_BUG);
+ }
+ }
+
+ if (this->cutsceneTimer >= 170) {
+ Boss01_SetupWait(this, play, ODOLWA_WAIT_READY);
+ func_80169AFC(play, this->subCamId, 0);
+ this->subCamId = SUB_CAM_ID_DONE;
+ Cutscene_StopManual(play, &play->csCtx);
+ func_800B7298(play, &this->actor, PLAYER_CSACTION_END);
+ this->actor.flags |= ACTOR_FLAG_TARGETABLE;
+ player->actor.world.rot.y = player->actor.shape.rot.y = -0x8000;
+ player->actor.world.pos.x = 0.0f;
+ player->actor.world.pos.z = -600.0f;
+ }
+ break;
+ }
+
+ if (this->subCamId != SUB_CAM_ID_DONE) {
+ ShrinkWindow_Letterbox_SetSizeTarget(27);
+ Play_SetCameraAtEye(play, this->subCamId, &this->subCamAt, &this->subCamEye);
+ }
+}
+
+void Boss01_Afterimage_SetupWaitToDespawn(Boss01* this, PlayState* play) {
+ this->actionFunc = Boss01_Afterimage_WaitToDespawn;
+}
+
+/**
+ * Waits until the despawn timer reaches 0, then kills the actor.
+ */
+void Boss01_Afterimage_WaitToDespawn(Boss01* this, PlayState* play) {
+ if (this->timers[TIMER_AFTERIMAGE_DESPAWN] == 0) {
+ Actor_Kill(&this->actor);
+ }
+}
+
+static AnimationHeader* sWaitAnimations[ODOLWA_WAIT_MAX] = {
+ &gOdolwaReadyAnim, // ODOLWA_WAIT_READY,
+ &gOdolwaSpinSwordAnim, // ODOLWA_WAIT_SPIN_SWORD,
+ &gOdolwaVerticalHopAnim, // ODOLWA_WAIT_VERTICAL_HOP,
+ &gOdolwaHipShakeDanceAnim, // ODOLWA_WAIT_SHAKE_DANCE,
+ &gOdolwaUpAndDownDanceAnim, // ODOLWA_WAIT_UP_AND_DOWN_DANCE,
+ &gOdolwaArmSwingDanceAnim, // ODOLWA_WAIT_ARM_SWING_DANCE,
+ &gOdolwaThurstAttackAnim, // ODOLWA_WAIT_THRUST_ATTACK,
+ &gOdolwaDoubleSlashAnim, // ODOLWA_WAIT_DOUBLE_SLASH,
+ &gOdolwaSideToSideHopAnim, // ODOLWA_WAIT_SIDE_TO_SIDE_HOP,
+ &gOdolwaSideToSideDanceAnim, // ODOLWA_WAIT_SIDE_TO_SIDE_DANCE,
+ &gOdolwaSpinDanceAnim, // ODOLWA_WAIT_SPIN_DANCE
+ &gOdolwaJumpDanceAnim, // ODOLWA_WAIT_JUMP_DANCE
+};
+
+/**
+ * Prepares Odolwa to enter his "wait" state, i.e., his primary state where he waits for a bit of time before attacking.
+ * If the fight has been going for 1000 or more frames, this function is also responsible for starting the bug summoning
+ * cutscene. Callers of this function can choose which type of wait action he will do using the waitType parameter,
+ * though this parameter is ignored if the fight is still in its first phase (i.e, when the fight has been going on for
+ * 999 or fewer frames); Odolwa will always perform the "ready" wait action in the first phase.
+ *
+ * If ODOLWA_WAIT_RANDOM is passed for the waitType, then Odolwa has a 30% chance of summoning moths instead of doing a
+ * standard wait action. Otherwise, he will randomly choose his own wait type.
+ */
+void Boss01_SetupWait(Boss01* this, PlayState* play, u8 waitType) {
+ if (this->phaseFrameCounter > 1000) {
+ if (!this->hasPlayedSummonBugCs) {
+ Boss01_SetupSummonBugsCutscene(this, play);
+ return;
+ }
+
+ if (waitType == ODOLWA_WAIT_RANDOM) {
+ if (Rand_ZeroOne() < 0.3f) {
+ Boss01_SetupSummonMoths(this, play);
+ return;
+ }
+
+ this->waitType = Rand_ZeroFloat(ODOLWA_WAIT_MAX - 0.001f);
+ } else {
+ this->waitType = waitType;
+ }
+ } else {
+ this->waitType = ODOLWA_WAIT_READY;
+ }
+
+ Animation_MorphToLoop(&this->skelAnime, sWaitAnimations[this->waitType], 2.0f * this->animMorphFrames2);
+ this->animEndFrame = Animation_GetLastFrame(sWaitAnimations[this->waitType]);
+ this->actionFunc = Boss01_Wait;
+ this->timers[TIMER_CURRENT_ACTION] = 80;
+ this->jumpIfPlayerIsClose = Rand_ZeroFloat(1.999f);
+ this->waitTimer = 0;
+}
+
+/**
+ * This function will make Odolwa perform one of 12 different "wait actions," after which he will either select an
+ * attack, jump, or start running. Because of the variety of actions this function handles, it has a large number of
+ * responsibilities, including summoning the ring of fire and spawning more bugs and falling blocks.
+ */
+void Boss01_Wait(Boss01* this, PlayState* play) {
+ s16 i;
+ Player* player = GET_PLAYER(play);
+
+ this->lookAtPlayer = true;
+
+ if ((this->waitType == ODOLWA_WAIT_SPIN_SWORD) || (this->waitType == ODOLWA_WAIT_VERTICAL_HOP) ||
+ (this->waitType == ODOLWA_WAIT_ARM_SWING_DANCE) || (this->waitType == ODOLWA_WAIT_THRUST_ATTACK)) {
+ Actor_PlaySfx(&this->actor, NA_SE_EN_MIBOSS_VOICE1_OLD - SFX_FLAG);
+ } else {
+ Actor_PlaySfx(&this->actor, NA_SE_EN_MIBOSS_VOICE2_OLD - SFX_FLAG);
+ }
+
+ if ((this->waitType == ODOLWA_WAIT_READY) || (this->waitType == ODOLWA_WAIT_SPIN_SWORD) ||
+ (this->waitType == ODOLWA_WAIT_VERTICAL_HOP) || (this->waitType == ODOLWA_WAIT_SHAKE_DANCE)) {
+ this->canGuardOrEvade = true;
+ this->swordAndShieldCollisionEnabled = true;
+ }
+
+ if (((this->waitType == ODOLWA_WAIT_VERTICAL_HOP) || (this->waitType == ODOLWA_WAIT_SIDE_TO_SIDE_HOP) ||
+ (this->waitType == ODOLWA_WAIT_SIDE_TO_SIDE_DANCE)) &&
+ Animation_OnFrame(&this->skelAnime, 6.0f)) {
+ Actor_PlaySfx(&this->actor, NA_SE_EN_MIBOSS_GND1_OLD);
+ for (i = 0; i < 3; i++) {
+ Boss01_SpawnDustAtFeet(this, play, 0);
+ }
+ }
+
+ if (this->waitType == ODOLWA_WAIT_SIDE_TO_SIDE_DANCE) {
+ this->animMorphFrames2 = 0.0f;
+ this->animMorphFrames1 = 0.0f;
+ }
+
+ this->swordState = ODOLWA_SWORD_STATE_ACTIVE;
+ this->waitTimer++;
+
+ // This will play "slash" sound effects at appropriate points in the thrust attack or double slash action.
+ if (this->waitType == ODOLWA_WAIT_THRUST_ATTACK) {
+ if (this->waitTimer == 7) {
+ Actor_PlaySfx(&this->actor, NA_SE_EN_ANSATSUSYA_ENTRY);
+ }
+ } else if ((this->waitType == ODOLWA_WAIT_DOUBLE_SLASH) && (((this->waitTimer == 12)) || (this->waitTimer == 20))) {
+ Actor_PlaySfx(&this->actor, NA_SE_EN_ANSATSUSYA_ENTRY);
+ }
+
+ SkelAnime_Update(&this->skelAnime);
+ Math_ApproachS(&this->actor.world.rot.y, this->actor.yawTowardsPlayer, 0xA, 0x800);
+ Math_ApproachZeroF(&this->actor.speed, 1.0f, 1.5f);
+
+ // Whether or not Odolwa should jump if the player gets close was randomly decided earlier in Boss01_SetupWait, and
+ // if it was decided that he *should* jump, this can interrupt any wait action.
+ if ((this->actor.xzDistToPlayer <= 150.0f) && this->jumpIfPlayerIsClose) {
+ Boss01_SetupJump(this, play, false);
+ this->canGuardOrEvade = false;
+ }
+
+ // The thrust attack and double slash wait types select Odolwa's next action after their animations end rather than
+ // waiting until the full 80 frames like every other wait type. The timer for the current action is not set to 0
+ // here, though, which may cause some unintended behavior in Boss01_HorizontalSlash.
+ if (((this->timers[TIMER_CURRENT_ACTION] == 0) && (this->waitType != ODOLWA_WAIT_THRUST_ATTACK) &&
+ (this->waitType != ODOLWA_WAIT_DOUBLE_SLASH)) ||
+ (Animation_OnFrame(&this->skelAnime, this->animEndFrame) &&
+ ((this->waitType == ODOLWA_WAIT_THRUST_ATTACK) || (this->waitType == ODOLWA_WAIT_DOUBLE_SLASH)))) {
+ if (this->actor.xzDistToPlayer <= 450.0f) {
+ Boss01_SelectAttack(this, play, false);
+ } else if (Rand_ZeroOne() < 0.5f) {
+ Boss01_SetupJump(this, play, true);
+ } else {
+ Boss01_SetupRun(this, play);
+ }
+ }
+
+ Boss01_SpawnDustAtFeet(this, play, 1);
+ this->animMorphFrames1 = -2.0f;
+ this->animMorphFrames2 = -4.0f;
+
+ if (((this->waitType == ODOLWA_WAIT_SHAKE_DANCE) || (this->waitType == ODOLWA_WAIT_UP_AND_DOWN_DANCE) ||
+ (this->waitType == ODOLWA_WAIT_DOUBLE_SLASH) || (this->waitType == ODOLWA_WAIT_SIDE_TO_SIDE_HOP)) &&
+ (this->waitTimer == 30)) {
+ Boss01_SpawnEffectRingOfFire((OdolwaEffect*)play->specialEffects, &player->actor.world.pos);
+ this->timers[TIMER_CURRENT_ACTION] = 120;
+ }
+
+ if (((this->timers[TIMER_CURRENT_ACTION] % 16) == 0) && (this->waitType != ODOLWA_WAIT_READY) &&
+ (sOdolwaBugCount < 5)) {
+ Vec3f pos;
+ Player* player2 = GET_PLAYER(play);
+ s32 pad;
+
+ if (Rand_ZeroOne() < 0.2f) {
+ pos = player2->actor.world.pos;
+ } else {
+ pos.x = Rand_CenteredFloat(1200.0f);
+ pos.z = Rand_CenteredFloat(1200.0f);
+ }
+
+ pos.y = 1200.0f;
+
+ switch (this->waitType) {
+ case ODOLWA_WAIT_SPIN_SWORD:
+ case ODOLWA_WAIT_VERTICAL_HOP:
+ case ODOLWA_WAIT_ARM_SWING_DANCE:
+ case ODOLWA_WAIT_THRUST_ATTACK:
+ Actor_Spawn(&play->actorCtx, play, ACTOR_BOSS_01, pos.x, pos.y, pos.z, 0, Rand_ZeroFloat(0x10000), 0,
+ ODOLWA_TYPE_BUG);
+ break;
+
+ case ODOLWA_WAIT_SIDE_TO_SIDE_DANCE:
+ case ODOLWA_WAIT_SPIN_DANCE:
+ case ODOLWA_WAIT_JUMP_DANCE:
+ Audio_PlaySfx_AtPos(&sFallingBlockSfxPos, NA_SE_EV_ROCK_FALL);
+ Boss01_SpawnEffectFallingBlock((OdolwaEffect*)play->specialEffects, &pos, false);
+ break;
+
+ default:
+ break;
+ }
+ }
+}
+
+void Boss01_SetupDazed(Boss01* this, PlayState* play) {
+ Animation_MorphToLoop(&this->skelAnime, &gOdolwaStunAnim, this->animMorphFrames2);
+ if (this->actionFunc != Boss01_Dazed) {
+ this->timers[TIMER_CURRENT_ACTION] = 70;
+ this->actionFunc = Boss01_Dazed;
+ }
+
+ this->canGuardOrEvade = false;
+}
+
+/**
+ * Plays the dazed animation for 70 frames, leaving Odolwa vulnerable to attacks. This state is often referred to as
+ * Odolwa being "stunned," though it was deliberately named something different here to avoid confusion with the state
+ * where Odolwa turns blue and stops all his animations (e.g., when he is hit with a Deku Nut). The latter state is
+ * consistently named "stunned" across all enemies in the codebase, so this function was given a different name to
+ * signify it does something else.
+ */
+void Boss01_Dazed(Boss01* this, PlayState* play) {
+ SkelAnime_Update(&this->skelAnime);
+ Actor_PlaySfx(&this->actor, NA_SE_EN_COMMON_WEAKENED - SFX_FLAG);
+ Math_ApproachZeroF(&this->actor.speed, 1.0f, 1.5f);
+ if (this->timers[TIMER_CURRENT_ACTION] == 0) {
+ Boss01_SetupWait(this, play, ODOLWA_WAIT_RANDOM);
+ }
+
+ this->canGuardOrEvade = false;
+ Boss01_SpawnDustAtFeet(this, play, 1);
+}
+
+void Boss01_SetupSpinAttack(Boss01* this, PlayState* play) {
+ Animation_MorphToLoop(&this->skelAnime, &gOdolwaSpinAttackAnim, this->animMorphFrames2);
+ this->timers[TIMER_CURRENT_ACTION] = 120;
+ this->actionFunc = Boss01_SpinAttack;
+ this->canGuardOrEvade = false;
+}
+
+/**
+ * Rapidly spin around and approach the player for 120 frames, spawning a new afterimage every frame.
+ * Transitions back to waiting once the attack completes.
+ */
+void Boss01_SpinAttack(Boss01* this, PlayState* play) {
+ SkelAnime_Update(&this->skelAnime);
+ Math_ApproachF(&this->actor.speed, 7.0f, 1.0f, 1.5f);
+ Math_ApproachS(&this->actor.world.rot.y, this->actor.yawTowardsPlayer, 4, 0x1000);
+
+ if (Animation_OnFrame(&this->skelAnime, 5.0f)) {
+ Actor_PlaySfx(&this->actor, NA_SE_EN_MIBOSS_SWORD_OLD);
+ Actor_PlaySfx(&this->actor, NA_SE_EN_MIBOSS_ROLLING_OLD);
+ }
+
+ if (this->timers[TIMER_CURRENT_ACTION] == 0) {
+ Boss01_SetupWait(this, play, ODOLWA_WAIT_RANDOM);
+ }
+
+ this->canGuardOrEvade = false;
+ this->swordState = ODOLWA_SWORD_STATE_ACTIVE;
+ this->swordAndShieldCollisionEnabled = true;
+ Boss01_SpawnDustAtFeet(this, play, 1);
+ this->afterimageSpawnFrameMask = 1;
+}
+
+void Boss01_SetupDanceBeforeAttack(Boss01* this, PlayState* play) {
+ Animation_MorphToLoop(&this->skelAnime, &gOdolwaJumpDanceAnim, this->animMorphFrames2);
+ this->timers[TIMER_CURRENT_ACTION] = 40;
+ this->actionFunc = Boss01_DanceBeforeAttack;
+ this->canGuardOrEvade = false;
+}
+
+/**
+ * Dance for 40 frames, then select an attack. Odolwa can only end up in this state if he randomly chose to dance
+ * instead of attack the last time Boss01_SelectAttack was called. This function calls Boss01_SelectAttack with the
+ * mustAttack parameter set to true, ensuring that he will not dance again immediately after this.
+ */
+void Boss01_DanceBeforeAttack(Boss01* this, PlayState* play) {
+ SkelAnime_Update(&this->skelAnime);
+ Math_ApproachZeroF(&this->actor.speed, 1.0f, 1.5f);
+ Actor_PlaySfx(&this->actor, NA_SE_EN_MIBOSS_VOICE2_OLD - SFX_FLAG);
+
+ if (this->timers[TIMER_CURRENT_ACTION] == 0) {
+ Boss01_SelectAttack(this, play, true);
+ }
+
+ this->canGuardOrEvade = false;
+ this->swordAndShieldCollisionEnabled = true;
+ Boss01_SpawnDustAtFeet(this, play, 1);
+}
+
+void Boss01_SetupRun(Boss01* this, PlayState* play) {
+ Animation_MorphToLoop(&this->skelAnime, &gOdolwaRunAnim, this->animMorphFrames2);
+ this->actionFunc = Boss01_Run;
+ this->timers[TIMER_CURRENT_ACTION] = Rand_ZeroFloat(100.0f) + 50.0f;
+ this->runTargetPosAngularVelocityY = 0.07f;
+
+ if (Rand_ZeroOne() < 0.5f) {
+ this->runTargetPosAngularVelocityY *= -1.0f;
+ }
+
+ this->actor.gravity = -3.0f;
+}
+
+/**
+ * Run in a circle for 50-150 frames by chasing a rotating target around the room, then go back to waiting.
+ */
+void Boss01_Run(Boss01* this, PlayState* play) {
+ Vec3f targetPos;
+ f32 diffX;
+ f32 diffZ;
+
+ this->lookAtPlayer = true;
+ SkelAnime_Update(&this->skelAnime);
+
+ if ((this->actor.bgCheckFlags & BGCHECKFLAG_GROUND) &&
+ (Animation_OnFrame(&this->skelAnime, 6.0f) || Animation_OnFrame(&this->skelAnime, 16.0f))) {
+ this->actor.velocity.y = 10.0f;
+ Actor_PlaySfx(&this->actor, NA_SE_EN_MIBOSS_GND1_OLD);
+ }
+
+ this->runTargetPosRotY += this->runTargetPosAngularVelocityY;
+ Matrix_RotateYF(this->runTargetPosRotY, MTXMODE_NEW);
+ Matrix_MultVecZ(450.0f, &targetPos);
+ diffX = targetPos.x - this->actor.world.pos.x;
+ diffZ = targetPos.z - this->actor.world.pos.z;
+ Math_ApproachS(&this->actor.world.rot.y, Math_Atan2S_XY(diffZ, diffX), 0xA, 0x1000);
+ Math_ApproachF(&this->actor.speed, 12.0f, 1.0f, 3.0f);
+
+ if (this->timers[TIMER_CURRENT_ACTION] == 0) {
+ Boss01_SetupWait(this, play, ODOLWA_WAIT_RANDOM);
+ }
+
+ Boss01_SpawnDustAtFeet(this, play, 3);
+ this->swordState = ODOLWA_SWORD_STATE_ACTIVE;
+ this->swordAndShieldCollisionEnabled = true;
+}
+
+/**
+ * This starts the process making Odolwa jump, though the actual jump is divided into three different action functions.
+ * The shouldPerformFallingSlash parameter controls whether Odolwa should try to perform a falling slash during the
+ * middle part of the jump or if he should just jump normally.
+ */
+void Boss01_SetupJump(Boss01* this, PlayState* play, u8 shouldPerformFallingSlash) {
+ this->shouldPerformFallingSlash = shouldPerformFallingSlash;
+ Animation_MorphToLoop(&this->skelAnime, &gOdolwaCrouchAnim, this->animMorphFrames2);
+ this->actionFunc = Boss01_JumpSquat;
+ this->timers[TIMER_CURRENT_ACTION] = 5;
+}
+
+/**
+ * Plays the crouch animation for 5 frames, then jumps off.
+ */
+void Boss01_JumpSquat(Boss01* this, PlayState* play) {
+ s32 pad[2];
+ u8 i;
+ Vec3f additionalVelocity;
+ f32 magnitude;
+
+ SkelAnime_Update(&this->skelAnime);
+
+ if (this->timers[TIMER_CURRENT_ACTION] == 0) {
+ this->actionFunc = Boss01_Jump;
+ Animation_MorphToPlayOnce(&this->skelAnime, &gOdolwaJumpAnim, this->animMorphFrames1);
+ this->actor.velocity.y = 35.0f;
+ this->actor.gravity = -2.5f;
+ Matrix_RotateYS(this->actor.world.rot.y, MTXMODE_NEW);
+
+ if (!this->shouldPerformFallingSlash) {
+ magnitude = Rand_ZeroFloat(10.0f) + 10.0f;
+ } else {
+ magnitude = 12.0f;
+ }
+
+ Matrix_MultVecZ(magnitude, &additionalVelocity);
+ this->additionalVelocityX = additionalVelocity.x;
+ this->additionalVelocityZ = additionalVelocity.z;
+
+ for (i = 0; i < 5; i++) {
+ Boss01_SpawnDustAtFeet(this, play, 0);
+ }
+
+ Actor_PlaySfx(&this->actor, NA_SE_EN_MIBOSS_JUMP1);
+ Actor_PlaySfx(&this->actor, NA_SE_EN_MIBOSS_JUMP2 - SFX_FLAG);
+ this->disableCollisionTimer = 5;
+ }
+
+ this->swordAndShieldCollisionEnabled = true;
+}
+
+/**
+ * As Odolwa moves through the air, this function will spawn an afterimage every other frame and rotate him to face the
+ * player. If Odolwa should perform a falling slash, and if he's falling fast enough, then this function will stop
+ * rotating him and transition him to the slash animation. Lastly, this function will transition Odolwa to his "landing"
+ * action once he gets close enough to the ground.
+ */
+void Boss01_Jump(Boss01* this, PlayState* play) {
+ SkelAnime_Update(&this->skelAnime);
+ Actor_PlaySfx(&this->actor, NA_SE_EN_MIBOSS_JUMP2 - SFX_FLAG);
+
+ if (!this->shouldPerformFallingSlash) {
+ Math_ApproachS(&this->actor.world.rot.y, this->actor.yawTowardsPlayer, 5, 0x1000);
+ } else if ((this->actor.velocity.y < -5.0f) && (this->prevJumpVelocityY >= -5.0f)) {
+ Animation_MorphToPlayOnce(&this->skelAnime, &gOdolwaFallingSlashAnim, this->animMorphFrames1);
+ Actor_PlaySfx(&this->actor, NA_SE_EN_MIBOSS_SWORD_OLD);
+ }
+
+ if (this->actor.world.pos.y < 40.0f) {
+ this->actionFunc = Boss01_JumpLand;
+ Animation_MorphToPlayOnce(&this->skelAnime, &gOdolwaCrouchAnim, this->animMorphFrames1);
+ this->timers[TIMER_CURRENT_ACTION] = 5;
+ this->landedFromJump = false;
+ }
+
+ this->swordState = ODOLWA_SWORD_STATE_ACTIVE;
+ this->swordAndShieldCollisionEnabled = true;
+ this->afterimageSpawnFrameMask = 2;
+ this->prevJumpVelocityY = this->actor.velocity.y;
+}
+
+/**
+ * Plays the crouch animation for five frames, then transitions either to selecting an attack or waiting based on
+ * Odolwa's distance to the player.
+ */
+void Boss01_JumpLand(Boss01* this, PlayState* play) {
+ u8 i;
+
+ SkelAnime_Update(&this->skelAnime);
+
+ if (!this->landedFromJump) {
+ if (this->actor.bgCheckFlags & BGCHECKFLAG_GROUND) {
+ this->landedFromJump = true;
+ for (i = 0; i < 5; i++) {
+ Boss01_SpawnDustAtFeet(this, play, 0);
+ }
+
+ Actor_PlaySfx(&this->actor, NA_SE_EN_MIBOSS_GND1_OLD);
+ }
+ }
+
+ if (!this->shouldPerformFallingSlash) {
+ Math_ApproachS(&this->actor.world.rot.y, this->actor.yawTowardsPlayer, 4, 0x2800);
+ }
+
+ if (this->timers[TIMER_CURRENT_ACTION] == 0) {
+ if (this->actor.xzDistToPlayer <= 450.0f) {
+ Boss01_SelectAttack(this, play, false);
+ } else {
+ Boss01_SetupWait(this, play, ODOLWA_WAIT_RANDOM);
+ }
+ }
+
+ this->swordState = ODOLWA_SWORD_STATE_ACTIVE;
+ this->swordAndShieldCollisionEnabled = true;
+}
+
+void Boss01_SetupVerticalSlash(Boss01* this, PlayState* play) {
+ Animation_MorphToPlayOnce(&this->skelAnime, &gOdolwaVerticalSlashAnim, this->animMorphFrames2);
+ this->animEndFrame = Animation_GetLastFrame(&gOdolwaVerticalSlashAnim);
+ this->actionFunc = Boss01_VerticalSlash;
+ this->timers[TIMER_CURRENT_ACTION] = 20;
+}
+
+/**
+ * Charges forward and performs a vertical slash. Transitions back to waiting once the attack completes.
+ */
+void Boss01_VerticalSlash(Boss01* this, PlayState* play) {
+ Vec3f additionalVelocity;
+
+ SkelAnime_Update(&this->skelAnime);
+
+ if ((this->skelAnime.curFrame >= 10.0f) && (this->skelAnime.curFrame <= 15.0f)) {
+ this->isPerformingVerticalSlash = true;
+ }
+
+ if ((this->timers[TIMER_CURRENT_ACTION] >= 7) && (this->timers[TIMER_CURRENT_ACTION] < 13)) {
+ Matrix_RotateYF(BINANG_TO_RAD_ALT(this->actor.world.rot.y), MTXMODE_NEW);
+ Matrix_MultVecZ(20.0f, &additionalVelocity);
+ this->additionalVelocityX = additionalVelocity.x;
+ this->additionalVelocityZ = additionalVelocity.z;
+ Boss01_SpawnDustAtFeet(this, play, 0);
+ }
+
+ sOdolwaSwordTrailPosX = 0.0f;
+ sOdolwaSwordTrailPosY = 90.0f;
+ sOdolwaSwordTrailPosZ = -70.0f;
+ sOdolwaSwordTrailRotX = 0.4712388f;
+ sOdolwaSwordTrailRotY = M_PI;
+ sOdolwaSwordTrailRotZ = 1.7278761f;
+
+ if (Animation_OnFrame(&this->skelAnime, 12.0f)) {
+ sOdolwaSwordTrailAlpha = 255.0f;
+ sSwordTrailAngularRangeDivisor = 100.0f;
+ }
+
+ if (Animation_OnFrame(&this->skelAnime, 13.0f)) {
+ sSwordTrailAngularRangeDivisor = 20.0f;
+ }
+
+ if (Animation_OnFrame(&this->skelAnime, 14.0f)) {
+ sSwordTrailAngularRangeDivisor = 7.0f;
+ }
+
+ if (Animation_OnFrame(&this->skelAnime, 7.0f)) {
+ Actor_PlaySfx(&this->actor, NA_SE_EN_MIBOSS_DASH_OLD);
+ }
+
+ if (Animation_OnFrame(&this->skelAnime, 10.0f)) {
+ Actor_PlaySfx(&this->actor, NA_SE_EN_MIBOSS_SWORD_OLD);
+ }
+
+ if (Animation_OnFrame(&this->skelAnime, this->animEndFrame)) {
+ Boss01_SetupWait(this, play, ODOLWA_WAIT_RANDOM);
+ this->additionalVelocityZ = 0.0f;
+ this->additionalVelocityX = 0.0f;
+ }
+
+ this->swordState = ODOLWA_SWORD_STATE_ACTIVE;
+ this->swordAndShieldCollisionEnabled = true;
+}
+
+void Boss01_SetupHorizontalSlash(Boss01* this, PlayState* play) {
+ Animation_MorphToPlayOnce(&this->skelAnime, &gOdolwaHorizontalSlashAnim, this->animMorphFrames2);
+ this->animEndFrame = Animation_GetLastFrame(&gOdolwaHorizontalSlashAnim);
+ this->actionFunc = Boss01_HorizontalSlash;
+}
+
+/**
+ * Stands in place and performs a horizontal slash. Transitions back to waiting once the attack completes.
+ */
+void Boss01_HorizontalSlash(Boss01* this, PlayState* play) {
+ Vec3f additionalVelocity;
+
+ SkelAnime_Update(&this->skelAnime);
+
+ if ((this->skelAnime.curFrame >= 10.0f) && (this->skelAnime.curFrame <= 15.0f)) {
+ this->swordState = ODOLWA_SWORD_STATE_HORIZONTAL_SLASH;
+ }
+
+ // This code is written very strangely and might never have been intended to run. The timer values checked here seem
+ // copied from Boss01_VerticalSlash, and they make sense there to control when Odolwa should dash forward. Unlike
+ // Boss01_VerticalSlash, however, this function uses Matrix_MultZero to initialize additionalVelocity; in other
+ // words, the additional velocity in all directions will be zero, and Odolwa will stay in place.
+ //
+ // Additionally, Boss01_SetupHorizontalSlash doesn't initialize this->timers[TIMER_CURRENT_ACTION] to anything, so
+ // most of the time, the timer will just be zero, and this code will never run in the first place. However, if
+ // Odolwa did a double slash or thrust attack wait action in Boss01_Wait, it is possible to enter this function with
+ // a non-zero current action timer, potentially allowing this code to run. The only consequence of this is that
+ // Odolwa will sometimes spawn dust at his feet during a horizontal slash when he was probably never supposed to.
+ if ((this->timers[TIMER_CURRENT_ACTION] >= 7) && (this->timers[TIMER_CURRENT_ACTION] < 13)) {
+ Matrix_RotateYF(BINANG_TO_RAD_ALT(this->actor.world.rot.y), MTXMODE_NEW);
+ Matrix_MultZero(&additionalVelocity);
+ this->additionalVelocityX = additionalVelocity.x;
+ this->additionalVelocityZ = additionalVelocity.z;
+ Boss01_SpawnDustAtFeet(this, play, 0);
+ }
+
+ sOdolwaSwordTrailPosX = 0.0f;
+ sOdolwaSwordTrailPosY = 140.0f;
+ sOdolwaSwordTrailPosZ = 0.0f;
+ sOdolwaSwordTrailRotX = 0.4712388f;
+ sOdolwaSwordTrailRotY = 0.0f;
+ sOdolwaSwordTrailRotZ = 0.0f;
+
+ if (Animation_OnFrame(&this->skelAnime, 12.0f)) {
+ sOdolwaSwordTrailAlpha = 255.0f;
+ sSwordTrailAngularRangeDivisor = 100.0f;
+ }
+
+ if (Animation_OnFrame(&this->skelAnime, 13.0f)) {
+ sSwordTrailAngularRangeDivisor = 20.0f;
+ }
+
+ if (Animation_OnFrame(&this->skelAnime, 14.0f)) {
+ sSwordTrailAngularRangeDivisor = 7.0f;
+ }
+
+ if (Animation_OnFrame(&this->skelAnime, 10.0f)) {
+ Actor_PlaySfx(&this->actor, NA_SE_EN_MIBOSS_SWORD_OLD);
+ }
+
+ if (Animation_OnFrame(&this->skelAnime, this->animEndFrame)) {
+ Boss01_SetupWait(this, play, ODOLWA_WAIT_RANDOM);
+ this->additionalVelocityZ = 0.0f;
+ this->additionalVelocityX = 0.0f;
+ }
+
+ this->swordAndShieldCollisionEnabled = true;
+}
+
+/**
+ * Prepares Odolwa to guard using either his shield or sword depending on the supplied parameter. The difference is
+ * entirely aesthetic, with him behaving in the exact same way regardless of which type of guard is chosen.
+ */
+void Boss01_SetupGuard(Boss01* this, PlayState* play, u8 guardUsingSword) {
+ if (this->actionFunc != Boss01_Guard) {
+ if (!guardUsingSword) {
+ Animation_MorphToPlayOnce(&this->skelAnime, &gOdolwaShieldGuardAnim, this->animMorphFrames1);
+ this->animEndFrame = Animation_GetLastFrame(&gOdolwaShieldGuardAnim);
+ } else {
+ Animation_MorphToPlayOnce(&this->skelAnime, &gOdolwaSwordGuardAnim, this->animMorphFrames1);
+ this->animEndFrame = Animation_GetLastFrame(&gOdolwaSwordGuardAnim);
+ }
+
+ this->actionFunc = Boss01_Guard;
+ }
+
+ this->timers[TIMER_CURRENT_ACTION] = 5;
+}
+
+/**
+ * Plays a guard animation (either blocking with his shield or his sword) for five frames, then transitions back to
+ * waiting.
+ */
+void Boss01_Guard(Boss01* this, PlayState* play) {
+ SkelAnime_Update(&this->skelAnime);
+ Math_ApproachS(&this->actor.world.rot.y, this->actor.yawTowardsPlayer, 2, 0x500);
+ Math_ApproachZeroF(&this->actor.speed, 1.0f, 1.0f);
+ if (this->timers[TIMER_CURRENT_ACTION] != 0) {
+ this->swordAndShieldCollisionEnabled = true;
+ } else {
+ Boss01_SetupWait(this, play, ODOLWA_WAIT_RANDOM);
+ }
+
+ this->canGuardOrEvade = true;
+}
+
+void Boss01_SetupKick(Boss01* this, PlayState* play) {
+ Animation_MorphToPlayOnce(&this->skelAnime, &gOdolwaKickAnim, this->animMorphFrames2);
+ this->animEndFrame = Animation_GetLastFrame(&gOdolwaKickAnim);
+ this->actionFunc = Boss01_Kick;
+}
+
+/**
+ * Plays the kick animation to completion, then transitions back to waiting.
+ */
+void Boss01_Kick(Boss01* this, PlayState* play) {
+ SkelAnime_Update(&this->skelAnime);
+ Math_ApproachZeroF(&this->actor.speed, 1.0f, 1.0f);
+ if (Animation_OnFrame(&this->skelAnime, this->animEndFrame)) {
+ Boss01_SetupWait(this, play, ODOLWA_WAIT_RANDOM);
+ }
+
+ this->kickAndShieldBashCollisionEnabled = true;
+}
+
+void Boss01_SetupShieldBash(Boss01* this, PlayState* play) {
+ Animation_MorphToPlayOnce(&this->skelAnime, &gOdolwaShieldBashAnim, this->animMorphFrames2);
+ this->animEndFrame = Animation_GetLastFrame(&gOdolwaShieldBashAnim);
+ this->actionFunc = Boss01_ShieldBash;
+}
+
+/**
+ * Plays the shield bash animation to completion, then transitions back to waiting.
+ */
+void Boss01_ShieldBash(Boss01* this, PlayState* play) {
+ SkelAnime_Update(&this->skelAnime);
+ Math_ApproachZeroF(&this->actor.speed, 1.0f, 1.0f);
+ if (Animation_OnFrame(&this->skelAnime, this->animEndFrame)) {
+ Boss01_SetupWait(this, play, ODOLWA_WAIT_RANDOM);
+ }
+
+ this->kickAndShieldBashCollisionEnabled = true;
+}
+
+/**
+ * If Odolwa is not currently in the "damaged" state (where he's crouched on one knee and breathing heavily), this
+ * function will transition him to that state and start a 20 frame timer; when this timer reaches 0, he will recover and
+ * jump away. However, if he is already in the "damaged" state, and if the player attacked him with an attack that has
+ * the ODOLWA_DMGEFF_DAMAGE_TIMER_CHECK damage effect, then one of two things will happen:
+ * - If the attack hit while his current action timer is 7 or more, the timer will be set to 20 again; this will keep
+ * Odolwa in the "damaged" state for longer and allow the player to attack him more.
+ * - If the attack hit while his current action timer is 6 or less, Odolwa will disable all of his collision for 20
+ * frames to ensure that the player cannot hit him with subsequent attacks.
+ */
+void Boss01_SetupDamaged(Boss01* this, PlayState* play, u8 damageEffect) {
+ if (this->actionFunc != Boss01_Damaged) {
+ this->timers[TIMER_CURRENT_ACTION] = 20;
+ Animation_MorphToPlayOnce(&this->skelAnime, &gOdolwaDamagedStartAnim, 0.0f);
+ this->animEndFrame = Animation_GetLastFrame(&gOdolwaDamagedStartAnim);
+ this->actionFunc = Boss01_Damaged;
+ } else if (damageEffect == ODOLWA_DMGEFF_DAMAGE_TIMER_CHECK) {
+ if (this->timers[TIMER_CURRENT_ACTION] > 5) {
+ this->disableCollisionTimer = 20;
+ } else {
+ this->timers[TIMER_CURRENT_ACTION] = 20;
+ }
+ }
+}
+
+/**
+ * Plays Odolwa's damaged animation until the current action timer reaches 0, then makes him jump away. If the player
+ * attacks Odolwa while he's "downed" in this state, the current action timer can be manipulated in Boss01_SetupDamaged,
+ * so it's possible for Odolwa to stay in this state for a long time with well-timed attacks.
+ */
+void Boss01_Damaged(Boss01* this, PlayState* play) {
+ SkelAnime_Update(&this->skelAnime);
+ Math_ApproachZeroF(&this->actor.speed, 1.0f, 1.0f);
+
+ if (Animation_OnFrame(&this->skelAnime, this->animEndFrame)) {
+ Animation_MorphToLoop(&this->skelAnime, &gOdolwaDamagedLoopAnim, this->animMorphFrames2);
+ this->animEndFrame = 1000.0f;
+ }
+
+ if (this->timers[TIMER_CURRENT_ACTION] == 0) {
+ Boss01_SetupJump(this, play, false);
+ }
+
+ Boss01_SpawnDustAtFeet(this, play, 1);
+}
+
+void Boss01_UpdateDamage(Boss01* this, PlayState* play) {
+ Player* player = GET_PLAYER(play);
+ u8 damage;
+ s32 i;
+
+ if (this->shieldCollider.elements[ODOLWA_SHIELD_COLLIDER_SHIELD].info.bumperFlags & BUMP_HIT) {
+ this->bodyInvincibilityTimer = 5;
+ if (this->damageTimer == 0) {
+ ColliderInfo* acHitInfo = this->shieldCollider.elements[ODOLWA_SHIELD_COLLIDER_SHIELD].info.acHitInfo;
+
+ if (acHitInfo->toucher.dmgFlags == DMG_SWORD_BEAM) {
+ Actor_Spawn(&play->actorCtx, play, ACTOR_EN_CLEAR_TAG, this->actor.focus.pos.x, this->actor.focus.pos.y,
+ this->actor.focus.pos.z, 0, 0, 3, CLEAR_TAG_PARAMS(CLEAR_TAG_LARGE_LIGHT_RAYS));
+ Actor_PlaySfx(&this->actor, NA_SE_IT_SHIELD_BOUND);
+ this->damageTimer = 5;
+ }
+ }
+ } else if (this->damageTimer == 0) {
+ for (i = 0; i < ODOLWA_SWORD_COLLIDER_MAX; i++) {
+ if (this->swordCollider.elements[i].info.toucherFlags & TOUCH_HIT) {
+ this->swordCollider.elements[i].info.toucherFlags &= ~TOUCH_HIT;
+ player->pushedYaw = this->actor.yawTowardsPlayer;
+ player->pushedSpeed = 15.0f;
+ }
+ }
+
+ for (i = 0; i < ODOLWA_KICK_AND_SHIELD_BASH_COLLIDER_MAX; i++) {
+ if (this->kickAndShieldBashCollider.elements[i].info.toucherFlags & TOUCH_HIT) {
+ this->kickAndShieldBashCollider.elements[i].info.toucherFlags &= ~TOUCH_HIT;
+ player->pushedYaw = this->actor.yawTowardsPlayer;
+ player->pushedSpeed = 20.0f;
+ }
+ }
+
+ for (i = 0; i < ODOLWA_COLLIDER_BODYPART_MAX; i++) {
+ if (this->bodyCollider.elements[i].info.bumperFlags & BUMP_HIT) {
+ this->bodyCollider.elements[i].info.bumperFlags &= ~BUMP_HIT;
+
+ switch (this->actor.colChkInfo.damageEffect) {
+ case ODOLWA_DMGEFF_FREEZE:
+ this->drawDmgEffState = ODOLWA_DRAW_DMGEFF_STATE_FROZEN_INIT;
+ goto stunned;
+
+ case ODOLWA_DMGEFF_FIRE:
+ this->drawDmgEffState = ODOLWA_DRAW_DMGEFF_STATE_FIRE_INIT;
+ break;
+
+ case ODOLWA_DMGEFF_LIGHT_ORB:
+ this->drawDmgEffState = ODOLWA_DRAW_DMGEFF_STATE_LIGHT_ORB_INIT;
+ Actor_Spawn(&play->actorCtx, play, ACTOR_EN_CLEAR_TAG, this->actor.focus.pos.x,
+ this->actor.focus.pos.y, this->actor.focus.pos.z, 0, 0, 0,
+ CLEAR_TAG_PARAMS(CLEAR_TAG_LARGE_LIGHT_RAYS));
+ break;
+
+ case ODOLWA_DMGEFF_ELECTRIC_STUN:
+ this->drawDmgEffState = ODOLWA_DRAW_DMGEFF_STATE_ELECTRIC_SPARKS_INIT;
+ goto stunned;
+
+ case ODOLWA_DMGEFF_BLUE_LIGHT_ORB:
+ this->drawDmgEffState = ODOLWA_DRAW_DMGEFF_STATE_BLUE_LIGHT_ORB_INIT;
+ Actor_Spawn(&play->actorCtx, play, ACTOR_EN_CLEAR_TAG, this->actor.focus.pos.x,
+ this->actor.focus.pos.y, this->actor.focus.pos.z, 0, 0, 3,
+ CLEAR_TAG_PARAMS(CLEAR_TAG_LARGE_LIGHT_RAYS));
+ break;
+
+ case ODOLWA_DMGEFF_STUN:
+ stunned:
+ Boss01_SetupStunned(this, play);
+ this->damageTimer = 15;
+ Actor_PlaySfx(&this->actor, NA_SE_EN_COMMON_FREEZE);
+ this->canGuardOrEvade = false;
+ return;
+
+ default:
+ break;
+ }
+
+ damage = this->actor.colChkInfo.damage;
+ sMothSwarm->unk_148 = 0;
+
+ if (this->actor.colChkInfo.damageEffect == ODOLWA_DMGEFF_STUN) {
+ //! @bug: unreachable code. If Odolwa's damage effect is ODOLWA_DMGEFF_STUN, we early-return out of
+ //! the function in the above switch statement.
+ Boss01_SetupStunned(this, play);
+ this->damageTimer = 15;
+ } else if (this->actor.colChkInfo.damageEffect == ODOLWA_DMGEFF_DAZE) {
+ Boss01_SetupDazed(this, play);
+ Audio_PlaySfx_AtPos(&sOdolwaDamageSfxPos, NA_SE_EN_MIBOSS_DAMAGE_OLD);
+ this->damageTimer = 15;
+ } else {
+ this->damageFlashTimer = 15;
+ this->damageTimer = 5;
+ this->actor.colChkInfo.health -= damage;
+ if ((s8)this->actor.colChkInfo.health <= 0) {
+ Boss01_SetupDeathCutscene(this, play);
+ Actor_PlaySfx(&this->actor, NA_SE_EN_MIBOSS_DEAD_OLD);
+ Enemy_StartFinishingBlow(play, &this->actor);
+ } else {
+ Boss01_SetupDamaged(this, play, this->actor.colChkInfo.damageEffect);
+ Audio_PlaySfx_AtPos(&sOdolwaDamageSfxPos, NA_SE_EN_MIBOSS_DAMAGE_OLD);
+ }
+ }
+
+ this->canGuardOrEvade = false;
+ break;
+ }
+ }
+ }
+}
+
+void Boss01_SetupSummonMoths(Boss01* this, PlayState* play) {
+ Animation_MorphToLoop(&this->skelAnime, &gOdolwaMothSummonDanceAnim, -5.0f);
+ this->animEndFrame = Animation_GetLastFrame(&gOdolwaMothSummonDanceAnim);
+ this->actionFunc = Boss01_SummonMoths;
+ this->summonMothsTimer = 0;
+}
+
+/**
+ * Plays a specific dancing animation and summons moths after 30 frames. After 131 frames pass, Odolwa will transition
+ * back to waiting; notably, this function always transitions him to a specific wait rather than choosing a random wait
+ * action like every other non-cutscene function.
+ */
+void Boss01_SummonMoths(Boss01* this, PlayState* play) {
+ this->summonMothsTimer++;
+ SkelAnime_Update(&this->skelAnime);
+ Actor_PlaySfx(&this->actor, NA_SE_EN_MIBOSS_VOICE3_OLD - SFX_FLAG);
+
+ if (Animation_OnFrame(&this->skelAnime, this->animEndFrame)) {
+ this->skelAnime.curFrame = this->animEndFrame - 20.0f;
+ }
+
+ if (this->summonMothsTimer >= 30) {
+ sMothSwarm->actor.world.pos.x = this->bodyPartsPos[ODOLWA_BODYPART_HEAD].x;
+ sMothSwarm->actor.world.pos.y = this->bodyPartsPos[ODOLWA_BODYPART_HEAD].y;
+ sMothSwarm->actor.world.pos.z = this->bodyPartsPos[ODOLWA_BODYPART_HEAD].z;
+ sMothSwarm->actor.world.rot.y = this->actor.world.rot.y;
+
+ if (this->summonMothsTimer == 30) {
+ sMothSwarm->unk_148 = 100;
+ Actor_PlaySfx(&this->actor, NA_SE_SY_TRANSFORM_MASK_FLASH);
+ }
+ }
+
+ Math_ApproachS(&this->actor.world.rot.y, this->actor.yawTowardsPlayer, 5, 0x500);
+ Math_ApproachZeroF(&this->actor.speed, 1.0f, 1.0f);
+ if (this->summonMothsTimer >= 131) {
+ Boss01_SetupWait(this, play, ODOLWA_WAIT_SPIN_DANCE);
+ }
+}
+
+void Boss01_SetupDeathCutscene(Boss01* this, PlayState* play) {
+ Animation_MorphToPlayOnce(&this->skelAnime, &gOdolwaDeathAnim, this->animMorphFrames1);
+ this->animEndFrame = Animation_GetLastFrame(&gOdolwaDeathAnim);
+ this->actionFunc = Boss01_DeathCutscene;
+ Actor_PlaySfx(&this->actor, NA_SE_EN_DAIOCTA_DAMAGE);
+ this->actor.flags &= ~ACTOR_FLAG_TARGETABLE;
+ this->disableCollisionTimer = 1000;
+ this->cutsceneTimer = 0;
+ this->cutsceneState = ODOLWA_DEATH_CS_STATE_STARTED;
+ SEQCMD_STOP_SEQUENCE(SEQ_PLAYER_BGM_MAIN, 1);
+ sMothSwarm->unk_144 = 250;
+ func_800BC154(play, &play->actorCtx, &sMothSwarm->actor, ACTORCAT_BOSS);
+}
+
+/**
+ * Handles the entirety of Odolwa's death cutscene, including manipulating the camera, starting the "boss defeated"
+ * theme, spawning the Heart Container and blue warp, surrounding Odolwa with flames, etc. Notably, the main Odolwa
+ * instance is *not* killed in this function, and it stays in this state forever. Instead, Odolwa is teleported far
+ * above the ceiling so the player cannot see or interact with him.
+ */
+void Boss01_DeathCutscene(Boss01* this, PlayState* play) {
+ Vec3f subCamOffset;
+ f32 diffX;
+ f32 diffZ;
+ Camera* mainCam = Play_GetCamera(play, CAM_ID_MAIN);
+
+ this->disableCollisionTimer = 1000;
+ this->actor.flags &= ~ACTOR_FLAG_TARGETABLE;
+ SkelAnime_Update(&this->skelAnime);
+ this->cutsceneTimer++;
+ Math_ApproachZeroF(&this->actor.speed, 1.0f, 1.0f);
+
+ switch (this->cutsceneState) {
+ case ODOLWA_DEATH_CS_STATE_STARTED:
+ if (CutsceneManager_GetCurrentCsId() != -1) {
+ break;
+ }
+
+ Cutscene_StartManual(play, &play->csCtx);
+ func_800B7298(play, &this->actor, PLAYER_CSACTION_1);
+ this->subCamId = Play_CreateSubCamera(play);
+ Play_ChangeCameraStatus(play, CAM_ID_MAIN, CAM_STATUS_WAIT);
+ Play_ChangeCameraStatus(play, this->subCamId, CAM_STATUS_ACTIVE);
+ this->cutsceneTimer = 0;
+ this->cutsceneState = ODOLWA_DEATH_CS_STATE_PLAY_ANIM_AND_FALL_FORWARD;
+ this->subCamEye.x = mainCam->eye.x;
+ this->subCamEye.y = mainCam->eye.y;
+ this->subCamEye.z = mainCam->eye.z;
+ this->subCamAt.x = mainCam->at.x;
+ this->subCamAt.y = mainCam->at.y;
+ this->subCamAt.z = mainCam->at.z;
+ diffX = this->subCamEye.x - this->actor.world.pos.x;
+ diffZ = this->subCamEye.z - this->actor.world.pos.z;
+ this->deathCsInitialSubCamRot = Math_Atan2F_XY(diffZ, diffX);
+ this->deathCsSubCamRot = -0.5f;
+ // fallthrough
+ case ODOLWA_DEATH_CS_STATE_PLAY_ANIM_AND_FALL_FORWARD:
+ if (this->cutsceneTimer < 15) {
+ Math_ApproachF(&this->actor.world.pos.x, 0.0f, 0.1f, 5.0f);
+ Math_ApproachF(&this->actor.world.pos.z, 0.0f, 0.1f, 5.0f);
+ }
+
+ if (this->cutsceneTimer == 70) {
+ SEQCMD_PLAY_SEQUENCE(SEQ_PLAYER_BGM_MAIN, 0, NA_BGM_CLEAR_BOSS | SEQ_FLAG_ASYNC);
+ }
+
+ if (this->cutsceneTimer == 71) {
+ Actor_PlaySfx(&this->actor, NA_SE_EN_MIBOSS_GND2_OLD);
+ this->cutsceneState = ODOLWA_DEATH_CS_STATE_BURST_INTO_FLAMES_AND_SHRINK;
+ this->cutsceneTimer = 0;
+ }
+ // fallthrough
+ case ODOLWA_DEATH_CS_STATE_BURST_INTO_FLAMES_AND_SHRINK:
+ Math_ApproachF(&this->deathCsSubCamRot, 1.3f, 0.1f, 0.008f);
+ subCamOffset.x = 0.0f;
+ subCamOffset.y = 30.0f;
+ subCamOffset.z = 300.0f;
+ Matrix_RotateYF(this->deathCsInitialSubCamRot + this->deathCsSubCamRot, MTXMODE_NEW);
+ Matrix_MultVec3f(&subCamOffset, &this->subCamEyeNext);
+ this->subCamEyeNext.x += this->pelvisPos.x;
+ this->subCamEyeNext.y += this->pelvisPos.y;
+ this->subCamEyeNext.z += this->pelvisPos.z;
+ Math_ApproachF(&this->subCamEye.x, this->subCamEyeNext.x, 0.1f, 40.0f);
+ Math_ApproachF(&this->subCamEye.y, this->subCamEyeNext.y, 0.1f, 40.0f);
+ Math_ApproachF(&this->subCamEye.z, this->subCamEyeNext.z, 0.1f, 40.0f);
+ Math_ApproachF(&this->subCamAt.x, this->pelvisPos.x, 0.1f, 70.0f);
+ Math_ApproachF(&this->subCamAt.y, this->pelvisPos.y + 50.0f, 0.1f, 70.0f);
+ Math_ApproachF(&this->subCamAt.z, this->pelvisPos.z, 0.1f, 70.0f);
+
+ if (this->cutsceneTimer >= 71) {
+ s16 i;
+ s16 fireCount;
+ Vec3f fireAccel;
+ Vec3f firePos;
+ s16 bodyPartIndex;
+
+ fireAccel = gZeroVec3f;
+ fireAccel.y = 0.03f;
+
+ if (this->cutsceneTimer > 80) {
+ Math_ApproachZeroF(&this->actor.scale.y, 0.1f, this->deathShrinkSpeed * 0.00075f);
+ Math_ApproachF(&this->deathShrinkSpeed, 1.0f, 1.0f, 0.01f);
+ }
+
+ if (this->cutsceneTimer > 120) {
+ fireCount = 1;
+ } else {
+ fireCount = 2;
+ }
+
+ for (i = 0; i < fireCount; i++) {
+ bodyPartIndex = Rand_ZeroFloat(14.9f);
+ firePos.x = this->bodyPartsPos[bodyPartIndex].x + Rand_CenteredFloat(40.0f);
+ firePos.y = this->bodyPartsPos[bodyPartIndex].y - 10.0f;
+ firePos.z = this->bodyPartsPos[bodyPartIndex].z + Rand_CenteredFloat(40.0f);
+ EffectSsKFire_Spawn(play, &firePos, &gZeroVec3f, &fireAccel, Rand_ZeroFloat(30.0f) + 30.0f, 0);
+ }
+
+ Actor_PlaySfx(&this->actor, NA_SE_EN_COMMON_EXTINCT_LEV - SFX_FLAG);
+ }
+
+ if (this->cutsceneTimer == 71) {
+ Actor_Spawn(&play->actorCtx, play, ACTOR_ITEM_B_HEART, this->actor.focus.pos.x, this->actor.focus.pos.y,
+ this->actor.focus.pos.z, 0, 0, 0, BHEART_PARAM_NORMAL);
+ }
+
+ if (this->cutsceneTimer == 180) {
+ static f32 sBlueWarpSpawnsX[] = { 0.0f, 350.0f, -350.0f, 350.0f, -350.0f };
+ static f32 sBlueWarpSpawnsZ[] = { 0.0f, 350.0f, 350.0f, -350.0f, -350.0f };
+ Player* player = GET_PLAYER(play);
+ f32 warpX;
+ f32 warpZ;
+ s32 i;
+
+ for (i = 0; i < ARRAY_COUNT(sBlueWarpSpawnsX); i++) {
+ warpX = sBlueWarpSpawnsX[i];
+ warpZ = sBlueWarpSpawnsZ[i];
+
+ if (((fabsf(warpX - this->actor.focus.pos.x) < 220.0f) &&
+ (fabsf(warpZ - this->actor.focus.pos.z) < 220.0f)) ||
+ ((fabsf(warpX - player->actor.world.pos.x) < 220.0f) &&
+ (fabsf(warpZ - player->actor.world.pos.z) < 220.0f))) {
+ } else {
+ break;
+ }
+ }
+
+ Actor_SpawnAsChild(&play->actorCtx, &this->actor, play, ACTOR_DOOR_WARP1, warpX, 0.0f, warpZ, 0, 0, 0,
+ ENDOORWARP1_FF_1);
+ this->cutsceneState = ODOLWA_DEATH_CS_STATE_DONE;
+ mainCam->eye = this->subCamEye;
+ mainCam->eyeNext = this->subCamEye;
+ mainCam->at = this->subCamAt;
+ func_80169AFC(play, this->subCamId, 0);
+ this->subCamId = SUB_CAM_ID_DONE;
+ Cutscene_StopManual(play, &play->csCtx);
+ func_800B7298(play, &this->actor, PLAYER_CSACTION_END);
+ this->actor.flags &= ~ACTOR_FLAG_TARGETABLE;
+ }
+ break;
+
+ case ODOLWA_DEATH_CS_STATE_DONE:
+ this->actor.world.pos.y = 5000.0f;
+ break;
+ }
+
+ if (this->subCamId != SUB_CAM_ID_DONE) {
+ Vec3f at;
+
+ // It seems like this code was copied from Boss01_IntroCutscene, since this function doesn't set
+ // screenShakeMagnitude to anything, and thus it is always 0.0f; all of the code involving screen
+ // shake here could be removed, since it is effectively useless.
+ this->screenShakeOffsetY = Math_CosS(play->gameplayFrames * 0x8000) * this->screenShakeMagnitude;
+ Math_ApproachZeroF(&this->screenShakeMagnitude, 1.0f, 0.75f);
+ at = this->subCamAt;
+ at.y += this->screenShakeOffsetY;
+ Play_SetCameraAtEye(play, this->subCamId, &at, &this->subCamEye);
+ }
+}
+
+void Boss01_SetupStunned(Boss01* this, PlayState* play) {
+ this->actionFunc = Boss01_Stunned;
+ this->timers[TIMER_CURRENT_ACTION] = 40;
+ Actor_SetColorFilter(&this->actor, COLORFILTER_COLORFLAG_BLUE, 120, COLORFILTER_BUFFLAG_OPA, 40);
+}
+
+/**
+ * Turns Odolwa blue, disables all animations, and makes him wait in place for 40 frames, after which he will transition
+ * to waiting. This is not to be confused with his "dazed" state, which is commonly referred to as Odolwa "being
+ * stunned"; this form of stun, where the enemy turns blue and stops their animations, is very common among regular
+ * enemies, but it's very rare to see on a boss, so the term "stunned" was used here for consistency with other enemies.
+ */
+void Boss01_Stunned(Boss01* this, PlayState* play) {
+ Math_ApproachZeroF(&this->actor.speed, 1.0f, 1.0f);
+ if (this->timers[TIMER_CURRENT_ACTION] == 0) {
+ Boss01_SetupWait(this, play, ODOLWA_WAIT_RANDOM);
+ }
+}
+
+/**
+ * Spawns two ice shards on all of Odolwa's body parts that fly off in random directions.
+ */
+void Boss01_Thaw(Boss01* this, PlayState* play) {
+ static Color_RGBA8 sIcePrimColor = { 170, 255, 255, 255 };
+ static Color_RGBA8 sIceEnvColor = { 200, 200, 255, 255 };
+ static Vec3f sIceAccel = { 0.0f, -1.0f, 0.0f };
+ Vec3f pos;
+ Vec3f velocity;
+ s32 i;
+
+ SoundSource_PlaySfxAtFixedWorldPos(play, this->bodyPartsPos, 30, NA_SE_EV_ICE_BROKEN);
+
+ for (i = 0; i < ODOLWA_BODYPART_MAX * 2; i++) {
+ velocity.x = Rand_CenteredFloat(7.0f);
+ velocity.z = Rand_CenteredFloat(7.0f);
+ velocity.y = Rand_ZeroFloat(6.0f) + 4.0f;
+
+ pos.x = this->bodyPartsPos[i / 2].x + velocity.x;
+ pos.y = this->bodyPartsPos[i / 2].y + velocity.y;
+ pos.z = this->bodyPartsPos[i / 2].z + velocity.z;
+
+ EffectSsEnIce_Spawn(play, &pos, Rand_ZeroFloat(1.0f) + 1.5f, &velocity, &sIceAccel, &sIcePrimColor,
+ &sIceEnvColor, 30);
+ }
+}
+
+/**
+ * Returns true if Odolwa's model is rotated such that he is looking at the player *and* if the player's model is
+ * rotated such that they are looking at Odolwa.
+ */
+s32 Boss01_ArePlayerAndOdolwaFacing(Boss01* this, PlayState* play) {
+ Player* player = GET_PLAYER(play);
+
+ if ((ABS_ALT(BINANG_SUB(this->actor.yawTowardsPlayer, this->actor.shape.rot.y)) < 0x3000) &&
+ (ABS_ALT(BINANG_SUB(this->actor.yawTowardsPlayer, BINANG_ROT180(player->actor.shape.rot.y))) < 0x3000)) {
+ return true;
+ }
+
+ return false;
+}
+
+void Boss01_Update(Actor* thisx, PlayState* play2) {
+ Boss01* this = THIS;
+ PlayState* play = play2;
+ s32 i;
+ Player* player = GET_PLAYER(play);
+ f32 diffX;
+ f32 diffY;
+ f32 diffZ;
+ s16 targetHeadRotY;
+ s16 targetHeadRotX;
+ s32 pad;
+
+ if (ODOLWA_GET_TYPE(&this->actor) == ODOLWA_TYPE_AFTERIMAGE) {
+ if (KREG(63) == 0) {
+ DECR(this->timers[TIMER_AFTERIMAGE_DESPAWN]);
+ this->actionFunc(this, play);
+ }
+ return;
+ }
+
+ Math_Vec3f_Copy(&sOdolwaDamageSfxPos, &this->actor.projectedPos);
+
+ play->envCtx.lightSetting = 0;
+ play->envCtx.prevLightSetting = 1;
+ Math_ApproachZeroF(&play->envCtx.lightBlend, 1.0f, 0.03f);
+
+ this->frameCounter++;
+
+ if (KREG(63) == 0) {
+ this->phaseFrameCounter++;
+ if (this->phaseFrameCounter == 800) {
+ sOdolwa->actor.hintId = TATL_HINT_ID_ODOLWA_CLOSE_TO_PHASE_TWO;
+ }
+
+ this->canGuardOrEvade = false;
+ this->swordAndShieldCollisionEnabled = false;
+ this->isPerformingVerticalSlash = false;
+ this->afterimageSpawnFrameMask = 0;
+ this->kickAndShieldBashCollisionEnabled = false;
+ this->swordState = ODOLWA_SWORD_STATE_INACTIVE;
+
+ for (i = 0; i < ARRAY_COUNT(this->timers); i++) {
+ DECR(this->timers[i]);
+ }
+
+ DECR(this->damageTimer);
+ DECR(this->damageFlashTimer);
+
+ this->actor.flags |= ACTOR_FLAG_TARGETABLE;
+ this->actionFunc(this, play);
+ Actor_MoveWithGravity(&this->actor);
+ this->actor.world.pos.x += this->additionalVelocityX;
+ this->actor.world.pos.z += this->additionalVelocityZ;
+ }
+
+ Actor_UpdateBgCheckInfo(play, &this->actor, 50.0f, 150.0f, 100.0f, UPDBGCHECKINFO_FLAG_1 | UPDBGCHECKINFO_FLAG_4);
+ if (this->actor.bgCheckFlags & BGCHECKFLAG_GROUND) {
+ Math_ApproachZeroF(&this->additionalVelocityX, 1.0f, 1.0f);
+ Math_ApproachZeroF(&this->additionalVelocityZ, 1.0f, 1.0f);
+ }
+
+ this->actor.shape.rot = this->actor.world.rot;
+
+ if (this->disableCollisionTimer == 0) {
+ Boss01_UpdateDamage(this, play);
+
+ if (this->bodyInvincibilityTimer == 0) {
+ CollisionCheck_SetAC(play, &play->colChkCtx, &this->bodyCollider.base);
+ } else {
+ this->bodyInvincibilityTimer--;
+ for (i = 0; i < ODOLWA_COLLIDER_BODYPART_MAX; i++) {
+ this->bodyCollider.elements[i].info.bumperFlags &= ~BUMP_HIT;
+ }
+ }
+
+ if (this->swordAndShieldCollisionEnabled) {
+ CollisionCheck_SetAC(play, &play->colChkCtx, &this->shieldCollider.base);
+ CollisionCheck_SetOC(play, &play->colChkCtx, &this->shieldCollider.base);
+ CollisionCheck_SetAC(play, &play->colChkCtx, &this->swordCollider.base);
+ }
+
+ if (this->swordState != ODOLWA_SWORD_STATE_INACTIVE) {
+ CollisionCheck_SetAT(play, &play->colChkCtx, &this->swordCollider.base);
+ }
+
+ if (this->kickAndShieldBashCollisionEnabled) {
+ CollisionCheck_SetAT(play, &play->colChkCtx, &this->kickAndShieldBashCollider.base);
+ }
+ } else {
+ this->disableCollisionTimer--;
+ for (i = 0; i < ODOLWA_COLLIDER_BODYPART_MAX; i++) {
+ this->bodyCollider.elements[i].info.bumperFlags &= ~BUMP_HIT;
+ }
+ }
+
+ CollisionCheck_SetOC(play, &play->colChkCtx, &this->bodyCollider.base);
+
+ // If Odolwa and the player are *not* facing each other, Odolwa will *not* block or jump away from the player's
+ // attacks, even if canGuardOrEvade is set to true. This allows the player to hit Odolwa even during states where he
+ // normally evades attacks, so long as the player is far enough to the side or behind him.
+ if (this->canGuardOrEvade &&
+ ((player->unk_D57 != 0) || ((player->unk_ADC != 0) && (this->actor.xzDistToPlayer <= 120.0f))) &&
+ Boss01_ArePlayerAndOdolwaFacing(this, play)) {
+ if ((Rand_ZeroOne() < 0.25f) && (this->actionFunc != Boss01_Guard)) {
+ Boss01_SetupJump(this, play, false);
+ this->disableCollisionTimer = 10;
+ } else if ((player->unk_ADC != 0) && (this->actor.xzDistToPlayer <= 120.0f)) {
+ Boss01_SetupGuard(this, play, true);
+ } else {
+ Boss01_SetupGuard(this, play, false);
+ }
+ }
+
+ if (this->canGuardOrEvade) {
+ Boss01_JumpAwayFromExplosive(this, play);
+ }
+
+ if (((this->frameCounter & (this->afterimageSpawnFrameMask - 1)) == 0) && (this->afterimageSpawnFrameMask != 0)) {
+ s16 afterimageTimer = (this->actionFunc == Boss01_SpinAttack) ? 4 : 10;
+ s32 pad;
+ Boss01* child =
+ (Boss01*)Actor_SpawnAsChild(&play->actorCtx, &this->actor, play, ACTOR_BOSS_01, this->actor.world.pos.x,
+ this->actor.world.pos.y, this->actor.world.pos.z, this->actor.world.rot.x,
+ this->actor.world.rot.y, afterimageTimer, ODOLWA_TYPE_AFTERIMAGE);
+
+ if (child != NULL) {
+ for (i = 0; i < ODOLWA_LIMB_MAX; i++) {
+ child->skelAnime.jointTable[i] = this->skelAnime.jointTable[i];
+ }
+ }
+ }
+
+ Boss01_UpdateEffects(this, play);
+
+ if (sOdolwaMusicStartTimer != 0) {
+ sOdolwaMusicStartTimer--;
+ if (sOdolwaMusicStartTimer == 0) {
+ SEQCMD_PLAY_SEQUENCE(SEQ_PLAYER_BGM_MAIN, 0, NA_BGM_BOSS | SEQ_FLAG_ASYNC);
+ }
+ }
+
+ if (this->lookAtPlayer) {
+ targetHeadRotY = this->actor.yawTowardsPlayer - this->actor.shape.rot.y;
+ if ((targetHeadRotY > 0x3800) || (targetHeadRotY < -0x3800)) {
+ targetHeadRotY = 0;
+ }
+
+ diffX = player->actor.world.pos.x - this->actor.focus.pos.x;
+ diffY = KREG(36) + ((player->actor.world.pos.y + 25.0f) - this->actor.focus.pos.y);
+ diffZ = player->actor.world.pos.z - this->actor.focus.pos.z;
+ targetHeadRotX = Math_Atan2S(diffY, sqrtf(SQ(diffX) + SQ(diffZ)));
+
+ // This line of code ensures that *not* following the player is the default behavior; if you want Odolwa's head
+ // to track the player, you'll need to set this variable to true for every single frame you want this behavior.
+ this->lookAtPlayer = false;
+ } else {
+ targetHeadRotX = 0;
+ targetHeadRotY = 0;
+ }
+
+ Math_ApproachS(&this->headRotY, targetHeadRotY, 3, 0x2000);
+ Math_ApproachS(&this->headRotX, targetHeadRotX, 3, 0x2000);
+
+ DECR(this->drawDmgEffTimer);
+
+ switch (this->drawDmgEffState) {
+ case ODOLWA_DRAW_DMGEFF_STATE_NONE:
+ this->drawDmgEffType = ACTOR_DRAW_DMGEFF_FIRE;
+ this->drawDmgEffTimer = 0;
+ this->drawDmgEffAlpha = 0.0f;
+ break;
+
+ case ODOLWA_DRAW_DMGEFF_STATE_FIRE_INIT:
+ this->drawDmgEffAlpha = 1.0f;
+ this->drawDmgEffScale = 0.0f;
+ this->drawDmgEffType = ACTOR_DRAW_DMGEFF_FIRE;
+ this->drawDmgEffTimer = 40;
+ this->drawDmgEffState++;
+ Actor_SetColorFilter(&this->actor, COLORFILTER_COLORFLAG_RED, 120, COLORFILTER_BUFFLAG_OPA, 60);
+ // fallthrough
+ case ODOLWA_DRAW_DMGEFF_STATE_FIRE_ACTIVE:
+ if (this->drawDmgEffTimer == 0) {
+ Math_ApproachZeroF(&this->drawDmgEffAlpha, 1.0f, 0.02f);
+ if (this->drawDmgEffAlpha == 0.0f) {
+ this->drawDmgEffState = ODOLWA_DRAW_DMGEFF_STATE_NONE;
+ }
+ } else {
+ Math_ApproachF(&this->drawDmgEffScale, 1.0f, 0.1f, 0.5f);
+ }
+ break;
+
+ case ODOLWA_DRAW_DMGEFF_STATE_FROZEN_INIT:
+ this->drawDmgEffType = ACTOR_DRAW_DMGEFF_FROZEN_SFX;
+ this->drawDmgEffTimer = 40;
+ this->drawDmgEffState++;
+ this->drawDmgEffAlpha = 1.0f;
+ this->drawDmgEffScale = 0.0f;
+ this->drawDmgEffFrozenSteamScale = 1.0f;
+ // fallthrough
+ case ODOLWA_DRAW_DMGEFF_STATE_FROZEN_ACTIVE:
+ if (this->drawDmgEffTimer == 0) {
+ Boss01_Thaw(this, play);
+ this->drawDmgEffState = ODOLWA_DRAW_DMGEFF_STATE_NONE;
+ break;
+ }
+
+ if (this->drawDmgEffTimer == 50) {
+ this->drawDmgEffType = ACTOR_DRAW_DMGEFF_FROZEN_NO_SFX;
+ }
+
+ Math_ApproachF(&this->drawDmgEffScale, 1.0f, 1.0f, 0.08f);
+ Math_ApproachF(&this->drawDmgEffFrozenSteamScale, 1.0f, 0.05f, 0.05f);
+ break;
+
+ case ODOLWA_DRAW_DMGEFF_STATE_LIGHT_ORB_INIT:
+ this->drawDmgEffType = ACTOR_DRAW_DMGEFF_LIGHT_ORBS;
+ this->drawDmgEffTimer = 40;
+ this->drawDmgEffScale = 1.0f;
+ goto lightOrbInitCommon;
+
+ case ODOLWA_DRAW_DMGEFF_STATE_BLUE_LIGHT_ORB_INIT:
+ this->drawDmgEffType = ACTOR_DRAW_DMGEFF_BLUE_LIGHT_ORBS;
+ this->drawDmgEffTimer = 40;
+ this->drawDmgEffScale = 3.0f;
+ lightOrbInitCommon:
+ this->drawDmgEffState = ODOLWA_DRAW_DMGEFF_STATE_LIGHT_ORB_ACTIVE;
+ this->drawDmgEffAlpha = 1.0f;
+ // fallthrough
+ case ODOLWA_DRAW_DMGEFF_STATE_LIGHT_ORB_ACTIVE:
+ if (this->drawDmgEffTimer == 0) {
+ Math_ApproachZeroF(&this->drawDmgEffScale, 1.0f, 0.03f);
+ if (this->drawDmgEffScale == 0.0f) {
+ this->drawDmgEffState = ODOLWA_DRAW_DMGEFF_STATE_NONE;
+ this->drawDmgEffAlpha = 0.0f;
+ }
+ } else {
+ Math_ApproachF(&this->drawDmgEffScale, 1.5f, 0.5f, 0.5f);
+ }
+ break;
+
+ case ODOLWA_DRAW_DMGEFF_STATE_ELECTRIC_SPARKS_INIT:
+ this->drawDmgEffType = ACTOR_DRAW_DMGEFF_ELECTRIC_SPARKS_SMALL;
+ this->drawDmgEffTimer = 50;
+ this->drawDmgEffAlpha = 1.0f;
+ this->drawDmgEffScale = (KREG(18) * 0.1f) + 1.0f;
+ this->drawDmgEffState++;
+ // fallthrough
+ case ODOLWA_DRAW_DMGEFF_STATE_ELECTRIC_SPARKS_ACTIVE:
+ if (this->drawDmgEffTimer == 0) {
+ Math_ApproachZeroF(&this->drawDmgEffScale, 1.0f, 0.05f);
+ if (this->drawDmgEffScale == 0.0f) {
+ this->drawDmgEffState = ODOLWA_DRAW_DMGEFF_STATE_NONE;
+ this->drawDmgEffAlpha = 0.0f;
+ }
+ }
+ break;
+
+ default:
+ break;
+ }
+}
+
+/**
+ * Draws the sword trail that follows Odolwa's sword during horizontal and vertical slashes.
+ */
+void Boss01_DrawSwordTrail(Boss01* this, PlayState* play) {
+ static u8 sSwordTrailOuterVertexIndices[] = { 0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20 };
+ static u8 sSwordTrailInnerVertexIndices[] = { 1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21 };
+ Vtx* vtx;
+ u32 i;
+
+ OPEN_DISPS(play->state.gfxCtx);
+
+ vtx = Lib_SegmentedToVirtual(&gOdolwaSwordTrailVtx);
+
+ for (i = 0; i < ARRAY_COUNT(sSwordTrailOuterVertexIndices); i++) {
+ vtx[sSwordTrailOuterVertexIndices[i]].v.ob[0] = cosf((i * M_PI) / sSwordTrailAngularRangeDivisor) * 200.0f;
+ vtx[sSwordTrailOuterVertexIndices[i]].v.ob[1] = 0;
+ vtx[sSwordTrailOuterVertexIndices[i]].v.ob[2] = sinf((i * M_PI) / sSwordTrailAngularRangeDivisor) * 200.0f;
+
+ vtx[sSwordTrailInnerVertexIndices[i]].v.ob[0] = cosf((i * M_PI) / sSwordTrailAngularRangeDivisor) * 100.0f;
+ vtx[sSwordTrailInnerVertexIndices[i]].v.ob[1] = 0;
+ vtx[sSwordTrailInnerVertexIndices[i]].v.ob[2] = sinf((i * M_PI) / sSwordTrailAngularRangeDivisor) * 100.0f;
+ }
+
+ gSPSegment(
+ POLY_XLU_DISP++, 0x08,
+ Gfx_TwoTexScroll(play->state.gfxCtx, G_TX_RENDERTILE, 0, 0, 32, 32, 1, play->gameplayFrames * 18, 0, 32, 32));
+ gDPPipeSync(POLY_XLU_DISP++);
+ gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 255, 255, 255, (u8)sOdolwaSwordTrailAlpha);
+
+ Matrix_Translate(this->actor.world.pos.x + sOdolwaSwordTrailPosX, this->actor.world.pos.y + sOdolwaSwordTrailPosY,
+ this->actor.world.pos.z + sOdolwaSwordTrailPosZ, MTXMODE_NEW);
+ Matrix_RotateYF(BINANG_TO_RAD(this->actor.shape.rot.y), MTXMODE_APPLY);
+ Matrix_RotateXFApply(sOdolwaSwordTrailRotX);
+ Matrix_RotateZF(sOdolwaSwordTrailRotZ, MTXMODE_APPLY);
+ Matrix_RotateYF(sOdolwaSwordTrailRotY, MTXMODE_APPLY);
+
+ gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(play->state.gfxCtx), G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW);
+ gSPDisplayList(POLY_XLU_DISP++, gOdolwaSwordTrailDL);
+
+ CLOSE_DISPS(play->state.gfxCtx);
+}
+
+s32 Boss01_OverrideLimbDraw(PlayState* play, s32 limbIndex, Gfx** dList, Vec3f* pos, Vec3s* rot, Actor* thisx) {
+ Boss01* this = THIS;
+
+ if (limbIndex == ODOLWA_LIMB_HEAD) {
+ // The rot variable here is in model space, whereas the headRot variables are in world space.
+ // Odolwa's head is lying on its side in model space, which is why this assignment looks weird.
+ rot->x += this->headRotY;
+ rot->y += this->headRotX;
+ }
+
+ return false;
+}
+
+static s8 sLimbToColliderBodyParts[] = {
+ BODYPART_NONE, // ODOLWA_LIMB_NONE
+ BODYPART_NONE, // ODOLWA_LIMB_ROOT
+ ODOLWA_COLLIDER_BODYPART_PELVIS, // ODOLWA_LIMB_PELVIS
+ BODYPART_NONE, // ODOLWA_LIMB_RIGHT_LEG_ROOT
+ ODOLWA_COLLIDER_BODYPART_RIGHT_THIGH, // ODOLWA_LIMB_RIGHT_THIGH
+ BODYPART_NONE, // ODOLWA_LIMB_RIGHT_LOWER_LEG_ROOT
+ ODOLWA_COLLIDER_BODYPART_RIGHT_SHIN, // ODOLWA_LIMB_RIGHT_SHIN
+ BODYPART_NONE, // ODOLWA_LIMB_RIGHT_FOOT
+ BODYPART_NONE, // ODOLWA_LIMB_LEFT_LEG_ROOT
+ ODOLWA_COLLIDER_BODYPART_LEFT_THIGH, // ODOLWA_LIMB_LEFT_THIGH
+ BODYPART_NONE, // ODOLWA_LIMB_LEFT_LOWER_LEG_ROOT
+ ODOLWA_COLLIDER_BODYPART_LEFT_SHIN, // ODOLWA_LIMB_LEFT_SHIN
+ BODYPART_NONE, // ODOLWA_LIMB_LEFT_FOOT
+ BODYPART_NONE, // ODOLWA_LIMB_UPPER_BODY_ROOT
+ BODYPART_NONE, // ODOLWA_LIMB_UPPER_BODY_WRAPPER
+ ODOLWA_COLLIDER_BODYPART_TORSO, // ODOLWA_LIMB_TORSO
+ BODYPART_NONE, // ODOLWA_LIMB_LEFT_ARM_ROOT
+ ODOLWA_COLLIDER_BODYPART_LEFT_UPPER_ARM, // ODOLWA_LIMB_LEFT_UPPER_ARM
+ BODYPART_NONE, // ODOLWA_LIMB_LEFT_LOWER_ARM_ROOT
+ ODOLWA_COLLIDER_BODYPART_LEFT_FOREARM, // ODOLWA_LIMB_LEFT_FOREARM
+ BODYPART_NONE, // ODOLWA_LIMB_LEFT_BANGLE
+ BODYPART_NONE, // ODOLWA_LIMB_LEFT_HAND_ROOT
+ BODYPART_NONE, // ODOLWA_LIMB_LEFT_HAND
+ BODYPART_NONE, // ODOLWA_LIMB_SHIELD
+ BODYPART_NONE, // ODOLWA_LIMB_RIGHT_ARM_ROOT
+ ODOLWA_COLLIDER_BODYPART_RIGHT_UPPER_ARM, // ODOLWA_LIMB_RIGHT_UPPER_ARM
+ BODYPART_NONE, // ODOLWA_LIMB_RIGHT_LOWER_ARM_ROOT
+ ODOLWA_COLLIDER_BODYPART_RIGHT_FOREARM, // ODOLWA_LIMB_RIGHT_FOREARM
+ BODYPART_NONE, // ODOLWA_LIMB_RIGHT_BANGLE
+ BODYPART_NONE, // ODOLWA_LIMB_RIGHT_HAND_ROOT
+ BODYPART_NONE, // ODOLWA_LIMB_RIGHT_HAND
+ BODYPART_NONE, // ODOLWA_LIMB_SWORD
+ ODOLWA_COLLIDER_BODYPART_HEAD, // ODOLWA_LIMB_HEAD
+ BODYPART_NONE, // ODOLWA_LIMB_RIGHT_EARRING_ROOT
+ BODYPART_NONE, // ODOLWA_LIMB_RIGHT_EARRING
+ BODYPART_NONE, // ODOLWA_LIMB_LEFT_EARRING_ROOT
+ BODYPART_NONE, // ODOLWA_LIMB_LEFT_EARRING
+ BODYPART_NONE, // ODOLWA_LIMB_RIGHT_PLUME_ROOT
+ BODYPART_NONE, // ODOLWA_LIMB_RIGHT_PLUME_BASE
+ BODYPART_NONE, // ODOLWA_LIMB_RIGHT_LOWER_PLUME_ROOT
+ BODYPART_NONE, // ODOLWA_LIMB_RIGHT_PLUME_MIDDLE
+ BODYPART_NONE, // ODOLWA_LIMB_RIGHT_PLUME_TIP
+ BODYPART_NONE, // ODOLWA_LIMB_LEFT_PLUME_ROOT
+ BODYPART_NONE, // ODOLWA_LIMB_LEFT_PLUME_BASE
+ BODYPART_NONE, // ODOLWA_LIMB_LEFT_LOWER_PLUME_ROOT
+ BODYPART_NONE, // ODOLWA_LIMB_LEFT_PLUME_MIDDLE
+ BODYPART_NONE, // ODOLWA_LIMB_LEFT_PLUME_TIP
+ BODYPART_NONE, // ODOLWA_LIMB_CENTER_PLUME_ROOT
+ BODYPART_NONE, // ODOLWA_LIMB_CENTER_PLUME_BASE
+ BODYPART_NONE, // ODOLWA_LIMB_CENTER_LOWER_PLUME_ROOT
+ BODYPART_NONE, // ODOLWA_LIMB_CENTER_PLUME_MIDDLE
+ BODYPART_NONE, // ODOLWA_LIMB_CENTER_PLUME_TIP
+ BODYPART_NONE, // Doesn't correspond to a real limb on Odolwa
+ BODYPART_NONE, // Doesn't correspond to a real limb on Odolwa
+ BODYPART_NONE, // Doesn't correspond to a real limb on Odolwa
+ BODYPART_NONE, // Doesn't correspond to a real limb on Odolwa
+ BODYPART_NONE, // Doesn't correspond to a real limb on Odolwa
+ BODYPART_NONE, // Doesn't correspond to a real limb on Odolwa
+ BODYPART_NONE, // Doesn't correspond to a real limb on Odolwa
+ BODYPART_NONE, // Doesn't correspond to a real limb on Odolwa
+};
+
+static Vec3f sLimbColliderOffsets[ODOLWA_COLLIDER_BODYPART_MAX] = {
+ { 1300.0f, 0.0f, 0.0f }, // ODOLWA_COLLIDER_BODYPART_HEAD
+ { 1000.0f, 0.0f, 0.0f }, // ODOLWA_COLLIDER_BODYPART_TORSO
+ { 0.0f, 0.0f, 0.0f }, // ODOLWA_COLLIDER_BODYPART_PELVIS
+ { 1000.0f, 0.0f, 0.0f }, // ODOLWA_COLLIDER_BODYPART_LEFT_UPPER_ARM
+ { 1000.0f, 0.0f, 0.0f }, // ODOLWA_COLLIDER_BODYPART_LEFT_FOREARM
+ { 1000.0f, 0.0f, 0.0f }, // ODOLWA_COLLIDER_BODYPART_RIGHT_UPPER_ARM
+ { 1000.0f, 0.0f, 0.0f }, // ODOLWA_COLLIDER_BODYPART_RIGHT_FOREARM
+ { 1500.0f, 0.0f, 0.0f }, // ODOLWA_COLLIDER_BODYPART_LEFT_THIGH
+ { 1500.0f, 0.0f, 0.0f }, // ODOLWA_COLLIDER_BODYPART_LEFT_SHIN
+ { 1500.0f, 0.0f, 0.0f }, // ODOLWA_COLLIDER_BODYPART_RIGHT_THIGH
+ { 1500.0f, 0.0f, 0.0f }, // ODOLWA_COLLIDER_BODYPART_RIGHT_SHIN
+};
+
+static Vec3f sShieldColliderOffset = { 0.0f, 500.0f, 0.0f };
+
+static Vec3f sSwordBaseColliderOffset = { 500.0f, -2500.0f, 0.0f };
+
+static Vec3f sSwordTipColliderOffset = { 1500.0f, -7000.0f, 0.0f };
+
+/**
+ * When Odolwa performs a horizontal slash, his sword is very likely to be too high to hit the player. In order to make
+ * the attack effective, there is an additional collider projected from Odolwa's pelvis that sweeps across the floor
+ * during his horizontal slash. This variable controls the offset of this collider from his pelvis limb.
+ */
+static Vec3f sHorizontalSlashPelvisColliderOffset = { 5000.0f, 0.0f, 9000.0f };
+
+static s8 sLimbToBodyParts[] = {
+ BODYPART_NONE, // ODOLWA_LIMB_NONE
+ BODYPART_NONE, // ODOLWA_LIMB_ROOT
+ ODOLWA_BODYPART_PELVIS, // ODOLWA_LIMB_PELVIS
+ BODYPART_NONE, // ODOLWA_LIMB_RIGHT_LEG_ROOT
+ ODOLWA_BODYPART_RIGHT_THIGH, // ODOLWA_LIMB_RIGHT_THIGH
+ BODYPART_NONE, // ODOLWA_LIMB_RIGHT_LOWER_LEG_ROOT
+ ODOLWA_BODYPART_RIGHT_SHIN, // ODOLWA_LIMB_RIGHT_SHIN
+ ODOLWA_BODYPART_RIGHT_FOOT, // ODOLWA_LIMB_RIGHT_FOOT
+ BODYPART_NONE, // ODOLWA_LIMB_LEFT_LEG_ROOT
+ ODOLWA_BODYPART_LEFT_THIGH, // ODOLWA_LIMB_LEFT_THIGH
+ BODYPART_NONE, // ODOLWA_LIMB_LEFT_LOWER_LEG_ROOT
+ ODOLWA_BODYPART_LEFT_SHIN, // ODOLWA_LIMB_LEFT_SHIN
+ ODOLWA_BODYPART_LEFT_FOOT, // ODOLWA_LIMB_LEFT_FOOT
+ BODYPART_NONE, // ODOLWA_LIMB_UPPER_BODY_ROOT
+ BODYPART_NONE, // ODOLWA_LIMB_UPPER_BODY_WRAPPER
+ ODOLWA_BODYPART_TORSO, // ODOLWA_LIMB_TORSO
+ BODYPART_NONE, // ODOLWA_LIMB_LEFT_ARM_ROOT
+ ODOLWA_BODYPART_LEFT_UPPER_ARM, // ODOLWA_LIMB_LEFT_UPPER_ARM
+ BODYPART_NONE, // ODOLWA_LIMB_LEFT_LOWER_ARM_ROOT
+ ODOLWA_BODYPART_LEFT_FOREARM, // ODOLWA_LIMB_LEFT_FOREARM
+ BODYPART_NONE, // ODOLWA_LIMB_LEFT_BANGLE
+ BODYPART_NONE, // ODOLWA_LIMB_LEFT_HAND_ROOT
+ BODYPART_NONE, // ODOLWA_LIMB_LEFT_HAND
+ ODOLWA_BODYPART_SHIELD, // ODOLWA_LIMB_SHIELD
+ BODYPART_NONE, // ODOLWA_LIMB_RIGHT_ARM_ROOT
+ ODOLWA_BODYPART_RIGHT_UPPER_ARM, // ODOLWA_LIMB_RIGHT_UPPER_ARM
+ BODYPART_NONE, // ODOLWA_LIMB_RIGHT_LOWER_ARM_ROOT
+ ODOLWA_BODYPART_RIGHT_FOREARM, // ODOLWA_LIMB_RIGHT_FOREARM
+ BODYPART_NONE, // ODOLWA_LIMB_RIGHT_BANGLE
+ BODYPART_NONE, // ODOLWA_LIMB_RIGHT_HAND_ROOT
+ BODYPART_NONE, // ODOLWA_LIMB_RIGHT_HAND
+ ODOLWA_BODYPART_SWORD, // ODOLWA_LIMB_SWORD
+ ODOLWA_BODYPART_HEAD, // ODOLWA_LIMB_HEAD
+ BODYPART_NONE, // ODOLWA_LIMB_RIGHT_EARRING_ROOT
+ BODYPART_NONE, // ODOLWA_LIMB_RIGHT_EARRING
+ BODYPART_NONE, // ODOLWA_LIMB_LEFT_EARRING_ROOT
+ BODYPART_NONE, // ODOLWA_LIMB_LEFT_EARRING
+ BODYPART_NONE, // ODOLWA_LIMB_RIGHT_PLUME_ROOT
+ BODYPART_NONE, // ODOLWA_LIMB_RIGHT_PLUME_BASE
+ BODYPART_NONE, // ODOLWA_LIMB_RIGHT_LOWER_PLUME_ROOT
+ BODYPART_NONE, // ODOLWA_LIMB_RIGHT_PLUME_MIDDLE
+ BODYPART_NONE, // ODOLWA_LIMB_RIGHT_PLUME_TIP
+ BODYPART_NONE, // ODOLWA_LIMB_LEFT_PLUME_ROOT
+ BODYPART_NONE, // ODOLWA_LIMB_LEFT_PLUME_BASE
+ BODYPART_NONE, // ODOLWA_LIMB_LEFT_LOWER_PLUME_ROOT
+ BODYPART_NONE, // ODOLWA_LIMB_LEFT_PLUME_MIDDLE
+ BODYPART_NONE, // ODOLWA_LIMB_LEFT_PLUME_TIP
+ BODYPART_NONE, // ODOLWA_LIMB_CENTER_PLUME_ROOT
+ BODYPART_NONE, // ODOLWA_LIMB_CENTER_PLUME_BASE
+ BODYPART_NONE, // ODOLWA_LIMB_CENTER_LOWER_PLUME_ROOT
+ BODYPART_NONE, // ODOLWA_LIMB_CENTER_PLUME_MIDDLE
+ BODYPART_NONE, // ODOLWA_LIMB_CENTER_PLUME_TIP
+ BODYPART_NONE, // Doesn't correspond to a real limb on Odolwa
+};
+
+void Boss01_PostLimbDraw(PlayState* play2, s32 limbIndex, Gfx** dList, Vec3s* rot, Actor* thisx) {
+ Boss01* this = THIS;
+ PlayState* play = play2;
+ s8 index;
+ Vec3f pos;
+
+ if (limbIndex == ODOLWA_LIMB_HEAD) {
+ Matrix_MultZero(&this->actor.focus.pos);
+ }
+
+ index = sLimbToBodyParts[limbIndex];
+ if (index > BODYPART_NONE) {
+ Matrix_MultZero(&this->bodyPartsPos[index]);
+ }
+
+ index = sLimbToColliderBodyParts[limbIndex];
+ if (index > BODYPART_NONE) {
+ Matrix_MultVec3f(&sLimbColliderOffsets[index], &pos);
+ Boss01_SetColliderSphere(index, &this->bodyCollider, &pos);
+ }
+
+ if (limbIndex == ODOLWA_LIMB_PELVIS) {
+ Matrix_MultZero(&this->pelvisPos);
+ if (this->swordState == ODOLWA_SWORD_STATE_HORIZONTAL_SLASH) {
+ Matrix_MultVec3f(&sHorizontalSlashPelvisColliderOffset, &pos);
+ Boss01_SetColliderSphere(ODOLWA_SWORD_COLLIDER_PELVIS, &this->swordCollider, &pos);
+ }
+ }
+
+ if (limbIndex == ODOLWA_LIMB_SWORD) {
+ Matrix_MultVec3f(&sSwordBaseColliderOffset, &pos);
+ Boss01_SetColliderSphere(ODOLWA_SWORD_COLLIDER_SWORD_BASE, &this->swordCollider, &pos);
+ Matrix_MultVec3f(&sSwordTipColliderOffset, &pos);
+ Boss01_SetColliderSphere(ODOLWA_SWORD_COLLIDER_SWORD_TIP, &this->swordCollider, &pos);
+ }
+
+ if (limbIndex == ODOLWA_LIMB_SHIELD) {
+ Matrix_MultVec3f(&sShieldColliderOffset, &pos);
+ Boss01_SetColliderSphere(ODOLWA_SHIELD_COLLIDER_SHIELD, &this->shieldCollider, &pos);
+ Boss01_SetColliderSphere(ODOLWA_KICK_AND_SHIELD_BASH_COLLIDER_SHIELD, &this->kickAndShieldBashCollider, &pos);
+ }
+
+ if (limbIndex == ODOLWA_LIMB_RIGHT_FOOT) {
+ Matrix_MultZero(&this->feetPos[0]);
+ }
+
+ if (limbIndex == ODOLWA_LIMB_LEFT_FOOT) {
+ Matrix_MultZero(&this->feetPos[1]);
+ Matrix_MultZero(&pos);
+ Boss01_SetColliderSphere(ODOLWA_KICK_AND_SHIELD_BASH_COLLIDER_LEFT_FOOT, &this->kickAndShieldBashCollider,
+ &pos);
+ }
+
+ if (limbIndex == ODOLWA_LIMB_HEAD) {
+ OPEN_DISPS(play->state.gfxCtx);
+
+ gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 255, 0, 0, 255);
+ gDPSetEnvColor(POLY_XLU_DISP++, 255, 0, 0, 0);
+
+ Matrix_Push();
+ Matrix_Translate(1470.0f, 400.0f, 450.0f, MTXMODE_APPLY);
+ Matrix_Scale(0.35f, 0.35f, 0.35f, MTXMODE_APPLY);
+ Matrix_ReplaceRotation(&play->billboardMtxF);
+ gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(play->state.gfxCtx), G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW);
+ gSPDisplayList(POLY_XLU_DISP++, gOdolwaEyeDL);
+ Matrix_Pop();
+
+ Matrix_Push();
+ Matrix_Translate(1470.0f, -360.0f, 450.0f, MTXMODE_APPLY);
+ Matrix_Scale(0.35f, 0.35f, 0.35f, MTXMODE_APPLY);
+ Matrix_ReplaceRotation(&play->billboardMtxF);
+ gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(play->state.gfxCtx), G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW);
+ gSPDisplayList(POLY_XLU_DISP++, gOdolwaEyeDL);
+ Matrix_Pop();
+
+ CLOSE_DISPS(play->state.gfxCtx);
+ }
+}
+
+void Boss01_Draw(Actor* thisx, PlayState* play) {
+ static Vec3f sDefaultPelvisColliderOffset = { 10000.0f, 10000.0f, 10000.0f };
+ Boss01* this = THIS;
+ s32 pad;
+ u8* tex = GRAPH_ALLOC(play->state.gfxCtx, ODOLWA_SHADOW_TEX_SIZE);
+
+ OPEN_DISPS(play->state.gfxCtx);
+
+ // When Odolwa is performing a horizontal slash, an additional collider offset from his pelvis will sweep across the
+ // floor and damage the player. However, this collider should not be present all the time, so it is offset very far
+ // out-of-bounds here. If Odolwa is performing a horizontal slash, then Boss01_PostLimbDraw (which is called later
+ // in this function) will offset it properly for this frame.
+ Boss01_SetColliderSphere(ODOLWA_SWORD_COLLIDER_PELVIS, &this->swordCollider, &sDefaultPelvisColliderOffset);
+
+ Gfx_SetupDL25_Opa(play->state.gfxCtx);
+ Gfx_SetupDL25_Xlu(play->state.gfxCtx);
+
+ if (this->damageFlashTimer & 1) {
+ POLY_OPA_DISP = Gfx_SetFog(POLY_OPA_DISP, 255, 0, 0, 255, 900, 1099);
+ }
+
+ SkelAnime_DrawFlexOpa(play, this->skelAnime.skeleton, this->skelAnime.jointTable, this->skelAnime.dListCount,
+ Boss01_OverrideLimbDraw, Boss01_PostLimbDraw, &this->actor);
+
+ POLY_OPA_DISP = Play_SetFog(play, POLY_OPA_DISP);
+
+ Boss01_GenShadowTex(tex, this, play);
+ Boss01_DrawShadowTex(tex, this, play);
+
+ if (sOdolwaSwordTrailAlpha > 0.0f) {
+ Boss01_DrawSwordTrail(this, play);
+ Math_ApproachZeroF(&sOdolwaSwordTrailAlpha, 1.0f, 50.0f);
+ }
+
+ Actor_DrawDamageEffects(play, &this->actor, this->bodyPartsPos, ODOLWA_BODYPART_MAX, this->drawDmgEffScale,
+ this->drawDmgEffFrozenSteamScale, this->drawDmgEffAlpha, this->drawDmgEffType);
+
+ Boss01_DrawEffects(play);
+
+ CLOSE_DISPS(play->state.gfxCtx);
+}
+
+void Boss01_Afterimage_Draw(Actor* thisx, PlayState* play) {
+ Boss01* this = THIS;
+ s32 pad;
+ Boss01* parent = (Boss01*)this->actor.parent;
+
+ OPEN_DISPS(play->state.gfxCtx);
+
+ Gfx_SetupDL25_Opa(play->state.gfxCtx);
+ Gfx_SetupDL25_Xlu(play->state.gfxCtx);
+ POLY_OPA_DISP = Gfx_SetFog(POLY_OPA_DISP, 50, 0, 40, 255, 900, 1099);
+ SkelAnime_DrawFlexOpa(play, parent->skelAnime.skeleton, this->skelAnime.jointTable, parent->skelAnime.dListCount,
+ NULL, NULL, &this->actor);
+ POLY_OPA_DISP = Play_SetFog(play, POLY_OPA_DISP);
+
+ CLOSE_DISPS(play->state.gfxCtx);
+}
+
+/**
+ * These four arrays encode circular shadow maps of various sizes. For an array of length N, the shadow map is N rows
+ * tall, and each entry in the array describes the start and end point of the shadow within a given row (the exact
+ * values of the start and end points are determined by the loops within Boss01_FillShadowTex). To illustrate using the
+ * sShadowSmallMap as an example:
+ * -3 -2 -1 0 1
+ * -------------
+ * 0 0 1 0 0
+ * 0 1 1 1 0
+ * 1 1 1 1 1
+ * 1 1 1 1 1
+ * 0 1 1 1 0
+ * 0 0 1 0 0
+ */
+static s32 sShadowSmallMap[] = {
+ 1, 2, 3, 3, 2, 1,
+};
+
+static s32 sShadowMediumMap[] = {
+ 2, 3, 4, 4, 4, 3, 2,
+};
+
+static s32 sShadowLargeMap[] = {
+ 2, 3, 4, 4, 4, 4, 3, 2,
+};
+
+static s32 sShadowExtraLargeMap[] = {
+ 2, 4, 5, 5, 6, 6, 6, 6, 5, 5, 4, 2,
+};
+
+static s32 sParentShadowBodyParts[ODOLWA_BODYPART_MAX] = {
+ ODOLWA_BODYPART_TORSO, // ODOLWA_BODYPART_HEAD
+ BODYPART_NONE, // ODOLWA_BODYPART_TORSO
+ ODOLWA_BODYPART_TORSO, // ODOLWA_BODYPART_PELVIS
+ ODOLWA_BODYPART_TORSO, // ODOLWA_BODYPART_LEFT_UPPER_ARM
+ ODOLWA_BODYPART_LEFT_UPPER_ARM, // ODOLWA_BODYPART_LEFT_FOREARM
+ ODOLWA_BODYPART_LEFT_FOREARM, // ODOLWA_BODYPART_SHIELD
+ ODOLWA_BODYPART_TORSO, // ODOLWA_BODYPART_RIGHT_UPPER_ARM
+ ODOLWA_BODYPART_RIGHT_UPPER_ARM, // ODOLWA_BODYPART_RIGHT_FOREARM
+ ODOLWA_BODYPART_RIGHT_FOREARM, // ODOLWA_BODYPART_SWORD
+ ODOLWA_BODYPART_PELVIS, // ODOLWA_BODYPART_RIGHT_THIGH
+ ODOLWA_BODYPART_RIGHT_THIGH, // ODOLWA_BODYPART_RIGHT_SHIN
+ ODOLWA_BODYPART_RIGHT_SHIN, // ODOLWA_BODYPART_RIGHT_FOOT
+ ODOLWA_BODYPART_PELVIS, // ODOLWA_BODYPART_LEFT_THIGH
+ ODOLWA_BODYPART_LEFT_THIGH, // ODOLWA_BODYPART_LEFT_SHIN
+ ODOLWA_BODYPART_LEFT_SHIN, // ODOLWA_BODYPART_LEFT_FOOT
+};
+
+static u8 sShadowSizes[ODOLWA_BODYPART_MAX] = {
+ ODOLWA_SHADOW_SIZE_SMALL, // ODOLWA_BODYPART_HEAD
+ ODOLWA_SHADOW_SIZE_EXTRA_LARGE, // ODOLWA_BODYPART_TORSO
+ ODOLWA_SHADOW_SIZE_EXTRA_LARGE, // ODOLWA_BODYPART_PELVIS
+ ODOLWA_SHADOW_SIZE_LARGE, // ODOLWA_BODYPART_LEFT_UPPER_ARM
+ ODOLWA_SHADOW_SIZE_SMALL, // ODOLWA_BODYPART_LEFT_FOREARM
+ ODOLWA_SHADOW_SIZE_EXTRA_LARGE, // ODOLWA_BODYPART_SHIELD
+ ODOLWA_SHADOW_SIZE_LARGE, // ODOLWA_BODYPART_RIGHT_UPPER_ARM
+ ODOLWA_SHADOW_SIZE_SMALL, // ODOLWA_BODYPART_RIGHT_FOREARM
+ ODOLWA_SHADOW_SIZE_SMALL, // ODOLWA_BODYPART_SWORD
+ ODOLWA_SHADOW_SIZE_LARGE, // ODOLWA_BODYPART_RIGHT_THIGH
+ ODOLWA_SHADOW_SIZE_MEDIUM, // ODOLWA_BODYPART_RIGHT_SHIN
+ ODOLWA_SHADOW_SIZE_SMALL, // ODOLWA_BODYPART_RIGHT_FOOT
+ ODOLWA_SHADOW_SIZE_LARGE, // ODOLWA_BODYPART_LEFT_THIGH
+ ODOLWA_SHADOW_SIZE_MEDIUM, // ODOLWA_BODYPART_LEFT_SHIN
+ ODOLWA_SHADOW_SIZE_SMALL, // ODOLWA_BODYPART_LEFT_FOOT
+};
+
+void Boss01_FillShadowTex(Boss01* this, u8* tex, f32 weight) {
+ s32 index;
+ s32 i;
+ s32 baseX;
+ s32 baseY;
+ s32 x;
+ s32 y = 0;
+ s32 addY;
+ Vec3f lerp;
+ Vec3f pos;
+ Vec3f startVec;
+
+ for (i = 0; i < ODOLWA_BODYPART_MAX; i++) {
+ if ((weight == 0.0f) || (y = sParentShadowBodyParts[i]) > BODYPART_NONE) {
+ if (weight > 0.0f) {
+ VEC3F_LERPIMPDST(&lerp, &this->bodyPartsPos[i], &this->bodyPartsPos[y], weight);
+
+ pos.x = lerp.x - this->actor.world.pos.x;
+ pos.y = lerp.y - this->actor.world.pos.y + 76.0f + 30.0f + 30.0f + 100.0f;
+ pos.z = lerp.z - this->actor.world.pos.z;
+ } else {
+ pos.x = this->bodyPartsPos[i].x - this->actor.world.pos.x;
+ pos.y = this->bodyPartsPos[i].y - this->actor.world.pos.y + 76.0f + 30.0f + 30.0f + 100.0f;
+ pos.z = this->bodyPartsPos[i].z - this->actor.world.pos.z;
+ }
+
+ Matrix_MultVec3f(&pos, &startVec);
+
+ startVec.x *= 0.2f;
+ startVec.y *= 0.2f;
+
+ baseX = (u16)(s32)(startVec.x + 32.0f);
+ baseY = (u16)((s32)startVec.y * 64);
+
+ if (sShadowSizes[i] == ODOLWA_SHADOW_SIZE_EXTRA_LARGE) {
+ for (y = 0, addY = -0x180; y < ARRAY_COUNT(sShadowExtraLargeMap); y++, addY += 0x40) {
+ for (x = -sShadowExtraLargeMap[y]; x < sShadowExtraLargeMap[y]; x++) {
+ index = baseX + x + baseY + addY;
+ if ((index >= 0) && (index < ODOLWA_SHADOW_TEX_SIZE)) {
+ tex[index] = 255;
+ }
+ }
+ }
+ } else if (sShadowSizes[i] == ODOLWA_SHADOW_SIZE_LARGE) {
+ for (y = 0, addY = -0x100; y < ARRAY_COUNT(sShadowLargeMap); y++, addY += 0x40) {
+ for (x = -sShadowLargeMap[y]; x < sShadowLargeMap[y]; x++) {
+ index = baseX + x + baseY + addY;
+ if ((index >= 0) && (index < ODOLWA_SHADOW_TEX_SIZE)) {
+ tex[index] = 255;
+ }
+ }
+ }
+ } else if (sShadowSizes[i] == ODOLWA_SHADOW_SIZE_MEDIUM) {
+ for (y = 0, addY = -0xC0; y < ARRAY_COUNT(sShadowMediumMap); y++, addY += 0x40) {
+ for (x = -sShadowMediumMap[y]; x < sShadowMediumMap[y] - 1; x++) {
+ index = baseX + x + baseY + addY;
+ if ((index >= 0) && (index < ODOLWA_SHADOW_TEX_SIZE)) {
+ tex[index] = 255;
+ }
+ }
+ }
+ } else {
+ for (y = 0, addY = -0x80; y < ARRAY_COUNT(sShadowSmallMap); y++, addY += 0x40) {
+ for (x = -sShadowSmallMap[y]; x < sShadowSmallMap[y] - 1; x++) {
+ index = baseX + x + baseY + addY;
+ if ((index >= 0) && (index < ODOLWA_SHADOW_TEX_SIZE)) {
+ tex[index] = 255;
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+void Boss01_GenShadowTex(u8* tex, Boss01* this, PlayState* play) {
+ s32* iter = (s32*)tex;
+ s16 i;
+
+ for (i = 0; i < (s32)(ODOLWA_SHADOW_TEX_SIZE / sizeof(s32)); i++, iter++) {
+ *iter = 0;
+ }
+
+ Matrix_RotateXFNew(1.0f);
+
+ for (i = 0; i <= 5; i++) {
+ Boss01_FillShadowTex(this, tex, i / 5.0f);
+ }
+}
+
+/**
+ * Draws Odolwa's dynamic shadow underneath him.
+ */
+void Boss01_DrawShadowTex(u8* tex, Boss01* this, PlayState* play) {
+ s32 pad[2];
+ f32 alpha;
+ GraphicsContext* gfxCtx = play->state.gfxCtx;
+
+ OPEN_DISPS(gfxCtx);
+
+ Gfx_SetupDL25_Opa(play->state.gfxCtx);
+
+ alpha = (400.0f - this->actor.world.pos.y) * (1.0f / 400.0f);
+ alpha = CLAMP_MIN(alpha, 0.0f);
+ alpha = CLAMP_MAX(alpha, 1.0f);
+
+ gDPSetPrimColor(POLY_OPA_DISP++, 0, 0, 0, 0, 0, (s8)(alpha * 80.0f));
+ gDPSetEnvColor(POLY_OPA_DISP++, 0, 0, 0, 0);
+ Matrix_Translate(this->actor.world.pos.x, this->actor.floorHeight, this->actor.world.pos.z - 20.0f, MTXMODE_NEW);
+ Matrix_Scale(1.65f, 1.0f, 1.65f, MTXMODE_APPLY);
+ gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(play->state.gfxCtx), G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW);
+ gSPDisplayList(POLY_OPA_DISP++, gOdolwaShadowMaterialDL);
+ gDPLoadTextureBlock(POLY_OPA_DISP++, tex, G_IM_FMT_I, G_IM_SIZ_8b, ODOLWA_SHADOW_TEX_WIDTH,
+ ODOLWA_SHADOW_TEX_HEIGHT, 0, G_TX_NOMIRROR | G_TX_CLAMP, G_TX_NOMIRROR | G_TX_CLAMP, 6, 6,
+ G_TX_NOLOD, G_TX_NOLOD);
+ gSPDisplayList(POLY_OPA_DISP++, gOdolwaShadowModelDL);
+
+ CLOSE_DISPS(gfxCtx);
+}
+
+void Boss01_Bug_SetupCrawl(Boss01* this, PlayState* play) {
+ this->actionFunc = Boss01_Bug_Crawl;
+ Animation_MorphToLoop(&this->skelAnime, &gOdolwaBugCrawlAnim, -5.0f);
+}
+
+/**
+ * If an explosive is present, the bug will crawl quickly towards it. Otherwise, it will crawl slowly towards the player
+ * and chase them around.
+ */
+void Boss01_Bug_Crawl(Boss01* this, PlayState* play) {
+ s32 pad;
+ Player* player = GET_PLAYER(play);
+ f32 targetSpeed = 3.0f;
+ Actor* targetActor = &player->actor;
+ Actor* explosive = play->actorCtx.actorLists[ACTORCAT_EXPLOSIVES].first;
+ s16 maxStep = 0x3E8;
+
+ if (this->actor.bgCheckFlags & BGCHECKFLAG_GROUND) {
+ this->skelAnime.playSpeed = 1.0f;
+
+ while (explosive != NULL) {
+ if (explosive->params == 1) {
+ explosive = explosive->next;
+ } else {
+ targetSpeed = 5.0f;
+ targetActor = explosive;
+ maxStep = 0x7D0;
+ this->skelAnime.playSpeed = 1.5f;
+ break;
+ }
+ }
+
+ SkelAnime_Update(&this->skelAnime);
+ Actor_PlaySfx(&this->actor, NA_SE_EN_MB_INSECT_WALK - SFX_FLAG);
+ Math_ApproachF(&this->actor.speed, targetSpeed, 1.0f, 1.0f);
+ Math_ApproachS(&this->actor.world.rot.y,
+ Math_Atan2S(targetActor->world.pos.x - this->actor.world.pos.x,
+ targetActor->world.pos.z - this->actor.world.pos.z),
+ 5, maxStep);
+ }
+}
+
+void Boss01_Bug_SetupDamaged(Boss01* this, PlayState* play) {
+ if ((s8)this->actor.colChkInfo.health > 0) {
+ this->actionFunc = Boss01_Bug_Damaged;
+ this->timers[TIMER_BUG_CURRENT_ACTION] = 30;
+ } else {
+ this->timers[TIMER_BUG_CURRENT_ACTION] = 15;
+ Enemy_StartFinishingBlow(play, &this->actor);
+ Boss01_Bug_SetupDead(this, play);
+ }
+}
+
+/**
+ * Stops the bug's animations and makes it wait in place for 30 frames, then it starts crawling again.
+ */
+void Boss01_Bug_Damaged(Boss01* this, PlayState* play) {
+ Math_ApproachZeroF(&this->actor.speed, 1.0f, 1.0f);
+ if (this->timers[TIMER_BUG_CURRENT_ACTION] == 0) {
+ Boss01_Bug_SetupCrawl(this, play);
+ }
+}
+
+void Boss01_Bug_SetupDead(Boss01* this, PlayState* play) {
+ this->actionFunc = Boss01_Bug_Dead;
+ this->additionalVelocityZ = 0.0f;
+ this->additionalVelocityX = 0.0f;
+ this->bugDrawDmgEffAlpha = 1.0f;
+ this->actor.speed = -15.0f;
+ this->actor.velocity.y = 12.0f;
+ this->actor.world.rot.y = this->actor.yawTowardsPlayer;
+ this->actor.flags &= ~ACTOR_FLAG_TARGETABLE;
+}
+
+void Boss01_Bug_SetupStunned(Boss01* this, PlayState* play) {
+ this->actionFunc = Boss01_Bug_Stunned;
+ this->timers[TIMER_BUG_CURRENT_ACTION] = 40;
+ Actor_SetColorFilter(&this->actor, COLORFILTER_COLORFLAG_BLUE, 120, COLORFILTER_BUFFLAG_OPA, 40);
+}
+
+/**
+ * Turns the bug blue, stops its animations, and makes it wait in place for 40 frames, then it starts crawling again.
+ */
+void Boss01_Bug_Stunned(Boss01* this, PlayState* play) {
+ Math_ApproachZeroF(&this->actor.speed, 1.0f, 1.0f);
+ if (this->timers[TIMER_BUG_CURRENT_ACTION] == 0) {
+ Boss01_Bug_SetupCrawl(this, play);
+ }
+}
+
+/**
+ * Flings the bug backwards, spinning randomly, until it hits the ground. After it lands, it shrinks and spawns a single
+ * blue flame. The bug will die and drop an item once the flame fades enough.
+ */
+void Boss01_Bug_Dead(Boss01* this, PlayState* play) {
+ if (this->actor.bgCheckFlags & BGCHECKFLAG_GROUND) {
+ Actor_PlaySfx(&this->actor, NA_SE_EN_COMMON_EXTINCT_LEV - SFX_FLAG);
+ Math_ApproachZeroF(&this->actor.speed, 1.0f, 2.0f);
+ Math_ApproachZeroF(&this->actor.scale.y, 1.0f, 0.00075f);
+ Math_ApproachF(&this->bugDrawDmgEffScale, 1.0f, 0.5f, 0.15f);
+
+ if (this->actor.scale.y < 0.001f) {
+ Math_ApproachZeroF(&this->bugDrawDmgEffAlpha, 1.0f, 0.05f);
+ if (this->bugDrawDmgEffAlpha < 0.01f) {
+ Actor_Kill(&this->actor);
+ Item_DropCollectibleRandom(play, NULL, &this->actor.world.pos, 0x60);
+ sOdolwaBugCount--;
+ }
+ }
+
+ Math_ApproachS(&this->actor.shape.rot.x, 0, 1, 0x800);
+ } else {
+ this->actor.shape.rot.x -= 0x2000;
+ this->actor.shape.rot.z += 0x1000;
+ }
+}
+
+void Boss01_Bug_UpdateDamage(Boss01* this, PlayState* play) {
+ Vec3f additionalVelocity;
+ s32 pad[2];
+ u8 damage;
+ ColliderInfo* acHitInfo;
+ OdolwaEffect* effect = play->specialEffects;
+
+ if (this->bugACCollider.base.acFlags & AC_HIT) {
+ this->bugACCollider.base.acFlags &= ~AC_HIT;
+ acHitInfo = this->bugACCollider.info.acHitInfo;
+
+ if (this->damageTimer == 0) {
+ Matrix_RotateYS(this->actor.yawTowardsPlayer, MTXMODE_NEW);
+ if (acHitInfo->toucher.dmgFlags & 0x300000) {
+ this->damageTimer = 10;
+ Matrix_MultVecZ(-10.0f, &additionalVelocity);
+ this->additionalVelocityX = additionalVelocity.x;
+ this->additionalVelocityZ = additionalVelocity.z;
+ } else {
+ this->damageTimer = 15;
+ this->damageFlashTimer = 15;
+ Matrix_MultVecZ(-20.0f, &additionalVelocity);
+ this->additionalVelocityX = additionalVelocity.x;
+ this->additionalVelocityZ = additionalVelocity.z;
+ if (this->actor.colChkInfo.damageEffect == BUG_DMGEFF_STUN) {
+ Boss01_Bug_SetupStunned(this, play);
+ Actor_PlaySfx(&this->actor, NA_SE_EN_COMMON_FREEZE);
+ } else {
+ damage = this->actor.colChkInfo.damage;
+ this->actor.colChkInfo.health -= damage;
+ Boss01_Bug_SetupDamaged(this, play);
+ Actor_PlaySfx(&this->actor, NA_SE_EN_MIZUBABA2_DAMAGE);
+ }
+ }
+ }
+ }
+
+ // If Odolwa dies, instantly kill any bugs in the room too. Setting their y-velocity to 0 will skip the part of
+ // their death where they fly backwards; they will immediately shrink and burst into a single blue flame.
+ if ((sOdolwa != NULL) && (sOdolwa->actionFunc == Boss01_DeathCutscene)) {
+ Boss01_Bug_SetupDead(this, play);
+ this->actor.velocity.y = 0.0f;
+ this->actor.speed = 0.0f;
+ }
+
+ if ((effect->type == ODOLWA_EFFECT_RING_OF_FIRE) && (effect->timer < 150)) {
+ f32 distXZ = sqrtf(SQ(effect->pos.x - this->actor.world.pos.x) + SQ(effect->pos.z - this->actor.world.pos.z));
+
+ // If the bugs touch the ring of fire, it will instantly kill them. Like before, setting the y-velocity to zero
+ // here will immediately make the bug shrink and burst into a single red flame.
+ if ((distXZ < (KREG(49) + 210.0f)) && (distXZ > (KREG(49) + 190.0f))) {
+ Actor_PlaySfx(&this->actor, NA_SE_EN_MIZUBABA2_DAMAGE);
+ Boss01_Bug_SetupDead(this, play);
+ this->damageFlashTimer = 15;
+ this->bugDrawDmgEffType = ACTOR_DRAW_DMGEFF_FIRE;
+ this->actor.speed = 0.0f;
+ this->actor.velocity.y = 5.0f;
+ }
+ }
+}
+
+void Boss01_Bug_Update(Actor* thisx, PlayState* play) {
+ Boss01* this = THIS;
+ s32 pad;
+ s32 i;
+
+ this->frameCounter++;
+
+ for (i = 0; i < ARRAY_COUNT(this->timers); i++) {
+ DECR(this->timers[i]);
+ }
+
+ DECR(this->damageTimer);
+ DECR(this->damageFlashTimer);
+
+ this->actionFunc(this, play);
+
+ Actor_MoveWithGravity(&this->actor);
+ this->actor.world.pos.x += this->additionalVelocityX;
+ this->actor.world.pos.z += this->additionalVelocityZ;
+ Actor_SetFocus(&this->actor, 10.0f);
+ Actor_UpdateBgCheckInfo(play, &this->actor, 50.0f, 21.0f, 100.0f, UPDBGCHECKINFO_FLAG_1 | UPDBGCHECKINFO_FLAG_4);
+ if (this->actor.bgCheckFlags & BGCHECKFLAG_GROUND) {
+ Math_ApproachZeroF(&this->additionalVelocityX, 1.0f, 1.0f);
+ Math_ApproachZeroF(&this->additionalVelocityZ, 1.0f, 1.0f);
+ }
+
+ if (this->actionFunc != Boss01_Bug_Dead) {
+ Boss01_Bug_UpdateDamage(this, play);
+ Collider_UpdateCylinder(&this->actor, &this->bugACCollider);
+ CollisionCheck_SetAC(play, &play->colChkCtx, &this->bugACCollider.base);
+ Collider_UpdateCylinder(&this->actor, &this->bugATCollider);
+ CollisionCheck_SetAT(play, &play->colChkCtx, &this->bugATCollider.base);
+ this->actor.shape.rot = this->actor.world.rot;
+ }
+}
+
+s32 Boss01_Bug_OverrideLimbDraw(PlayState* play, s32 limbIndex, Gfx** dList, Vec3f* pos, Vec3s* rot, Actor* thisx) {
+ if ((limbIndex == ODOLWA_BUG_LIMB_BODY) && (sOdolwa->actionFunc == Boss01_SummonBugsCutscene)) {
+ *dList = gOdolwaBugDarkBodyDL;
+ }
+
+ if (limbIndex == ODOLWA_BUG_LIMB_FRONT_RIGHT_UPPER_LEG) {
+ OPEN_DISPS(play->state.gfxCtx);
+
+ if (sOdolwa->actionFunc == Boss01_SummonBugsCutscene) {
+ gSPDisplayList(POLY_OPA_DISP++, gOdolwaBugBrightLegMaterialDL);
+ } else {
+ gSPDisplayList(POLY_OPA_DISP++, gOdolwaBugDullLegMaterialDL);
+ }
+
+ CLOSE_DISPS(play->state.gfxCtx);
+ }
+
+ return false;
+}
+
+void Boss01_Bug_Draw(Actor* thisx, PlayState* play) {
+ Boss01* this = THIS;
+ s32 pad;
+
+ OPEN_DISPS(play->state.gfxCtx);
+
+ Gfx_SetupDL25_Opa(play->state.gfxCtx);
+
+ if (this->damageFlashTimer & 1) {
+ POLY_OPA_DISP = Gfx_SetFog(POLY_OPA_DISP, 255, 0, 0, 255, 900, 1099);
+ }
+
+ SkelAnime_DrawFlexOpa(play, this->skelAnime.skeleton, this->skelAnime.jointTable, this->skelAnime.dListCount,
+ Boss01_Bug_OverrideLimbDraw, NULL, &this->actor);
+
+ POLY_OPA_DISP = Play_SetFog(play, POLY_OPA_DISP);
+
+ Actor_DrawDamageEffects(play, &this->actor, &this->actor.world.pos, 1, this->bugDrawDmgEffScale, 0.0f,
+ this->bugDrawDmgEffAlpha, this->bugDrawDmgEffType);
+
+ CLOSE_DISPS(play->state.gfxCtx);
+}
+
+/**
+ * Spawns five dust clouds randomly around the block's current position.
+ */
+void Boss01_SpawnDustForFallingBlock(PlayState* play, Vec3f* blockPos, f32 scale) {
+ s16 i;
+ Vec3f velocity;
+ Vec3f accel;
+ Vec3f pos;
+
+ for (i = 0; i < 5; i++) {
+ velocity.x = Rand_CenteredFloat(5.0f);
+ velocity.y = Rand_ZeroFloat(2.0f) + 1.0f;
+ velocity.z = Rand_CenteredFloat(5.0f);
+ accel.x = accel.z = 0.0f;
+ accel.y = -0.1f;
+ pos.x = (Rand_CenteredFloat(70.0f) * scale) + blockPos->x;
+ pos.y = (Rand_ZeroFloat(10.0f) * scale) + blockPos->y;
+ pos.z = (Rand_CenteredFloat(70.0f) * scale) + blockPos->z;
+ func_800B0EB0(play, &pos, &velocity, &accel, &sDustPrimColor, &sDustEnvColor,
+ (Rand_ZeroFloat(150.0f) + 350.0f) * scale, 10, Rand_ZeroFloat(5.0f) + 14.0f);
+ }
+}
+
+/**
+ * Responsible for updating the falling blocks and the ring of fire, including spawning block fragments when a block
+ * hits the floor and damaging the player.
+ */
+void Boss01_UpdateEffects(Boss01* this, PlayState* play) {
+ OdolwaEffect* effect = (OdolwaEffect*)play->specialEffects;
+ Player* player = GET_PLAYER(play);
+ s16 i;
+ s16 j;
+ s16 temp;
+ f32 diffX;
+ f32 diffZ;
+ f32 temp2;
+ s32 type;
+
+ for (i = 0; i < ODOLWA_EFFECT_COUNT; i++, effect++) {
+ if (effect->type != ODOLWA_EFFECT_NONE) {
+ effect->timer++;
+ type = effect->type;
+ if (type == ODOLWA_EFFECT_RING_OF_FIRE) {
+ if (effect->timer < 150) {
+ Math_ApproachF(&play->envCtx.lightBlend, (Math_SinS(effect->timer * 0x1000) * 0.1f) + 0.9f, 1.0f,
+ 0.2f);
+ sMothSwarm->unk_144 = 250;
+
+ // If Odolwa dies, this will make the ring of fire start fading away immediately.
+ if ((sOdolwa != NULL) && (sOdolwa->actionFunc == Boss01_DeathCutscene)) {
+ effect->timer = 150;
+ }
+
+ Audio_PlaySfx(NA_SE_EV_FIRE_PLATE - SFX_FLAG);
+ Math_ApproachF(&effect->scale, 1.0f, 0.1f, 0.3f);
+
+ // The ring of fire has no collision, so this code is responsible for checking to see if the player
+ // is touching it. If they are, then it will set them on fire and damage them manually.
+ if (!(player->stateFlags3 & PLAYER_STATE3_1000) && (player->actor.world.pos.y < 70.0f)) {
+ diffX = effect->pos.x - player->actor.world.pos.x;
+ diffZ = effect->pos.z - player->actor.world.pos.z;
+ temp2 = sqrtf(SQ(diffX) + SQ(diffZ));
+
+ if (player->invincibilityTimer == 0) {
+ if ((temp2 < (KREG(49) + 210.0f)) && ((KREG(49) + 190.0f) < temp2)) {
+ for (j = 0; j < PLAYER_BODYPART_MAX; j++) {
+ player->flameTimers[j] = Rand_S16Offset(0, 200);
+ }
+
+ player->isBurning = true;
+ temp = Math_Atan2S_XY(diffZ, diffX);
+ if ((KREG(49) + 100.0f) < temp2) {
+ temp += 0x8000;
+ }
+
+ func_800B8D50(play, &this->actor, 10.0f, temp, 0.0f, 8);
+ }
+ }
+ }
+ } else {
+ Math_ApproachZeroF(&effect->alpha, 1.0f, 10.0f);
+ if (effect->alpha < 0.1f) {
+ effect->type = ODOLWA_EFFECT_NONE;
+ }
+ }
+ } else {
+ effect->pos.x += effect->velocity.x;
+ effect->pos.y += effect->velocity.y;
+ effect->pos.z += effect->velocity.z;
+ effect->velocity.y += effect->gravity;
+
+ if (type == ODOLWA_EFFECT_FALLING_BLOCK) {
+ effect->rotY += effect->angularVelocityY;
+ effect->rotX += effect->angularVelocityX;
+
+ if (!effect->isFallingBlockFragment) {
+ // Falling blocks have no collision, so this code is responsible for checking to see if the
+ // player is touching one. If they are, then it will damage them manually.
+ diffX = player->actor.world.pos.x - effect->pos.x;
+ temp2 = (player->actor.world.pos.y + 20.0f) - effect->pos.y;
+ diffZ = player->actor.world.pos.z - effect->pos.z;
+
+ if ((SQ(diffX) + SQ(diffZ) + SQ(temp2)) < SQ(50.0f)) {
+ func_800B8D50(play, NULL, 0.0f, Rand_ZeroFloat(65526.0f), 0.0f, 8);
+ }
+
+ if (effect->pos.y < 10.0f) {
+ effect->pos.y = 10.0f;
+ temp = (Rand_ZeroFloat(2.0f) + 4.0f);
+ for (j = 0; j < temp; j++) {
+ Boss01_SpawnEffectFallingBlock((OdolwaEffect*)play->specialEffects, &effect->pos, true);
+ }
+
+ effect->type = ODOLWA_EFFECT_NONE;
+ SoundSource_PlaySfxAtFixedWorldPos(play, &effect->pos, 40, NA_SE_EV_WALL_BROKEN);
+ Boss01_SpawnDustForFallingBlock(play, &effect->pos, 1.0f);
+ CollisionCheck_SpawnShieldParticles(play, &effect->pos);
+ Actor_RequestQuakeAndRumble(&this->actor, play, 3, 10);
+ }
+ } else if ((effect->pos.y < 10.0f) && (effect->velocity.y < 0.0f)) {
+ effect->pos.y = 10.0f;
+ effect->type = ODOLWA_EFFECT_NONE;
+ Boss01_SpawnDustForFallingBlock(play, &effect->pos, 0.5f);
+ }
+ }
+ }
+ }
+ }
+}
+
+/**
+ * Draws the falling blocks, the circle shadows that appear under the blocks as they fall, and the ring of fire.
+ */
+void Boss01_DrawEffects(PlayState* play) {
+ s32 pad;
+ s16 i;
+ f32 alpha;
+ OdolwaEffect* effect = play->specialEffects;
+
+ OPEN_DISPS(play->state.gfxCtx);
+
+ Gfx_SetupDL25_Opa(play->state.gfxCtx);
+ Gfx_SetupDL25_Xlu(play->state.gfxCtx);
+
+ for (i = 1; i < ODOLWA_EFFECT_COUNT; i++, effect++) {
+ if (effect->type == ODOLWA_EFFECT_FALLING_BLOCK) {
+ Matrix_Translate(effect->pos.x, effect->pos.y, effect->pos.z, MTXMODE_NEW);
+ Matrix_RotateYS(effect->rotY, MTXMODE_APPLY);
+ Matrix_RotateXS(effect->rotX, MTXMODE_APPLY);
+ Matrix_Scale(effect->scale, effect->scale, effect->scale, MTXMODE_APPLY);
+ gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(play->state.gfxCtx), G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW);
+ gSPDisplayList(POLY_OPA_DISP++, gOdolwaFallingBlockDL);
+ }
+ }
+
+ effect = play->specialEffects;
+ Gfx_SetupDL44_Xlu(play->state.gfxCtx);
+ gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 0, 0, 0, 100);
+
+ for (i = 1; i < ODOLWA_EFFECT_COUNT; i++, effect++) {
+ if (effect->type == ODOLWA_EFFECT_FALLING_BLOCK) {
+ Matrix_Translate(effect->pos.x, 0.0f, effect->pos.z, MTXMODE_NEW);
+ Matrix_Scale(effect->scale * 50.0f, 1.0f, effect->scale * 50.0f, MTXMODE_APPLY);
+ gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(play->state.gfxCtx), G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW);
+ gSPDisplayList(POLY_XLU_DISP++, SEGMENTED_TO_K0(gCircleShadowDL));
+ }
+ }
+
+ Gfx_SetupDL25_Xlu(play->state.gfxCtx);
+ effect = play->specialEffects;
+ Boss01_InitRand(1, 0x71A5, 0x263A);
+
+ if (effect->type == ODOLWA_EFFECT_RING_OF_FIRE) {
+ gDPSetEnvColor(POLY_XLU_DISP++, 255, 10, 0, 0);
+ Matrix_Translate(effect->pos.x, 0.0f, effect->pos.z, MTXMODE_NEW);
+
+ for (i = 0; i < 32; i++) {
+ Matrix_Push();
+ alpha = effect->alpha - (Boss01_RandZeroOne() * 50.0f);
+ if (alpha < 0.0f) {
+ alpha = 0.0f;
+ }
+
+ gDPSetPrimColor(POLY_XLU_DISP++, 0x80, 0x80, 255, 255, 0, (u8)alpha);
+ gSPSegment(POLY_XLU_DISP++, 0x08,
+ Gfx_TwoTexScroll(play->state.gfxCtx, G_TX_RENDERTILE, 0, 0, 32, 64, 1, 0,
+ ((effect->timer + (i * 10)) * -20) & 0x1FF, 32, 128));
+
+ Matrix_RotateYF(i * (M_PI / 16.0f), MTXMODE_APPLY);
+ Matrix_Translate(0.0f, 0.0f, KREG(49) + 200.0f, MTXMODE_APPLY);
+ Matrix_ReplaceRotation(&play->billboardMtxF);
+ if (Boss01_RandZeroOne() < 0.5f) {
+ Matrix_RotateYF(M_PI, MTXMODE_APPLY);
+ }
+
+ Matrix_Scale(KREG(48) * 0.0001f + 0.018f,
+ ((0.007f + KREG(54) * 0.0001f) + (Boss01_RandZeroOne() * 30.0f * 0.0001f)) * effect->scale,
+ 1.0f, MTXMODE_APPLY);
+
+ gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(play->state.gfxCtx), G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW);
+ gSPDisplayList(POLY_XLU_DISP++, gEffFire1DL);
+ Matrix_Pop();
+ }
+ }
+
+ CLOSE_DISPS(play->state.gfxCtx);
+}
diff --git a/src/overlays/actors/ovl_Boss_01/z_boss_01.h b/src/overlays/actors/ovl_Boss_01/z_boss_01.h
index 37c8d4b00..358775179 100644
--- a/src/overlays/actors/ovl_Boss_01/z_boss_01.h
+++ b/src/overlays/actors/ovl_Boss_01/z_boss_01.h
@@ -2,16 +2,153 @@
#define Z_BOSS_01_H
#include "global.h"
+#include "assets/objects/object_boss01/object_boss01.h"
struct Boss01;
+#define ODOLWA_GET_TYPE(thisx) ((thisx)->params)
+#define ODOLWA_GET_AFTERIMAGE_DESPAWN_TIMER(thisx) ((thisx)->world.rot.z)
+
+#define ODOLWA_SHADOW_TEX_WIDTH 64
+#define ODOLWA_SHADOW_TEX_HEIGHT 64
+#define ODOLWA_SHADOW_TEX_SIZE ((s32)sizeof(u8[ODOLWA_SHADOW_TEX_HEIGHT][ODOLWA_SHADOW_TEX_WIDTH]))
+
typedef void (*Boss01ActionFunc)(struct Boss01*, PlayState*);
+typedef enum OdolwaType {
+ /* 0 */ ODOLWA_TYPE_ODOLWA,
+ /* 10 */ ODOLWA_TYPE_BUG = 10,
+ /* 35 */ ODOLWA_TYPE_AFTERIMAGE = 35
+} OdolwaType;
+
+typedef enum OdolwaBodyPart {
+ /* 0 */ ODOLWA_BODYPART_HEAD,
+ /* 1 */ ODOLWA_BODYPART_TORSO,
+ /* 2 */ ODOLWA_BODYPART_PELVIS,
+ /* 3 */ ODOLWA_BODYPART_LEFT_UPPER_ARM,
+ /* 4 */ ODOLWA_BODYPART_LEFT_FOREARM,
+ /* 5 */ ODOLWA_BODYPART_SHIELD,
+ /* 6 */ ODOLWA_BODYPART_RIGHT_UPPER_ARM,
+ /* 7 */ ODOLWA_BODYPART_RIGHT_FOREARM,
+ /* 8 */ ODOLWA_BODYPART_SWORD,
+ /* 9 */ ODOLWA_BODYPART_RIGHT_THIGH,
+ /* 10 */ ODOLWA_BODYPART_RIGHT_SHIN,
+ /* 11 */ ODOLWA_BODYPART_RIGHT_FOOT,
+ /* 12 */ ODOLWA_BODYPART_LEFT_THIGH,
+ /* 13 */ ODOLWA_BODYPART_LEFT_SHIN,
+ /* 14 */ ODOLWA_BODYPART_LEFT_FOOT,
+ /* 15 */ ODOLWA_BODYPART_MAX
+} OdolwaBodyPart;
+
+typedef enum OdolwaColliderBodyPart {
+ /* 0 */ ODOLWA_COLLIDER_BODYPART_HEAD,
+ /* 1 */ ODOLWA_COLLIDER_BODYPART_TORSO,
+ /* 2 */ ODOLWA_COLLIDER_BODYPART_PELVIS,
+ /* 3 */ ODOLWA_COLLIDER_BODYPART_LEFT_UPPER_ARM,
+ /* 4 */ ODOLWA_COLLIDER_BODYPART_LEFT_FOREARM,
+ /* 5 */ ODOLWA_COLLIDER_BODYPART_RIGHT_UPPER_ARM,
+ /* 6 */ ODOLWA_COLLIDER_BODYPART_RIGHT_FOREARM,
+ /* 7 */ ODOLWA_COLLIDER_BODYPART_LEFT_THIGH,
+ /* 8 */ ODOLWA_COLLIDER_BODYPART_LEFT_SHIN,
+ /* 9 */ ODOLWA_COLLIDER_BODYPART_RIGHT_THIGH,
+ /* 10 */ ODOLWA_COLLIDER_BODYPART_RIGHT_SHIN,
+ /* 11 */ ODOLWA_COLLIDER_BODYPART_MAX
+} OdolwaColliderBodyPart;
+
+typedef enum OdolwaSwordCollider {
+ /* 0 */ ODOLWA_SWORD_COLLIDER_SWORD_BASE,
+ /* 1 */ ODOLWA_SWORD_COLLIDER_SWORD_TIP,
+ /* 2 */ ODOLWA_SWORD_COLLIDER_PELVIS,
+ /* 3 */ ODOLWA_SWORD_COLLIDER_MAX
+} OdolwaSwordCollider;
+
+typedef enum OdolwaShieldCollider {
+ /* 0 */ ODOLWA_SHIELD_COLLIDER_SHIELD,
+ /* 1 */ ODOLWA_SHIELD_COLLIDER_MAX
+} OdolwaShieldCollider;
+
+typedef enum OdolwaKickAndShieldBashCollider {
+ /* 0 */ ODOLWA_KICK_AND_SHIELD_BASH_COLLIDER_SHIELD,
+ /* 1 */ ODOLWA_KICK_AND_SHIELD_BASH_COLLIDER_LEFT_FOOT,
+ /* 2 */ ODOLWA_KICK_AND_SHIELD_BASH_COLLIDER_MAX
+} OdolwaKickAndShieldBashCollider;
+
typedef struct Boss01 {
/* 0x000 */ Actor actor;
- /* 0x144 */ char unk_144[0x320];
+ /* 0x144 */ s16 frameCounter;
+ /* 0x148 */ s32 phaseFrameCounter;
+ /* 0x14C */ union {
+ u8 jumpIfPlayerIsClose;
+ u8 shouldPerformFallingSlash;
+ u8 bugDrawDmgEffType;
+ };
+ /* 0x14E */ s16 timers[3];
+ /* 0x154 */ f32 animMorphFrames1;
+ /* 0x158 */ f32 animMorphFrames2;
+ /* 0x15C */ s16 damageTimer;
+ /* 0x15E */ s16 damageFlashTimer;
+ /* 0x160 */ u8 isPerformingVerticalSlash; // set, but never checked
+ /* 0x160 */ u8 landedFromJump;
+ /* 0x162 */ u8 waitType;
+ /* 0x163 */ u8 lookAtPlayer;
+ /* 0x164 */ SkelAnime skelAnime;
+ /* 0x1A8 */ f32 animEndFrame;
+ /* 0x1AC */ f32 prevJumpVelocityY;
+ /* 0x1B0 */ f32 runTargetPosAngularVelocityY;
+ /* 0x1B4 */ f32 runTargetPosRotY;
+ /* 0x1B8 */ u8 swordState;
+ /* 0x1B9 */ u8 kickAndShieldBashCollisionEnabled;
+ /* 0x1BA */ u8 swordAndShieldCollisionEnabled;
+ /* 0x1BB */ u8 canGuardOrEvade;
+ /* 0x1BC */ u8 bodyInvincibilityTimer;
+ /* 0x1BE */ s16 disableCollisionTimer;
+ /* 0x1C0 */ u8 afterimageSpawnFrameMask; // used as a bitmask with frameCounter to control how often to spawn afterimages
+ /* 0x1C1 */ u8 hasPlayedSummonBugCs;
+ /* 0x1C4 */ f32 additionalVelocityX;
+ /* 0x1C8 */ f32 additionalVelocityZ;
+ /* 0x1CC */ s16 headRotY;
+ /* 0x1CE */ s16 headRotX;
+ /* 0x1D0 */ Vec3s jointTable[ODOLWA_LIMB_MAX];
+ /* 0x308 */ Vec3s morphTable[ODOLWA_LIMB_MAX];
+ /* 0x440 */ Vec3f feetPos[2];
+ /* 0x458 */ Vec3f pelvisPos;
/* 0x464 */ Boss01ActionFunc actionFunc;
- /* 0x468 */ char unk_468[0x674];
+ /* 0x468 */ ColliderJntSph swordCollider;
+ /* 0x488 */ ColliderJntSphElement swordColliderElements[ODOLWA_SWORD_COLLIDER_MAX];
+ /* 0x548 */ ColliderJntSph shieldCollider;
+ /* 0x568 */ ColliderJntSphElement shieldColliderElements[ODOLWA_SHIELD_COLLIDER_MAX];
+ /* 0x5A8 */ ColliderJntSph bodyCollider;
+ /* 0x5C8 */ ColliderJntSphElement bodyColliderElements[ODOLWA_COLLIDER_BODYPART_MAX];
+ /* 0x888 */ ColliderJntSph kickAndShieldBashCollider;
+ /* 0x8A8 */ ColliderJntSphElement kickAndShieldBashColliderElements[ODOLWA_KICK_AND_SHIELD_BASH_COLLIDER_MAX];
+ /* 0x928 */ Vec3f bodyPartsPos[ODOLWA_BODYPART_MAX];
+ /* 0x9DC */ union {
+ u32 cutsceneTimer;
+ u32 waitTimer;
+ u32 summonMothsTimer;
+ };
+ /* 0x9E0 */ s16 cutsceneState;
+ /* 0x9E2 */ s16 subCamId;
+ /* 0x9E4 */ Vec3f subCamEye;
+ /* 0x9F0 */ Vec3f subCamAt;
+ /* 0x9FC */ Vec3f subCamUp;
+ /* 0xA08 */ Vec3f subCamEyeNext;
+ /* 0xA14 */ f32 deathCsInitialSubCamRot;
+ /* 0xA18 */ f32 deathCsSubCamRot;
+ /* 0xA1C */ f32 subCamVelocity;
+ /* 0xA20 */ f32 deathShrinkSpeed;
+ /* 0xA24 */ f32 screenShakeOffsetY;
+ /* 0xA28 */ f32 screenShakeMagnitude;
+ /* 0xA2C */ ColliderCylinder bugACCollider;
+ /* 0xA78 */ ColliderCylinder bugATCollider;
+ /* 0xAC4 */ f32 bugDrawDmgEffScale;
+ /* 0xAC8 */ f32 bugDrawDmgEffAlpha;
+ /* 0xACC */ f32 drawDmgEffScale;
+ /* 0xAD0 */ f32 drawDmgEffFrozenSteamScale;
+ /* 0xAD4 */ f32 drawDmgEffAlpha;
+ /* 0xAD8 */ u8 drawDmgEffType;
+ /* 0xAD9 */ u8 drawDmgEffState;
+ /* 0xADA */ s16 drawDmgEffTimer;
} Boss01; // size = 0xADC
#endif // Z_BOSS_01_H
diff --git a/src/overlays/actors/ovl_Boss_02/z_boss_02.c b/src/overlays/actors/ovl_Boss_02/z_boss_02.c
index 1957ac369..e202a08cb 100644
--- a/src/overlays/actors/ovl_Boss_02/z_boss_02.c
+++ b/src/overlays/actors/ovl_Boss_02/z_boss_02.c
@@ -69,9 +69,9 @@ u8 sIsInGiantMode;
Boss02* sRedTwinmold;
Boss02* sBlueTwinmold;
Boss02* sTwinmoldStatic;
-u8 sMusicStartTimer;
+u8 sTwinmoldMusicStartTimer;
DoorWarp1* sBlueWarp;
-TwinmoldEffect sEffects[TWINMOLD_EFFECT_COUNT];
+TwinmoldEffect sTwinmoldEffects[TWINMOLD_EFFECT_COUNT];
static DamageTable sBlueTwinmoldDamageTable = {
/* Deku Nut */ DMG_ENTRY(0, 0x0),
@@ -588,7 +588,7 @@ void Boss02_Init(Actor* thisx, PlayState* play) {
if (CHECK_WEEKEVENTREG(WEEKEVENTREG_CLEARED_STONE_TOWER_TEMPLE) &&
(TWINMOLD_GET_TYPE(&this->actor) == TWINMOLD_TYPE_RED)) {
sBlueWarp = (DoorWarp1*)Actor_SpawnAsChild(&play->actorCtx, &this->actor, play, ACTOR_DOOR_WARP1, 0.0f, 60.0f,
- 0.0f, 0, 0, 0, 1);
+ 0.0f, 0, 0, 0, ENDOORWARP1_FF_1);
Actor_Spawn(&play->actorCtx, play, ACTOR_ITEM_B_HEART, 0.0f, 30.0f, -150.0f, 0, 1, 0, BHEART_PARAM_NORMAL);
}
@@ -597,14 +597,14 @@ void Boss02_Init(Actor* thisx, PlayState* play) {
this->subCamUp.y = 1.0f;
if (TWINMOLD_GET_TYPE(&this->actor) == TWINMOLD_TYPE_STATIC) {
sTwinmoldStatic = this;
- play->specialEffects = (void*)sEffects;
+ play->specialEffects = (void*)sTwinmoldEffects;
this->actor.update = Boss02_Static_Update;
this->actor.draw = Boss02_Static_Draw;
this->actor.flags &= ~ACTOR_FLAG_TARGETABLE;
this->playerScale = 0.01f;
if ((KREG(64) != 0) || CHECK_EVENTINF(EVENTINF_55) || (sBlueWarp != NULL)) {
this->unk_1D20 = 0;
- sMusicStartTimer = KREG(15) + 20;
+ sTwinmoldMusicStartTimer = KREG(15) + 20;
} else {
this->unk_1D20 = 1;
}
@@ -1346,9 +1346,9 @@ void Boss02_Static_Update(Actor* thisx, PlayState* play) {
}
if (sBlueWarp == NULL) {
- if (sMusicStartTimer != 0) {
- sMusicStartTimer--;
- if (sMusicStartTimer == 0) {
+ if (sTwinmoldMusicStartTimer != 0) {
+ sTwinmoldMusicStartTimer--;
+ if (sTwinmoldMusicStartTimer == 0) {
SEQCMD_PLAY_SEQUENCE(SEQ_PLAYER_BGM_MAIN, 0, NA_BGM_BOSS | SEQ_FLAG_ASYNC);
}
}
@@ -2173,7 +2173,7 @@ void func_809DEAC4(Boss02* this, PlayState* play) {
if (this->unk_1D1C == 45) {
func_800B7298(play, &this->actor, PLAYER_CSACTION_21);
- sMusicStartTimer = KREG(91) + 43;
+ sTwinmoldMusicStartTimer = KREG(91) + 43;
}
if (this->unk_1D1C == 85) {
@@ -2304,7 +2304,7 @@ void func_809DEAC4(Boss02* this, PlayState* play) {
phi_f0 = 3155.0f;
}
sBlueWarp = (DoorWarp1*)Actor_SpawnAsChild(&play->actorCtx, &this->actor, play, ACTOR_DOOR_WARP1,
- 0.0f, phi_f0, 0.0f, 0, 0, 0, 1);
+ 0.0f, phi_f0, 0.0f, 0, 0, 0, ENDOORWARP1_FF_1);
if (!sIsInGiantMode) {
sBlueWarp->unk_203 = 0;
diff --git a/src/overlays/actors/ovl_Boss_03/z_boss_03.c b/src/overlays/actors/ovl_Boss_03/z_boss_03.c
index a9c2fd9d7..f96626785 100644
--- a/src/overlays/actors/ovl_Boss_03/z_boss_03.c
+++ b/src/overlays/actors/ovl_Boss_03/z_boss_03.c
@@ -231,30 +231,30 @@ void Boss03_UpdateSphereElement(s32 index, ColliderJntSph* collider, Vec3f* sphe
/* Start of RNG section */
-static s32 sRandSeed0;
-static s32 sRandSeed1;
-static s32 sRandSeed2;
+s32 sGyorgRandSeed1;
+s32 sGyorgRandSeed2;
+s32 sGyorgRandSeed3;
-void Boss03_SeedRand(s32 seed0, s32 seed1, s32 seed2) {
- sRandSeed0 = seed0;
- sRandSeed1 = seed1;
- sRandSeed2 = seed2;
+void Boss03_InitRand(s32 seedInit1, s32 seedInit2, s32 seedInit3) {
+ sGyorgRandSeed1 = seedInit1;
+ sGyorgRandSeed2 = seedInit2;
+ sGyorgRandSeed3 = seedInit3;
}
f32 Boss03_RandZeroOne(void) {
- f32 rand;
-
// Wichmann-Hill algorithm
- sRandSeed0 = (sRandSeed0 * 171) % 30269;
- sRandSeed1 = (sRandSeed1 * 172) % 30307;
- sRandSeed2 = (sRandSeed2 * 170) % 30323;
+ f32 randFloat;
- rand = (sRandSeed0 / 30269.0f) + (sRandSeed1 / 30307.0f) + (sRandSeed2 / 30323.0f);
- while (rand >= 1.0f) {
- rand -= 1.0f;
+ sGyorgRandSeed1 = (sGyorgRandSeed1 * 171) % 30269;
+ sGyorgRandSeed2 = (sGyorgRandSeed2 * 172) % 30307;
+ sGyorgRandSeed3 = (sGyorgRandSeed3 * 170) % 30323;
+
+ randFloat = (sGyorgRandSeed1 / 30269.0f) + (sGyorgRandSeed2 / 30307.0f) + (sGyorgRandSeed3 / 30323.0f);
+ while (randFloat >= 1.0f) {
+ randFloat -= 1.0f;
}
- return fabsf(rand);
+ return fabsf(randFloat);
}
/* End of RNG section */
@@ -483,7 +483,7 @@ void Boss03_Init(Actor* thisx, PlayState* play2) {
this->actor.world.pos = sGyorgInitialPos;
// Since Boss03_RandZeroOne is only used on this Init function, the resulting values end up being deterministic
- Boss03_SeedRand(1, 29093, 9786);
+ Boss03_InitRand(1, 29093, 9786);
for (i = 0; i < 5; i++) {
f32 rand;
diff --git a/src/overlays/actors/ovl_En_Encount3/z_en_encount3.c b/src/overlays/actors/ovl_En_Encount3/z_en_encount3.c
index 7646f7113..f04714bb2 100644
--- a/src/overlays/actors/ovl_En_Encount3/z_en_encount3.c
+++ b/src/overlays/actors/ovl_En_Encount3/z_en_encount3.c
@@ -159,7 +159,7 @@ void EnEncount3_Update(Actor* thisx, PlayState* play2) {
Math_ApproachZeroF(&this->unk170, 0.3f, 10.0f);
Math_ApproachZeroF(&this->unk160, 0.3f, 5.0f);
if (this->unk160 < 1.0f) {
- play->unk_18880 = 0;
+ play->unk_18880 = false;
}
} else if (this->unk148 != 0) {
Math_ApproachF(&this->unk170, 255.0f, 0.4f, 10.0f);
diff --git a/src/overlays/actors/ovl_En_Fishing/z_en_fishing.c b/src/overlays/actors/ovl_En_Fishing/z_en_fishing.c
index 9c1a810f8..c1320150a 100644
--- a/src/overlays/actors/ovl_En_Fishing/z_en_fishing.c
+++ b/src/overlays/actors/ovl_En_Fishing/z_en_fishing.c
@@ -147,9 +147,9 @@ f32 sSubCamVelFactor;
f32 D_80911F50;
Vec3f sSinkingLureBasePos;
f32 D_80911F64;
-s32 sRandSeed0;
-s32 sRandSeed1;
-s32 sRandSeed2;
+s32 sFishingRandSeed1;
+s32 sFishingRandSeed2;
+s32 sFishingRandSeed3;
FishingProp sPondProps[POND_PROP_COUNT];
FishingGroupFish sGroupFishes[GROUP_FISH_COUNT];
f32 sFishGroupAngle1;
@@ -404,26 +404,26 @@ void EnFishing_SetColliderElement(s32 index, ColliderJntSph* collider, Vec3f* po
collider->elements[index].dim.modelSphere.radius * collider->elements[index].dim.scale * scale * 1.6f;
}
-void EnFishing_SeedRand(s32 seed0, s32 seed1, s32 seed2) {
- sRandSeed0 = seed0;
- sRandSeed1 = seed1;
- sRandSeed2 = seed2;
+void EnFishing_InitRand(s32 seedInit1, s32 seedInit2, s32 seedInit3) {
+ sFishingRandSeed1 = seedInit1;
+ sFishingRandSeed2 = seedInit2;
+ sFishingRandSeed3 = seedInit3;
}
f32 EnFishing_RandZeroOne(void) {
- f32 rand;
-
// Wichmann-Hill algorithm
- sRandSeed0 = (sRandSeed0 * 171) % 30269;
- sRandSeed1 = (sRandSeed1 * 172) % 30307;
- sRandSeed2 = (sRandSeed2 * 170) % 30323;
+ f32 randFloat;
- rand = (sRandSeed0 / 30269.0f) + (sRandSeed1 / 30307.0f) + (sRandSeed2 / 30323.0f);
- while (rand >= 1.0f) {
- rand -= 1.0f;
+ sFishingRandSeed1 = (sFishingRandSeed1 * 171) % 30269;
+ sFishingRandSeed2 = (sFishingRandSeed2 * 172) % 30307;
+ sFishingRandSeed3 = (sFishingRandSeed3 * 170) % 30323;
+
+ randFloat = (sFishingRandSeed1 / 30269.0f) + (sFishingRandSeed2 / 30307.0f) + (sFishingRandSeed3 / 30323.0f);
+ while (randFloat >= 1.0f) {
+ randFloat -= 1.0f;
}
- return fabsf(rand);
+ return fabsf(randFloat);
}
s16 EnFishing_SmoothStepToS(s16* pValue, s16 target, s16 scale, s16 step) {
@@ -730,7 +730,7 @@ void EnFishing_InitPondProps(EnFishing* this, PlayState* play) {
Vec3f colliderPos;
s16 i;
- EnFishing_SeedRand(1, 29100, 9786);
+ EnFishing_InitRand(1, 29100, 9786);
for (i = 0; i < POND_PROP_COUNT; i++) {
if (sPondPropInits[i].type == FS_PROP_INIT_STOP) {
diff --git a/src/overlays/actors/ovl_En_Invadepoh_Demo/z_en_invadepoh_demo.c b/src/overlays/actors/ovl_En_Invadepoh_Demo/z_en_invadepoh_demo.c
index 87164778e..03dd89c43 100644
--- a/src/overlays/actors/ovl_En_Invadepoh_Demo/z_en_invadepoh_demo.c
+++ b/src/overlays/actors/ovl_En_Invadepoh_Demo/z_en_invadepoh_demo.c
@@ -750,7 +750,8 @@ void EnInvadepohDemo_Ufo_Draw(EnInvadepohDemo* this, PlayState* play) {
CLOSE_DISPS(play->state.gfxCtx);
if (EnInvadepohDemo_Ufo_ShouldDrawLensFlare(play, &flashPos)) {
- Environment_DrawLensFlare(play, &play->envCtx, &play->view, play->state.gfxCtx, flashPos, 20.0f, 9.0f, 0, 0);
+ Environment_DrawLensFlare(play, &play->envCtx, &play->view, play->state.gfxCtx, flashPos, 20.0f, 9.0f, 0,
+ false);
}
}
diff --git a/src/overlays/actors/ovl_En_Jso/z_en_jso.c b/src/overlays/actors/ovl_En_Jso/z_en_jso.c
index 18c342296..48b9e0b40 100644
--- a/src/overlays/actors/ovl_En_Jso/z_en_jso.c
+++ b/src/overlays/actors/ovl_En_Jso/z_en_jso.c
@@ -553,7 +553,7 @@ void EnJso_HandleIntroCutscene(EnJso* this, PlayState* play) {
if (parent->actor.update != NULL) {
parent->unk148 = 1;
- play->unk_18880 = 1;
+ play->unk_18880 = true;
}
}
diff --git a/src/overlays/actors/ovl_En_Okarina_Effect/z_en_okarina_effect.c b/src/overlays/actors/ovl_En_Okarina_Effect/z_en_okarina_effect.c
index cef823a35..3e59cce62 100644
--- a/src/overlays/actors/ovl_En_Okarina_Effect/z_en_okarina_effect.c
+++ b/src/overlays/actors/ovl_En_Okarina_Effect/z_en_okarina_effect.c
@@ -48,7 +48,7 @@ void EnOkarinaEffect_Init(Actor* thisx, PlayState* play) {
}
void func_8096B104(EnOkarinaEffect* this, PlayState* play) {
- this->unk144 = 0x50;
+ this->timer = 80;
play->envCtx.precipitation[PRECIP_SOS_MAX] = 60;
gLightningStrike.delayTimer = 501.0f;
play->envCtx.lightningState = LIGHTNING_LAST;
@@ -57,9 +57,9 @@ void func_8096B104(EnOkarinaEffect* this, PlayState* play) {
}
void func_8096B174(EnOkarinaEffect* this, PlayState* play) {
- DECR(this->unk144);
+ DECR(this->timer);
if ((play->pauseCtx.state == PAUSE_STATE_OFF) && (play->gameOverCtx.state == GAMEOVER_INACTIVE) &&
- (play->msgCtx.msgLength == 0) && !FrameAdvance_IsEnabled(&play->state) && (this->unk144 == 0)) {
+ (play->msgCtx.msgLength == 0) && !FrameAdvance_IsEnabled(&play->state) && (this->timer == 0)) {
EnOkarinaEffect_SetupAction(this, func_8096B1FC);
}
}
diff --git a/src/overlays/actors/ovl_En_Okarina_Effect/z_en_okarina_effect.h b/src/overlays/actors/ovl_En_Okarina_Effect/z_en_okarina_effect.h
index b633ab390..8be64654b 100644
--- a/src/overlays/actors/ovl_En_Okarina_Effect/z_en_okarina_effect.h
+++ b/src/overlays/actors/ovl_En_Okarina_Effect/z_en_okarina_effect.h
@@ -9,8 +9,7 @@ typedef void (*EnOkarinaEffectActionFunc)(struct EnOkarinaEffect*, PlayState*);
typedef struct EnOkarinaEffect {
/* 0x000 */ Actor actor;
- /* 0x144 */ u16 unk144;
- /* 0x146 */ u16 unk146;
+ /* 0x144 */ u16 timer;
/* 0x148 */ EnOkarinaEffectActionFunc actionFunc;
} EnOkarinaEffect; // size = 0x14C
diff --git a/src/overlays/actors/ovl_En_Takaraya/z_en_takaraya.c b/src/overlays/actors/ovl_En_Takaraya/z_en_takaraya.c
index 0c3b2e7cb..24cd5779c 100644
--- a/src/overlays/actors/ovl_En_Takaraya/z_en_takaraya.c
+++ b/src/overlays/actors/ovl_En_Takaraya/z_en_takaraya.c
@@ -5,6 +5,7 @@
*/
#include "z_en_takaraya.h"
+#include "overlays/actors/ovl_En_Box/z_en_box.h"
#define FLAGS (ACTOR_FLAG_TARGETABLE | ACTOR_FLAG_FRIENDLY | ACTOR_FLAG_10 | ACTOR_FLAG_20)
@@ -76,11 +77,11 @@ u16 D_80ADFB2C[PLAYER_FORM_MAX] = {
};
u8 D_80ADFB38[PLAYER_FORM_MAX][2] = {
- { 5, 5 }, // PLAYER_FORM_FIERCE_DEITY
- { 12, 5 }, // PLAYER_FORM_GORON
- { 4, 4 }, // PLAYER_FORM_ZORA
- { 42, 41 }, // PLAYER_FORM_DEKU
- { 5, 5 }, // PLAYER_FORM_HUMAN
+ { GI_RUPEE_PURPLE, GI_RUPEE_PURPLE }, // PLAYER_FORM_FIERCE_DEITY
+ { GI_HEART_PIECE, GI_RUPEE_PURPLE }, // PLAYER_FORM_GORON
+ { GI_RUPEE_RED, GI_RUPEE_RED }, // PLAYER_FORM_ZORA
+ { GI_DEKU_NUTS_10, GI_DEKU_NUTS_5 }, // PLAYER_FORM_DEKU
+ { GI_RUPEE_PURPLE, GI_RUPEE_PURPLE }, // PLAYER_FORM_HUMAN
};
u16 D_80ADFB44[PLAYER_FORM_MAX] = {
@@ -190,16 +191,16 @@ void EnTakaraya_Wait(EnTakaraya* this, PlayState* play) {
}
void EnTakaraya_SpawnWalls(EnTakaraya* this, PlayState* play) {
- u8 var_v1;
+ u8 getItemId;
if (Flags_GetSwitch(play, this->formSwitchFlag)) {
- var_v1 = D_80ADFB38[GET_PLAYER_FORM][1];
+ getItemId = D_80ADFB38[GET_PLAYER_FORM][1];
} else {
- var_v1 = D_80ADFB38[GET_PLAYER_FORM][0];
+ getItemId = D_80ADFB38[GET_PLAYER_FORM][0];
}
Actor_SpawnAsChildAndCutscene(&play->actorCtx, play, ACTOR_OBJ_TAKARAYA_WALL, 0.0f, 0.0f, 0.0f, 0, 0, 5,
- ((var_v1 << 5) + this->actor.params) + 0xB000, this->actor.csId, HALFDAYBIT_ALL,
- NULL);
+ (ENBOX_TYPE_BIG_SWITCH_FLAG << 12) + (getItemId << 5) + this->actor.params,
+ this->actor.csId, HALFDAYBIT_ALL, NULL);
}
void EnTakaraya_SetupTalk(EnTakaraya* this) {
diff --git a/src/overlays/actors/ovl_En_Test4/z_en_test4.c b/src/overlays/actors/ovl_En_Test4/z_en_test4.c
index 75eed7148..0e2e81bbd 100644
--- a/src/overlays/actors/ovl_En_Test4/z_en_test4.c
+++ b/src/overlays/actors/ovl_En_Test4/z_en_test4.c
@@ -431,7 +431,7 @@ void func_80A42AB8(EnTest4* this, PlayState* play) {
this->unk_146 = gSaveContext.save.time += CLOCK_TIME_MINUTE;
}
} else if ((new_var * bellDiff) <= 0) {
- Audio_PlaySfx_BigBells(&this->actor.projectedPos, (this->actor.params >> 5) & 0xF);
+ Audio_PlaySfx_BigBells(&this->actor.projectedPos, TEST4_GET_BIG_BELLS_SFX_VOLUME_INDEX(&this->actor));
this->lastBellTime = gSaveContext.save.time;
if (CURRENT_DAY == 3) {
@@ -553,11 +553,11 @@ void func_80A431C8(EnTest4* this, PlayState* play) {
}
}
-void func_80A4323C(EnTest4* this, PlayState* play) {
- s32 temp_v0 = (this->actor.params >> 0xA) * 0x64;
+void EnTest4_SetSkyboxNumStars(EnTest4* this, PlayState* play) {
+ s32 numStars = TEST4_GET_SKYBOX_NUM_STARS(&this->actor);
- if (temp_v0 > 0) {
- gSkyboxNumStars = temp_v0;
+ if (numStars > 0) {
+ gSkyboxNumStars = numStars;
}
}
@@ -578,6 +578,6 @@ void EnTest4_Update(Actor* thisx, PlayState* play) {
break;
}
}
- func_80A4323C(this, play);
+ EnTest4_SetSkyboxNumStars(this, play);
}
}
diff --git a/src/overlays/actors/ovl_En_Test4/z_en_test4.h b/src/overlays/actors/ovl_En_Test4/z_en_test4.h
index 106a5a9aa..2d6e5a6ff 100644
--- a/src/overlays/actors/ovl_En_Test4/z_en_test4.h
+++ b/src/overlays/actors/ovl_En_Test4/z_en_test4.h
@@ -5,6 +5,9 @@
struct EnTest4;
+#define TEST4_GET_SKYBOX_NUM_STARS(thisx) (((thisx)->params >> 10) * 100)
+#define TEST4_GET_BIG_BELLS_SFX_VOLUME_INDEX(thisx) (((thisx)->params >> 5) & 0xF)
+
typedef void (*EnTest4ActionFunc)(struct EnTest4*, PlayState*);
typedef struct EnTest4 {
diff --git a/src/overlays/actors/ovl_En_Test6/z_en_test6.c b/src/overlays/actors/ovl_En_Test6/z_en_test6.c
index 5d99b99d4..6f1ed5090 100644
--- a/src/overlays/actors/ovl_En_Test6/z_en_test6.c
+++ b/src/overlays/actors/ovl_En_Test6/z_en_test6.c
@@ -68,34 +68,34 @@ CutsceneData sDoubleSoTCsCamData[] = {
CS_CAM_SPLINE(13, 424, 0, 100),
// Camera At Data
- /* 0x0 */ CS_CAM_POINT(CS_CAM_INTERP_4, 100, 13, 0, 21, -19, CS_CAM_REL_0),
- /* 0x1 */ CS_CAM_POINT(CS_CAM_INTERP_4, 100, 18, 0, 21, -19, CS_CAM_REL_0),
- /* 0x2 */ CS_CAM_POINT(CS_CAM_INTERP_4, 100, 16, 0, 21, -19, CS_CAM_REL_0),
- /* 0x3 */ CS_CAM_POINT(CS_CAM_INTERP_4, 100, 17, -26, -5, -32, CS_CAM_REL_0),
- /* 0x4 */ CS_CAM_POINT(CS_CAM_INTERP_4, 100, 15, 0, 18, -32, CS_CAM_REL_0),
- /* 0x5 */ CS_CAM_POINT(CS_CAM_INTERP_4, 100, 14, 1, 22, -27, CS_CAM_REL_0),
- /* 0x6 */ CS_CAM_POINT(CS_CAM_INTERP_4, 100, 12, 0, 1, -5, CS_CAM_REL_0),
- /* 0x7 */ CS_CAM_POINT(CS_CAM_INTERP_4, 100, 7, 16, 29, -77, CS_CAM_REL_0),
- /* 0x8 */ CS_CAM_POINT(CS_CAM_INTERP_4, 100, 3, 1, 19, 111, CS_CAM_REL_0),
- /* 0x9 */ CS_CAM_POINT(CS_CAM_INTERP_4, 100, 3, -59, 21, 91, CS_CAM_REL_0),
- /* 0xA */ CS_CAM_POINT(CS_CAM_INTERP_4, 100, 3, -19, 59, 84, CS_CAM_REL_0),
- /* 0xB */ CS_CAM_POINT(CS_CAM_INTERP_4, 100, 136, -19, 59, 84, CS_CAM_REL_0),
- /* 0xC */ CS_CAM_POINT(CS_CAM_INTERP_4, 100, 108, -17, 57, 82, CS_CAM_REL_0),
+ /* 0x0 */ CS_CAM_POINT(CS_CAM_INTERP_MP_CUBIC, 100, 13, 0, 21, -19, CS_CAM_REL_0),
+ /* 0x1 */ CS_CAM_POINT(CS_CAM_INTERP_MP_CUBIC, 100, 18, 0, 21, -19, CS_CAM_REL_0),
+ /* 0x2 */ CS_CAM_POINT(CS_CAM_INTERP_MP_CUBIC, 100, 16, 0, 21, -19, CS_CAM_REL_0),
+ /* 0x3 */ CS_CAM_POINT(CS_CAM_INTERP_MP_CUBIC, 100, 17, -26, -5, -32, CS_CAM_REL_0),
+ /* 0x4 */ CS_CAM_POINT(CS_CAM_INTERP_MP_CUBIC, 100, 15, 0, 18, -32, CS_CAM_REL_0),
+ /* 0x5 */ CS_CAM_POINT(CS_CAM_INTERP_MP_CUBIC, 100, 14, 1, 22, -27, CS_CAM_REL_0),
+ /* 0x6 */ CS_CAM_POINT(CS_CAM_INTERP_MP_CUBIC, 100, 12, 0, 1, -5, CS_CAM_REL_0),
+ /* 0x7 */ CS_CAM_POINT(CS_CAM_INTERP_MP_CUBIC, 100, 7, 16, 29, -77, CS_CAM_REL_0),
+ /* 0x8 */ CS_CAM_POINT(CS_CAM_INTERP_MP_CUBIC, 100, 3, 1, 19, 111, CS_CAM_REL_0),
+ /* 0x9 */ CS_CAM_POINT(CS_CAM_INTERP_MP_CUBIC, 100, 3, -59, 21, 91, CS_CAM_REL_0),
+ /* 0xA */ CS_CAM_POINT(CS_CAM_INTERP_MP_CUBIC, 100, 3, -19, 59, 84, CS_CAM_REL_0),
+ /* 0xB */ CS_CAM_POINT(CS_CAM_INTERP_MP_CUBIC, 100, 136, -19, 59, 84, CS_CAM_REL_0),
+ /* 0xC */ CS_CAM_POINT(CS_CAM_INTERP_MP_CUBIC, 100, 108, -17, 57, 82, CS_CAM_REL_0),
// Camera Eye Data
- /* 0x0 */ CS_CAM_POINT(CS_CAM_INTERP_4, 100, 13, 0, 50, 681, CS_CAM_REL_0),
- /* 0x1 */ CS_CAM_POINT(CS_CAM_INTERP_4, 100, 18, 0, 50, 681, CS_CAM_REL_0),
- /* 0x2 */ CS_CAM_POINT(CS_CAM_INTERP_4, 100, 16, 0, 50, 681, CS_CAM_REL_0),
- /* 0x3 */ CS_CAM_POINT(CS_CAM_INTERP_4, 100, 17, -104, 375, 345, CS_CAM_REL_0),
- /* 0x4 */ CS_CAM_POINT(CS_CAM_INTERP_4, 100, 15, 0, -62, 289, CS_CAM_REL_0),
- /* 0x5 */ CS_CAM_POINT(CS_CAM_INTERP_4, 100, 14, -47, 125, 205, CS_CAM_REL_0),
- /* 0x6 */ CS_CAM_POINT(CS_CAM_INTERP_4, 100, 12, -58, -17, 199, CS_CAM_REL_0),
- /* 0x7 */ CS_CAM_POINT(CS_CAM_INTERP_4, 100, 7, 16, 53, 211, CS_CAM_REL_0),
- /* 0x8 */ CS_CAM_POINT(CS_CAM_INTERP_4, 100, 3, -31, 63, 623, CS_CAM_REL_0),
- /* 0x9 */ CS_CAM_POINT(CS_CAM_INTERP_4, 100, 3, -341, 464, 542, CS_CAM_REL_0),
- /* 0xA */ CS_CAM_POINT(CS_CAM_INTERP_4, 100, 3, -341, 464, 542, CS_CAM_REL_0),
- /* 0xB */ CS_CAM_POINT(CS_CAM_INTERP_4, 100, 136, -341, 464, 542, CS_CAM_REL_0),
- /* 0xC */ CS_CAM_POINT(CS_CAM_INTERP_4, 100, 108, -339, 462, 540, CS_CAM_REL_0),
+ /* 0x0 */ CS_CAM_POINT(CS_CAM_INTERP_MP_CUBIC, 100, 13, 0, 50, 681, CS_CAM_REL_0),
+ /* 0x1 */ CS_CAM_POINT(CS_CAM_INTERP_MP_CUBIC, 100, 18, 0, 50, 681, CS_CAM_REL_0),
+ /* 0x2 */ CS_CAM_POINT(CS_CAM_INTERP_MP_CUBIC, 100, 16, 0, 50, 681, CS_CAM_REL_0),
+ /* 0x3 */ CS_CAM_POINT(CS_CAM_INTERP_MP_CUBIC, 100, 17, -104, 375, 345, CS_CAM_REL_0),
+ /* 0x4 */ CS_CAM_POINT(CS_CAM_INTERP_MP_CUBIC, 100, 15, 0, -62, 289, CS_CAM_REL_0),
+ /* 0x5 */ CS_CAM_POINT(CS_CAM_INTERP_MP_CUBIC, 100, 14, -47, 125, 205, CS_CAM_REL_0),
+ /* 0x6 */ CS_CAM_POINT(CS_CAM_INTERP_MP_CUBIC, 100, 12, -58, -17, 199, CS_CAM_REL_0),
+ /* 0x7 */ CS_CAM_POINT(CS_CAM_INTERP_MP_CUBIC, 100, 7, 16, 53, 211, CS_CAM_REL_0),
+ /* 0x8 */ CS_CAM_POINT(CS_CAM_INTERP_MP_CUBIC, 100, 3, -31, 63, 623, CS_CAM_REL_0),
+ /* 0x9 */ CS_CAM_POINT(CS_CAM_INTERP_MP_CUBIC, 100, 3, -341, 464, 542, CS_CAM_REL_0),
+ /* 0xA */ CS_CAM_POINT(CS_CAM_INTERP_MP_CUBIC, 100, 3, -341, 464, 542, CS_CAM_REL_0),
+ /* 0xB */ CS_CAM_POINT(CS_CAM_INTERP_MP_CUBIC, 100, 136, -341, 464, 542, CS_CAM_REL_0),
+ /* 0xC */ CS_CAM_POINT(CS_CAM_INTERP_MP_CUBIC, 100, 108, -339, 462, 540, CS_CAM_REL_0),
// Camera Roll and Fov Data
/* 0x0 */ CS_CAM_MISC(15, 0xA, 70, 0),
diff --git a/src/overlays/actors/ovl_En_Test7/z_en_test7.c b/src/overlays/actors/ovl_En_Test7/z_en_test7.c
index 59de7be65..6c2c6f461 100644
--- a/src/overlays/actors/ovl_En_Test7/z_en_test7.c
+++ b/src/overlays/actors/ovl_En_Test7/z_en_test7.c
@@ -988,6 +988,6 @@ void EnTest7_Draw(Actor* thisx, PlayState* play) {
if (this->unk_144 & 4) {
Environment_DrawLensFlare(play, &play->envCtx, &play->view, play->state.gfxCtx, this->actor.world.pos, 70.0f,
- 5.0f, 0, 0);
+ 5.0f, 0, false);
}
}
diff --git a/src/overlays/actors/ovl_En_Weather_Tag/z_en_weather_tag.c b/src/overlays/actors/ovl_En_Weather_Tag/z_en_weather_tag.c
index e0319ad7e..37f80d372 100644
--- a/src/overlays/actors/ovl_En_Weather_Tag/z_en_weather_tag.c
+++ b/src/overlays/actors/ovl_En_Weather_Tag/z_en_weather_tag.c
@@ -86,7 +86,7 @@ void EnWeatherTag_Init(Actor* thisx, PlayState* play) {
EnWeatherTag_SetupAction(this, func_80966EF0);
break;
- case WEATHERTAG_TYPE_UNK4:
+ case WEATHERTAG_TYPE_SKYBOX_STARS:
EnWeatherTag_SetupAction(this, func_80966FEC);
break;
@@ -352,14 +352,11 @@ void func_80966F74(EnWeatherTag* this, PlayState* play) {
}
}
-// WEATHERTAG_TYPE_UNK4: no visible effect, what does it doooo??
-// used in south clock town??? romani ranch, clock tower rooftop woodfall..? stt
-// all of them have shorter distances though, like 0xA and 0x6, so their locations are important
void func_80966FEC(EnWeatherTag* this, PlayState* play) {
- // weirdly, not the same as the other param lookup used in the rest of the file, which is float
- s32 distance = WEATHER_TAG_RANGE100INT(&this->actor);
- if (distance > 0) {
- gSkyboxNumStars = distance;
+ s32 numStars = WEATHER_TAG_GET_SKYBOX_NUM_STARS(&this->actor);
+
+ if (numStars > 0) {
+ gSkyboxNumStars = numStars;
}
if ((play->sceneId == SCENE_KAIZOKU) && (play->actorCtx.flags & ACTORCTX_FLAG_1)) {
diff --git a/src/overlays/actors/ovl_En_Weather_Tag/z_en_weather_tag.h b/src/overlays/actors/ovl_En_Weather_Tag/z_en_weather_tag.h
index 5313aacfd..dece04414 100644
--- a/src/overlays/actors/ovl_En_Weather_Tag/z_en_weather_tag.h
+++ b/src/overlays/actors/ovl_En_Weather_Tag/z_en_weather_tag.h
@@ -22,14 +22,14 @@ typedef struct EnWeatherTag {
#define WEATHER_TAG_TYPE(thisx) ((thisx)->params & 0xF)
#define WEATHER_TAG_PATHID(thisx) (((thisx)->params >> 4) & 0xF)
#define WEATHER_TAG_RANGE100(thisx) ((u8)(((thisx)->params >> 8) & 0xFF) * 100.0f)
-#define WEATHER_TAG_RANGE100INT(thisx) ((u8)(((thisx)->params >> 8) & 0xFF) * 100)
+#define WEATHER_TAG_GET_SKYBOX_NUM_STARS(thisx) ((u8)(((thisx)->params >> 8) & 0xFF) * 100)
typedef enum {
/* 0 */ WEATHERTAG_TYPE_UNK0,
/* 1 */ WEATHERTAG_TYPE_UNK1,
/* 2 */ WEATHERTAG_TYPE_WINTERFOG,
/* 3 */ WEATHERTAG_TYPE_UNK3,
- /* 4 */ WEATHERTAG_TYPE_UNK4,
+ /* 4 */ WEATHERTAG_TYPE_SKYBOX_STARS,
/* 5 */ WEATHERTAG_TYPE_UNK5,
/* 6 */ WEATHERTAG_TYPE_WATERMURK,
/* 7 */ WEATHERTAG_TYPE_LOCALDAY2RAIN
diff --git a/src/overlays/actors/ovl_Obj_Takaraya_Wall/z_obj_takaraya_wall.c b/src/overlays/actors/ovl_Obj_Takaraya_Wall/z_obj_takaraya_wall.c
index 4d9b48831..3218c3050 100644
--- a/src/overlays/actors/ovl_Obj_Takaraya_Wall/z_obj_takaraya_wall.c
+++ b/src/overlays/actors/ovl_Obj_Takaraya_Wall/z_obj_takaraya_wall.c
@@ -1,10 +1,31 @@
/*
* File: z_obj_takaraya_wall.c
* Overlay: ovl_Obj_Takaraya_Wall
- * Description: Treasure Chest Shop rising wall
+ * Description: Treasure Chest Shop board manager
+ */
+
+/**
+ * The board is laid out as follows:
+ *
+ * Back
+ * 7 6 5 4 3 2 1 0
+ * 0 x x x x x x x x <-- Chest spawns in this row
+ * 1 x x x x x x x x
+ * 2 x x x x x x x x
+ * 3 x x x x x x x x
+ * 4 x x x x x x x x
+ * Left 5 x x x x x x x x Right
+ * 6 x x x x x x x x
+ * 7 x x x x x x x x
+ * 8 x x x x x x x x
+ * 9 x x x x x x x x
+ * 10 x x x x x x x x
+ * Front
+ *
*/
#include "z_obj_takaraya_wall.h"
+#include "objects/object_takaraya_objects/object_takaraya_objects.h"
#define FLAGS (ACTOR_FLAG_10 | ACTOR_FLAG_20)
@@ -12,12 +33,11 @@
void ObjTakarayaWall_Init(Actor* thisx, PlayState* play);
void ObjTakarayaWall_Destroy(Actor* thisx, PlayState* play);
-void ObjTakarayaWall_Update(Actor* thisx, PlayState* play);
+void ObjTakarayaWall_Update(Actor* thisx, PlayState* play2);
void ObjTakarayaWall_Draw(Actor* thisx, PlayState* play);
-void func_80AD9B04(ObjTakarayaWall* this, PlayState* play);
+void ObjTakarayaWall_Manage(ObjTakarayaWall* this, PlayState* play);
-#if 0
ActorInit Obj_Takaraya_Wall_InitVars = {
/**/ ACTOR_OBJ_TAKARAYA_WALL,
/**/ ACTORCAT_BG,
@@ -30,39 +50,455 @@ ActorInit Obj_Takaraya_Wall_InitVars = {
/**/ ObjTakarayaWall_Draw,
};
-// static ColliderCylinderInit sCylinderInit = {
-static ColliderCylinderInit D_80ADA2A0 = {
- { COLTYPE_NONE, AT_NONE, AC_NONE, OC1_ON | OC1_TYPE_ALL, OC2_TYPE_2, COLSHAPE_CYLINDER, },
- { ELEMTYPE_UNK0, { 0x00000000, 0x00, 0x00 }, { 0x00000000, 0x00, 0x00 }, TOUCH_NONE | TOUCH_SFX_NORMAL, BUMP_NONE, OCELEM_ON, },
+static ColliderCylinderInit sCylinderInit = {
+ {
+ COLTYPE_NONE,
+ AT_NONE,
+ AC_NONE,
+ OC1_ON | OC1_TYPE_ALL,
+ OC2_TYPE_2,
+ COLSHAPE_CYLINDER,
+ },
+ {
+ ELEMTYPE_UNK0,
+ { 0x00000000, 0x00, 0x00 },
+ { 0x00000000, 0x00, 0x00 },
+ TOUCH_NONE | TOUCH_SFX_NORMAL,
+ BUMP_NONE,
+ OCELEM_ON,
+ },
{ 40, 120, 0, { 0, 0, 0 } },
};
-// static InitChainEntry sInitChain[] = {
-static InitChainEntry D_80ADA2CC[] = {
+static InitChainEntry sInitChain[] = {
ICHAIN_VEC3F_DIV1000(scale, 100, ICHAIN_STOP),
};
-#endif
+#define TAKARAYA_WALL_ROWS 11
+#define TAKARAYA_WALL_COLUMNS 8
-extern ColliderCylinderInit D_80ADA2A0;
-extern InitChainEntry D_80ADA2CC[];
+typedef struct ObjTakarayaWallCell {
+ /* 0x0 */ u8 row;
+ /* 0x1 */ u8 column;
+} ObjTakarayaWallCell; // size = 0x2
-extern UNK_TYPE D_06000D60;
+typedef enum ObjTakarayaWallCellState {
+ /* 0 */ TAKARAYA_WALL_INACTIVE,
+ /* 1 */ TAKARAYA_WALL_RISING,
+ /* 2 */ TAKARAYA_WALL_FALLING
+} ObjTakarayaWallCellState;
-#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Obj_Takaraya_Wall/func_80AD9240.s")
+s32 sTakarayaWallPathStackDepth;
+ObjTakarayaWallCell sTakarayaWallPathStack[TAKARAYA_WALL_ROWS * TAKARAYA_WALL_COLUMNS];
+s32 sTakarayaWallSuccessfulPathStackDepth; // Set but never used
+s32 sTakarayaWallPathReachedFront;
+Vec3f sTakarayaWallAudioPositions[TAKARAYA_WALL_ROWS][TAKARAYA_WALL_COLUMNS];
+u8 sTakarayaWallStates[TAKARAYA_WALL_ROWS][TAKARAYA_WALL_COLUMNS];
-#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Obj_Takaraya_Wall/func_80AD92FC.s")
+// -10.0f height means the wall does not raise (the cell is open)
+f32 sTakarayaWallHeights[TAKARAYA_WALL_ROWS][TAKARAYA_WALL_COLUMNS];
-#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Obj_Takaraya_Wall/func_80AD9358.s")
+// Note: returns true if the cell is valid
+s32 ObjTakarayaWall_PosToCell(Vec3f* pos, s32* row, s32* column) {
+ *row = ((s32)pos->x - -1680) / 120;
+ *column = ((s32)pos->z - 0) / 120;
-#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Obj_Takaraya_Wall/func_80AD9488.s")
+ if ((*row < 0) || (*row >= TAKARAYA_WALL_ROWS) || (*column < 0) || (*column >= TAKARAYA_WALL_COLUMNS)) {
+ return false;
+ }
+ return true;
+}
-#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Obj_Takaraya_Wall/ObjTakarayaWall_Init.s")
+/**
+ * Checks if the given row and column is valid and is open, i.e does not raise up.
+ */
+s32 ObjTakarayaWall_IsCellOpen(s32 row, s32 column) {
+ if ((row < 0) || (row >= TAKARAYA_WALL_ROWS) || (column < 0) || (column >= TAKARAYA_WALL_COLUMNS) ||
+ (sTakarayaWallHeights[row][column] == 0.0f)) {
+ return false;
+ }
-#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Obj_Takaraya_Wall/ObjTakarayaWall_Destroy.s")
+ return true;
+}
-#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Obj_Takaraya_Wall/func_80AD9B04.s")
+typedef enum TakarayaWallDirection {
+ /* 0 */ TAKARAYA_WALL_DIRECTION_BACK,
+ /* 1 */ TAKARAYA_WALL_DIRECTION_RIGHT,
+ /* 2 */ TAKARAYA_WALL_DIRECTION_FRONT,
+ /* 3 */ TAKARAYA_WALL_DIRECTION_LEFT
+} TakarayaWallDirection;
-#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Obj_Takaraya_Wall/ObjTakarayaWall_Update.s")
+/**
+ * Checks nearby spaces if the adjacent space or any of the three adjacent spaces to that
+ * space are closed (i.e the wall rises up).
+ */
+s32 ObjTakarayaWall_CanCarvePath(s32 row, s32 column, TakarayaWallDirection direction) {
+ s32 adjacentSpaceRow;
+ s32 adjacentSpaceColumn;
+ s32 row1;
+ s32 column1;
+ s32 row2;
+ s32 column2;
+ s32 row3;
+ s32 column3;
-#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Obj_Takaraya_Wall/ObjTakarayaWall_Draw.s")
+ if ((direction == TAKARAYA_WALL_DIRECTION_FRONT) || (direction == TAKARAYA_WALL_DIRECTION_BACK)) {
+ if (direction == TAKARAYA_WALL_DIRECTION_BACK) {
+ adjacentSpaceRow = row - 1;
+ row3 = row - 2;
+ } else { // TAKARAYA_WALL_DIRECTION_FRONT
+ adjacentSpaceRow = row + 1;
+ row3 = row + 2;
+ }
+ adjacentSpaceColumn = column;
+ column3 = column;
+ row1 = adjacentSpaceRow;
+ column1 = column + 1;
+ row2 = adjacentSpaceRow;
+ column2 = column - 1;
+ } else {
+ if (direction == TAKARAYA_WALL_DIRECTION_RIGHT) {
+ adjacentSpaceColumn = column - 1;
+ column3 = column - 2;
+ } else { // TAKARAYA_WALL_DIRECTION_LEFT
+ adjacentSpaceColumn = column + 1;
+ column3 = column + 2;
+ }
+ adjacentSpaceRow = row;
+ row3 = row;
+ row1 = row + 1;
+ column1 = adjacentSpaceColumn;
+ row2 = row - 1;
+ column2 = adjacentSpaceColumn;
+ }
+
+ if ((adjacentSpaceRow < 0) || (adjacentSpaceRow >= TAKARAYA_WALL_ROWS) || (adjacentSpaceColumn < 0) ||
+ (adjacentSpaceColumn >= TAKARAYA_WALL_COLUMNS) ||
+ (sTakarayaWallHeights[adjacentSpaceRow][adjacentSpaceColumn] == -10.0f) ||
+ ObjTakarayaWall_IsCellOpen(row1, column1) || ObjTakarayaWall_IsCellOpen(row2, column2) ||
+ ObjTakarayaWall_IsCellOpen(row3, column3)) {
+ return false;
+ }
+
+ return true;
+}
+
+void ObjTakarayaWall_CarvePath(s32 row, s32 column) {
+ s32 carveDirectionFlags = 0;
+ s32 carveDirectionNum = 0;
+ s32 randMode;
+ s32 direction;
+
+ if (ObjTakarayaWall_CanCarvePath(row, column, TAKARAYA_WALL_DIRECTION_BACK)) {
+ carveDirectionNum++;
+ carveDirectionFlags |= (1 << TAKARAYA_WALL_DIRECTION_BACK);
+ }
+
+ if (ObjTakarayaWall_CanCarvePath(row, column, TAKARAYA_WALL_DIRECTION_RIGHT)) {
+ carveDirectionNum++;
+ carveDirectionFlags |= (1 << TAKARAYA_WALL_DIRECTION_RIGHT);
+ }
+
+ if (ObjTakarayaWall_CanCarvePath(row, column, TAKARAYA_WALL_DIRECTION_FRONT)) {
+ carveDirectionNum++;
+ carveDirectionFlags |= (1 << TAKARAYA_WALL_DIRECTION_FRONT);
+ }
+
+ if (ObjTakarayaWall_CanCarvePath(row, column, TAKARAYA_WALL_DIRECTION_LEFT)) {
+ carveDirectionNum++;
+ carveDirectionFlags |= (1 << TAKARAYA_WALL_DIRECTION_LEFT);
+ }
+
+ if (carveDirectionNum == 0) {
+ sTakarayaWallPathStackDepth--;
+ if (!sTakarayaWallPathReachedFront) {
+ sTakarayaWallSuccessfulPathStackDepth--;
+ }
+
+ return;
+ }
+
+ // Randomly select direction to carve, each possible direction has an equal probability of getting carved out
+ randMode = (s32)Rand_ZeroFloat(carveDirectionNum) % carveDirectionNum;
+
+ if (randMode == 0) {
+ direction = TAKARAYA_WALL_DIRECTION_BACK;
+ while (!((1 << direction) & carveDirectionFlags)) {
+ direction++;
+ }
+ } else if (randMode == 1) {
+ direction = TAKARAYA_WALL_DIRECTION_LEFT;
+ while (!((1 << direction) & carveDirectionFlags)) {
+ direction--;
+ }
+ } else if (randMode == 2) {
+ direction = TAKARAYA_WALL_DIRECTION_BACK;
+ while (!((1 << direction) & carveDirectionFlags)) {
+ direction++;
+ }
+ direction++;
+ while (!((1 << direction) & carveDirectionFlags)) {
+ direction++;
+ }
+ } else {
+ direction = TAKARAYA_WALL_DIRECTION_LEFT;
+ while (!((1 << direction) & carveDirectionFlags)) {
+ direction--;
+ }
+ direction--;
+ while (!((1 << direction) & carveDirectionFlags)) {
+ direction--;
+ }
+ }
+
+ if (direction == TAKARAYA_WALL_DIRECTION_BACK) {
+ row--;
+ } else if (direction == TAKARAYA_WALL_DIRECTION_FRONT) {
+ row++;
+ } else if (direction == TAKARAYA_WALL_DIRECTION_RIGHT) {
+ column--;
+ } else { // TAKARAYA_WALL_DIRECTION_LEFT
+ column++;
+ }
+
+ sTakarayaWallHeights[row][column] = -10.0f;
+ sTakarayaWallPathStack[sTakarayaWallPathStackDepth].row = row;
+ sTakarayaWallPathStack[sTakarayaWallPathStackDepth].column = column;
+ sTakarayaWallPathStackDepth++;
+
+ if (!sTakarayaWallPathReachedFront) {
+ sTakarayaWallSuccessfulPathStackDepth++;
+ if (row == (TAKARAYA_WALL_ROWS - 1)) {
+ sTakarayaWallPathReachedFront = true;
+ }
+ }
+}
+
+void ObjTakarayaWall_Init(Actor* thisx, PlayState* play) {
+ ObjTakarayaWall* this = THIS;
+ Actor* chest;
+ s32 column;
+ s32 i;
+ s32 j;
+
+ Actor_ProcessInitChain(&this->actor, sInitChain);
+
+ for (i = 0; i < TAKARAYA_WALL_ROWS; i++) {
+ for (j = 0; j < TAKARAYA_WALL_COLUMNS; j++) {
+ sTakarayaWallHeights[i][j] = 0.0f;
+ }
+ }
+
+ Collider_InitAndSetCylinder(play, &this->columnCollider, &this->actor, &sCylinderInit);
+ Collider_InitAndSetCylinder(play, &this->rowCollider, &this->actor, &sCylinderInit);
+ this->actor.colChkInfo.mass = MASS_IMMOVABLE;
+
+ column = (s32)Rand_ZeroFloat(TAKARAYA_WALL_COLUMNS) % TAKARAYA_WALL_COLUMNS;
+ chest = Actor_SpawnAsChildAndCutscene(&play->actorCtx, play, ACTOR_EN_BOX, -1635.0f, 0.0f, (column * 120) + 60, 0,
+ 0x4000, this->actor.shape.rot.z, this->actor.params, this->actor.csId,
+ HALFDAYBIT_ALL, NULL);
+ Flags_SetSwitch(play, TAKARAYA_WALL_GET_SWTICH_FLAG(&this->actor));
+ this->actor.shape.rot.z = 0;
+
+ if (chest != NULL) {
+ chest->uncullZoneForward = 2000.0f;
+ }
+
+ sTakarayaWallHeights[0][column] = -10.0f;
+ sTakarayaWallPathStack[sTakarayaWallPathStackDepth].row = 0;
+ sTakarayaWallPathStack[sTakarayaWallPathStackDepth].column = column;
+ sTakarayaWallPathStackDepth++;
+ sTakarayaWallSuccessfulPathStackDepth++;
+
+ do {
+ ObjTakarayaWall_CarvePath(sTakarayaWallPathStack[sTakarayaWallPathStackDepth - 1].row,
+ sTakarayaWallPathStack[sTakarayaWallPathStackDepth - 1].column);
+ } while (sTakarayaWallPathStackDepth > 1);
+
+ for (j = 1; j < TAKARAYA_WALL_COLUMNS - 1; j++) {
+ if (sTakarayaWallHeights[TAKARAYA_WALL_ROWS - 1][j] == -10.0f) {
+ break;
+ }
+ }
+
+ // If either corner in the front is part of the success path, and there is no other opening in the front,
+ // open up the space next to the open corner since the Treasure Chest Shop scene blocks direct access to the
+ // front corners.
+ if (j == (TAKARAYA_WALL_COLUMNS - 1)) {
+ if (sTakarayaWallHeights[TAKARAYA_WALL_ROWS - 1][0] == -10.0f) {
+ sTakarayaWallHeights[TAKARAYA_WALL_ROWS - 1][1] = -10.0f;
+ } else {
+ sTakarayaWallHeights[TAKARAYA_WALL_ROWS - 1][TAKARAYA_WALL_COLUMNS - 2] = -10.0f;
+ }
+ }
+
+ this->actionFunc = ObjTakarayaWall_Manage;
+}
+
+void ObjTakarayaWall_Destroy(Actor* thisx, PlayState* play) {
+ ObjTakarayaWall* this = THIS;
+ s32 i;
+ s32 j;
+
+ Collider_DestroyCylinder(play, &this->columnCollider);
+ Collider_DestroyCylinder(play, &this->rowCollider);
+
+ for (i = 0; i < TAKARAYA_WALL_ROWS; i++) {
+ for (j = 0; j < TAKARAYA_WALL_COLUMNS; j++) {
+ AudioSfx_StopByPos(&sTakarayaWallAudioPositions[i][j]);
+ }
+ }
+}
+
+void ObjTakarayaWall_Manage(ObjTakarayaWall* this, PlayState* play) {
+ Player* player = GET_PLAYER(play);
+ ColliderCylinder* collider;
+ s32 playerRowBehind;
+ s32 playerRow;
+ s32 playerColumn;
+ s32 columnColliderActive;
+ s32 rowColliderActive;
+ s32 playerCellXPos;
+ s32 playerCellZPos;
+ s32 i;
+ s32 j;
+ s32 playerColumnRight;
+ s32 playerColumnLeft;
+ s32 playerRowInFront;
+
+ rowColliderActive = false;
+ columnColliderActive = false;
+ playerRowBehind = playerRowInFront = -1;
+ playerColumnLeft = playerColumnRight = -1;
+
+ ObjTakarayaWall_PosToCell(&player->actor.world.pos, &playerRow, &playerColumn);
+ playerCellXPos = (((s32)player->actor.world.pos.x - -1800) % 120) - 60;
+ playerCellZPos = (((s32)player->actor.world.pos.z - -120) % 120) - 60;
+
+ if (playerCellXPos > 20) {
+ playerRowBehind = playerRow + 1;
+ } else if (playerCellXPos < -20) {
+ playerRowInFront = playerRow - 1;
+ }
+
+ if (playerCellZPos > 20) {
+ playerColumnLeft = playerColumn + 1;
+ } else if (playerCellZPos < -20) {
+ playerColumnRight = playerColumn - 1;
+ }
+
+ for (i = 0; i < TAKARAYA_WALL_ROWS; i++) {
+ for (j = 0; j < TAKARAYA_WALL_COLUMNS; j++) {
+ if (sTakarayaWallHeights[i][j] >= 0.0f) {
+ if (((j == playerColumn) && ((i == playerRowInFront) || (i == playerRowBehind))) ||
+ ((i == playerRow) && ((j == playerColumnRight) || (j == playerColumnLeft)))) {
+ if (Math_StepToF(&sTakarayaWallHeights[i][j], 120.0f, 15.0f)) {
+ sTakarayaWallStates[i][j] = TAKARAYA_WALL_INACTIVE;
+ } else {
+ sTakarayaWallStates[i][j] = TAKARAYA_WALL_RISING;
+ }
+ if (j == playerColumn) {
+ columnColliderActive = true;
+ } else if (i == playerRow) {
+ rowColliderActive = true;
+ }
+ } else if ((i != playerRow) || (j != playerColumn)) {
+ Math_SmoothStepToF(&sTakarayaWallHeights[i][j], 0.0f, 0.2f, 5.0f, 1.0f);
+ sTakarayaWallStates[i][j] = TAKARAYA_WALL_FALLING;
+ }
+ }
+ }
+ }
+
+ collider = &this->columnCollider;
+ collider->dim.radius = 200;
+ if (!columnColliderActive) {
+ collider->dim.pos.y = (s32)this->actor.world.pos.y - 240;
+ } else {
+ collider->dim.pos.y = (s32)this->actor.world.pos.y;
+ collider->dim.pos.z = (s32)player->actor.world.pos.z;
+ if (playerCellXPos > 0) {
+ collider->dim.pos.x = ((((s32)player->actor.world.pos.x - -1680) / 120) * 120) - 1380;
+ } else {
+ collider->dim.pos.x = ((((s32)player->actor.world.pos.x - -1680) / 120) * 120) - 1860;
+ }
+ }
+
+ collider = &this->rowCollider;
+ collider->dim.radius = 200;
+ if (!rowColliderActive) {
+ collider->dim.pos.y = (s32)this->actor.world.pos.y - 240;
+ } else {
+ collider->dim.pos.y = (s32)this->actor.world.pos.y;
+ collider->dim.pos.x = (s32)player->actor.world.pos.x;
+ if (playerCellZPos > 0) {
+ collider->dim.pos.z = (((s32)player->actor.world.pos.z / 120) * 120) + 300;
+ } else {
+ collider->dim.pos.z = (((s32)player->actor.world.pos.z / 120) * 120) - 180;
+ }
+ }
+}
+
+void ObjTakarayaWall_Update(Actor* thisx, PlayState* play2) {
+ PlayState* play = play2;
+ ObjTakarayaWall* this = THIS;
+
+ this->actionFunc(this, play);
+
+ CollisionCheck_SetOC(play, &play->colChkCtx, &this->columnCollider.base);
+ CollisionCheck_SetOC(play, &play->colChkCtx, &this->rowCollider.base);
+}
+
+void ObjTakarayaWall_Draw(Actor* thisx, PlayState* play) {
+ Vec3f audioPos;
+ ObjTakarayaWall* this = THIS;
+ MtxF* mtx;
+ Gfx* gfx;
+ s32 i;
+ s32 j;
+
+ OPEN_DISPS(play->state.gfxCtx);
+
+ mtx = Matrix_GetCurrent();
+
+ gfx = POLY_OPA_DISP;
+ gSPDisplayList(gfx++, gSetupDLs[SETUPDL_25]);
+ gDPSetPrimColor(gfx++, 0, 0, 255, 255, 255, 255);
+
+ for (i = 0; i < TAKARAYA_WALL_ROWS; i++) {
+ for (j = 0; j < TAKARAYA_WALL_COLUMNS; j++) {
+ if (sTakarayaWallHeights[i][j] > 0.0f) {
+ mtx->xw = (i * 120) - 1620;
+ mtx->yw = sTakarayaWallHeights[i][j] + (this->actor.world.pos.y - 120.0f);
+ mtx->zw = (j * 120) + 60;
+
+ gSPMatrix(gfx++, Matrix_NewMtx(play->state.gfxCtx), G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW);
+
+ if (((i + j) % 2) != 0) {
+ gSPDisplayList(gfx++, gTreasureChestShopWallWhiteDL);
+ } else {
+ gSPDisplayList(gfx++, gTreasureChestShopWallBlackDL);
+ }
+
+ if (sTakarayaWallStates[i][j] != TAKARAYA_WALL_INACTIVE) {
+ audioPos.x = mtx->xw;
+ audioPos.y = mtx->yw;
+ audioPos.z = mtx->zw;
+
+ SkinMatrix_Vec3fMtxFMultXYZ(&play->viewProjectionMtxF, &audioPos,
+ &sTakarayaWallAudioPositions[i][j]);
+
+ if (sTakarayaWallStates[i][j] == TAKARAYA_WALL_RISING) {
+ Audio_PlaySfx_AtPos(&sTakarayaWallAudioPositions[i][j], NA_SE_EV_ROCK_CUBE_RISING - SFX_FLAG);
+ } else { // TAKARAYA_WALL_FALLING
+ Audio_PlaySfx_AtPos(&sTakarayaWallAudioPositions[i][j], NA_SE_EV_ROCK_CUBE_FALL - SFX_FLAG);
+ }
+ }
+ }
+ }
+ }
+
+ POLY_OPA_DISP = gfx;
+
+ CLOSE_DISPS(play->state.gfxCtx);
+}
diff --git a/src/overlays/actors/ovl_Obj_Takaraya_Wall/z_obj_takaraya_wall.h b/src/overlays/actors/ovl_Obj_Takaraya_Wall/z_obj_takaraya_wall.h
index 225e26512..b9897d655 100644
--- a/src/overlays/actors/ovl_Obj_Takaraya_Wall/z_obj_takaraya_wall.h
+++ b/src/overlays/actors/ovl_Obj_Takaraya_Wall/z_obj_takaraya_wall.h
@@ -5,12 +5,15 @@
struct ObjTakarayaWall;
+#define TAKARAYA_WALL_GET_SWTICH_FLAG(thisx) ((thisx)->shape.rot.z)
+
typedef void (*ObjTakarayaWallActionFunc)(struct ObjTakarayaWall*, PlayState*);
typedef struct ObjTakarayaWall {
/* 0x000 */ Actor actor;
/* 0x144 */ ObjTakarayaWallActionFunc actionFunc;
- /* 0x148 */ char unk_148[0x98];
+ /* 0x148 */ ColliderCylinder columnCollider;
+ /* 0x194 */ ColliderCylinder rowCollider;
} ObjTakarayaWall; // size = 0x1E0
#endif // Z_OBJ_TAKARAYA_WALL_H
diff --git a/src/overlays/actors/ovl_Object_Kankyo/z_object_kankyo.c b/src/overlays/actors/ovl_Object_Kankyo/z_object_kankyo.c
index 309bbd8ad..ab4df1a6a 100644
--- a/src/overlays/actors/ovl_Object_Kankyo/z_object_kankyo.c
+++ b/src/overlays/actors/ovl_Object_Kankyo/z_object_kankyo.c
@@ -511,7 +511,8 @@ void func_808DD3C8(Actor* thisx, PlayState* play2) {
f32 temp_f2;
f32 tempf;
- if ((play->cameraPtrs[CAM_ID_MAIN]->stateFlags & CAM_STATE_UNDERWATER) || ((u8)play->envCtx.stormState == 0)) {
+ if ((play->cameraPtrs[CAM_ID_MAIN]->stateFlags & CAM_STATE_UNDERWATER) ||
+ ((u8)play->envCtx.stormState == STORM_STATE_OFF)) {
return;
}
@@ -687,7 +688,7 @@ void func_808DDE9C(Actor* thisx, PlayState* play2) {
}
gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(play->state.gfxCtx), G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW);
- gSPDisplayList(POLY_XLU_DISP++, gameplay_keep_DL_0706E0);
+ gSPDisplayList(POLY_XLU_DISP++, gFallingRainDropDL);
}
}
diff --git a/src/overlays/gamestates/ovl_title/z_title.c b/src/overlays/gamestates/ovl_title/z_title.c
index 5cebc2fca..3e87bb72e 100644
--- a/src/overlays/gamestates/ovl_title/z_title.c
+++ b/src/overlays/gamestates/ovl_title/z_title.c
@@ -112,7 +112,7 @@ void ConsoleLogo_Draw(GameState* thisx) {
1 << 10, 1 << 10);
}
- Environment_FillScreen(this->state.gfxCtx, 0, 0, 0, this->coverAlpha, 2);
+ Environment_FillScreen(this->state.gfxCtx, 0, 0, 0, this->coverAlpha, FILL_SCREEN_XLU);
sTitleRotation += 300;
diff --git a/tools/disasm/functions.txt b/tools/disasm/functions.txt
index 1961714af..8dbee20aa 100644
--- a/tools/disasm/functions.txt
+++ b/tools/disasm/functions.txt
@@ -1720,7 +1720,7 @@
0x800F6AB8:("Environment_UpdateNextDayTime",),
0x800F6B44:("Environment_UpdateTime",),
0x800F6CEC:("func_800F6CEC",),
- 0x800F6EA4:("func_800F6EA4",),
+ 0x800F6EA4:("Environment_LerpColor",),
0x800F6FCC:("Environment_IsSceneUpsideDown",),
0x800F6FF8:("Environment_UpdateLights",),
0x800F8554:("Environment_UpdateSun",),
@@ -2904,7 +2904,7 @@
0x80160CD4:("ShrinkWindow_Destroy",),
0x80160CE4:("ShrinkWindow_Update",),
0x80160D98:("ShrinkWindow_Draw",),
- 0x80161180:("func_80161180",),
+ 0x80161180:("CutsceneCamera_Interp_Off",),
0x8016119C:("CutsceneCamera_Init",),
0x8016122C:("CutsceneCamera_Interpolate",),
0x801612B8:("CutsceneCamera_ProcessSpline",),
@@ -2912,18 +2912,18 @@
0x80161BAC:("func_80161BAC",),
0x80161BE0:("CutsceneCamera_SetState",),
0x80161C0C:("CutsceneCamera_Reset",),
- 0x80161C20:("func_80161C20",),
- 0x80161E4C:("func_80161E4C",),
- 0x801620CC:("func_801620CC",),
- 0x8016237C:("func_8016237C",),
- 0x801623E4:("func_801623E4",),
+ 0x80161C20:("CutsceneCamera_Interp_Linear",),
+ 0x80161E4C:("CutsceneCamera_Interp_Scale",),
+ 0x801620CC:("CutsceneCamera_Interp_Geo",),
+ 0x8016237C:("CutsceneCamera_Interp_None",),
+ 0x801623E4:("CutsceneCamera_Interp_Set",),
0x801624EC:("func_801624EC",),
- 0x8016253C:("func_8016253C",),
+ 0x8016253C:("CutsceneCamera_Interp_MultiPointQuadratic",),
0x801629BC:("func_801629BC",),
- 0x80162A50:("func_80162A50",),
+ 0x80162A50:("CutsceneCamera_Interp_MultiPointCubic",),
0x80162FF8:("func_80162FF8",),
0x801631DC:("func_801631DC",),
- 0x80163334:("func_80163334",),
+ 0x80163334:("CutsceneCamera_Interp_Unused",),
0x80163660:("func_80163660",),
0x80163700:("KaleidoManager_FaultAddrConv",),
0x80163758:("KaleidoManager_LoadOvl",),
@@ -6819,7 +6819,7 @@
0x808FC5AC:("EnFr_Destroy",),
0x808FC5BC:("EnFr_Update",),
0x808FC6C0:("EnFishing_SetColliderElement",),
- 0x808FC770:("EnFishing_SeedRand",),
+ 0x808FC770:("EnFishing_InitRand",),
0x808FC790:("EnFishing_RandZeroOne",),
0x808FC8B8:("EnFishing_SmoothStepToS",),
0x808FC964:("EnFishing_SpawnRipple",),
@@ -9217,81 +9217,81 @@
0x809D0090:("EnMttag_Init",),
0x809D0138:("EnMttag_Destroy",),
0x809D0168:("EnMttag_Update",),
- 0x809D0530:("func_809D0530",),
- 0x809D0550:("func_809D0550",),
- 0x809D0678:("func_809D0678",),
- 0x809D082C:("func_809D082C",),
- 0x809D089C:("func_809D089C",),
- 0x809D092C:("func_809D092C",),
- 0x809D0AA4:("func_809D0AA4",),
+ 0x809D0530:("Boss01_InitRand",),
+ 0x809D0550:("Boss01_RandZeroOne",),
+ 0x809D0678:("Boss01_SpawnEffectFallingBlock",),
+ 0x809D082C:("Boss01_SpawnEffectRingOfFire",),
+ 0x809D089C:("Boss01_SetColliderSphere",),
+ 0x809D092C:("Boss01_SelectAttack",),
+ 0x809D0AA4:("Boss01_SpawnDustAtFeet",),
0x809D0CE8:("Boss01_Init",),
0x809D118C:("Boss01_Destroy",),
- 0x809D119C:("func_809D119C",),
- 0x809D1258:("func_809D1258",),
- 0x809D12B4:("func_809D12B4",),
- 0x809D1AB8:("func_809D1AB8",),
- 0x809D1B2C:("func_809D1B2C",),
- 0x809D1E5C:("func_809D1E5C",),
- 0x809D1E74:("func_809D1E74",),
- 0x809D1EA4:("func_809D1EA4",),
- 0x809D20D0:("func_809D20D0",),
- 0x809D2588:("func_809D2588",),
- 0x809D25E8:("func_809D25E8",),
- 0x809D2664:("func_809D2664",),
- 0x809D26B8:("func_809D26B8",),
- 0x809D2780:("func_809D2780",),
- 0x809D27D4:("func_809D27D4",),
- 0x809D2858:("func_809D2858",),
- 0x809D2914:("func_809D2914",),
- 0x809D2A44:("func_809D2A44",),
- 0x809D2AA0:("func_809D2AA0",),
- 0x809D2BCC:("func_809D2BCC",),
- 0x809D2CDC:("func_809D2CDC",),
- 0x809D2DE8:("func_809D2DE8",),
- 0x809D2E4C:("func_809D2E4C",),
- 0x809D3074:("func_809D3074",),
- 0x809D30D0:("func_809D30D0",),
- 0x809D32B4:("func_809D32B4",),
- 0x809D3374:("func_809D3374",),
- 0x809D3400:("func_809D3400",),
- 0x809D345C:("func_809D345C",),
- 0x809D34D4:("func_809D34D4",),
- 0x809D3530:("func_809D3530",),
- 0x809D35A8:("func_809D35A8",),
- 0x809D365C:("func_809D365C",),
- 0x809D370C:("func_809D370C",),
- 0x809D3A7C:("func_809D3A7C",),
- 0x809D3ADC:("func_809D3ADC",),
- 0x809D3C10:("func_809D3C10",),
- 0x809D3CD0:("func_809D3CD0",),
- 0x809D441C:("func_809D441C",),
- 0x809D4464:("func_809D4464",),
- 0x809D44C0:("func_809D44C0",),
- 0x809D4668:("func_809D4668",),
+ 0x809D119C:("Boss01_JumpAwayFromExplosive",),
+ 0x809D1258:("Boss01_SetupIntroCutscene",),
+ 0x809D12B4:("Boss01_IntroCutscene",),
+ 0x809D1AB8:("Boss01_SetupSummonBugsCutscene",),
+ 0x809D1B2C:("Boss01_SummonBugsCutscene",),
+ 0x809D1E5C:("Boss01_Afterimage_SetupWaitToDespawn",),
+ 0x809D1E74:("Boss01_Afterimage_WaitToDespawn",),
+ 0x809D1EA4:("Boss01_SetupWait",),
+ 0x809D20D0:("Boss01_Wait",),
+ 0x809D2588:("Boss01_SetupDazed",),
+ 0x809D25E8:("Boss01_Dazed",),
+ 0x809D2664:("Boss01_SetupSpinAttack",),
+ 0x809D26B8:("Boss01_SpinAttack",),
+ 0x809D2780:("Boss01_SetupDanceBeforeAttack",),
+ 0x809D27D4:("Boss01_DanceBeforeAttack",),
+ 0x809D2858:("Boss01_SetupRun",),
+ 0x809D2914:("Boss01_Run",),
+ 0x809D2A44:("Boss01_SetupJump",),
+ 0x809D2AA0:("Boss01_JumpSquat",),
+ 0x809D2BCC:("Boss01_Jump",),
+ 0x809D2CDC:("Boss01_JumpLand",),
+ 0x809D2DE8:("Boss01_SetupVerticalSlash",),
+ 0x809D2E4C:("Boss01_VerticalSlash",),
+ 0x809D3074:("Boss01_SetupHorizontalSlash",),
+ 0x809D30D0:("Boss01_HorizontalSlash",),
+ 0x809D32B4:("Boss01_SetupGuard",),
+ 0x809D3374:("Boss01_Guard",),
+ 0x809D3400:("Boss01_SetupKick",),
+ 0x809D345C:("Boss01_Kick",),
+ 0x809D34D4:("Boss01_SetupShieldBash",),
+ 0x809D3530:("Boss01_ShieldBash",),
+ 0x809D35A8:("Boss01_SetupDamaged",),
+ 0x809D365C:("Boss01_Damaged",),
+ 0x809D370C:("Boss01_UpdateDamage",),
+ 0x809D3A7C:("Boss01_SetupSummonMoths",),
+ 0x809D3ADC:("Boss01_SummonMoths",),
+ 0x809D3C10:("Boss01_SetupDeathCutscene",),
+ 0x809D3CD0:("Boss01_DeathCutscene",),
+ 0x809D441C:("Boss01_SetupStunned",),
+ 0x809D4464:("Boss01_Stunned",),
+ 0x809D44C0:("Boss01_Thaw",),
+ 0x809D4668:("Boss01_ArePlayerAndOdolwaFacing",),
0x809D46E4:("Boss01_Update",),
- 0x809D519C:("func_809D519C",),
- 0x809D5584:("func_809D5584",),
- 0x809D55CC:("func_809D55CC",),
+ 0x809D519C:("Boss01_DrawSwordTrail",),
+ 0x809D5584:("Boss01_OverrideLimbDraw",),
+ 0x809D55CC:("Boss01_PostLimbDraw",),
0x809D5988:("Boss01_Draw",),
- 0x809D5B0C:("func_809D5B0C",),
- 0x809D5BC4:("func_809D5BC4",),
- 0x809D5FB4:("func_809D5FB4",),
- 0x809D606C:("func_809D606C",),
- 0x809D62D4:("func_809D62D4",),
- 0x809D6314:("func_809D6314",),
- 0x809D6424:("func_809D6424",),
- 0x809D6488:("func_809D6488",),
- 0x809D64E0:("func_809D64E0",),
- 0x809D6540:("func_809D6540",),
- 0x809D6588:("func_809D6588",),
- 0x809D65E0:("func_809D65E0",),
- 0x809D670C:("func_809D670C",),
- 0x809D694C:("func_809D694C",),
- 0x809D6B08:("func_809D6B08",),
- 0x809D6BB4:("func_809D6BB4",),
- 0x809D6C98:("func_809D6C98",),
- 0x809D6E7C:("func_809D6E7C",),
- 0x809D73D4:("func_809D73D4",),
+ 0x809D5B0C:("Boss01_Afterimage_Draw",),
+ 0x809D5BC4:("Boss01_FillShadowTex",),
+ 0x809D5FB4:("Boss01_GenShadowTex",),
+ 0x809D606C:("Boss01_DrawShadowTex",),
+ 0x809D62D4:("Boss01_Bug_SetupCrawl",),
+ 0x809D6314:("Boss01_Bug_Crawl",),
+ 0x809D6424:("Boss01_Bug_SetupDamaged",),
+ 0x809D6488:("Boss01_Bug_Damaged",),
+ 0x809D64E0:("Boss01_Bug_SetupDead",),
+ 0x809D6540:("Boss01_Bug_SetupStunned",),
+ 0x809D6588:("Boss01_Bug_Stunned",),
+ 0x809D65E0:("Boss01_Bug_Dead",),
+ 0x809D670C:("Boss01_Bug_UpdateDamage",),
+ 0x809D694C:("Boss01_Bug_Update",),
+ 0x809D6B08:("Boss01_Bug_OverrideLimbDraw",),
+ 0x809D6BB4:("Boss01_Bug_Draw",),
+ 0x809D6C98:("Boss01_SpawnDustForFallingBlock",),
+ 0x809D6E7C:("Boss01_UpdateEffects",),
+ 0x809D73D4:("Boss01_DrawEffects",),
0x809DA1D0:("Boss02_FillScreen",),
0x809DA22C:("Boss02_SetFillScreenAlpha",),
0x809DA24C:("Boss02_StopFillScreen",),
@@ -9321,7 +9321,7 @@
0x809E299C:("Boss03_SpawnEffectSplash",),
0x809E2AB4:("Boss03_SpawnEffectBubble",),
0x809E2B8C:("Boss03_UpdateSphereElement",),
- 0x809E2C1C:("Boss03_SeedRand",),
+ 0x809E2C1C:("Boss03_InitRand",),
0x809E2C3C:("Boss03_RandZeroOne",),
0x809E2D64:("Boss03_FindActorDblueMovebg",),
0x809E2DA0:("Boss03_SpawnDust",),
@@ -10412,7 +10412,7 @@
0x80A42F20:("func_80A42F20",),
0x80A430C8:("func_80A430C8",),
0x80A431C8:("func_80A431C8",),
- 0x80A4323C:("func_80A4323C",),
+ 0x80A4323C:("EnTest4_SetSkyboxNumStars",),
0x80A43274:("EnTest4_Update",),
0x80A434E0:("EnBat_Init",),
0x80A437A0:("EnBat_Destroy",),
@@ -12278,13 +12278,13 @@
0x80AD8BC0:("EnKame_Draw",),
0x80AD8CEC:("EnKame_SpikedSnapperOverrideLimbDraw",),
0x80AD8D64:("EnKame_DrawSpikedSnapper",),
- 0x80AD9240:("func_80AD9240",),
- 0x80AD92FC:("func_80AD92FC",),
- 0x80AD9358:("func_80AD9358",),
- 0x80AD9488:("func_80AD9488",),
+ 0x80AD9240:("ObjTakarayaWall_PosToCell",),
+ 0x80AD92FC:("ObjTakarayaWall_IsCellOpen",),
+ 0x80AD9358:("ObjTakarayaWall_CanCarvePath",),
+ 0x80AD9488:("ObjTakarayaWall_CarvePath",),
0x80AD97DC:("ObjTakarayaWall_Init",),
0x80AD9A6C:("ObjTakarayaWall_Destroy",),
- 0x80AD9B04:("func_80AD9B04",),
+ 0x80AD9B04:("ObjTakarayaWall_Manage",),
0x80AD9F90:("ObjTakarayaWall_Update",),
0x80AD9FF8:("ObjTakarayaWall_Draw",),
0x80ADAAF0:("BgFuMizu_Init",),
diff --git a/tools/disasm/variables.txt b/tools/disasm/variables.txt
index cfc49d513..6f083fbc1 100644
--- a/tools/disasm/variables.txt
+++ b/tools/disasm/variables.txt
@@ -323,7 +323,7 @@
0x8009CCD0:("sFaultDrawer","FaultDrawer","",0x3c),
0x8009CD10:("D_8009CD10","UNK_TYPE4","",0x4),
0x8009CD20:("gSystemArena","Arena","",0x24),
- 0x8009CD50:("sRandFloat","f32","",0x4),
+ 0x8009CD50:("gRandFloat","f32","",0x4),
0x8009CD60:("sArenaLockMsg","OSMesg","[1]",0x4),
0x8009CD70:("tmp_task","OSTask","",0x40),
0x8009CDB0:("__MotorDataBuf","OSPifRam","[4]",0x100),
@@ -3971,7 +3971,7 @@
0x801F6B40:("sShrinkWindow","ShrinkWindow","",0x4),
0x801F6B44:("sShrinkWindowPtr","ShrinkWindow*","",0x4),
0x801F6B50:("sCurCsCamera","UNK_TYPE4","",0x4),
- 0x801F6B58:("D_801F6B58","UNK_TYPE1","",0x1),
+ 0x801F6B58:("sKnots","UNK_TYPE1","",0x1),
0x801F6BF0:("sKaleidoMgrFaultAddrConvClient","FaultAddrConvClient","",0xC),
0x801F6C00:("sKaleidoScopeUpdateFunc","void*","",0x4),
0x801F6C04:("sKaleidoScopeDrawFunc","void*","",0x4),
@@ -7495,9 +7495,9 @@
0x80911F50:("D_80911F50","UNK_TYPE1","",0x1),
0x80911F58:("sSinkingLureBasePos","Vec3f","",0xC),
0x80911F64:("D_80911F64","f32","",0x4),
- 0x80911F68:("sRandSeed0","s32","",0x4),
- 0x80911F6C:("sRandSeed1","s32","",0x4),
- 0x80911F70:("sRandSeed2","s32","",0x4),
+ 0x80911F68:("sFishingRandSeed1","s32","",0x4),
+ 0x80911F6C:("sFishingRandSeed2","s32","",0x4),
+ 0x80911F70:("sFishingRandSeed3","s32","",0x4),
0x80911F78:("sPondProps","UNK_TYPE1","[140]",0x20D0),
0x80914048:("sGroupFishes","UNK_TYPE1","[60]",0x10E0),
0x80915128:("sFishGroupAngle1","f32","",0x4),
@@ -9820,54 +9820,45 @@
0x809D0434:("D_809D0434","f32","",0x4),
0x809D0438:("D_809D0438","f32","",0x4),
0x809D043C:("D_809D043C","f32","",0x4),
- 0x809D7980:("D_809D7980","UNK_TYPE1","",0x1),
- 0x809D798C:("D_809D798C","f32","",0x4),
- 0x809D7990:("D_809D7990","UNK_TYPE1","",0x1),
- 0x809D79B0:("D_809D79B0","UNK_TYPE1","",0x1),
- 0x809D79D0:("D_809D79D0","UNK_TYPE1","",0x1),
- 0x809D7A3C:("D_809D7A3C","UNK_TYPE1","",0x1),
- 0x809D7A4C:("D_809D7A4C","UNK_TYPE1","",0x1),
- 0x809D7A70:("D_809D7A70","UNK_TYPE1","",0x1),
- 0x809D7A80:("D_809D7A80","UNK_TYPE1","",0x1),
- 0x809D7C0C:("D_809D7C0C","UNK_TYPE1","",0x1),
- 0x809D7C1C:("D_809D7C1C","UNK_TYPE1","",0x1),
- 0x809D7C64:("D_809D7C64","UNK_TYPE1","",0x1),
- 0x809D7C74:("D_809D7C74","UNK_TYPE1","",0x1),
- 0x809D7CA0:("D_809D7CA0","UNK_TYPE1","",0x1),
- 0x809D7CCC:("Boss_01_InitVars","UNK_TYPE1","",0x1),
- 0x809D7CEC:("D_809D7CEC","UNK_TYPE1","",0x1),
- 0x809D7CF0:("D_809D7CF0","UNK_TYPE1","",0x1),
- 0x809D7CF4:("D_809D7CF4","UNK_TYPE1","",0x1),
- 0x809D7D24:("D_809D7D24","UNK_TYPE4","",0x4),
- 0x809D7D38:("D_809D7D38","UNK_TYPE4","",0x4),
- 0x809D7D4C:("D_809D7D4C","UNK_TYPE1","",0x1),
- 0x809D7D50:("D_809D7D50","UNK_TYPE1","",0x1),
- 0x809D7D54:("D_809D7D54","UNK_TYPE1","",0x1),
- 0x809D7D60:("D_809D7D60","UNK_TYPE1","",0x1),
- 0x809D7D6C:("D_809D7D6C","UNK_TYPE1","",0x1),
- 0x809D7D78:("D_809D7D78","UNK_TYPE1","",0x1),
- 0x809D7D7A:("D_809D7D7A","UNK_TYPE1","",0x1),
- 0x809D7D7E:("D_809D7D7E","UNK_TYPE1","",0x1),
- 0x809D7D7F:("D_809D7D7F","UNK_TYPE1","",0x1),
- 0x809D7D84:("D_809D7D84","UNK_TYPE1","",0x1),
- 0x809D7D8E:("D_809D7D8E","UNK_TYPE1","",0x1),
- 0x809D7D8F:("D_809D7D8F","UNK_TYPE1","",0x1),
- 0x809D7D96:("D_809D7D96","UNK_TYPE1","",0x1),
- 0x809D7D97:("D_809D7D97","UNK_TYPE1","",0x1),
- 0x809D7D98:("D_809D7D98","UNK_TYPE1","",0x1),
- 0x809D7DB4:("D_809D7DB4","UNK_TYPE1","",0x1),
- 0x809D7E38:("D_809D7E38","UNK_TYPE1","",0x1),
- 0x809D7E44:("D_809D7E44","UNK_TYPE1","",0x1),
- 0x809D7E50:("D_809D7E50","UNK_TYPE1","",0x1),
- 0x809D7E5C:("D_809D7E5C","UNK_TYPE1","",0x1),
- 0x809D7E68:("D_809D7E68","UNK_TYPE1","",0x1),
- 0x809D7EA0:("D_809D7EA0","UNK_TYPE1","",0x1),
- 0x809D7EAC:("D_809D7EAC","UNK_TYPE4","",0x4),
- 0x809D7EC4:("D_809D7EC4","UNK_TYPE4","",0x4),
- 0x809D7EE0:("D_809D7EE0","UNK_TYPE4","",0x4),
- 0x809D7F00:("D_809D7F00","UNK_TYPE4","",0x4),
- 0x809D7F30:("D_809D7F30","UNK_TYPE4","",0x4),
- 0x809D7F6C:("D_809D7F6C","UNK_TYPE1","",0x1),
+ 0x809D7980:("sFallingBlockSfxPos","Vec3f","",0xC),
+ 0x809D798C:("sSwordTrailAngularRangeDivisor","f32","",0x4),
+ 0x809D7990:("sOdolwaDamageTable","DamageTable","",0x20),
+ 0x809D79B0:("sBugDamageTable","DamageTable","",0x20),
+ 0x809D79D0:("sSwordColliderJntSphElementsInit","ColliderJntSphElementInit","[3]",0x6C),
+ 0x809D7A3C:("sSwordColliderJntSphInit","ColliderJntSphInit","",0x10),
+ 0x809D7A4C:("sShieldColliderJntSphElementsInit","ColliderJntSphElementInit","[1]",0x24),
+ 0x809D7A70:("sShieldColliderJntSphInit","ColliderJntSphInit","",0x10),
+ 0x809D7A80:("sBodyColliderJntSphElementsInit","ColliderJntSphElementInit","[11]", 0x18C),
+ 0x809D7C0C:("sBodyColliderJntSphInit","ColliderJntSphInit","",0x10),
+ 0x809D7C1C:("sKickAndShieldBashColliderJntSphElementsInit","ColliderJntSphElementInit","[2]",0x48),
+ 0x809D7C64:("sKickAndShieldBashColliderJntSphInit","ColliderJntSphInit","",0x10),
+ 0x809D7C74:("sBugACColliderCylinderInit","ColliderCylinderInit","",0x2C),
+ 0x809D7CA0:("sBugATColliderCylinderInit","ColliderCylinderInit","",0x2C),
+ 0x809D7CCC:("Boss_01_InitVars","ActorInit","",0x20),
+ 0x809D7CEC:("sDustPrimColor","Color_RGBA8","",0x4),
+ 0x809D7CF0:("sDustEnvColor","Color_RGBA8","",0x4),
+ 0x809D7CF4:("sWaitAnimations","AnimationHeader*","[12]",0x30),
+ 0x809D7D24:("sBlueWarpSpawnsX","f32","[5]",0x14),
+ 0x809D7D38:("sBlueWarpSpawnsZ","f32","[5]",0x14),
+ 0x809D7D4C:("sIcePrimColor","Color_RGBA8","",0x4),
+ 0x809D7D50:("sIceEnvColor","Color_RGBA8","",0x4),
+ 0x809D7D54:("sIceAccel","Vec3f","",0xC),
+ 0x809D7D60:("sSwordTrailOuterVertexIndices","u8","[11]",0xB),
+ 0x809D7D6C:("sSwordTrailInnerVertexIndices","u8","[11]",0xB),
+ 0x809D7D78:("sLimbToColliderBodyParts","s8","[60]",0x3C),
+ 0x809D7DB4:("sLimbColliderOffsets","Vec3f","[11]",0x84),
+ 0x809D7E38:("sShieldColliderOffset","Vec3f","",0xC),
+ 0x809D7E44:("sSwordBaseColliderOffset","Vec3f","",0xC),
+ 0x809D7E50:("sSwordTipColliderOffset","Vec3f","",0xC),
+ 0x809D7E5C:("sHorizontalSlashPelvisColliderOffset","Vec3f","",0xC),
+ 0x809D7E68:("sLimbToBodyParts","s8","[53]",0x35),
+ 0x809D7EA0:("sDefaultPelvisColliderOffset","Vec3f","",0xC),
+ 0x809D7EAC:("sShadowSmallMap","s32","[6]",0x18),
+ 0x809D7EC4:("sShadowMediumMap","s32","[7]",0x1C),
+ 0x809D7EE0:("sShadowLargeMap","s32","[8]",0x20),
+ 0x809D7F00:("sShadowExtraLargeMap","s32","[12]",0x30),
+ 0x809D7F30:("sParentShadowBodyParts","s32","[15]",0x3C),
+ 0x809D7F6C:("sShadowSizes","u8","[15]",0xF),
0x809D7F80:("D_809D7F80","f32","",0x4),
0x809D7F84:("D_809D7F84","f32","",0x4),
0x809D7F88:("D_809D7F88","f32","",0x4),
@@ -9925,22 +9916,22 @@
0x809D8150:("D_809D8150","f32","",0x4),
0x809D8154:("D_809D8154","f32","",0x4),
0x809D8158:("D_809D8158","f32","",0x4),
- 0x809D8A10:("D_809D8A10","UNK_TYPE2","",0x2),
- 0x809D8A14:("D_809D8A14","UNK_TYPE4","",0x4),
- 0x809D8A18:("D_809D8A18","UNK_TYPE4","",0x4),
- 0x809D8A1C:("D_809D8A1C","f32","",0x4),
- 0x809D8A20:("D_809D8A20","f32","",0x4),
- 0x809D8A24:("D_809D8A24","f32","",0x4),
- 0x809D8A28:("D_809D8A28","f32","",0x4),
- 0x809D8A2C:("D_809D8A2C","f32","",0x4),
- 0x809D8A30:("D_809D8A30","f32","",0x4),
- 0x809D8A34:("D_809D8A34","f32","",0x4),
- 0x809D8A38:("D_809D8A38","UNK_TYPE1","",0x1),
- 0x809D8A40:("D_809D8A40","UNK_TYPE1","",0x1),
- 0x809D8A4C:("D_809D8A4C","UNK_TYPE4","",0x4),
- 0x809D8A50:("D_809D8A50","UNK_TYPE4","",0x4),
- 0x809D8A54:("D_809D8A54","UNK_TYPE4","",0x4),
- 0x809D8A58:("D_809D8A58","UNK_TYPE1","",0x1),
+ 0x809D8A10:("sOdolwaBugCount","s16","",0x2),
+ 0x809D8A14:("sOdolwa","Boss01*","",0x4),
+ 0x809D8A18:("sMothSwarm","EnTanron1*","",0x4),
+ 0x809D8A1C:("sOdolwaSwordTrailPosX","f32","",0x4),
+ 0x809D8A20:("sOdolwaSwordTrailPosY","f32","",0x4),
+ 0x809D8A24:("sOdolwaSwordTrailPosZ","f32","",0x4),
+ 0x809D8A28:("sOdolwaSwordTrailRotX","f32","",0x4),
+ 0x809D8A2C:("sOdolwaSwordTrailRotY","f32","",0x4),
+ 0x809D8A30:("sOdolwaSwordTrailRotZ","f32","",0x4),
+ 0x809D8A34:("sOdolwaSwordTrailAlpha","f32","",0x4),
+ 0x809D8A38:("sOdolwaMusicStartTimer","u8","",0x1),
+ 0x809D8A40:("sOdolwaDamageSfxPos","Vec3f","",0xC),
+ 0x809D8A4C:("sOdolwaRandSeed1","s32","",0x4),
+ 0x809D8A50:("sOdolwaRandSeed2","s32","",0x4),
+ 0x809D8A54:("sOdolwaRandSeed3","s32","",0x4),
+ 0x809D8A58:("sOdolwaEffects","OdolwaEffect","[100]",0x1770),
0x809DF550:("D_809DF550","UNK_TYPE1","",0x1),
0x809DF570:("D_809DF570","UNK_TYPE1","",0x1),
0x809DF590:("Boss_02_InitVars","UNK_TYPE1","",0x1),
@@ -10035,9 +10026,9 @@
0x809E0424:("sRedTwinmold","UNK_TYPE4","",0x4),
0x809E0428:("sBlueTwinmold","UNK_TYPE4","",0x4),
0x809E042C:("sTwinmoldStatic","UNK_TYPE4","",0x4),
- 0x809E0430:("sMusicStartTimer","UNK_TYPE1","",0x1),
+ 0x809E0430:("sTwinmoldMusicStartTimer","UNK_TYPE1","",0x1),
0x809E0434:("sBlueWarp","UNK_TYPE4","",0x4),
- 0x809E0438:("sEffects","UNK_TYPE1","",0x1),
+ 0x809E0438:("sTwinmoldEffects","UNK_TYPE1","",0x1),
0x809E8EA0:("D_809E8EA0","UNK_TYPE4","",0x4),
0x809E8EAC:("Boss_03_InitVars","UNK_TYPE1","",0x1),
0x809E8ECC:("D_809E8ECC","UNK_TYPE1","",0x1),
@@ -10119,9 +10110,9 @@
0x809E9848:("D_809E9848","UNK_TYPE1","",0x1),
0x809E9858:("sGyorgEffects","UNK_TYPE1","[150]",0x27D8),
0x809EC030:("sGyorgBossInstance","UNK_TYPE4","",0x4),
- 0x809EC034:("sRandSeed0","UNK_TYPE4","",0x4),
- 0x809EC038:("sRandSeed1","UNK_TYPE4","",0x4),
- 0x809EC03C:("sRandSeed2","UNK_TYPE4","",0x4),
+ 0x809EC034:("sGyorgRandSeed1","UNK_TYPE4","",0x4),
+ 0x809EC038:("sGyorgRandSeed2","UNK_TYPE4","",0x4),
+ 0x809EC03C:("sGyorgRandSeed3","UNK_TYPE4","",0x4),
0x809EE150:("D_809EE150","UNK_TYPE1","",0x1),
0x809EE170:("Boss_04_InitVars","UNK_TYPE1","",0x1),
0x809EE190:("D_809EE190","UNK_TYPE1","",0x1),
@@ -12937,18 +12928,13 @@
0x80ADA2A0:("D_80ADA2A0","UNK_TYPE1","",0x1),
0x80ADA2CC:("D_80ADA2CC","UNK_TYPE1","",0x1),
0x80ADA2D0:("D_80ADA2D0","f32","",0x4),
- 0x80ADA450:("D_80ADA450","UNK_TYPE4","",0x4),
- 0x80ADA458:("D_80ADA458","UNK_TYPE1","",0x1),
- 0x80ADA508:("D_80ADA508","UNK_TYPE4","",0x4),
- 0x80ADA50C:("D_80ADA50C","UNK_TYPE4","",0x4),
- 0x80ADA510:("D_80ADA510","UNK_TYPE1","",0x1),
- 0x80ADA930:("D_80ADA930","UNK_TYPE1","",0x1),
- 0x80ADA988:("D_80ADA988","f32","",0x4),
- 0x80ADA98C:("D_80ADA98C","UNK_TYPE4","",0x4),
- 0x80ADA9A4:("D_80ADA9A4","UNK_TYPE4","",0x4),
- 0x80ADAAC8:("D_80ADAAC8","f32","",0x4),
- 0x80ADAACC:("D_80ADAACC","f32","",0x4),
- 0x80ADAAE0:("D_80ADAAE0","f32","",0x4),
+ 0x80ADA450:("sTakarayaWallPathStackDepth","UNK_TYPE4","",0x4),
+ 0x80ADA458:("sTakarayaWallPathStack","UNK_TYPE1","",0x1),
+ 0x80ADA508:("sTakarayaWallSuccessfulPathStackDepth","UNK_TYPE4","",0x4),
+ 0x80ADA50C:("sTakarayaWallPathReachedFront","UNK_TYPE4","",0x4),
+ 0x80ADA510:("sTakarayaWallAudioPositions","UNK_TYPE1","",0x1),
+ 0x80ADA930:("sTakarayaWallStates","UNK_TYPE1","",0x1),
+ 0x80ADA988:("sTakarayaWallHeights","f32","",0x160),
0x80ADAAE8:("D_80ADAAE8","UNK_TYPE1","",0x1),
0x80ADAD80:("Bg_Fu_Mizu_InitVars","UNK_TYPE1","",0x1),
0x80ADD910:("D_80ADD910","UNK_TYPE1","",0x1),
diff --git a/tools/sizes/code_functions.csv b/tools/sizes/code_functions.csv
index 5935c3262..06f470127 100644
--- a/tools/sizes/code_functions.csv
+++ b/tools/sizes/code_functions.csv
@@ -1229,12 +1229,12 @@ asm/non_matchings/code/z_kankyo/Environment_LerpWeightAccelDecel.s,Environment_L
asm/non_matchings/code/z_kankyo/Environment_UpdateSkybox.s,Environment_UpdateSkybox,0x800F5CD0,0x2D9
asm/non_matchings/code/z_kankyo/Environment_EnableUnderwaterLights.s,Environment_EnableUnderwaterLights,0x800F6834,0x46
asm/non_matchings/code/z_kankyo/Environment_DisableUnderwaterLights.s,Environment_DisableUnderwaterLights,0x800F694C,0x2E
-asm/non_matchings/code/z_kankyo/func_800F6A04.s,func_800F6A04,0x800F6A04,0xF
+asm/non_matchings/code/z_kankyo/Environment_WipeRumbleRequests.s,Environment_WipeRumbleRequests,0x800F6A04,0xF
asm/non_matchings/code/z_kankyo/Environment_UpdateSkyboxRotY.s,Environment_UpdateSkyboxRotY,0x800F6A40,0x1E
asm/non_matchings/code/z_kankyo/Environment_UpdateNextDayTime.s,Environment_UpdateNextDayTime,0x800F6AB8,0x23
asm/non_matchings/code/z_kankyo/Environment_UpdateTime.s,Environment_UpdateTime,0x800F6B44,0x6A
asm/non_matchings/code/z_kankyo/func_800F6CEC.s,func_800F6CEC,0x800F6CEC,0x6E
-asm/non_matchings/code/z_kankyo/func_800F6EA4.s,func_800F6EA4,0x800F6EA4,0x4A
+asm/non_matchings/code/z_kankyo/Environment_LerpColor.s,Environment_LerpColor,0x800F6EA4,0x4A
asm/non_matchings/code/z_kankyo/Environment_IsSceneUpsideDown.s,Environment_IsSceneUpsideDown,0x800F6FCC,0xB
asm/non_matchings/code/z_kankyo/Environment_UpdateLights.s,Environment_UpdateLights,0x800F6FF8,0x557
asm/non_matchings/code/z_kankyo/Environment_UpdateSun.s,Environment_UpdateSun,0x800F8554,0xDC
@@ -2418,7 +2418,7 @@ asm/non_matchings/code/z_shrink_window/ShrinkWindow_Init.s,ShrinkWindow_Init,0x8
asm/non_matchings/code/z_shrink_window/ShrinkWindow_Destroy.s,ShrinkWindow_Destroy,0x80160CD4,0x4
asm/non_matchings/code/z_shrink_window/ShrinkWindow_Update.s,ShrinkWindow_Update,0x80160CE4,0x2D
asm/non_matchings/code/z_shrink_window/ShrinkWindow_Draw.s,ShrinkWindow_Draw,0x80160D98,0xFA
-asm/non_matchings/code/cutscene_camera/func_80161180.s,func_80161180,0x80161180,0x7
+asm/non_matchings/code/cutscene_camera/CutsceneCamera_Interp_Off.s,CutsceneCamera_Interp_Off,0x80161180,0x7
asm/non_matchings/code/cutscene_camera/CutsceneCamera_Init.s,CutsceneCamera_Init,0x8016119C,0x24
asm/non_matchings/code/cutscene_camera/CutsceneCamera_Interpolate.s,CutsceneCamera_Interpolate,0x8016122C,0x23
asm/non_matchings/code/cutscene_camera/CutsceneCamera_ProcessSpline.s,CutsceneCamera_ProcessSpline,0x801612B8,0x1B8
@@ -2426,18 +2426,18 @@ asm/non_matchings/code/cutscene_camera/CutsceneCamera_UpdateSplines.s,CutsceneCa
asm/non_matchings/code/cutscene_camera/func_80161BAC.s,func_80161BAC,0x80161BAC,0xD
asm/non_matchings/code/cutscene_camera/CutsceneCamera_SetState.s,CutsceneCamera_SetState,0x80161BE0,0xB
asm/non_matchings/code/cutscene_camera/CutsceneCamera_Reset.s,CutsceneCamera_Reset,0x80161C0C,0x5
-asm/non_matchings/code/cutscene_camera/func_80161C20.s,func_80161C20,0x80161C20,0x8B
-asm/non_matchings/code/cutscene_camera/func_80161E4C.s,func_80161E4C,0x80161E4C,0xA0
-asm/non_matchings/code/cutscene_camera/func_801620CC.s,func_801620CC,0x801620CC,0xAC
-asm/non_matchings/code/cutscene_camera/func_8016237C.s,func_8016237C,0x8016237C,0x1A
-asm/non_matchings/code/cutscene_camera/func_801623E4.s,func_801623E4,0x801623E4,0x42
+asm/non_matchings/code/cutscene_camera/CutsceneCamera_Interp_Linear.s,CutsceneCamera_Interp_Linear,0x80161C20,0x8B
+asm/non_matchings/code/cutscene_camera/CutsceneCamera_Interp_Scale.s,CutsceneCamera_Interp_Scale,0x80161E4C,0xA0
+asm/non_matchings/code/cutscene_camera/CutsceneCamera_Interp_Geo.s,CutsceneCamera_Interp_Geo,0x801620CC,0xAC
+asm/non_matchings/code/cutscene_camera/CutsceneCamera_Interp_None.s,CutsceneCamera_Interp_None,0x8016237C,0x1A
+asm/non_matchings/code/cutscene_camera/CutsceneCamera_Interp_Set.s,CutsceneCamera_Interp_Set,0x801623E4,0x42
asm/non_matchings/code/cutscene_camera/func_801624EC.s,func_801624EC,0x801624EC,0x14
-asm/non_matchings/code/cutscene_camera/func_8016253C.s,func_8016253C,0x8016253C,0x120
+asm/non_matchings/code/cutscene_camera/CutsceneCamera_Interp_MultiPointQuadratic.s,CutsceneCamera_Interp_MultiPointQuadratic,0x8016253C,0x120
asm/non_matchings/code/cutscene_camera/func_801629BC.s,func_801629BC,0x801629BC,0x25
-asm/non_matchings/code/cutscene_camera/func_80162A50.s,func_80162A50,0x80162A50,0x16A
+asm/non_matchings/code/cutscene_camera/CutsceneCamera_Interp_MultiPointCubic.s,CutsceneCamera_Interp_MultiPointCubic,0x80162A50,0x16A
asm/non_matchings/code/cutscene_camera/func_80162FF8.s,func_80162FF8,0x80162FF8,0x79
asm/non_matchings/code/cutscene_camera/func_801631DC.s,func_801631DC,0x801631DC,0x56
-asm/non_matchings/code/cutscene_camera/func_80163334.s,func_80163334,0x80163334,0xCB
+asm/non_matchings/code/cutscene_camera/CutsceneCamera_Interp_Unused.s,CutsceneCamera_Interp_Unused,0x80163334,0xCB
asm/non_matchings/code/cutscene_camera/func_80163660.s,func_80163660,0x80163660,0x28
asm/non_matchings/code/z_kaleido_manager/func_80163700.s,func_80163700,0x80163700,0x16
asm/non_matchings/code/z_kaleido_manager/func_80163758.s,func_80163758,0x80163758,0x17
diff --git a/undefined_syms.txt b/undefined_syms.txt
index ecae60b8c..c3b6862a0 100644
--- a/undefined_syms.txt
+++ b/undefined_syms.txt
@@ -196,42 +196,6 @@ D_060005C4 = 0x060005C4;
D_06000A20 = 0x06000A20;
D_06000040 = 0x06000040;
-// ovl_Boss_01
-
-D_06000C44 = 0x06000C44;
-D_06001884 = 0x06001884;
-D_0600C338 = 0x0600C338;
-D_0600C498 = 0x0600C498;
-D_0600C5E0 = 0x0600C5E0;
-D_0600C7A8 = 0x0600C7A8;
-D_0600C7C8 = 0x0600C7C8;
-D_0600C7F8 = 0x0600C7F8;
-D_0600E3E8 = 0x0600E3E8;
-D_0600F0A8 = 0x0600F0A8;
-D_0600FDEC = 0x0600FDEC;
-D_0600FF94 = 0x0600FF94;
-D_06010150 = 0x06010150;
-D_06010980 = 0x06010980;
-D_060124CC = 0x060124CC;
-D_06012B70 = 0x06012B70;
-D_06012D10 = 0x06012D10;
-D_06012EBC = 0x06012EBC;
-D_06013480 = 0x06013480;
-D_0601407C = 0x0601407C;
-D_06014F14 = 0x06014F14;
-D_06015A30 = 0x06015A30;
-D_06016168 = 0x06016168;
-D_060164CC = 0x060164CC;
-D_06018438 = 0x06018438;
-D_06019C10 = 0x06019C10;
-D_0601F6A4 = 0x0601F6A4;
-D_060204AC = 0x060204AC;
-D_060213A8 = 0x060213A8;
-D_060220A0 = 0x060220A0;
-D_06022118 = 0x06022118;
-D_060222D0 = 0x060222D0;
-D_06022550 = 0x06022550;
-
// ovl_Boss_05
D_060006A4 = 0x060006A4;
@@ -539,15 +503,6 @@ D_06006F38 = 0x06006F38;
D_06006FD8 = 0x06006FD8;
D_06009930 = 0x06009930;
-// ovl_En_Takaraya
-
-D_06000968 = 0x06000968;
-D_06001384 = 0x06001384;
-D_06008FC8 = 0x06008FC8;
-D_06009890 = 0x06009890;
-D_0600A280 = 0x0600A280;
-D_0600AD98 = 0x0600AD98;
-
// ovl_En_Wdhand
D_060000F4 = 0x060000F4;
@@ -573,11 +528,6 @@ D_06000030 = 0x06000030;
D_06002068 = 0x06002068;
D_06002188 = 0x06002188;
-// ovl_Obj_Takaraya_Wall
-
-D_06000B70 = 0x06000B70;
-D_06000D60 = 0x06000D60;
-
// segment 0x07
// segment 0x08