diff --git a/assets/xml/objects/object_boss02.xml b/assets/xml/objects/object_boss02.xml
index 8a80a57f25..0f2fdd0529 100644
--- a/assets/xml/objects/object_boss02.xml
+++ b/assets/xml/objects/object_boss02.xml
@@ -36,13 +36,13 @@
-
-
-
+
+
+
-
-
-
+
+
+
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 393c882328..02d9cfc4c9 100644
--- a/src/overlays/actors/ovl_Boss_02/z_boss_02.c
+++ b/src/overlays/actors/ovl_Boss_02/z_boss_02.c
@@ -9,6 +9,7 @@
#include "z64rumble.h"
#include "z64shrink_window.h"
#include "overlays/actors/ovl_Door_Warp1/z_door_warp1.h"
+#include "overlays/actors/ovl_En_Tanron5/z_en_tanron5.h"
#include "overlays/actors/ovl_Item_B_Heart/z_item_b_heart.h"
#include "objects/gameplay_keep/gameplay_keep.h"
@@ -41,7 +42,7 @@ Boss02* sBlueTwinmold;
Boss02* sTwinmoldStatic;
u8 sMusicStartTimer;
DoorWarp1* sBlueWarp;
-TwinmoldEffect sEffects[150];
+TwinmoldEffect sEffects[TWINMOLD_EFFECT_COUNT];
static DamageTable sBlueTwinmoldDamageTable = {
/* Deku Nut */ DMG_ENTRY(0, 0x0),
@@ -125,7 +126,11 @@ ActorInit Boss_02_InitVars = {
(ActorFunc)Boss02_Twinmold_Draw,
};
-f32 D_809DF5B0 = 1.0f;
+/**
+ * Multiplies the scale differently depending on whether the player is wearing the Giant's Mask or not.
+ * When the player is wearing the Giant's Mask, this value is smaller to make the player seem larger.
+ */
+static f32 sGiantModeScaleFactor = 1.0f;
s16 D_809DF5B4[] = {
0, 195, 190, 185, 180, 175, 170, 165, 160, 155, 150, 145, 140, 135, 130, 125, 120, 115, 110, 105, 100, 95, 90, 0,
@@ -482,7 +487,7 @@ void func_809DA24C(PlayState* play) {
void Boss02_SpawnEffectSand(TwinmoldEffect* effects, Vec3f* pos, f32 scale) {
s16 i;
- for (i = 0; i < ARRAY_COUNT(sEffects); i++, effects++) {
+ for (i = 0; i < TWINMOLD_EFFECT_COUNT; i++, effects++) {
if (effects->type == TWINMOLD_EFFECT_NONE) {
effects->type = TWINMOLD_EFFECT_SAND;
effects->pos = *pos;
@@ -503,7 +508,7 @@ void Boss02_SpawnEffectSand(TwinmoldEffect* effects, Vec3f* pos, f32 scale) {
void Boss02_SpawnEffectFragment(TwinmoldEffect* effects, Vec3f* pos) {
s16 i;
- for (i = 0; i < ARRAY_COUNT(sEffects); i++, effects++) {
+ for (i = 0; i < TWINMOLD_EFFECT_COUNT; i++, effects++) {
if (effects->type == TWINMOLD_EFFECT_NONE) {
effects->type = TWINMOLD_EFFECT_FRAGMENT;
effects->pos = *pos;
@@ -525,7 +530,7 @@ void Boss02_SpawnEffectFragment(TwinmoldEffect* effects, Vec3f* pos) {
void Boss02_SpawnEffectFlash(TwinmoldEffect* effects, Vec3f* pos) {
s16 i;
- for (i = 0; i < ARRAY_COUNT(sEffects); i++, effects++) {
+ for (i = 0; i < TWINMOLD_EFFECT_COUNT; i++, effects++) {
if ((effects->type == TWINMOLD_EFFECT_NONE) || (effects->type == TWINMOLD_EFFECT_FRAGMENT)) {
effects->type = TWINMOLD_EFFECT_FLASH;
effects->pos = *pos;
@@ -543,7 +548,7 @@ void func_809DA50C(s32 arg0, ColliderJntSph* collider, Vec3f* arg2) {
collider->elements[arg0].dim.worldSphere.center.y = arg2->y;
collider->elements[arg0].dim.worldSphere.center.z = arg2->z;
collider->elements[arg0].dim.worldSphere.radius =
- collider->elements[arg0].dim.modelSphere.radius * collider->elements[arg0].dim.scale * D_809DF5B0;
+ collider->elements[arg0].dim.modelSphere.radius * collider->elements[arg0].dim.scale * sGiantModeScaleFactor;
}
void Boss02_Init(Actor* thisx, PlayState* play) {
@@ -551,7 +556,8 @@ void Boss02_Init(Actor* thisx, PlayState* play) {
s32 i;
s32 pad[2];
- if (CHECK_WEEKEVENTREG(WEEKEVENTREG_CLEARED_STONE_TOWER_TEMPLE) && (this->actor.params == TWINMOLD_RED)) {
+ 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);
Actor_Spawn(&play->actorCtx, play, ACTOR_ITEM_B_HEART, 0.0f, 30.0f, -150.0f, 0, 1, 0, BHEART_PARAM_NORMAL);
@@ -560,7 +566,7 @@ void Boss02_Init(Actor* thisx, PlayState* play) {
this->actor.targetMode = 10;
this->subCamUp.z = this->subCamUp.x = 0.0f;
this->subCamUp.y = 1.0f;
- if (this->actor.params == TWINMOLD_STATIC) {
+ if (TWINMOLD_GET_TYPE(&this->actor) == TWINMOLD_TYPE_STATIC) {
sTwinmoldStatic = this;
play->specialEffects = (void*)sEffects;
this->actor.update = Boss02_Static_Update;
@@ -574,21 +580,22 @@ void Boss02_Init(Actor* thisx, PlayState* play) {
this->unk_1D20 = 1;
}
R_MAGIC_CONSUME_TIMER_GIANTS_MASK = KREG(14) + 20;
- this->unk_01AC = 1.0f;
- Actor_SpawnAsChild(&play->actorCtx, &this->actor, play, ACTOR_EN_TANRON5, 0.0f, 1000.0f, 0.0f, 0, 0, 0, 0);
- } else if (this->actor.params == TWINMOLD_TAIL) {
+ this->giantModeScaleFactor = 1.0f;
+ Actor_SpawnAsChild(&play->actorCtx, &this->actor, play, ACTOR_EN_TANRON5, 0.0f, 1000.0f, 0.0f, 0, 0, 0,
+ TWINMOLD_PROP_PARAMS(TWINMOLD_PROP_TYPE_STATIC));
+ } else if (TWINMOLD_GET_TYPE(&this->actor) == TWINMOLD_TYPE_TAIL) {
this->actor.update = Boss02_Tail_Update;
this->actor.draw = NULL;
this->actor.hintId = TATL_HINT_ID_TWINMOLD;
} else {
- if (this->actor.params != TWINMOLD_BLUE) {
- this->actor.params = TWINMOLD_RED;
- Actor_Spawn(&play->actorCtx, play, ACTOR_BOSS_02, 0.0f, 0.0f, 0.0f, 0, 0, 0, TWINMOLD_STATIC);
+ if (TWINMOLD_GET_TYPE(&this->actor) != TWINMOLD_TYPE_BLUE) {
+ this->actor.params = TWINMOLD_TYPE_RED;
+ Actor_Spawn(&play->actorCtx, play, ACTOR_BOSS_02, 0.0f, 0.0f, 0.0f, 0, 0, 0, TWINMOLD_TYPE_STATIC);
sRedTwinmold = this;
sBlueTwinmold =
(Boss02*)Actor_Spawn(&play->actorCtx, play, ACTOR_BOSS_02, 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, this->actor.world.rot.z, TWINMOLD_BLUE);
+ this->actor.world.rot.y, this->actor.world.rot.z, TWINMOLD_TYPE_BLUE);
sRedTwinmold->actor.colChkInfo.damageTable = &sRedTwinmoldDamageTable;
sBlueTwinmold->actor.colChkInfo.damageTable = &sBlueTwinmoldDamageTable;
sRedTwinmold->otherTwinmold = sBlueTwinmold;
@@ -621,7 +628,7 @@ void Boss02_Init(Actor* thisx, PlayState* play) {
this->unk_014C = Rand_ZeroFloat(1000.0f);
this->unk_1678 = 22;
Actor_SpawnAsChild(&play->actorCtx, &this->actor, play, ACTOR_BOSS_02, 0.0f, 0.0f, 0.0f, 0, 0, 0,
- TWINMOLD_TAIL);
+ TWINMOLD_TYPE_TAIL);
}
}
@@ -710,10 +717,10 @@ void func_809DAB78(Boss02* this, PlayState* play) {
this->unk_0168 = 2000.0f;
if (this->unk_0195 != 0) {
- this->actor.speed = this->unk_01A8 * D_809DF5B0 * 1.25f;
+ this->actor.speed = this->unk_01A8 * sGiantModeScaleFactor * 1.25f;
this->skelAnime.playSpeed = 2.0f;
} else {
- this->actor.speed = this->unk_01A8 * D_809DF5B0;
+ this->actor.speed = this->unk_01A8 * sGiantModeScaleFactor;
}
Actor_UpdateVelocityWithoutGravity(&this->actor);
@@ -757,7 +764,7 @@ void func_809DAB78(Boss02* this, PlayState* play) {
}
if ((this->unk_0144 < 10) && (otherTwinmold->unk_0144 >= 20)) {
- this->unk_01B0.y = -1000.0f * D_809DF5B0;
+ this->unk_01B0.y = -1000.0f * sGiantModeScaleFactor;
if (sIsInGiantMode) {
this->unk_01B0.y += 3150.0f;
}
@@ -770,28 +777,28 @@ void func_809DAB78(Boss02* this, PlayState* play) {
return;
case 0:
- if ((this->actor.params == 0) && (Rand_ZeroOne() < 0.75f)) {
+ if ((TWINMOLD_GET_TYPE(&this->actor) == TWINMOLD_TYPE_RED) && (Rand_ZeroOne() < 0.75f)) {
this->actor.world.pos.x = player->actor.world.pos.x;
this->actor.world.pos.z = player->actor.world.pos.z;
- this->actor.world.pos.y = player->actor.world.pos.y - (600.0f * D_809DF5B0);
+ this->actor.world.pos.y = player->actor.world.pos.y - (600.0f * sGiantModeScaleFactor);
} else {
- this->actor.world.pos.x = Rand_CenteredFloat(5000.0f * D_809DF5B0);
- this->actor.world.pos.z = Rand_CenteredFloat(5000.0f * D_809DF5B0);
- this->actor.world.pos.y = -500.0f * D_809DF5B0;
+ this->actor.world.pos.x = Rand_CenteredFloat(5000.0f * sGiantModeScaleFactor);
+ this->actor.world.pos.z = Rand_CenteredFloat(5000.0f * sGiantModeScaleFactor);
+ this->actor.world.pos.y = -500.0f * sGiantModeScaleFactor;
if (sIsInGiantMode) {
this->actor.world.pos.y += 3150.0f;
}
}
- if ((fabsf(this->actor.world.pos.x) < (500.0f * D_809DF5B0)) &&
- (fabsf(this->actor.world.pos.z) < (500.0f * D_809DF5B0))) {
+ if ((fabsf(this->actor.world.pos.x) < (500.0f * sGiantModeScaleFactor)) &&
+ (fabsf(this->actor.world.pos.z) < (500.0f * sGiantModeScaleFactor))) {
return;
}
case 100:
this->actor.shape.rot.x = 0x4000;
this->unk_01B0.x = this->actor.world.pos.x;
- this->unk_01B0.y = this->actor.world.pos.y + (1000.0f * D_809DF5B0);
+ this->unk_01B0.y = this->actor.world.pos.y + (1000.0f * sGiantModeScaleFactor);
this->unk_01B0.z = this->actor.world.pos.z;
this->unk_0146[0] = 100;
this->unk_0144 = 1;
@@ -803,14 +810,15 @@ void func_809DAB78(Boss02* this, PlayState* play) {
this->unk_0146[0] = 150;
} else {
this->unk_0144 = 2;
- this->unk_01B0.x = Rand_CenteredFloat(3000.0f * D_809DF5B0);
- this->unk_01B0.z = Rand_CenteredFloat(3000.0f * D_809DF5B0);
- if ((fabsf(this->unk_01B0.x) < (500.0f * D_809DF5B0)) &&
- (fabsf(this->unk_01B0.z) < (500.0f * D_809DF5B0))) {
+ this->unk_01B0.x = Rand_CenteredFloat(3000.0f * sGiantModeScaleFactor);
+ this->unk_01B0.z = Rand_CenteredFloat(3000.0f * sGiantModeScaleFactor);
+ if ((fabsf(this->unk_01B0.x) < (500.0f * sGiantModeScaleFactor)) &&
+ (fabsf(this->unk_01B0.z) < (500.0f * sGiantModeScaleFactor))) {
this->unk_01B0.x = 500.0f;
this->unk_01B0.z = 500.0f;
}
- this->unk_01B0.y = Rand_ZeroFloat(800.0f * D_809DF5B0) + (200.0f * D_809DF5B0);
+ this->unk_01B0.y =
+ Rand_ZeroFloat(800.0f * sGiantModeScaleFactor) + (200.0f * sGiantModeScaleFactor);
if (sIsInGiantMode) {
this->unk_01B0.y += 3150.0f;
}
@@ -825,9 +833,9 @@ void func_809DAB78(Boss02* this, PlayState* play) {
} else {
phi_f2 = 500.0f;
}
- if (sqrtf(SQ(spCC) + SQ(spC8) + SQ(spC4)) < (phi_f2 * D_809DF5B0)) {
+ if (sqrtf(SQ(spCC) + SQ(spC8) + SQ(spC4)) < (phi_f2 * sGiantModeScaleFactor)) {
this->unk_0144 = 3;
- this->unk_01B0.y = -3000.0f * D_809DF5B0;
+ this->unk_01B0.y = -3000.0f * sGiantModeScaleFactor;
if (sIsInGiantMode) {
this->unk_01B0.y += 3150.0f;
}
@@ -844,16 +852,16 @@ void func_809DAB78(Boss02* this, PlayState* play) {
case 5:
this->unk_01B0.x = player->actor.world.pos.x;
- this->unk_01B0.y = player->actor.world.pos.y + (100.0f * D_809DF5B0);
+ this->unk_01B0.y = player->actor.world.pos.y + (100.0f * sGiantModeScaleFactor);
this->unk_01B0.z = player->actor.world.pos.z;
if (this->unk_0146[0] == 0) {
this->unk_0144 = 3;
- this->unk_01B0.x = Rand_CenteredFloat(500.0f * D_809DF5B0) + this->actor.world.pos.x;
- this->unk_01B0.y = -3000.0f * D_809DF5B0;
+ this->unk_01B0.x = Rand_CenteredFloat(500.0f * sGiantModeScaleFactor) + this->actor.world.pos.x;
+ this->unk_01B0.y = -3000.0f * sGiantModeScaleFactor;
if (sIsInGiantMode) {
this->unk_01B0.y += 3150.0f;
}
- this->unk_01B0.z = Rand_CenteredFloat(500.0f * D_809DF5B0) + this->actor.world.pos.z;
+ this->unk_01B0.z = Rand_CenteredFloat(500.0f * sGiantModeScaleFactor) + this->actor.world.pos.z;
this->unk_0146[0] = 150;
this->unk_0164 = 0.0f;
}
@@ -864,8 +872,8 @@ void func_809DAB78(Boss02* this, PlayState* play) {
this->unk_019A = 0x500;
this->unk_01A0 = 0x1200;
} else {
- this->colliderCylinder.dim.radius = 150.0f * D_809DF5B0;
- this->colliderCylinder.dim.height = 200.0f * D_809DF5B0;
+ this->colliderCylinder.dim.radius = 150.0f * sGiantModeScaleFactor;
+ this->colliderCylinder.dim.height = 200.0f * sGiantModeScaleFactor;
this->colliderCylinder.dim.yShift = 0;
Collider_UpdateCylinder(&this->actor, &this->colliderCylinder);
CollisionCheck_SetOC(play, &play->colChkCtx, &this->colliderCylinder.base);
@@ -915,10 +923,11 @@ void func_809DAB78(Boss02* this, PlayState* play) {
if ((s16)(BREG(71) + 140) < this->unk_0146[1]) {
if (this->unk_0146[0] == 0) {
Matrix_RotateYS(Math_Atan2S(-player->actor.world.pos.x, -player->actor.world.pos.z), MTXMODE_NEW);
- Matrix_MultVecZ(1500.0f * D_809DF5B0, &spA4);
+ Matrix_MultVecZ(1500.0f * sGiantModeScaleFactor, &spA4);
this->unk_0146[0] = 50;
this->unk_01B0.x = player->actor.world.pos.x + spA4.x;
- this->unk_01B0.y = Rand_CenteredFloat(500.0f * D_809DF5B0) + (600.0f * D_809DF5B0);
+ this->unk_01B0.y =
+ Rand_CenteredFloat(500.0f * sGiantModeScaleFactor) + (600.0f * sGiantModeScaleFactor);
if (sIsInGiantMode) {
this->unk_01B0.y += 3150.0f;
}
@@ -926,7 +935,7 @@ void func_809DAB78(Boss02* this, PlayState* play) {
}
this->unk_0168 = 3000.0f;
} else {
- this->unk_01B0.y += 10.0f * D_809DF5B0;
+ this->unk_01B0.y += 10.0f * sGiantModeScaleFactor;
this->unk_0168 = 5000.0f;
}
this->unk_019A = 0x1000;
@@ -961,21 +970,21 @@ void func_809DAB78(Boss02* this, PlayState* play) {
this->unk_1678--;
if (this->unk_1678 <= 0) {
this->unk_0144 = 22;
- this->actor.gravity = -1.0f * D_809DF5B0;
+ this->actor.gravity = -1.0f * sGiantModeScaleFactor;
this->actor.velocity.y = 0.0f;
- this->actor.terminalVelocity = -1000.0f * D_809DF5B0;
+ this->actor.terminalVelocity = -1000.0f * sGiantModeScaleFactor;
this->unk_0164 = Rand_CenteredFloat(0.05f);
spCC = player->actor.world.pos.x - this->actor.world.pos.x;
spC4 = player->actor.world.pos.z - this->actor.world.pos.z;
- if (sqrtf(SQ(spCC) + SQ(spC4)) < (400.0f * D_809DF5B0)) {
- this->actor.speed = 15.0f * D_809DF5B0;
+ if (sqrtf(SQ(spCC) + SQ(spC4)) < (400.0f * sGiantModeScaleFactor)) {
+ this->actor.speed = 15.0f * sGiantModeScaleFactor;
}
spCC = this->actor.world.pos.x;
spC4 = this->actor.world.pos.z;
- if (sqrtf(SQ(spCC) + SQ(spC4)) < (400.0f * D_809DF5B0)) {
- this->actor.speed = 15.0f * D_809DF5B0;
+ if (sqrtf(SQ(spCC) + SQ(spC4)) < (400.0f * sGiantModeScaleFactor)) {
+ this->actor.speed = 15.0f * sGiantModeScaleFactor;
}
if (otherTwinmold->unk_0144 >= 10) {
@@ -1026,30 +1035,30 @@ void func_809DAB78(Boss02* this, PlayState* play) {
for (i = 0; i < 15; i++) {
Matrix_RotateYF(((2.0f * (i * M_PI)) / 15.0f) + sp9C, MTXMODE_NEW);
- Matrix_MultVecZ((10 - this->unk_0146[0]) * (D_809DF5B0 * 300.0f) * 0.1f, &sp90);
+ Matrix_MultVecZ((10 - this->unk_0146[0]) * (sGiantModeScaleFactor * 300.0f) * 0.1f, &sp90);
spD0.x = this->unk_0170.x + sp90.x;
- spD0.y = this->unk_0170.y + (1000.0f * D_809DF5B0);
+ spD0.y = this->unk_0170.y + (1000.0f * sGiantModeScaleFactor);
spD0.z = this->unk_0170.z + sp90.z;
if (BgCheck_EntityRaycastFloor3(&play->colCtx, &sp8C, &sp88, &spD0) != BGCHECK_Y_MIN) {
spA0 = BgCheck_EntityRaycastFloor1(&play->colCtx, &sp8C, &spD0);
- Matrix_MultVecZ(5.0f * D_809DF5B0, &sp70);
- sp70.y = 2.0f * D_809DF5B0;
- sp64.y = 0.3f * D_809DF5B0;
+ Matrix_MultVecZ(5.0f * sGiantModeScaleFactor, &sp70);
+ sp70.y = 2.0f * sGiantModeScaleFactor;
+ sp64.y = 0.3f * sGiantModeScaleFactor;
sp64.z = 0.0f;
sp64.x = 0.0f;
sp7C.x = spD0.x;
sp7C.y = spA0;
sp7C.z = spD0.z;
func_800B0F18(play, &sp7C, &sp70, &sp64, &D_809DFA98, &D_809DFA98,
- (Rand_ZeroFloat(500.0f) + 1200.0f) * D_809DF5B0, 20.0f * D_809DF5B0,
- Rand_ZeroFloat(5.0f) + 14.0f);
+ (Rand_ZeroFloat(500.0f) + 1200.0f) * sGiantModeScaleFactor,
+ 20.0f * sGiantModeScaleFactor, Rand_ZeroFloat(5.0f) + 14.0f);
}
}
}
}
- this->colliderCylinder.dim.radius = 150.0f * D_809DF5B0;
- this->colliderCylinder.dim.height = 200.0f * D_809DF5B0;
+ this->colliderCylinder.dim.radius = 150.0f * sGiantModeScaleFactor;
+ this->colliderCylinder.dim.height = 200.0f * sGiantModeScaleFactor;
this->colliderCylinder.dim.yShift = 0;
Collider_UpdateCylinder(&this->actor, &this->colliderCylinder);
CollisionCheck_SetAT(play, &play->colChkCtx, &this->colliderCylinder.base);
@@ -1206,16 +1215,16 @@ void Boss02_Twinmold_Update(Actor* thisx, PlayState* play) {
Actor_PlaySfx(&this->actor, NA_SE_EN_INBOSS_SAND_OLD - SFX_FLAG);
if (this->unk_0144 > 20) {
- sp3C.x = Rand_CenteredFloat(100.0f * D_809DF5B0) + this->unk_0170.x;
- sp3C.y = Rand_CenteredFloat(50.0f * D_809DF5B0) + this->unk_0170.y;
- sp3C.z = Rand_CenteredFloat(100.0f * D_809DF5B0) + this->unk_0170.z;
+ sp3C.x = Rand_CenteredFloat(100.0f * sGiantModeScaleFactor) + this->unk_0170.x;
+ sp3C.y = Rand_CenteredFloat(50.0f * sGiantModeScaleFactor) + this->unk_0170.y;
+ sp3C.z = Rand_CenteredFloat(100.0f * sGiantModeScaleFactor) + this->unk_0170.z;
Boss02_SpawnEffectSand(play->specialEffects, &sp3C, Rand_ZeroFloat(3.0f) + 6.0f);
}
if ((this->unk_014C % 2) == 0) {
- sp3C.x = Rand_CenteredFloat(100.0f * D_809DF5B0) + this->unk_0170.x;
- sp3C.y = Rand_CenteredFloat(50.0f * D_809DF5B0) + this->unk_0170.y;
- sp3C.z = Rand_CenteredFloat(100.0f * D_809DF5B0) + this->unk_0170.z;
+ sp3C.x = Rand_CenteredFloat(100.0f * sGiantModeScaleFactor) + this->unk_0170.x;
+ sp3C.y = Rand_CenteredFloat(50.0f * sGiantModeScaleFactor) + this->unk_0170.y;
+ sp3C.z = Rand_CenteredFloat(100.0f * sGiantModeScaleFactor) + this->unk_0170.z;
Boss02_SpawnEffectSand(play->specialEffects, &sp3C, Rand_ZeroFloat(3.0f) + 6.0f);
}
}
@@ -1281,7 +1290,7 @@ void Boss02_Twinmold_Update(Actor* thisx, PlayState* play) {
void Boss02_Static_Update(Actor* thisx, PlayState* play) {
Boss02* this = THIS;
- this->unk_01AC = D_809DF5B0;
+ this->giantModeScaleFactor = sGiantModeScaleFactor;
play->envCtx.sandstormState = 0xD;
if (sBlueWarp != NULL) {
@@ -1352,7 +1361,7 @@ void Boss02_Twinmold_Draw(Actor* thisx, PlayState* play2) {
Gfx_SetupDL25_Opa(play->state.gfxCtx);
- if (this->actor.params == 0) {
+ if (TWINMOLD_GET_TYPE(&this->actor) == TWINMOLD_TYPE_RED) {
gSPSegment(POLY_OPA_DISP++, 0x08, Lib_SegmentedToVirtual(gTwinmoldRedSkinTex));
} else {
gSPSegment(POLY_OPA_DISP++, 0x08, Lib_SegmentedToVirtual(gTwinmoldBlueSkinTex));
@@ -1478,12 +1487,12 @@ void Boss02_UpdateEffects(PlayState* play) {
floorY = 3150.0f;
}
- for (i = 0; i < ARRAY_COUNT(sEffects); i++, effect++) {
+ for (i = 0; i < TWINMOLD_EFFECT_COUNT; i++, effect++) {
if (effect->type) {
effect->timer++;
- effect->pos.x += effect->velocity.x * D_809DF5B0;
- effect->pos.y += effect->velocity.y * D_809DF5B0;
- effect->pos.z += effect->velocity.z * D_809DF5B0;
+ effect->pos.x += effect->velocity.x * sGiantModeScaleFactor;
+ effect->pos.y += effect->velocity.y * sGiantModeScaleFactor;
+ effect->pos.z += effect->velocity.z * sGiantModeScaleFactor;
effect->velocity.y += effect->accel.y;
if (effect->type < TWINMOLD_EFFECT_FRAGMENT) {
@@ -1525,7 +1534,7 @@ void Boss02_DrawEffects(PlayState* play) {
Gfx_SetupDL25_Opa(play->state.gfxCtx);
Gfx_SetupDL25_Xlu(play->state.gfxCtx);
- for (i = 0; i < ARRAY_COUNT(sEffects); i++, effect++) {
+ for (i = 0; i < TWINMOLD_EFFECT_COUNT; i++, effect++) {
if (effect->type == TWINMOLD_EFFECT_SAND) {
if (!flag) {
gSPDisplayList(POLY_XLU_DISP++, gTwinmoldDustMaterialDL);
@@ -1545,7 +1554,8 @@ void Boss02_DrawEffects(PlayState* play) {
Matrix_Translate(effect->pos.x, effect->pos.y, effect->pos.z, MTXMODE_NEW);
Matrix_ReplaceRotation(&play->billboardMtxF);
- Matrix_Scale(effect->scale * D_809DF5B0, effect->scale * D_809DF5B0, 1.0f, MTXMODE_APPLY);
+ Matrix_Scale(effect->scale * sGiantModeScaleFactor, effect->scale * sGiantModeScaleFactor, 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++, gTwinmoldDustModelDL);
@@ -1553,7 +1563,7 @@ void Boss02_DrawEffects(PlayState* play) {
}
effect = (TwinmoldEffect*)play->specialEffects;
- for (i = 0, flag = false; i < ARRAY_COUNT(sEffects); i++, effect++) {
+ for (i = 0, flag = false; i < TWINMOLD_EFFECT_COUNT; i++, effect++) {
if (effect->type == TWINMOLD_EFFECT_FRAGMENT) {
if (!flag) {
gDPSetCombineLERP(POLY_OPA_DISP++, SHADE, 0, PRIMITIVE, 0, SHADE, 0, PRIMITIVE, 0, SHADE, 0, PRIMITIVE,
@@ -1565,8 +1575,8 @@ void Boss02_DrawEffects(PlayState* play) {
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 * D_809DF5B0, effect->scale * D_809DF5B0, effect->scale * D_809DF5B0,
- MTXMODE_APPLY);
+ Matrix_Scale(effect->scale * sGiantModeScaleFactor, effect->scale * sGiantModeScaleFactor,
+ effect->scale * sGiantModeScaleFactor, MTXMODE_APPLY);
gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(play->state.gfxCtx), G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW);
gSPDisplayList(POLY_OPA_DISP++, gEffFragments1DL);
@@ -1574,7 +1584,7 @@ void Boss02_DrawEffects(PlayState* play) {
}
effect = (TwinmoldEffect*)play->specialEffects;
- for (i = 0, flag = false; i < ARRAY_COUNT(sEffects); i++, effect++) {
+ for (i = 0, flag = false; i < TWINMOLD_EFFECT_COUNT; i++, effect++) {
if (effect->type == TWINMOLD_EFFECT_FLASH) {
if (!flag) { //! @bug - dev forgot to set flag to 1, should only apply to first entry?
gSPDisplayList(POLY_XLU_DISP++, gLightOrbMaterial1DL);
@@ -1585,7 +1595,8 @@ void Boss02_DrawEffects(PlayState* play) {
Matrix_Translate(effect->pos.x, effect->pos.y, effect->pos.z, MTXMODE_NEW);
Matrix_ReplaceRotation(&play->billboardMtxF);
- Matrix_Scale(effect->scale * D_809DF5B0, effect->scale * D_809DF5B0, 1.0f, MTXMODE_APPLY);
+ Matrix_Scale(effect->scale * sGiantModeScaleFactor, effect->scale * sGiantModeScaleFactor, 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++, gLightOrbModelDL);
@@ -1593,7 +1604,7 @@ void Boss02_DrawEffects(PlayState* play) {
}
effect = (TwinmoldEffect*)play->specialEffects;
- for (i = 0, flag = false; i < ARRAY_COUNT(sEffects); i++, effect++) {
+ for (i = 0, flag = false; i < TWINMOLD_EFFECT_COUNT; i++, effect++) {
if (effect->type == TWINMOLD_EFFECT_BLACK_DUST) {
if (!flag) {
gSPDisplayList(POLY_XLU_DISP++, gTwinmoldDustMaterialDL);
@@ -1608,7 +1619,8 @@ void Boss02_DrawEffects(PlayState* play) {
Matrix_Translate(effect->pos.x, effect->pos.y, effect->pos.z, MTXMODE_NEW);
Matrix_ReplaceRotation(&play->billboardMtxF);
- Matrix_Scale(effect->scale * D_809DF5B0, effect->scale * D_809DF5B0, 1.0f, MTXMODE_APPLY);
+ Matrix_Scale(effect->scale * sGiantModeScaleFactor, effect->scale * sGiantModeScaleFactor, 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++, gTwinmoldDustModelDL);
@@ -1771,12 +1783,12 @@ void func_809DD934(Boss02* this, PlayState* play) {
if (sp57) {
sIsInGiantMode = 1 - sIsInGiantMode;
if (!sIsInGiantMode) {
- D_809DF5B0 = 1.0f;
+ sGiantModeScaleFactor = 1.0f;
} else {
- D_809DF5B0 = 0.1f;
+ sGiantModeScaleFactor = 0.1f;
}
- this->unk_01AC = D_809DF5B0;
+ this->giantModeScaleFactor = sGiantModeScaleFactor;
if (!sIsInGiantMode) {
if (sBlueWarp != NULL) {
sBlueWarp->unk_203 = 0;
@@ -2183,8 +2195,8 @@ void func_809DEAC4(Boss02* this, PlayState* play) {
this->subCamEye.y = 3160.0f;
}
- if (this->subCamAtNext.y < (100.0f * D_809DF5B0)) {
- this->subCamAtNext.y = (100.0f * D_809DF5B0);
+ if (this->subCamAtNext.y < (100.0f * sGiantModeScaleFactor)) {
+ this->subCamAtNext.y = (100.0f * sGiantModeScaleFactor);
}
this->subCamEye.z = player->actor.world.pos.z;
@@ -2236,7 +2248,7 @@ void func_809DEAC4(Boss02* this, PlayState* play) {
if ((this->unk_1D20 != 0) && (this->subCamId != SUB_CAM_ID_DONE)) {
subCamEye = this->subCamEye;
- subCamEye.y += sp58 * D_809DF5B0;
+ subCamEye.y += sp58 * sGiantModeScaleFactor;
Play_SetCameraAtEyeUp(play, this->subCamId, &this->subCamAt, &subCamEye, &this->subCamUp);
this->subCamUp.z = this->subCamUp.x = 0.0f;
this->subCamUp.y = 1.0f;
diff --git a/src/overlays/actors/ovl_Boss_02/z_boss_02.h b/src/overlays/actors/ovl_Boss_02/z_boss_02.h
index 4fbd8b471b..f3024c241f 100644
--- a/src/overlays/actors/ovl_Boss_02/z_boss_02.h
+++ b/src/overlays/actors/ovl_Boss_02/z_boss_02.h
@@ -6,6 +6,8 @@
struct Boss02;
+#define TWINMOLD_GET_TYPE(thisx) ((thisx)->params)
+
typedef void (*Boss02ActionFunc)(struct Boss02*, PlayState*);
typedef struct {
@@ -25,17 +27,19 @@ typedef struct {
typedef enum {
/* 0 */ TWINMOLD_EFFECT_NONE,
/* 1 */ TWINMOLD_EFFECT_SAND, // The sand kicked up when Twinmold touches the ground
- /* 2 */ TWINMOLD_EFFECT_BLACK_DUST, // Unused
+ /* 2 */ TWINMOLD_EFFECT_BLACK_DUST, // The dust that appears when either Twinmold or the player destroys a ruin
/* 3 */ TWINMOLD_EFFECT_FRAGMENT, // The fragments that fly off when the parts of Twinmold explode
/* 4 */ TWINMOLD_EFFECT_FLASH // The flashes of light that appear when the parts of Twinmold explode
} TwinmoldEffectType;
+#define TWINMOLD_EFFECT_COUNT 150
+
typedef enum {
- /* 0 */ TWINMOLD_RED,
- /* 35 */ TWINMOLD_BLUE = 35,
- /* 100 */ TWINMOLD_TAIL = 100,
- /* 200 */ TWINMOLD_STATIC = 200
-} TwinmoldParam;
+ /* 0 */ TWINMOLD_TYPE_RED,
+ /* 35 */ TWINMOLD_TYPE_BLUE = 35,
+ /* 100 */ TWINMOLD_TYPE_TAIL = 100,
+ /* 200 */ TWINMOLD_TYPE_STATIC = 200
+} TwinmoldType;
typedef struct Boss02 {
/* 0x0000 */ Actor actor;
@@ -66,7 +70,7 @@ typedef struct Boss02 {
/* 0x01A0 */ f32 unk_01A0;
/* 0x01A4 */ s16 unk_01A4;
/* 0x01A8 */ f32 unk_01A8;
- /* 0x01AC */ f32 unk_01AC;
+ /* 0x01AC */ f32 giantModeScaleFactor;
/* 0x01B0 */ Vec3f unk_01B0;
/* 0x01BC */ Vec3f unk_01BC[200];
/* 0x0B1C */ Vec3f unk_0B1C[200];
diff --git a/src/overlays/actors/ovl_En_Tanron5/z_en_tanron5.c b/src/overlays/actors/ovl_En_Tanron5/z_en_tanron5.c
index e9c43fca8d..38e1ba8c66 100644
--- a/src/overlays/actors/ovl_En_Tanron5/z_en_tanron5.c
+++ b/src/overlays/actors/ovl_En_Tanron5/z_en_tanron5.c
@@ -1,7 +1,23 @@
/*
* File: z_en_tanron5.c
* Overlay: ovl_En_Tanron5
- * Description: Destructible props in Twinmold's arena
+ * Description: Destructible ruins, fragments, and item drops in Twinmold's arena.
+ *
+ * This actor is responsible for three different interactive props in Twinmold's arena, all of which can be further
+ * divided into more categories. The main thing this actor handles are the destructible ruins placed around the arena,
+ * of which there are two kinds. There are pillars with Majora's Mask on them, and there are ruins in a roughly
+ * pyramidal shape. While both types of ruin behave in roughly the same way, how they implement this behavior can
+ * sometimes be quite different.
+ *
+ * When Twinmold or the player wearing the Giant's Mask hits one of these destructible ruins, various fragments
+ * of the ruin fly off from the point of impact. These fragments are handled by this actor as well. The fragments
+ * can be large or small, and the two sizes behave almost identically outside of two small differences. Large
+ * fragments can damage the player and sink into the sand once they hit the ground, whereas small fragments
+ * deal no damage and despawn upon touching the ground.
+ *
+ * Sometimes, item drops can also appear when a destructible ruin is hit; this actor is responsible for handling
+ * these drops too. There are drops that give the player 10 arrows, and drops that give the player a big magic
+ * jar, and these drops behave identically outside of what item they give when collected by the player.
*/
#include "z_en_tanron5.h"
@@ -18,10 +34,15 @@ void EnTanron5_Destroy(Actor* thisx, PlayState* play);
void EnTanron5_Update(Actor* thisx, PlayState* play2);
void EnTanron5_Draw(Actor* thisx, PlayState* play);
-void func_80BE5818(Actor* thisx, PlayState* play2);
-void func_80BE5C10(Actor* thisx, PlayState* play);
+void EnTanron5_RuinFragmentItemDrop_Update(Actor* thisx, PlayState* play2);
+void EnTanron5_ItemDrop_Draw(Actor* thisx, PlayState* play);
-s32 D_80BE5D80 = 0;
+typedef enum {
+ /* 0 */ TWINMOLD_PROP_ITEM_DROP_TYPE_10_ARROWS,
+ /* 1 */ TWINMOLD_PROP_ITEM_DROP_TYPE_MAGIC_JAR_BIG
+} TwinmoldPropItemDropType;
+
+s32 sFragmentAndItemDropCount = 0;
ActorInit En_Tanron5_InitVars = {
ACTOR_EN_TANRON5,
@@ -55,90 +76,136 @@ static ColliderCylinderInit sCylinderInit = {
{ 70, 450, 0, { 0, 0, 0 } },
};
-f32 D_80BE5DD0 = 1.0f;
+/**
+ * Multiplies the scale differently depending on whether the player is wearing the Giant's Mask or not.
+ * When the player is wearing the Giant's Mask, this value is smaller to make the player seem larger.
+ */
+static f32 sGiantModeScaleFactor = 1.0f;
-Vec2s D_80BE5DD4[] = {
- { 0x4B0, 0x9C4 }, { -0x4B0, 0x9C4 }, { 0x4B0, -0x9C4 }, { -0x4B0, -0x9C4 }, { 0x9C4, 0x4B0 },
- { -0x9C4, 0x4B0 }, { 0x9C4, -0x4B0 }, { -0x9C4, -0x4B0 }, { 0x3E8, 0x3E8 }, { -0x3E8, 0x3E8 },
- { 0x3E8, -0x3E8 }, { -0x3E8, -0x3E8 }, { 0x000, -0x3E8 }, { 0x000, 0x3E8 }, { 0x3E8, 0x000 },
- { -0x3E8, 0x000 }, { 0x000, -0x7D0 }, { 0x000, 0x7D0 }, { 0x7D0, 0x000 }, { -0x7D0, 0x000 },
+/**
+ * Stores the X and Z spawn positions for all of the ruins. Their Y spawn position is determined by the
+ * height of the floor, so there's no need to store it.
+ */
+static Vec2s sSpawnPosList[] = {
+ { 1200, 2500 }, // TWINMOLD_PROP_TYPE_RUIN_PILLAR_1
+ { -1200, 2500 }, // TWINMOLD_PROP_TYPE_RUIN_PILLAR_2
+ { 1200, -2500 }, // TWINMOLD_PROP_TYPE_RUIN_PILLAR_3
+ { -1200, -2500 }, // TWINMOLD_PROP_TYPE_RUIN_PILLAR_4
+ { 2500, 1200 }, // TWINMOLD_PROP_TYPE_RUIN_PILLAR_5
+ { -2500, 1200 }, // TWINMOLD_PROP_TYPE_RUIN_PILLAR_6
+ { 2500, -1200 }, // TWINMOLD_PROP_TYPE_RUIN_PILLAR_7
+ { -2500, -1200 }, // TWINMOLD_PROP_TYPE_RUIN_PILLAR_8
+ { 1000, 1000 }, // TWINMOLD_PROP_TYPE_RUIN_PILLAR_9
+ { -1000, 1000 }, // TWINMOLD_PROP_TYPE_RUIN_PILLAR_10
+ { 1000, -1000 }, // TWINMOLD_PROP_TYPE_RUIN_PILLAR_11
+ { -1000, -1000 }, // TWINMOLD_PROP_TYPE_RUIN_PILLAR_12
+ { 0, -1000 }, // TWINMOLD_PROP_TYPE_RUIN_PYRAMID_1
+ { 0, 1000 }, // TWINMOLD_PROP_TYPE_RUIN_PYRAMID_2
+ { 1000, 0 }, // TWINMOLD_PROP_TYPE_RUIN_PYRAMID_3
+ { -1000, 0 }, // TWINMOLD_PROP_TYPE_RUIN_PYRAMID_4
+ { 0, -2000 }, // TWINMOLD_PROP_TYPE_RUIN_PYRAMID_5
+ { 0, 2000 }, // TWINMOLD_PROP_TYPE_RUIN_PYRAMID_6
+ { 2000, 0 }, // TWINMOLD_PROP_TYPE_RUIN_PYRAMID_7
+ { -2000, 0 }, // TWINMOLD_PROP_TYPE_RUIN_PYRAMID_8
};
-Gfx* D_80BE5E24[] = {
- gTwinmoldMajoraPillarDL, gTwinmoldMajoraPillarDL, gTwinmoldMajoraPillarDL, gTwinmoldMajoraPillarDL,
- gTwinmoldMajoraPillarDL, gTwinmoldMajoraPillarDL, gTwinmoldMajoraPillarDL, gTwinmoldMajoraPillarDL,
- gTwinmoldMajoraPillarDL, gTwinmoldMajoraPillarDL, gTwinmoldMajoraPillarDL, gTwinmoldMajoraPillarDL,
- gTwinmoldPyramidRuinDL, gTwinmoldPyramidRuinDL, gTwinmoldPyramidRuinDL, gTwinmoldPyramidRuinDL,
- gTwinmoldPyramidRuinDL, gTwinmoldPyramidRuinDL, gTwinmoldPyramidRuinDL, gTwinmoldPyramidRuinDL,
+/**
+ * Display lists for all ruins.
+ */
+static Gfx* sDLists[] = {
+ gTwinmoldRuinPillarDL, // TWINMOLD_PROP_TYPE_RUIN_PILLAR_1
+ gTwinmoldRuinPillarDL, // TWINMOLD_PROP_TYPE_RUIN_PILLAR_2
+ gTwinmoldRuinPillarDL, // TWINMOLD_PROP_TYPE_RUIN_PILLAR_3
+ gTwinmoldRuinPillarDL, // TWINMOLD_PROP_TYPE_RUIN_PILLAR_4
+ gTwinmoldRuinPillarDL, // TWINMOLD_PROP_TYPE_RUIN_PILLAR_5
+ gTwinmoldRuinPillarDL, // TWINMOLD_PROP_TYPE_RUIN_PILLAR_6
+ gTwinmoldRuinPillarDL, // TWINMOLD_PROP_TYPE_RUIN_PILLAR_7
+ gTwinmoldRuinPillarDL, // TWINMOLD_PROP_TYPE_RUIN_PILLAR_8
+ gTwinmoldRuinPillarDL, // TWINMOLD_PROP_TYPE_RUIN_PILLAR_9
+ gTwinmoldRuinPillarDL, // TWINMOLD_PROP_TYPE_RUIN_PILLAR_10
+ gTwinmoldRuinPillarDL, // TWINMOLD_PROP_TYPE_RUIN_PILLAR_11
+ gTwinmoldRuinPillarDL, // TWINMOLD_PROP_TYPE_RUIN_PILLAR_12
+ gTwinmoldRuinPyramidDL, // TWINMOLD_PROP_TYPE_RUIN_PYRAMID_1
+ gTwinmoldRuinPyramidDL, // TWINMOLD_PROP_TYPE_RUIN_PYRAMID_2
+ gTwinmoldRuinPyramidDL, // TWINMOLD_PROP_TYPE_RUIN_PYRAMID_3
+ gTwinmoldRuinPyramidDL, // TWINMOLD_PROP_TYPE_RUIN_PYRAMID_4
+ gTwinmoldRuinPyramidDL, // TWINMOLD_PROP_TYPE_RUIN_PYRAMID_5
+ gTwinmoldRuinPyramidDL, // TWINMOLD_PROP_TYPE_RUIN_PYRAMID_6
+ gTwinmoldRuinPyramidDL, // TWINMOLD_PROP_TYPE_RUIN_PYRAMID_7
+ gTwinmoldRuinPyramidDL, // TWINMOLD_PROP_TYPE_RUIN_PYRAMID_8
};
-f32 D_80BE5E74[] = {
- 0.09f, 0.09f, 0.09f, 0.09f, 0.09f, 0.09f, 0.09f, 0.09f, 0.09f, 0.09f,
- 0.09f, 0.09f, 0.09f, 0.09f, 0.09f, 0.09f, 0.09f, 0.09f, 0.09f, 0.09f,
+/**
+ * The initial base scale for all ruins. In the final game, this array isn't very useful,
+ * since they're all the same value, but this could be used to make some ruins larger or
+ * smaller than the others.
+ */
+static f32 sBaseScales[] = {
+ 0.09f, // TWINMOLD_PROP_TYPE_RUIN_PILLAR_1
+ 0.09f, // TWINMOLD_PROP_TYPE_RUIN_PILLAR_2
+ 0.09f, // TWINMOLD_PROP_TYPE_RUIN_PILLAR_3
+ 0.09f, // TWINMOLD_PROP_TYPE_RUIN_PILLAR_4
+ 0.09f, // TWINMOLD_PROP_TYPE_RUIN_PILLAR_5
+ 0.09f, // TWINMOLD_PROP_TYPE_RUIN_PILLAR_6
+ 0.09f, // TWINMOLD_PROP_TYPE_RUIN_PILLAR_7
+ 0.09f, // TWINMOLD_PROP_TYPE_RUIN_PILLAR_8
+ 0.09f, // TWINMOLD_PROP_TYPE_RUIN_PILLAR_9
+ 0.09f, // TWINMOLD_PROP_TYPE_RUIN_PILLAR_10
+ 0.09f, // TWINMOLD_PROP_TYPE_RUIN_PILLAR_11
+ 0.09f, // TWINMOLD_PROP_TYPE_RUIN_PILLAR_12
+ 0.09f, // TWINMOLD_PROP_TYPE_RUIN_PYRAMID_1
+ 0.09f, // TWINMOLD_PROP_TYPE_RUIN_PYRAMID_2
+ 0.09f, // TWINMOLD_PROP_TYPE_RUIN_PYRAMID_3
+ 0.09f, // TWINMOLD_PROP_TYPE_RUIN_PYRAMID_4
+ 0.09f, // TWINMOLD_PROP_TYPE_RUIN_PYRAMID_5
+ 0.09f, // TWINMOLD_PROP_TYPE_RUIN_PYRAMID_6
+ 0.09f, // TWINMOLD_PROP_TYPE_RUIN_PYRAMID_7
+ 0.09f, // TWINMOLD_PROP_TYPE_RUIN_PYRAMID_8
};
-typedef struct {
- /* 0x00 */ Vec3f unk_00;
- /* 0x0C */ f32 unk_0C;
- /* 0x10 */ f32 unk_10;
- /* 0x14 */ f32 unk_14;
- /* 0x18 */ Vec3f unk_18;
- /* 0x24 */ u8 unk_24;
- /* 0x26 */ s16 unk_26;
- /* 0x28 */ UNK_TYPE1 unk28[0x4];
- /* 0x2C */ s16 unk_2C;
- /* 0x2E */ UNK_TYPE1 unk2E[0x6];
- /* 0x34 */ f32 unk_34;
- /* 0x38 */ f32 unk_38;
-} EnTanron5Effect; // size = 0x3C
-
-void func_80BE4930(EnTanron5Effect* effect, Vec3f* arg1, f32 arg2) {
+/**
+ * Spawns the sand effect that appears when a ruin fragment hits the ground.
+ */
+void EnTanron5_SpawnEffectSand(TwinmoldEffect* effect, Vec3f* pos, f32 scale) {
s16 i;
- for (i = 0; i < 150; i++, effect++) {
- if (effect->unk_24 == 0) {
- effect->unk_24 = 1;
-
- effect->unk_00 = *arg1;
-
- effect->unk_0C = Rand_CenteredFloat(10.0f);
- effect->unk_10 = Rand_ZeroFloat(2.0f) + 3.0f;
- effect->unk_14 = Rand_CenteredFloat(10.0f);
-
- effect->unk_18.y = -0.15f;
- effect->unk_18.x = 0.0f;
- effect->unk_18.z = 0.0f;
-
- effect->unk_2C = Rand_ZeroFloat(100.0f) + 200.0f;
- effect->unk_26 = 0;
- effect->unk_34 = arg2;
- effect->unk_38 = 2.0f * arg2;
+ for (i = 0; i < TWINMOLD_EFFECT_COUNT; i++, effect++) {
+ if (effect->type == TWINMOLD_EFFECT_NONE) {
+ effect->type = TWINMOLD_EFFECT_SAND;
+ effect->pos = *pos;
+ effect->velocity.x = Rand_CenteredFloat(10.0f);
+ effect->velocity.y = Rand_ZeroFloat(2.0f) + 3.0f;
+ effect->velocity.z = Rand_CenteredFloat(10.0f);
+ effect->accel.y = -0.15f;
+ effect->accel.x = effect->accel.z = 0.0f;
+ effect->alpha = Rand_ZeroFloat(100.0f) + 200.0f;
+ effect->timer = 0;
+ effect->scale = scale;
+ effect->targetScale = 2.0f * scale;
break;
}
}
}
-void func_80BE4A2C(EnTanron5Effect* effect, Vec3f* arg1, f32 arg2) {
+/**
+ * Spawns the black dust that appears whenever part of a ruin is destroyed.
+ */
+void EnTanron5_SpawnEffectBlackDust(TwinmoldEffect* effect, Vec3f* pos, f32 scale) {
s16 i;
- for (i = 0; i < 150; i++, effect++) {
- if (effect->unk_24 == 0) {
- effect->unk_24 = 2;
-
- effect->unk_00 = *arg1;
-
- effect->unk_0C = Rand_CenteredFloat(30.0f);
- effect->unk_10 = Rand_ZeroFloat(7.0f);
- effect->unk_14 = Rand_CenteredFloat(30.0f);
-
- effect->unk_18.y = -0.3f;
- effect->unk_18.x = 0.0f;
- effect->unk_18.z = 0.0f;
-
- effect->unk_2C = Rand_ZeroFloat(70.0f) + 150.0f;
- effect->unk_26 = 0;
- effect->unk_34 = arg2;
- effect->unk_38 = 2.0f * arg2;
+ for (i = 0; i < TWINMOLD_EFFECT_COUNT; i++, effect++) {
+ if (effect->type == TWINMOLD_EFFECT_NONE) {
+ effect->type = TWINMOLD_EFFECT_BLACK_DUST;
+ effect->pos = *pos;
+ effect->velocity.x = Rand_CenteredFloat(30.0f);
+ effect->velocity.y = Rand_ZeroFloat(7.0f);
+ effect->velocity.z = Rand_CenteredFloat(30.0f);
+ effect->accel.y = -0.3f;
+ effect->accel.x = effect->accel.z = 0.0f;
+ effect->alpha = Rand_ZeroFloat(70.0f) + 150.0f;
+ effect->timer = 0;
+ effect->scale = scale;
+ effect->targetScale = 2.0f * scale;
break;
}
}
@@ -147,54 +214,60 @@ void func_80BE4A2C(EnTanron5Effect* effect, Vec3f* arg1, f32 arg2) {
void EnTanron5_Init(Actor* thisx, PlayState* play) {
EnTanron5* this = THIS;
- if (ENTANRON5_GET(&this->actor) >= ENTANRON5_100) {
- D_80BE5D80++;
- if (D_80BE5D80 > 60) {
+ if (TWINMOLD_PROP_GET_TYPE(&this->actor) >= TWINMOLD_PROP_TYPE_FRAGMENT_LARGE_1) {
+ // This is a ruin fragment or item drop; if there are more than 60 fragments or drops
+ // already spawned, immediately kill this one. Otherwise, set up the fragment or drop
+ // to fly off while spinning randomly.
+ sFragmentAndItemDropCount++;
+ if (sFragmentAndItemDropCount > 60) {
Actor_Kill(&this->actor);
return;
}
- this->unk_198 = Rand_CenteredFloat(0x2000);
- this->unk_19A = Rand_CenteredFloat(0x2000);
+ // fragmentRotationalVelocityX is in a union with itemDropRotZ, so for item drops, this code
+ // will initialize its z-rotation to a random value.
+ this->fragmentRotationalVelocityX = Rand_CenteredFloat(0x2000);
+ this->fragmentRotationalVelocityY = Rand_CenteredFloat(0x2000);
- if (ENTANRON5_GET(&this->actor) < ENTANRON5_107) {
- Actor_SetScale(&this->actor, (Rand_ZeroFloat(0.025f) + 0.085f) * D_80BE5DD0);
+ if (TWINMOLD_PROP_GET_TYPE(&this->actor) <= TWINMOLD_PROP_TYPE_FRAGMENT_LARGE_7) {
+ Actor_SetScale(&this->actor, (Rand_ZeroFloat(0.025f) + 0.085f) * sGiantModeScaleFactor);
} else {
- Actor_SetScale(&this->actor, (Rand_ZeroFloat(0.015f) + 0.01f) * D_80BE5DD0);
+ Actor_SetScale(&this->actor, (Rand_ZeroFloat(0.015f) + 0.01f) * sGiantModeScaleFactor);
}
- this->actor.speed = (Rand_ZeroFloat(10.0f) + 10.0f) * D_80BE5DD0;
- this->actor.velocity.y = (Rand_ZeroFloat(10.0f) + 15.0f) * D_80BE5DD0;
- this->actor.gravity = -2.5f * D_80BE5DD0;
- this->actor.terminalVelocity = -1000.0f * D_80BE5DD0;
- this->actor.update = func_80BE5818;
+ this->actor.speed = (Rand_ZeroFloat(10.0f) + 10.0f) * sGiantModeScaleFactor;
+ this->actor.velocity.y = (Rand_ZeroFloat(10.0f) + 15.0f) * sGiantModeScaleFactor;
+ this->actor.gravity = -2.5f * sGiantModeScaleFactor;
+ this->actor.terminalVelocity = -1000.0f * sGiantModeScaleFactor;
+ this->actor.update = EnTanron5_RuinFragmentItemDrop_Update;
- if (ENTANRON5_GET(&this->actor) >= ENTANRON5_110) {
- this->actor.draw = func_80BE5C10;
- this->unk_1A0 = Rand_ZeroFloat(1.999f);
- Actor_SetScale(&this->actor, D_80BE5DD0 * 0.03f);
- this->unk_144 = 250;
+ if (TWINMOLD_PROP_GET_TYPE(&this->actor) >= TWINMOLD_PROP_TYPE_ITEM_DROP_1) {
+ this->actor.draw = EnTanron5_ItemDrop_Draw;
+ this->itemDropType = Rand_ZeroFloat(1.999f);
+ Actor_SetScale(&this->actor, sGiantModeScaleFactor * 0.03f);
+ this->timer = 250;
this->actor.shape.rot.x = this->actor.shape.rot.y = this->actor.shape.rot.z = 0;
} else {
- this->unk_148 = gRuinFragmentDL;
- this->unk_144 = 150;
+ this->dList = gRuinFragmentDL;
+ this->timer = 150;
}
- } else if (ENTANRON5_GET(&this->actor) == ENTANRON5_0) {
+ } else if (TWINMOLD_PROP_GET_TYPE(&this->actor) == TWINMOLD_PROP_TYPE_STATIC) {
EnTanron5* child;
s32 i;
- for (i = 0; i < ARRAY_COUNT(D_80BE5E74); i++) {
- child =
- (EnTanron5*)Actor_Spawn(&play->actorCtx, play, ACTOR_EN_TANRON5, D_80BE5DD4[i].x,
- this->actor.world.pos.y, D_80BE5DD4[i].z, 0, Rand_ZeroFloat(0x10000), 0, i + 1);
+ // Spawns all of the ruins in the right places. Gets killed after everything is spawned.
+ for (i = 0; i < ARRAY_COUNT(sSpawnPosList); i++) {
+ child = (EnTanron5*)Actor_Spawn(&play->actorCtx, play, ACTOR_EN_TANRON5, sSpawnPosList[i].x,
+ this->actor.world.pos.y, sSpawnPosList[i].z, 0, Rand_ZeroFloat(0x10000), 0,
+ TWINMOLD_PROP_PARAMS(TWINMOLD_PROP_TYPE_RUIN_PILLAR_1 + i));
child->actor.parent = this->actor.parent;
- child->unk_19C = D_80BE5E74[i];
+ child->baseScale = sBaseScales[i];
- Actor_SetScale(&child->actor, child->unk_19C);
+ Actor_SetScale(&child->actor, child->baseScale);
- child->unk_148 = D_80BE5E24[i];
- if (child->unk_148 == gTwinmoldPyramidRuinDL) {
+ child->dList = sDLists[i];
+ if (child->dList == gTwinmoldRuinPyramidDL) {
child->actor.shape.rot.y = 0;
}
@@ -203,6 +276,8 @@ void EnTanron5_Init(Actor* thisx, PlayState* play) {
Actor_Kill(&this->actor);
} else {
+ // This is a ruin; update its y-position to be just below the floor, so it looks like it's
+ // buried in the sand.
Actor_UpdateBgCheckInfo(play, &this->actor, 50.0f, 150.0f, 100.0f, UPDBGCHECKINFO_FLAG_4);
this->actor.world.pos.y = this->actor.floorHeight + -20.0f;
}
@@ -211,25 +286,31 @@ void EnTanron5_Init(Actor* thisx, PlayState* play) {
void EnTanron5_Destroy(Actor* thisx, PlayState* play) {
EnTanron5* this = THIS;
- if (ENTANRON5_GET(&this->actor) >= ENTANRON5_100) {
- D_80BE5D80--;
+ if (TWINMOLD_PROP_GET_TYPE(&this->actor) >= TWINMOLD_PROP_TYPE_FRAGMENT_LARGE_1) {
+ sFragmentAndItemDropCount--;
}
}
+/**
+ * This is the update function for the destructible ruins (both the pillar and pyarmid ruins).
+ */
void EnTanron5_Update(Actor* thisx, PlayState* play2) {
PlayState* play = play2;
EnTanron5* this = THIS;
Boss02* boss02 = (Boss02*)this->actor.parent;
Player* player = GET_PLAYER(play2);
s32 i;
- s32 phi_v0;
- s32 spC4;
- Vec3f spB8;
+ s32 yawDiff;
+ s32 fragmentAndItemCount;
+ Vec3f pos;
- if (this->unk_1A0 >= 3) {
- this->unk_1A0++;
+ // When a ruin is destroyed (i.e., it is hit three times), it will reduce its scale to 0.0f (making
+ // it effectively invisible and intangible), then wait an additional 37 frames before actually
+ // calling Actor_Kill to despawn. The reason for this extra waiting period is unknown.
+ if (this->hitCount >= 3) {
+ this->hitCount++;
Actor_SetScale(&this->actor, 0.0f);
- if (this->unk_1A0 >= 40) {
+ if (this->hitCount >= 40) {
Actor_Kill(&this->actor);
}
return;
@@ -237,154 +318,184 @@ void EnTanron5_Update(Actor* thisx, PlayState* play2) {
// required
}
- if (this->unk_144 != 0) {
- this->unk_144--;
- }
+ DECR(this->timer);
+ //! @bug This code will keep sGiantModeScaleFactor up-to-date so long as at least one ruin is still active.
+ //! However, once the last ruin is destroyed, this code will no longer run, so sGiantModeScaleFactor will
+ //! get "stuck" at whatever its current value is. This is a problem, because other instances of EnTanron5,
+ //! like the item drops, rely on this variable being updated to function properly. Getting in this "stuck"
+ //! state can result in odd behavior for these other instances, like item drops not being obtainable when
+ //! the player is normal-sized.
+ //!
+ //! The strange waiting period before despawning seen above may be an attempt to mitigate this, but it
+ //! doesn't work. It doesn't update sGiantModeScaleFactor, and even if it did, waiting 37 frames before
+ //! despawning is far too short of a time to wait, since item drops take 250 frames to despawn.
if (boss02->actor.update != NULL) {
- D_80BE5DD0 = boss02->unk_01AC;
+ sGiantModeScaleFactor = boss02->giantModeScaleFactor;
} else {
- D_80BE5DD0 = 1.0f;
+ sGiantModeScaleFactor = 1.0f;
}
- Actor_SetScale(&this->actor, this->unk_19C * D_80BE5DD0);
+ Actor_SetScale(&this->actor, this->baseScale * sGiantModeScaleFactor);
- if (this->unk_148 == gTwinmoldMajoraPillarDL) {
- this->collider.dim.radius = 65.0f * D_80BE5DD0;
- this->collider.dim.height = 380.0f * D_80BE5DD0;
- } else if (this->unk_1A0 == 0) {
- this->collider.dim.radius = 85.0f * D_80BE5DD0;
- this->collider.dim.height = 200.0f * D_80BE5DD0;
- } else if (this->unk_1A0 == 1) {
- this->collider.dim.radius = 95.0f * D_80BE5DD0;
- this->collider.dim.height = 100.0f * D_80BE5DD0;
- } else if (this->unk_1A0 == 2) {
- this->collider.dim.radius = 95.0f * D_80BE5DD0;
- this->collider.dim.height = 30.0f * D_80BE5DD0;
+ if (this->dList == gTwinmoldRuinPillarDL) {
+ this->collider.dim.radius = 65.0f * sGiantModeScaleFactor;
+ this->collider.dim.height = 380.0f * sGiantModeScaleFactor;
+ } else if (this->hitCount == 0) {
+ this->collider.dim.radius = 85.0f * sGiantModeScaleFactor;
+ this->collider.dim.height = 200.0f * sGiantModeScaleFactor;
+ } else if (this->hitCount == 1) {
+ this->collider.dim.radius = 95.0f * sGiantModeScaleFactor;
+ this->collider.dim.height = 100.0f * sGiantModeScaleFactor;
+ } else if (this->hitCount == 2) {
+ this->collider.dim.radius = 95.0f * sGiantModeScaleFactor;
+ this->collider.dim.height = 30.0f * sGiantModeScaleFactor;
}
- if (this->unk_144 == 0) {
+ if (this->timer == 0) {
if (this->collider.base.acFlags & AC_HIT) {
ColliderInfo* acHitInfo = this->collider.info.acHitInfo;
Actor* ac = this->collider.base.ac;
this->collider.base.acFlags &= ~AC_HIT;
- spC4 = 10;
+ fragmentAndItemCount = 10;
if (Play_InCsMode(play)) {
- this->unk_144 = 1;
+ // In Twinmold's opening cutscene, it emerges from the sand beneath a ruin and destroys it.
+ // Setting the timer to 1 here allows Twinmold to hit the ruin every single frame during the
+ // cutscene, allowing it to destory the ruin in only 3 frames.
+ this->timer = 1;
} else {
- this->unk_144 = 5;
- spC4 = (s32)Rand_ZeroFloat(2.99f) + 10;
+ this->timer = 5;
+ fragmentAndItemCount = (s32)Rand_ZeroFloat(2.99f) + 10;
}
- if ((KREG(19) != 0) || ((acHitInfo->toucher.dmgFlags & 0x05000202) && (D_80BE5DD0 < 0.5f)) ||
+ if ((KREG(19) != 0) || ((acHitInfo->toucher.dmgFlags & 0x05000202) && (sGiantModeScaleFactor < 0.5f)) ||
(ac->id == ACTOR_BOSS_02)) {
- if (this->unk_148 == gTwinmoldMajoraPillarDL) {
- Math_Vec3f_Copy(&spB8, &this->actor.world.pos);
- spB8.y += D_80BE5DD0 * 300.0f;
+ if (this->dList == gTwinmoldRuinPillarDL) {
+ // To create the appearance of the pillar shrinking after being hit, push it further into the floor,
+ // spawn some ruin fragments, and also spawn some black dust effects.
+ Math_Vec3f_Copy(&pos, &this->actor.world.pos);
+ pos.y += sGiantModeScaleFactor * 300.0f;
- for (i = 3; i < spC4; i++) {
- Actor_Spawn(&play->actorCtx, play, ACTOR_EN_TANRON5, spB8.x, spB8.y, spB8.z,
- Rand_ZeroFloat(0x10000), Rand_ZeroFloat(0x10000), 0, i + 100);
+ // This will spawn four normal-sized ruin fragments, three small ruin fragments, and
+ // zero, one, or two item drops, depending on the result of Rand_ZeroFloat above.
+ for (i = 3; i < fragmentAndItemCount; i++) {
+ Actor_Spawn(&play->actorCtx, play, ACTOR_EN_TANRON5, pos.x, pos.y, pos.z,
+ Rand_ZeroFloat(0x10000), Rand_ZeroFloat(0x10000), 0,
+ TWINMOLD_PROP_PARAMS(TWINMOLD_PROP_TYPE_FRAGMENT_LARGE_1 + i));
}
for (i = 0; i < 6; i++) {
- func_80BE4A2C(play->specialEffects, &spB8, Rand_ZeroFloat(3.0f) + 6.0f);
+ EnTanron5_SpawnEffectBlackDust(play->specialEffects, &pos, Rand_ZeroFloat(3.0f) + 6.0f);
}
- this->actor.world.pos.y -= D_80BE5DD0 * 130.0f;
+ this->actor.world.pos.y -= sGiantModeScaleFactor * 130.0f;
} else {
- f32 spAC;
- f32 spA8;
- Vec3f sp9C;
+ f32 yFactor;
+ f32 xzFactor;
+ Vec3f fragmentAndDustPos;
- if (this->unk_1A0 == 0) {
- spAC = 180.0f;
- this->unk_19C *= 1.4f;
- } else if (this->unk_1A0 == 1) {
- spAC = 230.0f;
- this->unk_19C *= 1.37f;
- } else if (this->unk_1A0 == 2) {
- spAC = 780.0f;
- this->unk_19C *= 1.5f;
+ // Check the number of times this pyramid ruin has been hit to scale it accordingly.
+ if (this->hitCount == 0) {
+ yFactor = 180.0f;
+ this->baseScale *= 1.4f;
+ } else if (this->hitCount == 1) {
+ yFactor = 230.0f;
+ this->baseScale *= 1.37f;
+ } else if (this->hitCount == 2) {
+ yFactor = 780.0f;
+ this->baseScale *= 1.5f;
}
- // TODO: determine if unk_1A0 ever has a different value from these 3, which will cause UB from spAC
- // being uninitialised
- this->actor.world.pos.y -= D_80BE5DD0 * spAC;
- Actor_SetScale(&this->actor, this->unk_19C * D_80BE5DD0);
- Math_Vec3f_Copy(&spB8, &this->actor.world.pos);
- for (i = 0; i < spC4; i++) {
- if (this->unk_1A0 == 0) {
- spA8 = 100.0f;
- spAC = 180.0f;
- } else if (this->unk_1A0 == 1) {
- spA8 = 200.0f;
- spAC = 100.0f;
- } else if (this->unk_1A0 == 2) {
- spA8 = 250.0f;
- spAC = 50.0f;
+ this->actor.world.pos.y -= sGiantModeScaleFactor * yFactor;
+ Actor_SetScale(&this->actor, this->baseScale * sGiantModeScaleFactor);
+ Math_Vec3f_Copy(&pos, &this->actor.world.pos);
+
+ for (i = 0; i < fragmentAndItemCount; i++) {
+ if (this->hitCount == 0) {
+ xzFactor = 100.0f;
+ yFactor = 180.0f;
+ } else if (this->hitCount == 1) {
+ xzFactor = 200.0f;
+ yFactor = 100.0f;
+ } else if (this->hitCount == 2) {
+ xzFactor = 250.0f;
+ yFactor = 50.0f;
}
- sp9C.x = (Rand_CenteredFloat(spA8) * D_80BE5DD0) + spB8.x;
- sp9C.z = (Rand_CenteredFloat(spA8) * D_80BE5DD0) + spB8.z;
- sp9C.y = this->actor.floorHeight + (spAC * D_80BE5DD0);
+ fragmentAndDustPos.x = pos.x + (Rand_CenteredFloat(xzFactor) * sGiantModeScaleFactor);
+ fragmentAndDustPos.z = pos.z + (Rand_CenteredFloat(xzFactor) * sGiantModeScaleFactor);
+ fragmentAndDustPos.y = this->actor.floorHeight + (yFactor * sGiantModeScaleFactor);
- Actor_Spawn(&play->actorCtx, play, ACTOR_EN_TANRON5, sp9C.x, sp9C.y, sp9C.z,
- Rand_ZeroFloat(0x10000), Rand_ZeroFloat(0x10000), 0, i + 100);
+ // This will spawn seven normal-sized ruin fragments, three small ruin fragments, and
+ // zero, one, or two item drops, depending on the result of Rand_ZeroFloat above.
+ Actor_Spawn(&play->actorCtx, play, ACTOR_EN_TANRON5, fragmentAndDustPos.x, fragmentAndDustPos.y,
+ fragmentAndDustPos.z, Rand_ZeroFloat(0x10000), Rand_ZeroFloat(0x10000), 0,
+ TWINMOLD_PROP_PARAMS(TWINMOLD_PROP_TYPE_FRAGMENT_LARGE_1 + i));
if (i < 8) {
- func_80BE4A2C(play->specialEffects, &sp9C, Rand_ZeroFloat(3.0f) + 6.0f);
+ EnTanron5_SpawnEffectBlackDust(play->specialEffects, &fragmentAndDustPos,
+ Rand_ZeroFloat(3.0f) + 6.0f);
}
}
}
+ // To better sell the illusion of the ruin being partially destroyed when it's hit
+ // rather than just being pushed into the ground (which is what actually happens),
+ // this code will rotate the ruin in a somewhat-random way.
if (Rand_ZeroOne() < 0.333f) {
- phi_v0 = 0x4000;
+ yawDiff = 0x4000;
} else if (Rand_ZeroOne() < 0.5f) {
- phi_v0 = -0x8000;
+ yawDiff = -0x8000;
} else {
- phi_v0 = -0x4000;
+ yawDiff = -0x4000;
}
- this->actor.shape.rot.y += phi_v0;
+ this->actor.shape.rot.y += yawDiff;
Actor_PlaySfx(&this->actor, NA_SE_IT_BIG_BOMB_EXPLOSION);
Actor_RequestQuakeAndRumble(&this->actor, play, 4, 4);
- this->unk_1A0++;
+ this->hitCount++;
} else {
- Vec3f sp90;
+ // Something hit the ruin, but it wasn't Twinmold, and it wasn't the player while in giant
+ // mode. Play the reflect sound effect and spawn some sparks instead of breaking.
+ Vec3f hitPos;
ColliderInfo* info = this->collider.info.acHitInfo;
- sp90.x = info->bumper.hitPos.x;
- sp90.y = info->bumper.hitPos.y;
- sp90.z = info->bumper.hitPos.z;
+ hitPos.x = info->bumper.hitPos.x;
+ hitPos.y = info->bumper.hitPos.y;
+ hitPos.z = info->bumper.hitPos.z;
Actor_PlaySfx(&this->actor, NA_SE_IT_SHIELD_REFLECT_SW);
- CollisionCheck_SpawnShieldParticlesMetal(play, &sp90);
+ CollisionCheck_SpawnShieldParticlesMetal(play, &hitPos);
}
}
}
Collider_UpdateCylinder(&this->actor, &this->collider);
- if (this->unk_148 == gTwinmoldPyramidRuinDL) {
+ if (this->dList == gTwinmoldRuinPyramidDL) {
this->collider.dim.pos.y = this->actor.floorHeight;
}
- if ((this->unk_148 == gTwinmoldMajoraPillarDL) || (D_80BE5DD0 < 0.5f)) {
+ if ((this->dList == gTwinmoldRuinPillarDL) || (sGiantModeScaleFactor < 0.5f)) {
CollisionCheck_SetOC(play, &play->colChkCtx, &this->collider.base);
} else {
+ // The collider cylinder used for the pyramid ruin is smaller than the ruin's visual appearance. When
+ // the player is wearing the Giant's Mask, it works fine, but when the player is normal-sized, they can
+ // walk through the sides of the ruin and wander around inside it without the collider pushing them out.
+ // The below code prevents this from happening by manually updating the player's position if they get
+ // close enough to the ruin, effectively creating a collision "box" around it that pushes players out.
f32 xDiff = player->actor.world.pos.x - this->actor.world.pos.x;
- f32 yDiff = player->actor.world.pos.z - this->actor.world.pos.z;
+ f32 zDiff = player->actor.world.pos.z - this->actor.world.pos.z;
- if ((fabsf(xDiff) < 120.0f) && (fabsf(yDiff) < 120.0f)) {
- if (fabsf(yDiff) < fabsf(xDiff)) {
+ if ((fabsf(xDiff) < 120.0f) && (fabsf(zDiff) < 120.0f)) {
+ if (fabsf(zDiff) < fabsf(xDiff)) {
if (xDiff > 0.0f) {
player->actor.prevPos.x = player->actor.world.pos.x = this->actor.world.pos.x + 120.0f;
} else {
player->actor.prevPos.x = player->actor.world.pos.x = this->actor.world.pos.x - 120.0f;
}
- } else if (yDiff > 0.0f) {
+ } else if (zDiff > 0.0f) {
player->actor.prevPos.z = player->actor.world.pos.z = this->actor.world.pos.z + 120.0f;
} else {
player->actor.prevPos.z = player->actor.world.pos.z = this->actor.world.pos.z - 120.0f;
@@ -395,23 +506,29 @@ void EnTanron5_Update(Actor* thisx, PlayState* play2) {
CollisionCheck_SetAC(play, &play->colChkCtx, &this->collider.base);
}
-void func_80BE5818(Actor* thisx, PlayState* play2) {
- f32 sp6C;
+/**
+ * This is the update function for the fragments and the item drops that fly off from a destructible ruin.
+ */
+void EnTanron5_RuinFragmentItemDrop_Update(Actor* thisx, PlayState* play2) {
+ f32 interactionDistSq;
s32 i;
- Vec3f sp5C;
+ Vec3f pos;
EnTanron5* this = THIS;
PlayState* play = play2;
- if ((ENTANRON5_GET(&this->actor) < ENTANRON5_110) && (this->unk_1A0 != 0)) {
- this->unk_1A0++;
- this->actor.world.pos.y -= 2.0f * D_80BE5DD0;
- if (this->unk_1A0 == 40) {
+ // When a ruin fragment hits the floor, it will slowly sink into the sand. After sinking for 38 frames,
+ // the ruin fragment will despawn.
+ if ((TWINMOLD_PROP_GET_TYPE(&this->actor) < TWINMOLD_PROP_TYPE_ITEM_DROP_1) && (this->sinkTimer != 0)) {
+ this->sinkTimer++;
+ this->actor.world.pos.y -= 2.0f * sGiantModeScaleFactor;
+ if (this->sinkTimer == 40) {
Actor_Kill(&this->actor);
}
+
return;
}
- if (DECR(this->unk_144) == 0) {
+ if (DECR(this->timer) == 0) {
Actor_Kill(&this->actor);
}
@@ -420,94 +537,102 @@ void func_80BE5818(Actor* thisx, PlayState* play2) {
Actor_UpdateBgCheckInfo(play, &this->actor, 50.0f, 150.0f, 100.0f, UPDBGCHECKINFO_FLAG_4);
}
- if (ENTANRON5_GET(&this->actor) < ENTANRON5_110) {
- this->actor.shape.rot.x += this->unk_198;
- this->actor.shape.rot.y += this->unk_19A;
- sp6C = 1225.0f;
+ if (TWINMOLD_PROP_GET_TYPE(&this->actor) < TWINMOLD_PROP_TYPE_ITEM_DROP_1) {
+ this->actor.shape.rot.x += this->fragmentRotationalVelocityX;
+ this->actor.shape.rot.y += this->fragmentRotationalVelocityY;
+ interactionDistSq = SQ(35.0f);
if (this->actor.bgCheckFlags & BGCHECKFLAG_GROUND) {
- if (ENTANRON5_GET(&this->actor) < ENTANRON5_108) {
- Math_Vec3f_Copy(&sp5C, &this->actor.world.pos);
- sp5C.y = this->actor.floorHeight;
+ if (TWINMOLD_PROP_GET_TYPE(&this->actor) <= TWINMOLD_PROP_TYPE_FRAGMENT_SMALL_1) {
+ Math_Vec3f_Copy(&pos, &this->actor.world.pos);
+ pos.y = this->actor.floorHeight;
for (i = 0; i < 4; i++) {
- func_80BE4930(play->specialEffects, &sp5C, Rand_ZeroFloat(1.0f) + 2.0f);
+ EnTanron5_SpawnEffectSand(play->specialEffects, &pos, Rand_ZeroFloat(1.0f) + 2.0f);
}
- this->unk_1A0++;
+
+ // Set the sinkTimer to a non-zero value so that this fragment will start sinking on the next update.
+ this->sinkTimer++;
} else {
+ // Small ruin fragments don't sink into the sand; they just immediately despawn.
Actor_Kill(&this->actor);
}
}
} else {
- sp6C = 400.0f;
- this->unk_198 += 0x2000;
+ interactionDistSq = SQ(20.0f);
+ this->itemDropRotZ += 0x2000;
if (this->actor.bgCheckFlags & BGCHECKFLAG_GROUND) {
- this->unk_198 = 0;
+ this->itemDropRotZ = 0;
this->actor.speed = 0.0f;
}
}
- if (this->unk_1A1 == 0) {
- if ((D_80BE5DD0 > 0.5f) &&
- ((ENTANRON5_GET(&this->actor) < ENTANRON5_108) || (ENTANRON5_GET(&this->actor) >= ENTANRON5_110))) {
+ if (this->hitTimer == 0) {
+ if ((sGiantModeScaleFactor > 0.5f) &&
+ ((TWINMOLD_PROP_GET_TYPE(&this->actor) <= TWINMOLD_PROP_TYPE_FRAGMENT_SMALL_1) ||
+ (TWINMOLD_PROP_GET_TYPE(&this->actor) >= TWINMOLD_PROP_TYPE_ITEM_DROP_1))) {
Player* player = GET_PLAYER(play);
- Vec3f temp;
+ Vec3f pos;
- temp.x = player->actor.world.pos.x - this->actor.world.pos.x;
- temp.y = (player->actor.world.pos.y + 10.0f) - this->actor.world.pos.y;
- temp.z = player->actor.world.pos.z - this->actor.world.pos.z;
+ pos.x = player->actor.world.pos.x - this->actor.world.pos.x;
+ pos.y = (player->actor.world.pos.y + 10.0f) - this->actor.world.pos.y;
+ pos.z = player->actor.world.pos.z - this->actor.world.pos.z;
- if (SQXYZ(temp) < sp6C) {
- if (ENTANRON5_GET(&this->actor) >= ENTANRON5_110) {
- if (this->unk_1A0 == 0) {
+ if (SQXYZ(pos) < interactionDistSq) {
+ if (TWINMOLD_PROP_GET_TYPE(&this->actor) >= TWINMOLD_PROP_TYPE_ITEM_DROP_1) {
+ if (this->itemDropType == TWINMOLD_PROP_ITEM_DROP_TYPE_10_ARROWS) {
Item_Give(play, ITEM_ARROWS_10);
} else {
Item_Give(play, ITEM_MAGIC_JAR_BIG);
}
+
Actor_Kill(&this->actor);
Audio_PlaySfx(NA_SE_SY_GET_ITEM);
} else {
- this->unk_1A1 = 20;
+ // Damages the player and knocks them back. Starts a 20-frame timer to prevent
+ // this same ruin fragment from damaging the player again too quickly.
+ this->hitTimer = 20;
func_800B8D50(play, NULL, 5.0f, this->actor.world.rot.y, 0.0f, 8);
}
}
}
} else {
- this->unk_1A1--;
+ this->hitTimer--;
}
}
void EnTanron5_Draw(Actor* thisx, PlayState* play) {
EnTanron5* this = THIS;
- if ((-500.0f * D_80BE5DD0) < this->actor.projectedPos.z) {
+ if ((-500.0f * sGiantModeScaleFactor) < this->actor.projectedPos.z) {
OPEN_DISPS(play->state.gfxCtx);
Gfx_SetupDL25_Opa(play->state.gfxCtx);
gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(play->state.gfxCtx), G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW);
- gSPDisplayList(POLY_OPA_DISP++, this->unk_148);
+ gSPDisplayList(POLY_OPA_DISP++, this->dList);
CLOSE_DISPS(play->state.gfxCtx);
}
}
-void func_80BE5C10(Actor* thisx, PlayState* play) {
+void EnTanron5_ItemDrop_Draw(Actor* thisx, PlayState* play) {
EnTanron5* this = THIS;
TexturePtr texture;
- s32 phi_v0;
+ s32 shouldDraw;
- if ((this->unk_144 > 50) || (this->unk_144 & 1)) {
- phi_v0 = true;
+ // This makes the item drop flicker when it's close to despawning.
+ if ((this->timer > 50) || (this->timer & 1)) {
+ shouldDraw = true;
} else {
- phi_v0 = false;
+ shouldDraw = false;
}
- if (((-500.0f * D_80BE5DD0) < this->actor.projectedPos.z) && phi_v0) {
+ if (((-500.0f * sGiantModeScaleFactor) < this->actor.projectedPos.z) && shouldDraw) {
OPEN_DISPS(play->state.gfxCtx);
Gfx_SetupDL25_Opa(play->state.gfxCtx);
- if (this->unk_1A0 == 0) {
+ if (this->itemDropType == TWINMOLD_PROP_ITEM_DROP_TYPE_10_ARROWS) {
texture = gDropArrows1Tex;
} else {
texture = gDropMagicLargeTex;
@@ -518,7 +643,7 @@ void func_80BE5C10(Actor* thisx, PlayState* play) {
gSPSegment(POLY_OPA_DISP++, 0x08, Lib_SegmentedToVirtual(texture));
Matrix_Translate(0.0f, 200.0f, 0.0f, MTXMODE_APPLY);
- Matrix_RotateZS(this->unk_198, MTXMODE_APPLY);
+ Matrix_RotateZS(this->itemDropRotZ, MTXMODE_APPLY);
gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(play->state.gfxCtx), G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW);
gSPDisplayList(POLY_OPA_DISP++, gItemDropDL);
diff --git a/src/overlays/actors/ovl_En_Tanron5/z_en_tanron5.h b/src/overlays/actors/ovl_En_Tanron5/z_en_tanron5.h
index d40938bf86..9f8c9b6760 100644
--- a/src/overlays/actors/ovl_En_Tanron5/z_en_tanron5.h
+++ b/src/overlays/actors/ovl_En_Tanron5/z_en_tanron5.h
@@ -5,24 +5,62 @@
struct EnTanron5;
-#define ENTANRON5_GET(thisx) ((thisx)->params)
+#define TWINMOLD_PROP_GET_TYPE(thisx) ((thisx)->params)
+#define TWINMOLD_PROP_PARAMS(type) (type)
-#define ENTANRON5_0 0
-#define ENTANRON5_100 100
-#define ENTANRON5_107 107
-#define ENTANRON5_108 108
-#define ENTANRON5_110 110
+typedef enum {
+ /* 0 */ TWINMOLD_PROP_TYPE_STATIC,
+ /* 1 */ TWINMOLD_PROP_TYPE_RUIN_PILLAR_1,
+ /* 2 */ TWINMOLD_PROP_TYPE_RUIN_PILLAR_2,
+ /* 3 */ TWINMOLD_PROP_TYPE_RUIN_PILLAR_3,
+ /* 4 */ TWINMOLD_PROP_TYPE_RUIN_PILLAR_4,
+ /* 5 */ TWINMOLD_PROP_TYPE_RUIN_PILLAR_5,
+ /* 6 */ TWINMOLD_PROP_TYPE_RUIN_PILLAR_6,
+ /* 7 */ TWINMOLD_PROP_TYPE_RUIN_PILLAR_7,
+ /* 8 */ TWINMOLD_PROP_TYPE_RUIN_PILLAR_8,
+ /* 9 */ TWINMOLD_PROP_TYPE_RUIN_PILLAR_9,
+ /* 10 */ TWINMOLD_PROP_TYPE_RUIN_PILLAR_10,
+ /* 11 */ TWINMOLD_PROP_TYPE_RUIN_PILLAR_11,
+ /* 12 */ TWINMOLD_PROP_TYPE_RUIN_PILLAR_12,
+ /* 13 */ TWINMOLD_PROP_TYPE_RUIN_PYRAMID_1,
+ /* 14 */ TWINMOLD_PROP_TYPE_RUIN_PYRAMID_2,
+ /* 15 */ TWINMOLD_PROP_TYPE_RUIN_PYRAMID_3,
+ /* 16 */ TWINMOLD_PROP_TYPE_RUIN_PYRAMID_4,
+ /* 17 */ TWINMOLD_PROP_TYPE_RUIN_PYRAMID_5,
+ /* 18 */ TWINMOLD_PROP_TYPE_RUIN_PYRAMID_6,
+ /* 19 */ TWINMOLD_PROP_TYPE_RUIN_PYRAMID_7,
+ /* 20 */ TWINMOLD_PROP_TYPE_RUIN_PYRAMID_8,
+ /* 100 */ TWINMOLD_PROP_TYPE_FRAGMENT_LARGE_1 = 100,
+ /* 101 */ TWINMOLD_PROP_TYPE_FRAGMENT_LARGE_2,
+ /* 102 */ TWINMOLD_PROP_TYPE_FRAGMENT_LARGE_3,
+ /* 103 */ TWINMOLD_PROP_TYPE_FRAGMENT_LARGE_4,
+ /* 104 */ TWINMOLD_PROP_TYPE_FRAGMENT_LARGE_5,
+ /* 105 */ TWINMOLD_PROP_TYPE_FRAGMENT_LARGE_6,
+ /* 106 */ TWINMOLD_PROP_TYPE_FRAGMENT_LARGE_7,
+ /* 107 */ TWINMOLD_PROP_TYPE_FRAGMENT_SMALL_1,
+ /* 108 */ TWINMOLD_PROP_TYPE_FRAGMENT_SMALL_2,
+ /* 109 */ TWINMOLD_PROP_TYPE_FRAGMENT_SMALL_3,
+ /* 110 */ TWINMOLD_PROP_TYPE_ITEM_DROP_1,
+ /* 111 */ TWINMOLD_PROP_TYPE_ITEM_DROP_2
+} TwinmoldPropType;
typedef struct EnTanron5 {
/* 0x000 */ Actor actor;
- /* 0x144 */ s16 unk_144;
- /* 0x148 */ Gfx* unk_148;
+ /* 0x144 */ s16 timer;
+ /* 0x148 */ Gfx* dList;
/* 0x14C */ ColliderCylinder collider;
- /* 0x198 */ s16 unk_198;
- /* 0x19A */ s16 unk_19A;
- /* 0x19C */ f32 unk_19C;
- /* 0x1A0 */ u8 unk_1A0;
- /* 0x1A1 */ u8 unk_1A1;
+ /* 0x198 */ union {
+ s16 fragmentRotationalVelocityX;
+ s16 itemDropRotZ;
+ };
+ /* 0x19A */ s16 fragmentRotationalVelocityY;
+ /* 0x19C */ f32 baseScale;
+ /* 0x1A0 */ union {
+ u8 hitCount; // used by destructible ruins
+ u8 sinkTimer; // used by ruin fragments
+ u8 itemDropType; // used by item drops
+ };
+ /* 0x1A1 */ u8 hitTimer;
} EnTanron5; // size = 0x1A4
#endif // Z_EN_TANRON5_H
diff --git a/tools/disasm/functions.txt b/tools/disasm/functions.txt
index c9bdddf048..c897532615 100644
--- a/tools/disasm/functions.txt
+++ b/tools/disasm/functions.txt
@@ -16100,14 +16100,14 @@
0x80BE42A4:("EnTanron4_FlyNearActor",),
0x80BE4734:("EnTanron4_Update",),
0x80BE4804:("EnTanron4_Draw",),
- 0x80BE4930:("func_80BE4930",),
- 0x80BE4A2C:("func_80BE4A2C",),
+ 0x80BE4930:("EnTanron5_SpawnEffectSand",),
+ 0x80BE4A2C:("EnTanron5_SpawnEffectBlackDust",),
0x80BE4B1C:("EnTanron5_Init",),
0x80BE4F24:("EnTanron5_Destroy",),
0x80BE4F54:("EnTanron5_Update",),
- 0x80BE5818:("func_80BE5818",),
+ 0x80BE5818:("EnTanron5_RuinFragmentItemDrop_Update",),
0x80BE5B58:("EnTanron5_Draw",),
- 0x80BE5C10:("func_80BE5C10",),
+ 0x80BE5C10:("EnTanron5_ItemDrop_Draw",),
0x80BE6040:("EnTanron6_Init",),
0x80BE60AC:("EnTanron6_Destroy",),
0x80BE60BC:("EnTanron6_DoNothing",),
diff --git a/tools/disasm/variables.txt b/tools/disasm/variables.txt
index 9b61c54c7a..4faf91aea8 100644
--- a/tools/disasm/variables.txt
+++ b/tools/disasm/variables.txt
@@ -10024,7 +10024,7 @@
0x809DF550:("D_809DF550","UNK_TYPE1","",0x1),
0x809DF570:("D_809DF570","UNK_TYPE1","",0x1),
0x809DF590:("Boss_02_InitVars","UNK_TYPE1","",0x1),
- 0x809DF5B0:("D_809DF5B0","f32","",0x4),
+ 0x809DF5B0:("sGiantModeScaleFactor","f32","",0x4),
0x809DF5B4:("D_809DF5B4","UNK_TYPE2","[24]",0x30),
0x809DF5E4:("D_809DF5E4","UNK_TYPE2","[24]",0x30),
0x809DF614:("D_809DF614","UNK_TYPE1","",0x1),
@@ -15917,13 +15917,13 @@
0x80BE48AC:("D_80BE48AC","f32","",0x4),
0x80BE48B0:("D_80BE48B0","f32","",0x4),
0x80BE48B4:("D_80BE48B4","f32","",0x4),
- 0x80BE5D80:("D_80BE5D80","UNK_TYPE4","",0x4),
+ 0x80BE5D80:("sFragmentAndItemDropCount","UNK_TYPE4","",0x4),
0x80BE5D84:("En_Tanron5_InitVars","UNK_TYPE1","",0x1),
0x80BE5DA4:("D_80BE5DA4","UNK_TYPE1","",0x1),
- 0x80BE5DD0:("D_80BE5DD0","f32","",0x4),
- 0x80BE5DD4:("D_80BE5DD4","UNK_TYPE2","",0x2),
- 0x80BE5E24:("D_80BE5E24","UNK_TYPE1","",0x1),
- 0x80BE5E74:("D_80BE5E74","UNK_TYPE1","",0x1),
+ 0x80BE5DD0:("sGiantModeScaleFactor","f32","",0x4),
+ 0x80BE5DD4:("sSpawnPosList","UNK_TYPE2","",0x2),
+ 0x80BE5E24:("sDLists","UNK_TYPE1","",0x1),
+ 0x80BE5E74:("sBaseScales","UNK_TYPE1","",0x1),
0x80BE5ED0:("D_80BE5ED0","f32","",0x4),
0x80BE5ED4:("D_80BE5ED4","f32","",0x4),
0x80BE5ED8:("D_80BE5ED8","f32","",0x4),