Document EnTanron5 (destructible props, fragments, and item drops in Twinmold's arena) (#1319)

* Document EnTanron5 (destructible props, fragments, and item drops in Twinmold's arena)

* Respond to engineer's review

* Updated the type names, used "ruin" instead of "prop" in comments, and addressed Anon's review

* Missed this instance of "prop"

* Respond to engineer's review

* Update OutNames too
This commit is contained in:
Tom Overton 2023-07-10 15:02:22 -07:00 committed by GitHub
parent 82318a1c1b
commit 08006cc0ea
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 532 additions and 353 deletions

View File

@ -36,13 +36,13 @@
<DList Name="gTwinmoldHeadRightMandibleDL" Offset="0x6C68" />
<!-- DisplayLists and Textures for the destructible props in Twinmold's arena -->
<DList Name="gTwinmoldPyramidRuinDL" Offset="0x6FD0" />
<Texture Name="gTwinmoldPyramidRuinTLUT" OutName="pyramid_ruin_tlut" Format="rgba16" Width="4" Height="4" Offset="0x7158" />
<Texture Name="gTwinmoldPyramidRuinTex" OutName="pyramid_ruin" Format="ci4" Width="64" Height="64" Offset="0x7178" />
<DList Name="gTwinmoldRuinPyramidDL" Offset="0x6FD0" />
<Texture Name="gTwinmoldRuinPyramidTLUT" OutName="ruin_pyramid_tlut" Format="rgba16" Width="4" Height="4" Offset="0x7158" />
<Texture Name="gTwinmoldRuinPyramidTex" OutName="ruin_pyramid" Format="ci4" Width="64" Height="64" Offset="0x7178" />
<DList Name="gRuinFragmentDL" Offset="0x7A88" />
<DList Name="gTwinmoldMajoraPillarDL" Offset="0x7D18" />
<Texture Name="gTwinmoldMajoraPillarTLUT" OutName="majora_pillar_tlut" Format="rgba16" Width="4" Height="4" Offset="0x7E30" />
<Texture Name="gTwinmoldMajoraPillarTex" OutName="majora_pillar" Format="ci4" Width="64" Height="64" Offset="0x7E50" />
<DList Name="gTwinmoldRuinPillarDL" Offset="0x7D18" />
<Texture Name="gTwinmoldRuinPillarTLUT" OutName="ruin_pillar_tlut" Format="rgba16" Width="4" Height="4" Offset="0x7E30" />
<Texture Name="gTwinmoldRuinPillarTex" OutName="ruin_pillar" Format="ci4" Width="64" Height="64" Offset="0x7E50" />
<!-- Twinmold Title Card -->
<Texture Name="gTwinmoldTitleCardTex" OutName="twinmold_title_card" Format="ia8" Width="128" Height="40" Offset="0x8650" />

View File

@ -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;

View File

@ -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];

View File

@ -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);

View File

@ -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

View File

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

View File

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