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),