diff --git a/assets/xml/objects/gameplay_keep.xml b/assets/xml/objects/gameplay_keep.xml
index 802ac68435..ef31ca7b76 100644
--- a/assets/xml/objects/gameplay_keep.xml
+++ b/assets/xml/objects/gameplay_keep.xml
@@ -838,8 +838,11 @@
-
-
+
+
+
+
+
diff --git a/spec b/spec
index fcfb12e4c5..2bdf82937e 100644
--- a/spec
+++ b/spec
@@ -1285,8 +1285,7 @@ beginseg
name "ovl_En_Bom_Chu"
compress
include "build/src/overlays/actors/ovl_En_Bom_Chu/z_en_bom_chu.o"
- include "build/data/ovl_En_Bom_Chu/ovl_En_Bom_Chu.data.o"
- include "build/data/ovl_En_Bom_Chu/ovl_En_Bom_Chu.reloc.o"
+ include "build/src/overlays/actors/ovl_En_Bom_Chu/ovl_En_Bom_Chu_reloc.o"
endseg
beginseg
diff --git a/src/overlays/actors/ovl_En_Bom_Chu/z_en_bom_chu.c b/src/overlays/actors/ovl_En_Bom_Chu/z_en_bom_chu.c
index 47c25941d2..fc63795dea 100644
--- a/src/overlays/actors/ovl_En_Bom_Chu/z_en_bom_chu.c
+++ b/src/overlays/actors/ovl_En_Bom_Chu/z_en_bom_chu.c
@@ -5,21 +5,26 @@
*/
#include "z_en_bom_chu.h"
+#include "overlays/actors/ovl_En_Bom/z_en_bom.h"
+#include "objects/gameplay_keep/gameplay_keep.h"
#define FLAGS (ACTOR_FLAG_10)
#define THIS ((EnBomChu*)thisx)
+#define BOMBCHU_SCALE 0.01f
+
void EnBomChu_Init(Actor* thisx, GlobalContext* globalCtx);
void EnBomChu_Destroy(Actor* thisx, GlobalContext* globalCtx);
void EnBomChu_Update(Actor* thisx, GlobalContext* globalCtx);
void EnBomChu_Draw(Actor* thisx, GlobalContext* globalCtx);
-void func_808F7868(EnBomChu* this, GlobalContext* globalCtx);
-void func_808F7A84(EnBomChu* this, GlobalContext* globalCtx);
-void func_808F7FA0(EnBomChu* this, GlobalContext* globalCtx);
+void EnBomChu_WaitForRelease(EnBomChu* this, GlobalContext* globalCtx);
+void EnBomChu_SetupMove(EnBomChu* this);
+void EnBomChu_Move(EnBomChu* this, GlobalContext* globalCtx);
+void EnBomChu_Explode(EnBomChu* this, GlobalContext* globalCtx);
+void EnBomChu_WaitForDeath(EnBomChu* this, GlobalContext* globalCtx);
-#if 0
const ActorInit En_Bom_Chu_InitVars = {
ACTOR_EN_BOM_CHU,
ACTORCAT_EXPLOSIVES,
@@ -32,50 +37,536 @@ const ActorInit En_Bom_Chu_InitVars = {
(ActorFunc)EnBomChu_Draw,
};
-// static ColliderSphereInit sSphereInit = {
-static ColliderSphereInit D_808F88E0 = {
- { COLTYPE_NONE, AT_NONE, AC_ON | AC_TYPE_PLAYER, OC1_ON | OC1_TYPE_1 | OC1_TYPE_2, OC2_TYPE_2, COLSHAPE_SPHERE, },
- { ELEMTYPE_UNK0, { 0x00000000, 0x00, 0x00 }, { 0xF7CFFFFF, 0x00, 0x00 }, TOUCH_NONE | TOUCH_SFX_NORMAL, BUMP_ON, OCELEM_ON, },
+static ColliderSphereInit sSphereInit = {
+ {
+ COLTYPE_NONE,
+ AT_NONE,
+ AC_ON | AC_TYPE_PLAYER,
+ OC1_ON | OC1_TYPE_1 | OC1_TYPE_2,
+ OC2_TYPE_2,
+ COLSHAPE_SPHERE,
+ },
+ {
+ ELEMTYPE_UNK0,
+ { 0x00000000, 0x00, 0x00 },
+ { 0xF7CFFFFF, 0x00, 0x00 },
+ TOUCH_NONE | TOUCH_SFX_NORMAL,
+ BUMP_ON,
+ OCELEM_ON,
+ },
{ 1, { { 0, 0, 0 }, 13 }, 100 },
};
-// static InitChainEntry sInitChain[] = {
-static InitChainEntry D_808F890C[] = {
+static InitChainEntry sInitChain[] = {
ICHAIN_U8(targetMode, 2, ICHAIN_CONTINUE),
- ICHAIN_VEC3F_DIV1000(scale, 10, ICHAIN_STOP),
+ ICHAIN_VEC3F_DIV1000(scale, 1000 * BOMBCHU_SCALE, ICHAIN_STOP),
};
-#endif
+static EffectBlureInit2 sBlureInit = {
+ 0, 0, 0, { 250, 0, 0, 250 }, { 200, 0, 0, 130 }, { 150, 0, 0, 100 }, { 100, 0, 0, 50 }, 16,
+ 0, 0, 0, { 0, 0, 0, 0 }, { 0, 0, 0, 0 },
+};
-extern ColliderSphereInit D_808F88E0;
-extern InitChainEntry D_808F890C[];
+void EnBomChu_Init(Actor* thisx, GlobalContext* globalCtx) {
+ EnBomChu* this = THIS;
-#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Bom_Chu/EnBomChu_Init.s")
+ Actor_ProcessInitChain(&this->actor, sInitChain);
+ Collider_InitAndSetSphere(globalCtx, &this->collider, &this->actor, &sSphereInit);
+ this->collider.dim.worldSphere.radius = sSphereInit.dim.modelSphere.radius;
-#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Bom_Chu/EnBomChu_Destroy.s")
+ Effect_Add(globalCtx, &this->blure1Index, EFFECT_BLURE2, 0, 0, &sBlureInit);
+ Effect_Add(globalCtx, &this->blure2Index, EFFECT_BLURE2, 0, 0, &sBlureInit);
-#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Bom_Chu/func_808F75D0.s")
+ this->timer = 120;
+ this->actor.room = -1;
+ this->shouldTimerCountDown = true;
+ this->unk_174 = 0.0f;
+ this->actionFunc = EnBomChu_WaitForRelease;
+}
-#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Bom_Chu/func_808F77E4.s")
+void EnBomChu_Destroy(Actor* thisx, GlobalContext* globalCtx) {
+ EnBomChu* this = THIS;
-#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Bom_Chu/func_808F7868.s")
+ Effect_Destroy(globalCtx, this->blure1Index);
+ Effect_Destroy(globalCtx, this->blure2Index);
+ Collider_DestroySphere(globalCtx, &this->collider);
+}
-#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Bom_Chu/func_808F7944.s")
+/**
+ * Returns true if floorPoly is valid for the Bombchu to move on, false otherwise.
+ */
+s32 EnBomChu_UpdateFloorPoly(EnBomChu* this, CollisionPoly* floorPoly, GlobalContext* globalCtx) {
+ Vec3f normal;
+ Vec3f vec;
+ f32 angle;
+ f32 magnitude;
+ f32 normDotUp;
-#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Bom_Chu/func_808F79D4.s")
+ this->actor.floorPoly = floorPoly;
-#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Bom_Chu/func_808F7A84.s")
+ // This NULL check means if the player releases a Bombchu with no collision
+ // below them, the game will not crash, unlike OoT.
+ if (floorPoly != NULL) {
+ normal.x = COLPOLY_GET_NORMAL(floorPoly->normal.x);
+ normal.y = COLPOLY_GET_NORMAL(floorPoly->normal.y);
+ normal.z = COLPOLY_GET_NORMAL(floorPoly->normal.z);
+ } else {
+ EnBomChu_Explode(this, globalCtx);
+ return false;
+ }
-#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Bom_Chu/func_808F7E74.s")
+ normDotUp = DOTXYZ(normal, this->axisUp);
-#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Bom_Chu/func_808F7FA0.s")
+ if (fabsf(normDotUp) >= 0.999f) {
+ return false;
+ }
-#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Bom_Chu/func_808F7FD0.s")
+ angle = func_80086C48(normDotUp);
+ if (angle < 0.001f) {
+ return false;
+ }
-#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Bom_Chu/func_808F8080.s")
+ Math3D_CrossProduct(&this->axisUp, &normal, &vec);
-#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Bom_Chu/func_808F818C.s")
+ magnitude = Math3D_Vec3fMagnitude(&vec);
-#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Bom_Chu/EnBomChu_Update.s")
+ if (magnitude < 0.001f) {
+ EnBomChu_Explode(this, globalCtx);
+ return false;
+ }
-#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Bom_Chu/EnBomChu_Draw.s")
+ Math_Vec3f_Scale(&vec, 1.0f / magnitude);
+ Matrix_InsertRotationAroundUnitVector_f(angle, &vec, MTXMODE_NEW);
+ Matrix_MultiplyVector3fByState(&this->axisLeft, &vec);
+ Math_Vec3f_Copy(&this->axisLeft, &vec);
+ Math3D_CrossProduct(&this->axisLeft, &normal, &this->axisForwards);
+
+ magnitude = Math3D_Vec3fMagnitude(&this->axisForwards);
+ if (magnitude < 0.001f) {
+ EnBomChu_Explode(this, globalCtx);
+ return false;
+ }
+
+ Math_Vec3f_Scale(&this->axisForwards, 1.0f / magnitude);
+ Math_Vec3f_Copy(&this->axisUp, &normal);
+ return true;
+}
+
+void EnBomChu_UpdateRotation(EnBomChu* this) {
+ MtxF mf;
+
+ mf.xx = this->axisLeft.x;
+ mf.xy = this->axisLeft.y;
+ mf.xz = this->axisLeft.z;
+
+ mf.yx = this->axisUp.x;
+ mf.yy = this->axisUp.y;
+ mf.yz = this->axisUp.z;
+
+ mf.zx = this->axisForwards.x;
+ mf.zy = this->axisForwards.y;
+ mf.zz = this->axisForwards.z;
+
+ func_8018219C(&mf, &this->actor.world.rot, 0);
+ this->actor.world.rot.x = -this->actor.world.rot.x;
+}
+
+void EnBomChu_WaitForRelease(EnBomChu* this, GlobalContext* globalCtx) {
+ Player* player;
+
+ if (this->timer == 0) {
+ EnBomChu_Explode(this, globalCtx);
+ } else if (Actor_HasNoParent(&this->actor, globalCtx)) {
+ player = GET_PLAYER(globalCtx);
+ Math_Vec3f_Copy(&this->actor.world.pos, &player->actor.world.pos);
+ Actor_UpdateBgCheckInfo(globalCtx, &this->actor, 0.0f, 0.0f, 0.0f, 4);
+
+ this->actor.shape.rot.y = player->actor.shape.rot.y;
+ this->actor.flags |= ACTOR_FLAG_1;
+ func_800B8EF4(globalCtx, &this->actor);
+
+ this->isMoving = true;
+ this->actor.speedXZ = 8.0f;
+ this->movingSpeed = 8.0f;
+ EnBomChu_SetupMove(this);
+ }
+}
+
+s32 EnBomChu_IsOnCollisionPoly(GlobalContext* globalCtx, Vec3f* posA, Vec3f* posB, Vec3f* posResult,
+ CollisionPoly** poly, s32* bgId) {
+ if ((BgCheck_EntityLineTest1(&globalCtx->colCtx, posA, posB, posResult, poly, true, true, true, true, bgId)) &&
+ (!(func_800C9A4C(&globalCtx->colCtx, *poly, *bgId) & 0x30))) {
+ return true;
+ }
+
+ return false;
+}
+
+void EnBomChu_SetupMove(EnBomChu* this) {
+ func_800BE3D0(&this->actor, this->actor.shape.rot.y, &this->actor.shape.rot);
+
+ Matrix_RotateY(this->actor.shape.rot.y, MTXMODE_NEW);
+ Matrix_InsertXRotation_s(this->actor.shape.rot.x, MTXMODE_APPLY);
+ Matrix_InsertZRotation_s(this->actor.shape.rot.z, MTXMODE_APPLY);
+
+ Matrix_GetStateTranslationAndScaledY(1.0f, &this->axisUp);
+ Matrix_GetStateTranslationAndScaledZ(1.0f, &this->axisForwards);
+ Matrix_GetStateTranslationAndScaledX(1.0f, &this->axisLeft);
+
+ this->actor.world.rot.x = -this->actor.shape.rot.x;
+ this->actor.world.rot.y = this->actor.shape.rot.y;
+ this->actor.world.rot.z = this->actor.shape.rot.z;
+ this->actionFunc = EnBomChu_Move;
+}
+
+void EnBomChu_Move(EnBomChu* this, GlobalContext* globalCtx) {
+ CollisionPoly* polySide = NULL;
+ CollisionPoly* polyUpDown = NULL;
+ s32 bgIdSide;
+ s32 bgIdUpDown;
+ s32 i;
+ s32 isFloorPolyValid;
+ f32 lineLength;
+ Vec3f posA;
+ Vec3f posB;
+ Vec3f posSide;
+ Vec3f posUpDown;
+
+ bgIdUpDown = bgIdSide = BGCHECK_SCENE;
+ isFloorPolyValid = false;
+
+ this->actor.speedXZ = this->movingSpeed;
+ lineLength = 2.0f * this->movingSpeed;
+
+ if ((this->timer == 0) || (this->collider.base.acFlags & AC_HIT) || (this->collider.base.ocFlags1 & OC1_HIT)) {
+ EnBomChu_Explode(this, globalCtx);
+ return;
+ }
+
+ posA.x = this->actor.world.pos.x + (this->axisUp.x * 2.0f);
+ posA.y = this->actor.world.pos.y + (this->axisUp.y * 2.0f);
+ posA.z = this->actor.world.pos.z + (this->axisUp.z * 2.0f);
+
+ posB.x = this->actor.world.pos.x - (this->axisUp.x * 4.0f);
+ posB.y = this->actor.world.pos.y - (this->axisUp.y * 4.0f);
+ posB.z = this->actor.world.pos.z - (this->axisUp.z * 4.0f);
+
+ if (EnBomChu_IsOnCollisionPoly(globalCtx, &posA, &posB, &posUpDown, &polyUpDown, &bgIdUpDown)) {
+ // forwards
+ posB.x = (this->axisForwards.x * lineLength) + posA.x;
+ posB.y = (this->axisForwards.y * lineLength) + posA.y;
+ posB.z = (this->axisForwards.z * lineLength) + posA.z;
+
+ if (EnBomChu_IsOnCollisionPoly(globalCtx, &posA, &posB, &posSide, &polySide, &bgIdSide)) {
+ isFloorPolyValid = EnBomChu_UpdateFloorPoly(this, polySide, globalCtx);
+ Math_Vec3f_Copy(&this->actor.world.pos, &posSide);
+ this->actor.floorBgId = bgIdSide;
+ this->actor.speedXZ = 0.0f;
+ } else {
+ if (this->actor.floorPoly != polyUpDown) {
+ isFloorPolyValid = EnBomChu_UpdateFloorPoly(this, polyUpDown, globalCtx);
+ }
+
+ Math_Vec3f_Copy(&this->actor.world.pos, &posUpDown);
+ this->actor.floorBgId = bgIdUpDown;
+ }
+ } else {
+ this->actor.speedXZ = 0.0f;
+ lineLength *= 3.0f;
+ Math_Vec3f_Copy(&posA, &posB);
+
+ for (i = 0; i < 3; i++) {
+ if (i == 0) {
+ // backwards
+ posB.x = posA.x - (this->axisForwards.x * lineLength);
+ posB.y = posA.y - (this->axisForwards.y * lineLength);
+ posB.z = posA.z - (this->axisForwards.z * lineLength);
+ } else if (i == 1) {
+ // left
+ posB.x = posA.x + (this->axisLeft.x * lineLength);
+ posB.y = posA.y + (this->axisLeft.y * lineLength);
+ posB.z = posA.z + (this->axisLeft.z * lineLength);
+ } else {
+ // right
+ posB.x = posA.x - (this->axisLeft.x * lineLength);
+ posB.y = posA.y - (this->axisLeft.y * lineLength);
+ posB.z = posA.z - (this->axisLeft.z * lineLength);
+ }
+
+ if (EnBomChu_IsOnCollisionPoly(globalCtx, &posA, &posB, &posSide, &polySide, &bgIdSide)) {
+ isFloorPolyValid = EnBomChu_UpdateFloorPoly(this, polySide, globalCtx);
+ Math_Vec3f_Copy(&this->actor.world.pos, &posSide);
+ this->actor.floorBgId = bgIdSide;
+ break;
+ }
+ }
+
+ if (i == 3) {
+ // no collision nearby
+ EnBomChu_Explode(this, globalCtx);
+ }
+ }
+
+ if (isFloorPolyValid) {
+ EnBomChu_UpdateRotation(this);
+ this->actor.shape.rot.x = -this->actor.world.rot.x;
+ this->actor.shape.rot.y = this->actor.world.rot.y;
+ this->actor.shape.rot.z = this->actor.world.rot.z;
+ }
+
+ if (this->isMoving) {
+ func_800B8F98(&this->actor, NA_SE_IT_BOMBCHU_MOVE - SFX_FLAG);
+ }
+
+ if (this->actor.speedXZ != 0.0f) {
+ this->movingSpeed = this->actor.speedXZ;
+ }
+}
+
+void EnBomChu_Explode(EnBomChu* this, GlobalContext* globalCtx) {
+ EnBom* bomb;
+ s32 i;
+
+ bomb = (EnBom*)Actor_Spawn(&globalCtx->actorCtx, globalCtx, ACTOR_EN_BOM, this->actor.world.pos.x,
+ this->actor.world.pos.y, this->actor.world.pos.z, 0, 0, 0, 0);
+
+ this->shouldTimerCountDown = true;
+ this->isMoving = false;
+
+ if (bomb != NULL) {
+ bomb->timer = 0;
+ }
+
+ this->timer = 1;
+ this->actor.speedXZ = 0.0f;
+
+ if (this->actor.depthInWater > 0.0f) {
+ for (i = 0; i < 40; i++) {
+ EffectSsBubble_Spawn(globalCtx, &this->actor.world.pos, 1.0f, 5.0f, 30.0f, 0.25f);
+ }
+ }
+
+ this->actor.draw = NULL;
+ this->actionFunc = EnBomChu_WaitForDeath;
+}
+
+void EnBomChu_WaitForDeath(EnBomChu* this, GlobalContext* globalCtx) {
+ if (this->timer == 0) {
+ Actor_MarkForDeath(&this->actor);
+ }
+}
+
+/**
+ * Transform coordinates from actor coordinate space to world space, according to current orientation.
+ * `offset` is expected to already be at world scale.
+ */
+void EnBomChu_ActorCoordsToWorld(EnBomChu* this, Vec3f* offset, Vec3f* pos) {
+ f32 x = offset->x + this->visualJitter;
+
+ pos->x = this->actor.world.pos.x + (this->axisLeft.x * x) + (this->axisUp.x * offset->y) +
+ (this->axisForwards.x * offset->z);
+ pos->y = this->actor.world.pos.y + (this->axisLeft.y * x) + (this->axisUp.y * offset->y) +
+ (this->axisForwards.y * offset->z);
+ pos->z = this->actor.world.pos.z + (this->axisLeft.z * x) + (this->axisUp.z * offset->y) +
+ (this->axisForwards.z * offset->z);
+}
+
+void EnBomChu_SpawnRipplesAndSplashes(EnBomChu* this, GlobalContext* globalCtx, f32 y, s32 spawnExtraRipples) {
+ s32 pad;
+ Vec3f pos;
+
+ pos.x = this->actor.world.pos.x;
+ pos.y = y;
+ pos.z = this->actor.world.pos.z;
+
+ EffectSsGRipple_Spawn(globalCtx, &pos, 70, 500, 0);
+
+ if (spawnExtraRipples) {
+ EffectSsGRipple_Spawn(globalCtx, &pos, 70, 500, 4);
+ EffectSsGRipple_Spawn(globalCtx, &pos, 70, 500, 8);
+ } else {
+ pos.x -= this->axisForwards.x * 10.0f;
+ pos.z -= this->axisForwards.z * 10.0f;
+ }
+
+ pos.y += 5.0f;
+ EffectSsGSplash_Spawn(globalCtx, &pos, NULL, NULL, 1, 450);
+}
+
+void EnBomChu_HandleNonSceneCollision(EnBomChu* this, GlobalContext* globalCtx) {
+ Vec3f originalWorldPos;
+ Vec3f posA;
+ Vec3f posB;
+ Vec3f originalAxisUp;
+ s16 yaw;
+ f32 sin;
+ f32 cos;
+ f32 tempX;
+ CollisionPoly* poly = NULL;
+ s32 bgId = BGCHECK_SCENE;
+ s32 isFloorPolyValid;
+
+ Math_Vec3f_Copy(&originalWorldPos, &this->actor.world.pos);
+ Math_Vec3f_Copy(&originalAxisUp, &this->axisUp);
+ yaw = this->actor.shape.rot.y;
+ BgCheck2_UpdateActorAttachedToMesh(&globalCtx->colCtx, this->actor.floorBgId, &this->actor);
+
+ if (yaw != this->actor.shape.rot.y) {
+ yaw = this->actor.shape.rot.y - yaw;
+
+ sin = Math_SinS(yaw);
+ cos = Math_CosS(yaw);
+
+ tempX = this->axisForwards.x;
+ this->axisForwards.x = (sin * this->axisForwards.z) + (cos * tempX);
+ this->axisForwards.z = (cos * this->axisForwards.z) - (sin * tempX);
+
+ tempX = this->axisUp.x;
+ this->axisUp.x = (sin * this->axisUp.z) + (cos * tempX);
+ this->axisUp.z = (cos * this->axisUp.z) - (sin * tempX);
+
+ tempX = this->axisLeft.x;
+ this->axisLeft.x = (sin * this->axisLeft.z) + (cos * tempX);
+ this->axisLeft.z = (cos * this->axisLeft.z) - (sin * tempX);
+ }
+
+ posA.x = originalWorldPos.x + (2.0f * originalAxisUp.x);
+ posA.y = originalWorldPos.y + (2.0f * originalAxisUp.y);
+ posA.z = originalWorldPos.z + (2.0f * originalAxisUp.z);
+
+ posB.x = this->actor.world.pos.x + (2.0f * this->axisUp.x);
+ posB.y = this->actor.world.pos.y + (2.0f * this->axisUp.y);
+ posB.z = this->actor.world.pos.z + (2.0f * this->axisUp.z);
+
+ if (EnBomChu_IsOnCollisionPoly(globalCtx, &posA, &posB, &originalWorldPos, &poly, &bgId)) {
+ isFloorPolyValid = EnBomChu_UpdateFloorPoly(this, poly, globalCtx);
+ Math_Vec3f_Copy(&this->actor.world.pos, &originalWorldPos);
+ this->actor.floorBgId = bgId;
+ this->actor.speedXZ = 0.0f;
+
+ if (isFloorPolyValid) {
+ EnBomChu_UpdateRotation(this);
+ this->actor.shape.rot.x = -this->actor.world.rot.x;
+ this->actor.shape.rot.y = this->actor.world.rot.y;
+ this->actor.shape.rot.z = this->actor.world.rot.z;
+ }
+ }
+}
+
+void EnBomChu_Update(Actor* thisx, GlobalContext* globalCtx) {
+ static Vec3f sBlureP1Offset = { 0.0f, 7.0f, -6.0f };
+ static Vec3f sBlureP2LeftOffset = { 12.0f, 0.0f, -5.0f };
+ static Vec3f sBlureP2RightOffset = { -12.0f, 0.0f, -5.0f };
+ s32 pad;
+ EnBomChu* this = THIS;
+ Vec3f blureP1;
+ Vec3f blureP2;
+ WaterBox* waterBox;
+ f32 waterY;
+
+ if (this->actor.floorBgId != BGCHECK_SCENE) {
+ EnBomChu_HandleNonSceneCollision(this, globalCtx);
+ }
+
+ if (this->shouldTimerCountDown) {
+ this->timer--;
+ }
+
+ this->actionFunc(this, globalCtx);
+
+ if ((this->actionFunc != EnBomChu_WaitForDeath) &&
+ (SurfaceType_IsWallDamage(&globalCtx->colCtx, this->actor.floorPoly, this->actor.floorBgId))) {
+ EnBomChu_Explode(this, globalCtx);
+ return;
+ }
+
+ Actor_MoveWithoutGravity(&this->actor);
+
+ this->collider.dim.worldSphere.center.x = this->actor.world.pos.x;
+ this->collider.dim.worldSphere.center.y = this->actor.world.pos.y;
+ this->collider.dim.worldSphere.center.z = this->actor.world.pos.z;
+
+ CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, &this->collider.base);
+ if (this->actionFunc != EnBomChu_WaitForRelease) {
+ CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->collider.base);
+ }
+
+ this->actor.focus.pos.x = this->actor.world.pos.x + (20.0f * this->axisUp.x);
+ this->actor.focus.pos.y = this->actor.world.pos.y + (20.0f * this->axisUp.y);
+ this->actor.focus.pos.z = this->actor.world.pos.z + (20.0f * this->axisUp.z);
+
+ if (this->isMoving) {
+ this->visualJitter =
+ (5.0f + (Rand_ZeroOne() * 3.0f)) * Math_SinS((((s32)(Rand_ZeroOne() * 512.0f) + 0x3000) * this->timer));
+ EnBomChu_ActorCoordsToWorld(this, &sBlureP1Offset, &blureP1);
+
+ EnBomChu_ActorCoordsToWorld(this, &sBlureP2LeftOffset, &blureP2);
+ EffectBlure_AddVertex(Effect_GetByIndex(this->blure1Index), &blureP1, &blureP2);
+
+ EnBomChu_ActorCoordsToWorld(this, &sBlureP2RightOffset, &blureP2);
+ EffectBlure_AddVertex(Effect_GetByIndex(this->blure2Index), &blureP1, &blureP2);
+
+ waterY = this->actor.world.pos.y;
+
+ if (WaterBox_GetSurface1(globalCtx, &globalCtx->colCtx, this->actor.world.pos.x, this->actor.world.pos.z,
+ &waterY, &waterBox)) {
+ this->actor.depthInWater = waterY - this->actor.world.pos.y;
+
+ if (this->actor.depthInWater < 0.0f) {
+ if (this->actor.bgCheckFlags & 0x20) {
+ EnBomChu_SpawnRipplesAndSplashes(this, globalCtx, waterY, true);
+ }
+
+ this->actor.bgCheckFlags &= ~0x20;
+ return;
+ }
+
+ if (!(this->actor.bgCheckFlags & 0x20) && (this->timer != 120)) {
+ EnBomChu_SpawnRipplesAndSplashes(this, globalCtx, waterY, true);
+ } else {
+ EffectSsBubble_Spawn(globalCtx, &this->actor.world.pos, 0.0f, 3.0f, 15.0f, 0.25f);
+ }
+
+ this->actor.bgCheckFlags |= 0x20;
+ } else {
+ this->actor.bgCheckFlags &= ~0x20;
+ this->actor.depthInWater = BGCHECK_Y_MIN;
+ }
+ }
+}
+
+void EnBomChu_Draw(Actor* thisx, GlobalContext* globalCtx) {
+ EnBomChu* this = THIS;
+ f32 colorIntensity;
+ s32 blinkHalfPeriod;
+ s32 blinkTime;
+
+ OPEN_DISPS(globalCtx->state.gfxCtx);
+
+ func_8012C28C(globalCtx->state.gfxCtx);
+ func_800B8050(&this->actor, globalCtx, 0);
+
+ if (this->timer >= 40) {
+ blinkTime = this->timer % 20;
+ blinkHalfPeriod = 10;
+ } else if (this->timer >= 10) {
+ blinkTime = this->timer % 10;
+ blinkHalfPeriod = 5;
+ } else {
+ blinkTime = this->timer & 1;
+ blinkHalfPeriod = 1;
+ }
+
+ if (blinkTime > blinkHalfPeriod) {
+ blinkTime = 2 * blinkHalfPeriod - blinkTime;
+ }
+
+ colorIntensity = blinkTime / (f32)blinkHalfPeriod;
+ gDPSetEnvColor(POLY_OPA_DISP++, (s32)(colorIntensity * 209.0f) + 9, (s32)(colorIntensity * 34.0f) + 9,
+ (s32)(colorIntensity * -35.0f) + 35, 255);
+ Matrix_InsertTranslation(this->visualJitter * (1.0f / BOMBCHU_SCALE), 0.0f, 0.0f, MTXMODE_APPLY);
+ gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx), G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW);
+ gSPDisplayList(POLY_OPA_DISP++, gBombchuDL);
+
+ CLOSE_DISPS(globalCtx->state.gfxCtx);
+}
diff --git a/src/overlays/actors/ovl_En_Bom_Chu/z_en_bom_chu.h b/src/overlays/actors/ovl_En_Bom_Chu/z_en_bom_chu.h
index 910ad51745..30a3dab9ae 100644
--- a/src/overlays/actors/ovl_En_Bom_Chu/z_en_bom_chu.h
+++ b/src/overlays/actors/ovl_En_Bom_Chu/z_en_bom_chu.h
@@ -8,9 +8,21 @@ struct EnBomChu;
typedef void (*EnBomChuActionFunc)(struct EnBomChu*, GlobalContext*);
typedef struct EnBomChu {
- /* 0x0000 */ Actor actor;
- /* 0x0144 */ EnBomChuActionFunc actionFunc;
- /* 0x0148 */ char unk_148[0x98];
+ /* 0x000 */ Actor actor;
+ /* 0x144 */ EnBomChuActionFunc actionFunc;
+ /* 0x148 */ u8 shouldTimerCountDown; // the actor always sets this to true
+ /* 0x149 */ u8 isMoving;
+ /* 0x14A */ s16 timer;
+ /* 0x14C */ Vec3f axisForwards;
+ /* 0x158 */ Vec3f axisUp;
+ /* 0x164 */ Vec3f axisLeft;
+ /* 0x170 */ f32 visualJitter;
+ /* 0x174 */ f32 unk_174; // set but never used
+ /* 0x178 */ UNK_TYPE1 unk_178[0x4];
+ /* 0x17C */ f32 movingSpeed;
+ /* 0x180 */ s32 blure1Index;
+ /* 0x184 */ s32 blure2Index;
+ /* 0x188 */ ColliderSphere collider;
} EnBomChu; // size = 0x1E0
extern const ActorInit En_Bom_Chu_InitVars;
diff --git a/tools/disasm/functions.txt b/tools/disasm/functions.txt
index 0cc7d3933d..b78680dd4c 100644
--- a/tools/disasm/functions.txt
+++ b/tools/disasm/functions.txt
@@ -6749,17 +6749,17 @@
0x808F69B4:("EnIn_Draw",),
0x808F74B0:("EnBomChu_Init",),
0x808F7580:("EnBomChu_Destroy",),
- 0x808F75D0:("func_808F75D0",),
- 0x808F77E4:("func_808F77E4",),
- 0x808F7868:("func_808F7868",),
- 0x808F7944:("func_808F7944",),
- 0x808F79D4:("func_808F79D4",),
- 0x808F7A84:("func_808F7A84",),
- 0x808F7E74:("func_808F7E74",),
- 0x808F7FA0:("func_808F7FA0",),
- 0x808F7FD0:("func_808F7FD0",),
- 0x808F8080:("func_808F8080",),
- 0x808F818C:("func_808F818C",),
+ 0x808F75D0:("EnBomChu_UpdateFloorPoly",),
+ 0x808F77E4:("EnBomChu_UpdateRotation",),
+ 0x808F7868:("EnBomChu_WaitForRelease",),
+ 0x808F7944:("EnBomChu_IsOnCollisionPoly",),
+ 0x808F79D4:("EnBomChu_SetupMove",),
+ 0x808F7A84:("EnBomChu_Move",),
+ 0x808F7E74:("EnBomChu_Explode",),
+ 0x808F7FA0:("EnBomChu_WaitForDeath",),
+ 0x808F7FD0:("EnBomChu_ActorCoordsToWorld",),
+ 0x808F8080:("EnBomChu_SpawnRipplesAndSplashes",),
+ 0x808F818C:("EnBomChu_HandleNonSceneCollision",),
0x808F83B8:("EnBomChu_Update",),
0x808F8714:("EnBomChu_Draw",),
0x808F8AA0:("func_808F8AA0",),