Shipwright/soh/src/overlays/actors/ovl_Boss_Tw/z_boss_tw.c

5490 lines
220 KiB
C

#include "z_boss_tw.h"
#include "objects/gameplay_keep/gameplay_keep.h"
#include "objects/object_tw/object_tw.h"
#include "overlays/actors/ovl_Door_Warp1/z_door_warp1.h"
#include "soh/frame_interpolation.h"
#include <string.h>
#define FLAGS (ACTOR_FLAG_0 | ACTOR_FLAG_2 | ACTOR_FLAG_4 | ACTOR_FLAG_5)
typedef enum {
/* 0x00 */ TW_KOTAKE,
/* 0x01 */ TW_KOUME,
/* 0x02 */ TW_TWINROVA,
/* 0x64 */ TW_FIRE_BLAST = 0x64,
/* 0x65 */ TW_FIRE_BLAST_GROUND,
/* 0x66 */ TW_ICE_BLAST,
/* 0x67 */ TW_ICE_BLAST_GROUND,
/* 0x68 */ TW_DEATHBALL_KOTAKE,
/* 0x69 */ TW_DEATHBALL_KOUME
} TwinrovaType;
void BossTw_Init(Actor* thisx, GlobalContext* globalCtx);
void BossTw_Destroy(Actor* thisx, GlobalContext* globalCtx);
void BossTw_Update(Actor* thisx, GlobalContext* globalCtx);
void BossTw_Draw(Actor* thisx, GlobalContext* globalCtx);
void BossTw_Reset(void);
void BossTw_TwinrovaDamage(BossTw* this, GlobalContext* globalCtx, u8 arg2);
void BossTw_TwinrovaSetupFly(BossTw* this, GlobalContext* globalCtx);
void BossTw_DrawEffects(GlobalContext* globalCtx);
void BossTw_TwinrovaLaugh(BossTw* this, GlobalContext* globalCtx);
void BossTw_TwinrovaFly(BossTw* this, GlobalContext* globalCtx);
void BossTw_TwinrovaGetUp(BossTw* this, GlobalContext* globalCtx);
void BossTw_TwinrovaSetupGetUp(BossTw* this, GlobalContext* globalCtx);
void BossTw_TwinrovaSetupLaugh(BossTw* this, GlobalContext* globalCtx);
void BossTw_TwinrovaDoneBlastShoot(BossTw* this, GlobalContext* globalCtx);
void BossTw_TwinrovaSetupDoneBlastShoot(BossTw* this, GlobalContext* globalCtx);
void BossTw_TwinrovaSetupShootBlast(BossTw* this, GlobalContext* globalCtx);
void BossTw_TwinrovaSetupChargeBlast(BossTw* this, GlobalContext* globalCtx);
void BossTw_TwinrovaArriveAtTarget(BossTw* this, GlobalContext* globalCtx);
void BossTw_TwinrovaDeathCS(BossTw* this, GlobalContext* globalCtx);
void BossTw_TwinrovaIntroCS(BossTw* this, GlobalContext* globalCtx);
void BossTw_CSWait(BossTw* this, GlobalContext* globalCtx);
void BossTw_DeathCS(BossTw* this, GlobalContext* globalCtx);
void BossTw_TwinrovaMergeCS(BossTw* this, GlobalContext* globalCtx);
void BossTw_TwinrovaSetupMergeCS(BossTw* this, GlobalContext* globalCtx);
void BossTw_MergeCS(BossTw* this, GlobalContext* globalCtx);
void BossTw_Spin(BossTw* this, GlobalContext* globalCtx);
void BossTw_Laugh(BossTw* this, GlobalContext* globalCtx);
void BossTw_SetupLaugh(BossTw* this, GlobalContext* globalCtx);
void BossTw_FinishBeamShoot(BossTw* this, GlobalContext* globalCtx);
void BossTw_SetupFinishBeamShoot(BossTw* this, GlobalContext* globalCtx);
void BossTw_SetupHitByBeam(BossTw* this, GlobalContext* globalCtx);
void BossTw_HitByBeam(BossTw* this, GlobalContext* globalCtx);
void BossTw_Wait(BossTw* this, GlobalContext* globalCtx);
void BossTw_ShootBeam(BossTw* this, GlobalContext* globalCtx);
void BossTw_FlyTo(BossTw* this, GlobalContext* globalCtx);
void BossTw_SetupShootBeam(BossTw* this, GlobalContext* globalCtx);
void BossTw_TurnToPlayer(BossTw* this, GlobalContext* globalCtx);
void BossTw_TwinrovaUpdate(Actor* thisx, GlobalContext* globalCtx);
void BossTw_TwinrovaDraw(Actor* thisx, GlobalContext* globalCtx);
void BossTw_SetupWait(BossTw* this, GlobalContext* globalCtx);
void BossTw_TwinrovaSetupIntroCS(BossTw* this, GlobalContext* globalCtx);
void BossTw_SetupFlyTo(BossTw* this, GlobalContext* globalCtx);
void BossTw_SetupCSWait(BossTw* this, GlobalContext* globalCtx);
void BossTw_BlastUpdate(Actor* thisx, GlobalContext* globalCtx);
void BossTw_BlastDraw(Actor* thisx, GlobalContext* globalCtx);
void BossTw_BlastFire(BossTw* this, GlobalContext* globalCtx);
void BossTw_BlastIce(BossTw* this, GlobalContext* globalCtx);
void BossTw_DeathBall(BossTw* this, GlobalContext* globalCtx);
void BossTw_DrawDeathBall(Actor* thisx, GlobalContext* globalCtx);
void BossTw_TwinrovaStun(BossTw* this, GlobalContext* globalCtx);
void BossTw_TwinrovaSpin(BossTw* this, GlobalContext* globalCtx);
void BossTw_TwinrovaShootBlast(BossTw* this, GlobalContext* globalCtx);
void BossTw_TwinrovaChargeBlast(BossTw* this, GlobalContext* globalCtx);
void BossTw_TwinrovaSetupSpin(BossTw* this, GlobalContext* globalCtx);
void BossTw_UpdateEffects(GlobalContext* globalCtx);
const ActorInit Boss_Tw_InitVars = {
ACTOR_BOSS_TW,
ACTORCAT_BOSS,
FLAGS,
OBJECT_TW,
sizeof(BossTw),
(ActorFunc)BossTw_Init,
(ActorFunc)BossTw_Destroy,
(ActorFunc)BossTw_Update,
(ActorFunc)BossTw_Draw,
(ActorResetFunc)BossTw_Reset,
};
static Vec3f D_8094A7D0 = { 0.0f, 0.0f, 1000.0f };
static Vec3f sZeroVector = { 0.0f, 0.0f, 0.0f };
static ColliderCylinderInit sCylinderInitBlasts = {
{
COLTYPE_NONE,
AT_ON | AT_TYPE_ALL,
AC_ON | AC_TYPE_PLAYER,
OC1_ON | OC1_TYPE_PLAYER,
OC2_TYPE_1,
COLSHAPE_CYLINDER,
},
{
ELEMTYPE_UNK0,
{ 0xFFCFFFFF, 0x00, 0x30 },
{ 0x00100000, 0x00, 0x00 },
TOUCH_ON | TOUCH_SFX_NORMAL,
BUMP_ON,
OCELEM_ON,
},
{ 25, 35, -17, { 0, 0, 0 } },
};
static ColliderCylinderInit sCylinderInitKoumeKotake = {
{
COLTYPE_HIT3,
AT_ON | AT_TYPE_ENEMY,
AC_ON | AC_TYPE_PLAYER,
OC1_ON | OC1_TYPE_PLAYER,
OC2_TYPE_1,
COLSHAPE_CYLINDER,
},
{
ELEMTYPE_UNK0,
{ 0xFFCFFFFF, 0x00, 0x20 },
{ 0xFFCDFFFE, 0x00, 0x00 },
TOUCH_ON | TOUCH_SFX_NORMAL,
BUMP_ON,
OCELEM_ON,
},
{ 45, 120, -30, { 0, 0, 0 } },
};
static ColliderCylinderInit sCylinderInitTwinrova = {
{
COLTYPE_HIT3,
AT_ON | AT_TYPE_ENEMY,
AC_ON | AC_TYPE_PLAYER,
OC1_ON | OC1_TYPE_ALL,
OC2_TYPE_1,
COLSHAPE_CYLINDER,
},
{
ELEMTYPE_UNK0,
{ 0xFFCFFFFF, 0x00, 0x20 },
{ 0xFFCDFFFE, 0x00, 0x00 },
TOUCH_ON | TOUCH_SFX_NORMAL,
BUMP_ON | BUMP_HOOKABLE,
OCELEM_ON,
},
{ 45, 120, -30, { 0, 0, 0 } },
};
static Vec3f sTwinrovaPillarPos[] = {
{ 580.0f, 380.0f, 0.0f },
{ 0.0f, 380.0f, 580.0f },
{ -580.0f, 380.0f, 0.0f },
{ 0.0f, 380.0f, -580.0f },
};
u8 sTwInitalized = false;
static InitChainEntry sInitChain[] = {
ICHAIN_U8(targetMode, 5, ICHAIN_CONTINUE),
ICHAIN_F32_DIV1000(gravity, 0, ICHAIN_CONTINUE),
ICHAIN_F32(targetArrowOffset, 0, ICHAIN_STOP),
};
static s8 sEnvType;
static u8 sGroundBlastType;
static BossTw* sKotakePtr;
static BossTw* sKoumePtr;
static BossTw* sTwinrovaPtr;
static u8 sShieldFireCharge;
static u8 sShieldIceCharge;
static f32 D_8094C854;
static f32 D_8094C858;
static u8 sTwinrovaBlastType;
static u8 sFixedBlastType;
static u8 sFixedBlatSeq;
static u8 sFreezeState;
static Vec3f sShieldHitPos;
static s16 sShieldHitYaw;
static u8 sBeamDivertTimer;
static u8 D_8094C86F;
static u8 D_8094C870;
static s16 D_8094C872;
static s16 D_8094C874;
static s16 D_8094C876;
static u8 D_8094C878;
static s16 D_8094C87A;
static s16 D_8094C87C;
static u8 D_8094C87E;
BossTwEffect sTwEffects[150];
void BossTw_AddDotEffect(GlobalContext* globalCtx, Vec3f* initalPos, Vec3f* initalSpeed, Vec3f* accel, f32 scale,
s16 args, s16 countLimit) {
s16 i;
BossTwEffect* eff;
for (i = 0, eff = globalCtx->specialEffects; i < countLimit; i++, eff++) {
if (eff->type == TWEFF_NONE) {
eff->type = TWEFF_DOT;
eff->pos = *initalPos;
eff->curSpeed = *initalSpeed;
eff->accel = *accel;
eff->workf[EFF_SCALE] = scale / 1000.0f;
eff->alpha = 255;
eff->frame = (s16)Rand_ZeroFloat(10.0f);
eff->work[EFF_ARGS] = args;
break;
}
}
}
void BossTw_AddDmgCloud(GlobalContext* globalCtx, s16 type, Vec3f* initialPos, Vec3f* initalSpeed, Vec3f* accel,
f32 scale, s16 alpha, s16 args, s16 countLimit) {
s16 i;
BossTwEffect* eff;
for (i = 0, eff = globalCtx->specialEffects; i < countLimit; i++, eff++) {
if (eff->type == TWEFF_NONE) {
eff->type = type;
eff->pos = *initialPos;
eff->curSpeed = *initalSpeed;
eff->accel = *accel;
eff->workf[EFF_SCALE] = scale / 1000.0f;
eff->work[EFF_ARGS] = args;
eff->alpha = alpha;
eff->frame = (s16)Rand_ZeroFloat(100.0f);
break;
}
}
}
void BossTw_AddRingEffect(GlobalContext* globalCtx, Vec3f* initalPos, f32 scale, f32 arg3, s16 alpha, s16 args,
s16 arg6, s16 arg7) {
s16 i;
BossTwEffect* eff;
for (i = 0, eff = globalCtx->specialEffects; i < arg7; i++, eff++) {
if (eff->type == TWEFF_NONE) {
eff->type = TWEFF_RING;
eff->pos = *initalPos;
eff->curSpeed = sZeroVector;
eff->accel = sZeroVector;
eff->workf[EFF_SCALE] = scale * 0.0025f;
eff->workf[EFF_DIST] = arg3 * 0.0025f;
eff->work[EFF_ARGS] = args;
eff->work[EFF_UNKS1] = arg6;
eff->alpha = alpha;
eff->workf[EFF_ROLL] = Rand_ZeroFloat(M_PI);
eff->frame = 0;
break;
}
}
}
void BossTw_AddPlayerFreezeEffect(GlobalContext* globalCtx, Actor* target) {
BossTwEffect* eff;
s16 i;
for (eff = globalCtx->specialEffects, i = 0; i < ARRAY_COUNT(sTwEffects); i++, eff++) {
if (eff->type == TWEFF_NONE) {
eff->type = TWEFF_PLYR_FRZ;
eff->curSpeed = sZeroVector;
eff->accel = sZeroVector;
eff->frame = 0;
eff->target = target;
eff->workf[EFF_DIST] = 0.0f;
eff->workf[EFF_SCALE] = 0.0f;
eff->workf[EFF_ROLL] = 0.0f;
if (target == NULL) {
eff->work[EFF_ARGS] = 100;
} else {
eff->work[EFF_ARGS] = 20;
}
break;
}
}
}
void BossTw_AddFlameEffect(GlobalContext* globalCtx, Vec3f* initalPos, Vec3f* initalSpeed, Vec3f* accel, f32 scale,
s16 args) {
s16 i;
BossTwEffect* eff;
for (i = 0, eff = globalCtx->specialEffects; i < ARRAY_COUNT(sTwEffects); i++, eff++) {
if (eff->type == TWEFF_NONE) {
eff->type = TWEFF_FLAME;
eff->pos = *initalPos;
eff->curSpeed = *initalSpeed;
eff->accel = *accel;
eff->workf[EFF_SCALE] = scale / 1000.0f;
eff->work[EFF_ARGS] = args;
eff->work[EFF_UNKS1] = 0;
eff->alpha = 0;
eff->frame = (s16)Rand_ZeroFloat(1000.0f);
break;
}
}
}
void BossTw_AddMergeFlameEffect(GlobalContext* globalCtx, Vec3f* initialPos, f32 scale, f32 dist, s16 args) {
s16 i;
BossTwEffect* eff;
for (i = 0, eff = globalCtx->specialEffects; i < ARRAY_COUNT(sTwEffects); i++, eff++) {
if (eff->type == TWEFF_NONE) {
eff->type = TWEFF_MERGEFLAME;
eff->pos = *initialPos;
eff->curSpeed = sZeroVector;
eff->accel = sZeroVector;
eff->workf[EFF_SCALE] = scale / 1000.0f;
eff->work[EFF_ARGS] = args;
eff->work[EFF_UNKS1] = 0;
eff->workf[EFF_DIST] = dist;
eff->workf[EFF_ROLL] = Rand_ZeroFloat(2.0f * M_PI);
eff->alpha = 0;
eff->frame = (s16)Rand_ZeroFloat(1000.0f);
break;
}
}
}
void BossTw_AddShieldBlastEffect(GlobalContext* globalCtx, Vec3f* initalPos, Vec3f* initalSpeed, Vec3f* accel,
f32 scale, f32 arg5, s16 alpha, s16 args) {
s16 i;
BossTwEffect* eff;
for (i = 0, eff = globalCtx->specialEffects; i < ARRAY_COUNT(sTwEffects); i++, eff++) {
if (eff->type == TWEFF_NONE) {
eff->type = TWEFF_SHLD_BLST;
eff->pos = *initalPos;
eff->curSpeed = *initalSpeed;
eff->accel = *accel;
eff->workf[EFF_SCALE] = scale / 1000.0f;
eff->workf[EFF_DIST] = arg5 / 1000.0f;
eff->work[EFF_ARGS] = args;
eff->work[EFF_UNKS1] = 0;
eff->alpha = alpha;
eff->frame = (s16)Rand_ZeroFloat(1000.0f);
break;
}
}
}
void BossTw_AddShieldDeflectEffect(GlobalContext* globalCtx, f32 arg1, s16 arg2) {
s16 i;
s16 j;
BossTwEffect* eff;
Player* player = GET_PLAYER(globalCtx);
sShieldHitPos = player->bodyPartsPos[15];
sShieldHitYaw = player->actor.shape.rot.y;
for (i = 0; i < 8; i++) {
for (eff = globalCtx->specialEffects, j = 0; j < ARRAY_COUNT(sTwEffects); j++, eff++) {
if (eff->type == TWEFF_NONE) {
eff->type = TWEFF_SHLD_DEFL;
eff->pos = sShieldHitPos;
eff->curSpeed = sZeroVector;
eff->accel = sZeroVector;
eff->workf[EFF_ROLL] = i * (M_PI / 4.0f);
eff->workf[EFF_YAW] = M_PI / 2.0f;
eff->workf[EFF_DIST] = 0.0f;
eff->workf[EFF_SCALE] = arg1 / 1000.0f;
eff->work[EFF_ARGS] = arg2;
eff->work[EFF_UNKS1] = 0;
eff->alpha = 255;
eff->frame = (s16)Rand_ZeroFloat(1000.0f);
break;
}
}
}
}
void BossTw_AddShieldHitEffect(GlobalContext* globalCtx, f32 arg1, s16 arg2) {
s16 i;
s16 j;
BossTwEffect* eff;
Player* player = GET_PLAYER(globalCtx);
sShieldHitPos = player->bodyPartsPos[15];
sShieldHitYaw = player->actor.shape.rot.y;
for (i = 0; i < 8; i++) {
for (eff = globalCtx->specialEffects, j = 0; j < ARRAY_COUNT(sTwEffects); j++, eff++) {
if (eff->type == TWEFF_NONE) {
eff->type = TWEFF_SHLD_HIT;
eff->pos = sShieldHitPos;
eff->curSpeed = sZeroVector;
eff->accel = sZeroVector;
eff->workf[EFF_ROLL] = i * (M_PI / 4.0f);
eff->workf[EFF_YAW] = M_PI / 2.0f;
eff->workf[EFF_DIST] = 0.0f;
eff->workf[EFF_SCALE] = arg1 / 1000.0f;
eff->work[EFF_ARGS] = arg2;
eff->work[EFF_UNKS1] = 0;
eff->alpha = 255;
eff->frame = (s16)Rand_ZeroFloat(1000.0f);
break;
}
}
}
}
void BossTw_Init(Actor* thisx, GlobalContext* globalCtx2) {
GlobalContext* globalCtx = globalCtx2;
BossTw* this = (BossTw*)thisx;
s16 i;
Actor_ProcessInitChain(&this->actor, sInitChain);
ActorShape_Init(&this->actor.shape, 0.0f, NULL, 0.0f);
if (this->actor.params >= TW_FIRE_BLAST) {
// Blasts
Actor_SetScale(&this->actor, 0.01f);
this->actor.update = BossTw_BlastUpdate;
this->actor.draw = BossTw_BlastDraw;
this->actor.flags &= ~ACTOR_FLAG_0;
Collider_InitCylinder(globalCtx, &this->collider);
Collider_SetCylinder(globalCtx, &this->collider, &this->actor, &sCylinderInitBlasts);
if (this->actor.params == TW_FIRE_BLAST || this->actor.params == TW_FIRE_BLAST_GROUND) {
this->actionFunc = BossTw_BlastFire;
this->collider.info.toucher.effect = 1;
} else if (this->actor.params == TW_ICE_BLAST || this->actor.params == TW_ICE_BLAST_GROUND) {
this->actionFunc = BossTw_BlastIce;
} else if (this->actor.params >= TW_DEATHBALL_KOTAKE) {
this->actionFunc = BossTw_DeathBall;
this->actor.draw = BossTw_DrawDeathBall;
this->workf[TAIL_ALPHA] = 128.0f;
if (thisx->params == TW_DEATHBALL_KOTAKE) {
thisx->world.rot.y = sTwinrovaPtr->actor.world.rot.y + 0x4000;
} else {
thisx->world.rot.y = sTwinrovaPtr->actor.world.rot.y - 0x4000;
}
}
this->timers[1] = 150;
return;
}
Actor_SetScale(&this->actor, 2.5 * 0.01f);
this->actor.colChkInfo.mass = 255;
this->actor.colChkInfo.health = 0;
Collider_InitCylinder(globalCtx, &this->collider);
if (!sTwInitalized) {
sTwInitalized = true;
globalCtx->envCtx.unk_BF = 1;
globalCtx->envCtx.unk_BE = 1;
globalCtx->envCtx.unk_BD = 1;
globalCtx->envCtx.unk_D8 = 0.0f;
D_8094C874 = D_8094C876 = D_8094C878 = D_8094C87A = D_8094C87C = D_8094C87E = D_8094C870 = D_8094C86F =
D_8094C872 = sBeamDivertTimer = sEnvType = sGroundBlastType = sFreezeState = sTwinrovaBlastType =
sFixedBlatSeq = sShieldFireCharge = sShieldIceCharge = 0;
D_8094C858 = D_8094C854 = 0.0f;
sFixedBlastType = Rand_ZeroFloat(1.99f);
globalCtx->specialEffects = sTwEffects;
for (i = 0; i < ARRAY_COUNT(sTwEffects); i++) {
sTwEffects[i].type = TWEFF_NONE;
sTwEffects[i].epoch++;
}
}
if (this->actor.params == TW_KOTAKE) {
Collider_SetCylinder(globalCtx, &this->collider, &this->actor, &sCylinderInitKoumeKotake);
this->actor.naviEnemyId = 0x33;
SkelAnime_InitFlex(globalCtx, &this->skelAnime, &object_tw_Skel_0070E0, &object_tw_Anim_006F28, NULL, NULL, 0);
if (gSaveContext.eventChkInf[7] & 0x20) {
// began twinrova battle
BossTw_SetupFlyTo(this, globalCtx);
this->actor.world.pos.x = -600.0f;
this->actor.world.pos.y = 400.0f;
this->actor.world.pos.z = 0.0f;
Audio_QueueSeqCmd(SEQ_PLAYER_BGM_MAIN << 24 | NA_BGM_BOSS);
} else {
BossTw_SetupCSWait(this, globalCtx);
}
Animation_MorphToLoop(&this->skelAnime, &object_tw_Anim_006F28, -3.0f);
this->visible = true;
} else if (this->actor.params == TW_KOUME) {
Collider_SetCylinder(globalCtx, &this->collider, &this->actor, &sCylinderInitKoumeKotake);
this->actor.naviEnemyId = 0x32;
SkelAnime_InitFlex(globalCtx, &this->skelAnime, &object_tw_Skel_01F888, &object_tw_Anim_006F28, NULL, NULL, 0);
if (gSaveContext.eventChkInf[7] & 0x20) {
// began twinrova battle
BossTw_SetupFlyTo(this, globalCtx);
this->actor.world.pos.x = 600.0f;
this->actor.world.pos.y = 400.0f;
this->actor.world.pos.z = 0.0f;
} else {
BossTw_SetupCSWait(this, globalCtx);
}
Animation_MorphToLoop(&this->skelAnime, &object_tw_Anim_006F28, -3.0f);
this->visible = true;
} else {
// Twinrova
Collider_SetCylinder(globalCtx, &this->collider, &this->actor, &sCylinderInitTwinrova);
this->actor.naviEnemyId = 0x5B;
this->actor.colChkInfo.health = 24;
this->actor.update = BossTw_TwinrovaUpdate;
this->actor.draw = BossTw_TwinrovaDraw;
SkelAnime_InitFlex(globalCtx, &this->skelAnime, &object_tw_Skel_032020, &object_tw_Anim_0244B4, NULL, NULL, 0);
Animation_MorphToLoop(&this->skelAnime, &object_tw_Anim_0244B4, -3.0f);
if (gSaveContext.eventChkInf[7] & 0x20) {
// began twinrova battle
BossTw_SetupWait(this, globalCtx);
} else {
BossTw_TwinrovaSetupIntroCS(this, globalCtx);
this->actor.world.pos.x = 0.0f;
this->actor.world.pos.y = 1000.0f;
this->actor.world.pos.z = 0.0f;
}
this->actor.params = TW_TWINROVA;
sTwinrovaPtr = this;
if (Flags_GetClear(globalCtx, globalCtx->roomCtx.curRoom.num)) {
// twinrova has been defeated.
Actor_Kill(&this->actor);
Actor_SpawnAsChild(&globalCtx->actorCtx, &this->actor, globalCtx, ACTOR_DOOR_WARP1, 600.0f, 230.0f, 0.0f, 0,
0, 0, WARP_DUNGEON_ADULT);
Actor_Spawn(&globalCtx->actorCtx, globalCtx, ACTOR_ITEM_B_HEART, -600.0f, 230.0f, 0.0f, 0, 0, 0, 0);
} else {
sKotakePtr = (BossTw*)Actor_SpawnAsChild(&globalCtx->actorCtx, &this->actor, globalCtx, ACTOR_BOSS_TW,
this->actor.world.pos.x, this->actor.world.pos.y,
this->actor.world.pos.z, 0, 0, 0, TW_KOTAKE);
sKoumePtr = (BossTw*)Actor_SpawnAsChild(&globalCtx->actorCtx, &this->actor, globalCtx, ACTOR_BOSS_TW,
this->actor.world.pos.x, this->actor.world.pos.y,
this->actor.world.pos.z, 0, 0, 0, TW_KOUME);
sKotakePtr->actor.parent = &sKoumePtr->actor;
sKoumePtr->actor.parent = &sKotakePtr->actor;
}
}
this->fogR = globalCtx->lightCtx.fogColor[0];
this->fogG = globalCtx->lightCtx.fogColor[1];
this->fogB = globalCtx->lightCtx.fogColor[2];
this->fogNear = globalCtx->lightCtx.fogNear;
this->fogFar = 1000.0f;
}
void BossTw_Destroy(Actor* thisx, GlobalContext* globalCtx) {
BossTw* this = (BossTw*)thisx;
Collider_DestroyCylinder(globalCtx, &this->collider);
if (thisx->params < TW_FIRE_BLAST) {
SkelAnime_Free(&this->skelAnime, globalCtx);
}
if (thisx->params == TW_TWINROVA) {
sTwInitalized = false;
}
}
void BossTw_SetupTurnToPlayer(BossTw* this, GlobalContext* globalCtx) {
BossTw* otherTw = (BossTw*)this->actor.parent;
this->actionFunc = BossTw_TurnToPlayer;
if ((otherTw != NULL) && (otherTw->actionFunc == BossTw_ShootBeam)) {
this->timers[0] = 40;
} else {
this->timers[0] = 60;
}
this->rotateSpeed = 0.0f;
}
void BossTw_TurnToPlayer(BossTw* this, GlobalContext* globalCtx) {
BossTw* otherTw = (BossTw*)this->actor.parent;
SkelAnime_Update(&this->skelAnime);
Math_ApproachF(&this->actor.speedXZ, 0.0f, 1.0f, 1.0f);
Math_ApproachS(&this->actor.shape.rot.y, this->actor.yawTowardsPlayer, 5, this->rotateSpeed);
Math_ApproachS(&this->actor.shape.rot.x, 0, 5, this->rotateSpeed);
Math_ApproachF(&this->rotateSpeed, 4096.0f, 1.0f, 200.0f);
func_8002D908(&this->actor);
func_8002D7EC(&this->actor);
if (this->timers[0] == 0) {
if ((otherTw->actionFunc != BossTw_ShootBeam) && this->work[CAN_SHOOT]) {
this->work[CAN_SHOOT] = false;
BossTw_SetupShootBeam(this, globalCtx);
this->actor.speedXZ = 0.0f;
} else {
BossTw_SetupFlyTo(this, globalCtx);
}
}
}
void BossTw_SetupFlyTo(BossTw* this, GlobalContext* globalCtx) {
static Vec3f sPillarPositions[] = {
{ 600.0f, 400.0f, 0.0f }, { 0.0f, 400.0f, 600.0f }, { -600.0f, 400.0f, 0.0f }, { 0.0f, 400.0f, -600.0f }
};
BossTw* otherTw = (BossTw*)this->actor.parent;
this->unk_5F8 = 1;
this->actor.flags |= ACTOR_FLAG_0;
this->actionFunc = BossTw_FlyTo;
this->rotateSpeed = 0.0f;
Animation_MorphToLoop(&this->skelAnime, &object_tw_Anim_006F28, -10.0f);
if ((Rand_ZeroOne() < 0.5f) && (otherTw != NULL && otherTw->actionFunc == BossTw_ShootBeam)) {
// Other Sister is shooting a beam, go near them.
this->targetPos.x = otherTw->actor.world.pos.x + Rand_CenteredFloat(200.0f);
this->targetPos.y = Rand_ZeroFloat(200.0f) + 340.0f;
this->targetPos.z = otherTw->actor.world.pos.z + Rand_CenteredFloat(200.0f);
this->timers[0] = (s16)Rand_ZeroFloat(50.0f) + 50;
} else if (Rand_ZeroOne() < 0.5f) {
// Fly to a random spot.
this->targetPos.x = Rand_CenteredFloat(800.0f);
this->targetPos.y = Rand_ZeroFloat(200.0f) + 340.0f;
this->targetPos.z = Rand_CenteredFloat(800.0f);
this->timers[0] = (s16)Rand_ZeroFloat(50.0f) + 50;
} else {
// fly to a random pillar.
s16 idx = Rand_ZeroFloat(ARRAY_COUNT(sPillarPositions) - 0.01f);
this->targetPos = sPillarPositions[idx];
this->timers[0] = 200;
this->work[CAN_SHOOT] = true;
}
}
void BossTw_FlyTo(BossTw* this, GlobalContext* globalCtx) {
f32 xDiff;
f32 yDiff;
f32 zDiff;
f32 pitchTarget;
f32 yawTarget;
f32 xzDist;
Audio_PlayActorSound2(&this->actor, NA_SE_EN_TWINROBA_FLY - SFX_FLAG);
Math_ApproachF(&this->scepterAlpha, 0.0f, 1.0f, 10.0f);
SkelAnime_Update(&this->skelAnime);
xDiff = this->targetPos.x - this->actor.world.pos.x;
yDiff = this->targetPos.y - this->actor.world.pos.y;
zDiff = this->targetPos.z - this->actor.world.pos.z;
yawTarget = (s16)(Math_FAtan2F(xDiff, zDiff) * (32768.0f / M_PI));
xzDist = sqrtf(SQ(xDiff) + SQ(zDiff));
pitchTarget = (s16)(Math_FAtan2F(yDiff, xzDist) * (32768.0f / M_PI));
Math_ApproachS(&this->actor.world.rot.x, pitchTarget, 0xA, this->rotateSpeed);
Math_ApproachS(&this->actor.world.rot.y, yawTarget, 0xA, this->rotateSpeed);
Math_ApproachS(&this->actor.shape.rot.y, yawTarget, 0xA, this->rotateSpeed);
Math_ApproachS(&this->actor.shape.rot.x, pitchTarget, 0xA, this->rotateSpeed);
Math_ApproachF(&this->rotateSpeed, 4096.0f, 1.0f, 100.0f);
Math_ApproachF(&this->actor.speedXZ, 10.0f, 1.0f, 1.0f);
func_8002D908(&this->actor);
func_8002D7EC(&this->actor);
if ((this->timers[0] == 0) || (xzDist < 70.0f)) {
BossTw_SetupTurnToPlayer(this, globalCtx);
}
}
void BossTw_SetupShootBeam(BossTw* this, GlobalContext* globalCtx) {
Player* player = GET_PLAYER(globalCtx);
this->actionFunc = BossTw_ShootBeam;
Animation_MorphToPlayOnce(&this->skelAnime, &object_tw_Anim_007688, -5.0f);
this->workf[ANIM_SW_TGT] = Animation_GetLastFrame(&object_tw_Anim_007688);
this->timers[1] = 70;
this->targetPos = player->actor.world.pos;
this->csState1 = 0;
this->beamDist = 0.0f;
this->beamReflectionDist = 0.0f;
this->beamShootState = -1;
this->beamScale = 0.01f;
this->beamReflectionOrigin = this->beamOrigin;
this->flameAlpha = 0.0f;
this->spawnPortalAlpha = 0.0f;
this->spawnPortalScale = 2000.0f;
this->updateRate1 = 0.0f;
this->portalRotation = 0.0f;
this->updateRate2 = 0.0f;
}
void BossTw_SpawnGroundBlast(BossTw* this, GlobalContext* globalCtx, s16 blastType) {
BossTw* groundBlast;
s16 i;
Vec3f pos;
Vec3f velocity;
Vec3f accel;
for (i = 0; i < ARRAY_COUNT(sTwEffects); i++) {
velocity.x = Rand_CenteredFloat(20.0f);
velocity.y = Rand_ZeroFloat(10.0f);
velocity.z = Rand_CenteredFloat(20.0f);
accel.y = 0.2f;
accel.x = Rand_CenteredFloat(0.25f);
accel.z = Rand_CenteredFloat(0.25f);
pos = this->groundBlastPos;
BossTw_AddDotEffect(globalCtx, &pos, &velocity, &accel, (s16)Rand_ZeroFloat(2.0f) + 8, blastType, 75);
}
if (blastType == 1) {
sGroundBlastType = 1;
groundBlast = (BossTw*)Actor_SpawnAsChild(&globalCtx->actorCtx, &this->actor, globalCtx, ACTOR_BOSS_TW,
this->groundBlastPos.x, this->groundBlastPos.y,
this->groundBlastPos.z, 0, 0, 0, TW_FIRE_BLAST_GROUND);
if (groundBlast != NULL) {
if (sTwinrovaPtr->actionFunc == BossTw_Wait) {
groundBlast->timers[0] = 100;
} else {
groundBlast->timers[0] = 50;
}
sKoumePtr->workf[KM_GD_FLM_A] = sKoumePtr->workf[KM_GD_SMOKE_A] = sKoumePtr->workf[KM_GRND_CRTR_A] = 255.0f;
sKoumePtr->workf[KM_GD_FLM_SCL] = 1.0f;
sKoumePtr->workf[KM_GD_CRTR_SCL] = 0.005f;
sKoumePtr->groundBlastPos2 = groundBlast->actor.world.pos;
sEnvType = 4;
}
} else {
sGroundBlastType = 2;
groundBlast = (BossTw*)Actor_SpawnAsChild(&globalCtx->actorCtx, &this->actor, globalCtx, ACTOR_BOSS_TW,
this->groundBlastPos.x, this->groundBlastPos.y,
this->groundBlastPos.z, 0, 0, 0, TW_ICE_BLAST_GROUND);
if (groundBlast != NULL) {
if (sTwinrovaPtr->actionFunc == BossTw_Wait) {
groundBlast->timers[0] = 100;
} else {
groundBlast->timers[0] = 50;
}
sKotakePtr->workf[UNK_F11] = 50.0f;
sKotakePtr->workf[UNK_F9] = 250.0f;
sKotakePtr->workf[UNK_F12] = 0.005f;
sKotakePtr->workf[UNK_F14] = 1.0f;
sKotakePtr->workf[UNK_F16] = 70.0f;
sKotakePtr->groundBlastPos2 = groundBlast->actor.world.pos;
sEnvType = 3;
}
}
}
s32 BossTw_BeamHitPlayerCheck(BossTw* this, GlobalContext* globalCtx) {
Vec3f offset;
Vec3f beamDistFromPlayer;
Player* player = GET_PLAYER(globalCtx);
s16 i;
offset.x = player->actor.world.pos.x - this->beamOrigin.x;
offset.y = player->actor.world.pos.y - this->beamOrigin.y;
offset.z = player->actor.world.pos.z - this->beamOrigin.z;
Matrix_RotateX(-this->beamPitch, MTXMODE_NEW);
Matrix_RotateY(-this->beamYaw, MTXMODE_APPLY);
Matrix_MultVec3f(&offset, &beamDistFromPlayer);
if (fabsf(beamDistFromPlayer.x) < 20.0f && fabsf(beamDistFromPlayer.y) < 50.0f && beamDistFromPlayer.z > 100.0f &&
beamDistFromPlayer.z <= this->beamDist) {
if (sTwinrovaPtr->timers[2] == 0) {
sTwinrovaPtr->timers[2] = 150;
this->beamDist = sqrtf(SQ(offset.x) + SQ(offset.y) + SQ(offset.z));
func_8002F6D4(globalCtx, &this->actor, 3.0f, this->actor.shape.rot.y, 0.0f, 0x20);
if (this->actor.params == 0) {
if (sFreezeState == 0) {
sFreezeState = 1;
}
} else if (!player->isBurning) {
for (i = 0; i < ARRAY_COUNT(player->flameTimers); i++) {
player->flameTimers[i] = Rand_S16Offset(0, 200);
}
player->isBurning = true;
func_8002F7DC(&player->actor, player->ageProperties->unk_92 + NA_SE_VO_LI_DEMO_DAMAGE);
}
}
return true;
}
return false;
}
/**
* Checks if the beam shot by `this` will be reflected
* returns 0 if the beam will not be reflected,
* returns 1 if the beam will be reflected,
* and returns 2 if the beam will be diverted backwards
*/
s32 BossTw_CheckBeamReflection(BossTw* this, GlobalContext* globalCtx) {
Vec3f offset;
Vec3f vec;
Player* player = GET_PLAYER(globalCtx);
if (player->stateFlags1 & 0x400000 &&
(s16)(player->actor.shape.rot.y - this->actor.shape.rot.y + 0x8000) < 0x2000 &&
(s16)(player->actor.shape.rot.y - this->actor.shape.rot.y + 0x8000) > -0x2000) {
// player is shielding and facing angles are less than 45 degrees in either direction
offset.x = 0.0f;
offset.y = 0.0f;
offset.z = 10.0f;
// set beam check point to 10 units in front of link.
Matrix_RotateY(player->actor.shape.rot.y / 32768.0f * M_PI, MTXMODE_NEW);
Matrix_MultVec3f(&offset, &vec);
// calculates a vector where the origin is at the beams origin,
// and the positive z axis is pointing in the direction the beam
// is shooting
offset.x = player->actor.world.pos.x + vec.x - this->beamOrigin.x;
offset.y = player->actor.world.pos.y + vec.y - this->beamOrigin.y;
offset.z = player->actor.world.pos.z + vec.z - this->beamOrigin.z;
Matrix_RotateX(-this->beamPitch, MTXMODE_NEW);
Matrix_RotateY(-this->beamYaw, MTXMODE_APPLY);
Matrix_MultVec3f(&offset, &vec);
if (fabsf(vec.x) < 30.0f && fabsf(vec.y) < 70.0f && vec.z > 100.0f && vec.z <= this->beamDist) {
// if the beam's origin is within 30 x units, 70 y units, is farther than 100 units
// and the distance from the beams origin to 10 units in front of link is less than the beams
// current distance (the distance of the beam is equal to or longer than the distance to 10 units
// in front of link)
if (Player_HasMirrorShieldEquipped(globalCtx)) {
// player has mirror shield equipped
this->beamDist = sqrtf(SQ(offset.x) + SQ(offset.y) + SQ(offset.z));
return 1;
}
if (sBeamDivertTimer > 10) {
return 0;
}
if (sBeamDivertTimer == 0) {
// beam hit the shield, normal shield equipped,
// divert the beam backwards from link's Y rotation
BossTw_AddShieldDeflectEffect(globalCtx, 10.0f, this->actor.params);
globalCtx->envCtx.unk_D8 = 1.0f;
this->timers[0] = 10;
func_80078884(NA_SE_IT_SHIELD_REFLECT_MG2);
}
sBeamDivertTimer++;
this->beamDist = sqrtf(SQ(offset.x) + SQ(offset.y) + SQ(offset.z));
return 2;
}
}
return 0;
}
s32 BossTw_BeamReflHitCheck(BossTw* this, Vec3f* pos) {
Vec3f offset;
Vec3f beamDistFromTarget;
offset.x = pos->x - this->beamReflectionOrigin.x;
offset.y = pos->y - this->beamReflectionOrigin.y;
offset.z = pos->z - this->beamReflectionOrigin.z;
Matrix_RotateX(-this->beamReflectionPitch, MTXMODE_NEW);
Matrix_RotateY(-this->beamReflectionYaw, MTXMODE_APPLY);
Matrix_MultVec3f(&offset, &beamDistFromTarget);
if (fabsf(beamDistFromTarget.x) < 50.0f && fabsf(beamDistFromTarget.y) < 50.0f && beamDistFromTarget.z > 100.0f &&
beamDistFromTarget.z <= this->beamReflectionDist) {
this->beamReflectionDist = sqrtf(SQ(offset.x) + SQ(offset.y) + SQ(offset.z)) * 1.1f;
return true;
} else {
return false;
}
}
f32 BossTw_GetFloorY(Vec3f* pos) {
Vec3f posRotated;
if (fabsf(pos->x) < 350.0f && fabsf(pos->z) < 350.0f && pos->y < 240.0f) {
if (pos->y > 200.0f) {
return 240.0f;
}
return 35.0f;
}
if (fabsf(pos->x) < 110.0f && ((fabsf(pos->z - 600.0f) < 110.0f) || (fabsf(pos->z + 600.0f) < 110.0f)) &&
(pos->y < 230.0f)) {
if (pos->y > 190.0f) {
return 230.0f;
}
return 35.0f;
}
if (fabsf(pos->z) < 110.0f && ((fabsf(pos->x - 600.0f) < 110.0f) || (fabsf(pos->x + 600.0f) < 110.0f)) &&
(pos->y < 230.0f)) {
if (pos->y > 190.0f) {
return 230.0f;
}
return 35.0f;
}
if (pos->y < -20.0f) {
return 0.0f;
}
if (fabsf(pos->x) > 1140.0f || fabsf(pos->z) > 1140.0f) {
return 35.0f;
}
Matrix_Push();
Matrix_RotateY((45.0f * (M_PI / 180.0f)), MTXMODE_NEW);
Matrix_MultVec3f(pos, &posRotated);
Matrix_Pop();
if (fabsf(posRotated.x) > 920.0f || fabsf(posRotated.z) > 920.0f) {
return 35.0f;
}
return -100.0f;
}
void BossTw_ShootBeam(BossTw* this, GlobalContext* globalCtx) {
s16 i;
f32 xDiff;
f32 yDiff;
f32 zDiff;
f32 floorY;
Vec3f sp130;
Vec3s sp128;
Player* player = GET_PLAYER(globalCtx);
BossTw* otherTw = (BossTw*)this->actor.parent;
Input* input = &globalCtx->state.input[0];
Math_ApproachF(&this->actor.world.pos.y, 400.0f, 0.05f, this->actor.speedXZ);
Math_ApproachF(&this->actor.speedXZ, 5.0f, 1.0f, 0.25f);
SkelAnime_Update(&this->skelAnime);
this->beamRoll += -0.3f;
if (this->timers[1] != 0) {
Math_ApproachS(&this->actor.shape.rot.y, this->actor.yawTowardsPlayer, 5, this->rotateSpeed);
if ((player->stateFlags1 & 0x400000) &&
((s16)((player->actor.shape.rot.y - this->actor.shape.rot.y) + 0x8000) < 0x2000) &&
((s16)((player->actor.shape.rot.y - this->actor.shape.rot.y) + 0x8000) > -0x2000)) {
Math_ApproachF(&this->targetPos.x, player->bodyPartsPos[15].x, 1.0f, 400.0f);
Math_ApproachF(&this->targetPos.y, player->bodyPartsPos[15].y, 1.0f, 400.0f);
Math_ApproachF(&this->targetPos.z, player->bodyPartsPos[15].z, 1.0f, 400.0f);
} else {
Math_ApproachF(&this->targetPos.x, player->actor.world.pos.x, 1.0f, 400.0f);
Math_ApproachF(&this->targetPos.y, player->actor.world.pos.y + 30.0f, 1.0f, 400.0f);
Math_ApproachF(&this->targetPos.z, player->actor.world.pos.z, 1.0f, 400.0f);
}
this->timers[0] = 70;
this->groundBlastPos.x = this->groundBlastPos.y = this->groundBlastPos.z = 0.0f;
this->portalRotation += this->updateRate2 * 0.0025f;
Math_ApproachF(&this->spawnPortalAlpha, 255.0f, 1.0f, 10.0f);
Math_ApproachF(&this->updateRate2, 50.0f, 1.0f, 2.0f);
if (this->timers[1] < 50) {
if (this->timers[1] < 10) {
if (this->timers[1] == 9) {
globalCtx->envCtx.unk_D8 = 0.5f;
globalCtx->envCtx.unk_BD = 3 - this->actor.params;
Audio_PlayActorSound2(&this->actor, NA_SE_EN_TWINROBA_MASIC_SET);
}
if (this->timers[1] == 5) {
this->scepterAlpha = 255;
}
if (this->timers[1] > 4) {
s16 j;
for (j = 0; j < 2; j++) {
for (i = 0; i < ARRAY_COUNT(this->scepterFlamePos); i++) {
Vec3f pos;
Vec3f velocity;
Vec3f accel;
pos.x = this->scepterFlamePos[i].x;
pos.y = this->scepterFlamePos[i].y;
pos.z = this->scepterFlamePos[i].z;
velocity.x = Rand_CenteredFloat(10.0f);
velocity.y = Rand_CenteredFloat(10.0f);
velocity.z = Rand_CenteredFloat(10.0f);
accel.x = 0.0f;
accel.y = 0.0f;
accel.z = 0.0f;
BossTw_AddFlameEffect(globalCtx, &pos, &velocity, &accel, Rand_ZeroFloat(10.0f) + 25.0f,
this->actor.params);
}
}
}
}
if (this->timers[1] < 20) {
Math_ApproachF(&this->flameAlpha, 0, 1.0f, 20.0f);
Math_ApproachF(&this->spawnPortalAlpha, 0, 1.0f, 30.0f);
} else {
Math_ApproachF(&this->flameAlpha, 255.0f, 1.0f, 10.0f);
if (this->actor.params == 1) {
Audio_PlayActorSound2(&this->actor, NA_SE_EN_TWINROBA_MS_FIRE - SFX_FLAG);
} else {
Audio_PlayActorSound2(&this->actor, NA_SE_EN_TWINROBA_MS_FREEZE - SFX_FLAG);
}
}
this->flameRotation += this->updateRate1 * 0.0025f;
Math_ApproachF(&this->spawnPortalScale, 0.0f, 0.1f, this->updateRate1);
Math_ApproachF(&this->updateRate1, 50.0f, 1.0f, 2.0f);
}
if (Animation_OnFrame(&this->skelAnime, this->workf[ANIM_SW_TGT])) {
Animation_MorphToLoop(&this->skelAnime, &object_tw_Anim_009398, 0.0f);
this->workf[ANIM_SW_TGT] = 10000.0f;
}
if (this->timers[1] == 1) {
Animation_MorphToPlayOnce(&this->skelAnime, &object_tw_Anim_003614, 0.0f);
this->workf[ANIM_SW_TGT] = Animation_GetLastFrame(&object_tw_Anim_003614);
this->unk_4DC = 0.0f;
this->spawnPortalAlpha = 0.0f;
this->flameAlpha = 0.0f;
sBeamDivertTimer = 0;
}
} else {
if (Animation_OnFrame(&this->skelAnime, this->workf[ANIM_SW_TGT])) {
Animation_MorphToLoop(&this->skelAnime, &object_tw_Anim_003E34, 0.0f);
this->workf[ANIM_SW_TGT] = 10000.0f;
}
if (Animation_OnFrame(&this->skelAnime, this->workf[ANIM_SW_TGT] - 5.0f)) {
this->beamShootState = 0;
sEnvType = this->actor.params + 1;
}
if (Animation_OnFrame(&this->skelAnime, this->workf[ANIM_SW_TGT] - 13.0f)) {
Audio_PlayActorSound2(&this->actor, NA_SE_EN_TWINROBA_THROW_MASIC);
Audio_PlayActorSound2(&this->actor, NA_SE_EN_TWINROBA_SHOOT_VOICE);
}
xDiff = this->targetPos.x - this->beamOrigin.x;
yDiff = this->targetPos.y - this->beamOrigin.y;
zDiff = this->targetPos.z - this->beamOrigin.z;
this->beamYaw = Math_FAtan2F(xDiff, zDiff);
this->beamPitch = -Math_FAtan2F(yDiff, sqrtf(SQ(xDiff) + SQ(zDiff)));
switch (this->beamShootState) {
case -1:
break;
case 0:
if (this->timers[0] != 0) {
s32 beamReflection = BossTw_CheckBeamReflection(this, globalCtx);
if (beamReflection == 1) {
Vec3f pos;
Vec3f velocity;
Vec3f accel = { 0.0f, 0.0f, 0.0f };
for (i = 0; i < ARRAY_COUNT(sTwEffects); i++) {
velocity.x = Rand_CenteredFloat(15.0f);
velocity.y = Rand_CenteredFloat(15.0f);
velocity.z = Rand_CenteredFloat(15.0f);
pos = player->bodyPartsPos[15];
BossTw_AddDotEffect(globalCtx, &pos, &velocity, &accel, (s16)Rand_ZeroFloat(2.0f) + 5,
this->actor.params, 150);
}
this->beamShootState = 1;
func_80078914(&player->actor.projectedPos, NA_SE_IT_SHIELD_REFLECT_MG);
Matrix_MtxFToYXZRotS(&player->shieldMf, &sp128, 0);
sp128.y += 0x8000;
sp128.x = -sp128.x;
this->magicDir.x = sp128.x;
this->magicDir.y = sp128.y;
this->groundBlastPos.x = 0.0f;
this->groundBlastPos.y = 0.0f;
this->groundBlastPos.z = 0.0f;
globalCtx->envCtx.unk_D8 = 1.0f;
func_800AA000(0.0f, 0x64, 5, 4);
} else if (beamReflection == 0) {
BossTw_BeamHitPlayerCheck(this, globalCtx);
if (this->csState1 == 0) {
Math_ApproachF(&this->beamDist, 2.0f * sqrtf(SQ(xDiff) + SQ(yDiff) + SQ(zDiff)), 1.0f,
40.0f);
}
}
}
SkinMatrix_Vec3fMtxFMultXYZW(&globalCtx->viewProjectionMtxF, &this->beamReflectionOrigin,
&this->unk_54C, &this->actor.projectedW);
if (this->actor.params == 1) {
Audio_PlaySoundGeneral(NA_SE_EN_TWINROBA_SHOOT_FIRE - SFX_FLAG, &this->unk_54C, 4, &D_801333E0,
&D_801333E0, &D_801333E8);
} else {
Audio_PlaySoundGeneral(NA_SE_EN_TWINROBA_SHOOT_FREEZE - SFX_FLAG, &this->unk_54C, 4, &D_801333E0,
&D_801333E0, &D_801333E8);
}
break;
case 1:
if (CHECK_BTN_ALL(input->cur.button, BTN_R)) {
Player* player = GET_PLAYER(globalCtx);
this->beamDist = sqrtf(SQ(xDiff) + SQ(yDiff) + SQ(zDiff));
Math_ApproachF(&this->beamReflectionDist, 2000.0f, 1.0f, 40.0f);
Math_ApproachF(&this->targetPos.x, player->bodyPartsPos[15].x, 1.0f, 400.0f);
Math_ApproachF(&this->targetPos.y, player->bodyPartsPos[15].y, 1.0f, 400.0f);
Math_ApproachF(&this->targetPos.z, player->bodyPartsPos[15].z, 1.0f, 400.0f);
if ((this->work[CS_TIMER_1] % 4) == 0) {
BossTw_AddRingEffect(globalCtx, &player->bodyPartsPos[15], 0.5f, 3.0f, 0xFF, this->actor.params,
1, 150);
}
} else {
this->beamShootState = 0;
this->beamReflectionDist = 0.0f;
}
SkinMatrix_Vec3fMtxFMultXYZW(&globalCtx->viewProjectionMtxF, &this->unk_530, &this->unk_558,
&this->actor.projectedW);
if (this->actor.params == 1) {
Audio_PlaySoundGeneral(NA_SE_EN_TWINROBA_SHOOT_FIRE - SFX_FLAG, &this->unk_558, 4U, &D_801333E0,
&D_801333E0, &D_801333E8);
Audio_PlaySoundGeneral(NA_SE_EN_TWINROBA_REFL_FIRE - SFX_FLAG, &this->unk_558, 4, &D_801333E0,
&D_801333E0, &D_801333E8);
} else {
Audio_PlaySoundGeneral(NA_SE_EN_TWINROBA_SHOOT_FREEZE - SFX_FLAG, &this->unk_558, 4, &D_801333E0,
&D_801333E0, &D_801333E8);
Audio_PlaySoundGeneral(NA_SE_EN_TWINROBA_REFL_FREEZE - SFX_FLAG, &this->unk_558, 4, &D_801333E0,
&D_801333E0, &D_801333E8);
}
break;
}
if (this->timers[0] == 0 && (sEnvType == 1 || sEnvType == 2)) {
sEnvType = 0;
}
if (this->timers[0] == 0) {
Math_ApproachF(&this->beamScale, 0.0f, 1.0f, 0.0005f);
if (this->beamScale == 0.0f) {
BossTw_SetupFinishBeamShoot(this, globalCtx);
this->beamReflectionDist = 0.0f;
this->beamDist = 0.0f;
}
}
}
Matrix_Translate(this->beamOrigin.x, this->beamOrigin.y, this->beamOrigin.z, MTXMODE_NEW);
Matrix_RotateY(this->beamYaw, MTXMODE_APPLY);
Matrix_RotateX(this->beamPitch, MTXMODE_APPLY);
sp130.x = 0.0f;
sp130.y = 0.0f;
sp130.z = this->beamDist + -5.0f;
Matrix_MultVec3f(&sp130, &this->beamReflectionOrigin);
if ((this->csState1 == 0) && (this->beamShootState == 0) && (this->timers[0] != 0)) {
this->groundBlastPos.y = BossTw_GetFloorY(&this->beamReflectionOrigin);
if (this->groundBlastPos.y >= 0.0f) {
this->csState1 = 1;
this->groundBlastPos.x = this->beamReflectionOrigin.x;
this->groundBlastPos.z = this->beamReflectionOrigin.z;
BossTw_SpawnGroundBlast(this, globalCtx, this->actor.params);
this->timers[0] = 20;
}
}
if (this->beamShootState == 1) {
if (this->csState1 == 0) {
Matrix_MtxFToYXZRotS(&player->shieldMf, &sp128, 0);
sp128.y += 0x8000;
sp128.x = -sp128.x;
Math_ApproachS(&this->magicDir.x, sp128.x, 5, 0x2000);
Math_ApproachS(&this->magicDir.y, sp128.y, 5, 0x2000);
this->beamReflectionPitch = (this->magicDir.x / 32768.0f) * M_PI;
this->beamReflectionYaw = (this->magicDir.y / 32768.0f) * M_PI;
}
Matrix_Translate(this->beamReflectionOrigin.x, this->beamReflectionOrigin.y, this->beamReflectionOrigin.z,
MTXMODE_NEW);
Matrix_RotateY(this->beamReflectionYaw, MTXMODE_APPLY);
Matrix_RotateX(this->beamReflectionPitch, MTXMODE_APPLY);
sp130.x = 0.0f;
sp130.y = 0.0f;
sp130.z = this->beamReflectionDist + -170.0f;
Matrix_MultVec3f(&sp130, &this->unk_530);
if (this->csState1 == 0) {
sp130.z = 0.0f;
for (i = 0; i < 200; i++) {
Vec3f spBC;
Matrix_MultVec3f(&sp130, &spBC);
floorY = BossTw_GetFloorY(&spBC);
this->groundBlastPos.y = floorY;
if (floorY >= 0.0f) {
if ((this->groundBlastPos.y != 35.0f) && (0.0f < this->beamReflectionPitch) &&
(this->timers[0] != 0)) {
this->csState1 = 1;
this->groundBlastPos.x = spBC.x;
this->groundBlastPos.z = spBC.z;
BossTw_SpawnGroundBlast(this, globalCtx, this->actor.params);
this->timers[0] = 20;
} else {
for (i = 0; i < 5; i++) {
Vec3f velocity;
Vec3f accel;
velocity.x = Rand_CenteredFloat(20.0f);
velocity.y = Rand_CenteredFloat(20.0f);
velocity.z = Rand_CenteredFloat(20.0f);
accel.x = 0.0f;
accel.y = 0.0f;
accel.z = 0.0f;
BossTw_AddFlameEffect(globalCtx, &this->unk_530, &velocity, &accel,
Rand_ZeroFloat(10.0f) + 25.0f, this->actor.params);
}
this->beamReflectionDist = sp130.z;
Math_ApproachF(&globalCtx->envCtx.unk_D8, 0.8f, 1.0f, 0.2f);
}
break;
}
sp130.z += 20.0f;
if (this->beamReflectionDist < sp130.z) {
break;
}
}
}
if (BossTw_BeamReflHitCheck(this, &this->actor.world.pos) && (this->work[CS_TIMER_1] % 4) == 0) {
BossTw_AddRingEffect(globalCtx, &this->unk_530, 0.5f, 3.0f, 255, this->actor.params, 1, 150);
}
if (BossTw_BeamReflHitCheck(this, &otherTw->actor.world.pos) && otherTw->actionFunc != BossTw_HitByBeam) {
for (i = 0; i < 50; i++) {
Vec3f pos;
Vec3f velocity;
Vec3f accel;
pos.x = otherTw->actor.world.pos.x + Rand_CenteredFloat(50.0f);
pos.y = otherTw->actor.world.pos.y + Rand_CenteredFloat(50.0f);
pos.z = otherTw->actor.world.pos.z + Rand_CenteredFloat(50.0f);
velocity.x = Rand_CenteredFloat(20.0f);
velocity.y = Rand_CenteredFloat(20.0f);
velocity.z = Rand_CenteredFloat(20.0f);
accel.x = 0.0f;
accel.y = 0.0f;
accel.z = 0.0f;
BossTw_AddFlameEffect(globalCtx, &pos, &velocity, &accel, Rand_ZeroFloat(10.0f) + 25.0f,
this->actor.params);
}
BossTw_SetupHitByBeam(otherTw, globalCtx);
Audio_PlayActorSound2(&otherTw->actor, NA_SE_EN_TWINROBA_DAMAGE_VOICE);
globalCtx->envCtx.unk_D8 = 1.0f;
otherTw->actor.colChkInfo.health++;
}
}
}
void BossTw_SetupFinishBeamShoot(BossTw* this, GlobalContext* globalCtx) {
this->actionFunc = BossTw_FinishBeamShoot;
Animation_MorphToPlayOnce(&this->skelAnime, &object_tw_Anim_004548, 0.0f);
this->workf[ANIM_SW_TGT] = Animation_GetLastFrame(&object_tw_Anim_004548);
}
void BossTw_FinishBeamShoot(BossTw* this, GlobalContext* globalCtx) {
SkelAnime_Update(&this->skelAnime);
Math_ApproachF(&this->scepterAlpha, 0.0f, 1.0f, 10.0f);
if (Animation_OnFrame(&this->skelAnime, this->workf[ANIM_SW_TGT])) {
if (sTwinrovaPtr->timers[2] == 0) {
BossTw_SetupFlyTo(this, globalCtx);
} else {
BossTw_SetupLaugh(this, globalCtx);
}
this->scepterAlpha = 0.0f;
}
}
void BossTw_SetupHitByBeam(BossTw* this, GlobalContext* globalCtx) {
this->actionFunc = BossTw_HitByBeam;
Animation_MorphToPlayOnce(&this->skelAnime, &object_tw_Anim_00578C, 0.0f);
this->timers[0] = 53;
this->actor.speedXZ = 0.0f;
if (this->actor.params == 0) {
this->work[FOG_TIMER] = 20;
}
}
void BossTw_HitByBeam(BossTw* this, GlobalContext* globalCtx) {
SkelAnime_Update(&this->skelAnime);
if ((this->work[CS_TIMER_1] % 4) == 0) {
Vec3f pos;
Vec3f velocity;
Vec3f accel;
pos.x = this->actor.world.pos.x + Rand_CenteredFloat(80.0f);
pos.y = this->actor.world.pos.y + Rand_CenteredFloat(80.0f);
pos.z = this->actor.world.pos.z + Rand_CenteredFloat(80.0f);
velocity.x = 0.0f;
velocity.y = 0.0f;
velocity.z = 0.0f;
accel.x = 0.0f;
accel.y = 0.1f;
accel.z = 0.0f;
BossTw_AddDmgCloud(globalCtx, this->actor.params + 2, &pos, &velocity, &accel, Rand_ZeroFloat(10.0f) + 15.0f, 0,
0, 150);
}
if (this->actor.params == 1) {
Math_ApproachF(&this->fogR, 255.0f, 1.0f, 30.0f);
Math_ApproachF(&this->fogG, 255.0f, 1.0f, 30.0f);
Math_ApproachF(&this->fogB, 255.0f, 1.0f, 30.0f);
Math_ApproachF(&this->fogNear, 900.0f, 1.0f, 30.0f);
Math_ApproachF(&this->fogFar, 1099.0f, 1.0f, 30.0f);
}
Math_ApproachF(&this->actor.world.pos.y, ((Math_SinS(this->work[CS_TIMER_1] * 1500) * 20.0f) + 350.0f) + 50.0f,
0.1f, this->actor.speedXZ);
Math_ApproachF(&this->actor.speedXZ, 5.0f, 1.0f, 1.0f);
this->actor.world.pos.y -= 50.0f;
Actor_UpdateBgCheckInfo(globalCtx, &this->actor, 50.0f, 50.0f, 100.0f, 4);
this->actor.world.pos.y += 50.0f;
if (this->actor.bgCheckFlags & 1) {
this->actor.speedXZ = 0.0f;
}
if (this->timers[0] == 1) {
Animation_MorphToPlayOnce(&this->skelAnime, &object_tw_Anim_006530, 0.0f);
this->workf[ANIM_SW_TGT] = Animation_GetLastFrame(&object_tw_Anim_006530);
}
if ((this->timers[0] == 0) && Animation_OnFrame(&this->skelAnime, this->workf[ANIM_SW_TGT])) {
BossTw_SetupFlyTo(this, globalCtx);
}
}
void BossTw_SetupLaugh(BossTw* this, GlobalContext* globalCtx) {
this->actionFunc = BossTw_Laugh;
Animation_MorphToPlayOnce(&this->skelAnime, &object_tw_Anim_0088C8, 0.0f);
this->workf[ANIM_SW_TGT] = Animation_GetLastFrame(&object_tw_Anim_0088C8);
this->actor.speedXZ = 0.0f;
}
void BossTw_Laugh(BossTw* this, GlobalContext* globalCtx) {
SkelAnime_Update(&this->skelAnime);
if (Animation_OnFrame(&this->skelAnime, 10.0f)) {
if (this->actor.params == TW_KOUME) {
Audio_PlayActorSound2(&this->actor, NA_SE_EN_TWINROBA_LAUGH);
} else {
Audio_PlayActorSound2(&this->actor, NA_SE_EN_TWINROBA_LAUGH2);
}
}
if (Animation_OnFrame(&this->skelAnime, this->workf[ANIM_SW_TGT])) {
BossTw_SetupFlyTo(this, globalCtx);
}
}
void BossTw_SetupSpin(BossTw* this, GlobalContext* globalCtx) {
this->actionFunc = BossTw_Spin;
Animation_MorphToPlayOnce(&this->skelAnime, &object_tw_Anim_007CA8, -3.0f);
this->workf[ANIM_SW_TGT] = Animation_GetLastFrame(&object_tw_Anim_007CA8);
this->actor.speedXZ = 0.0f;
SkelAnime_Update(&this->skelAnime);
this->timers[0] = 20;
}
void BossTw_Spin(BossTw* this, GlobalContext* globalCtx) {
if (this->timers[0] != 0) {
this->collider.base.colType = COLTYPE_METAL;
this->actor.shape.rot.y -= 0x3000;
if ((this->timers[0] % 4) == 0) {
Audio_PlayActorSound2(&this->actor, NA_SE_EN_TWINROBA_ROLL);
}
} else {
SkelAnime_Update(&this->skelAnime);
Math_ApproachS(&this->actor.shape.rot.y, this->actor.world.rot.y, 3, 0x2000);
if (Animation_OnFrame(&this->skelAnime, this->workf[ANIM_SW_TGT])) {
BossTw_SetupFlyTo(this, globalCtx);
}
}
}
void BossTw_SetupMergeCS(BossTw* this, GlobalContext* globalCtx) {
this->actionFunc = BossTw_MergeCS;
this->rotateSpeed = 0.0f;
this->actor.speedXZ = 0.0f;
Animation_MorphToLoop(&this->skelAnime, &object_tw_Anim_006F28, -10.0f);
}
void BossTw_MergeCS(BossTw* this, GlobalContext* globalCtx) {
Math_ApproachF(&this->scepterAlpha, 0.0f, 1.0f, 10.0f);
SkelAnime_Update(&this->skelAnime);
}
void BossTw_SetupWait(BossTw* this, GlobalContext* globalCtx) {
this->actionFunc = BossTw_Wait;
this->visible = false;
this->actor.world.pos.y = -2000.0f;
this->actor.flags &= ~ACTOR_FLAG_0;
}
void BossTw_Wait(BossTw* this, GlobalContext* globalCtx) {
if ((this->actor.params == TW_TWINROVA) && (sKoumePtr->actionFunc == BossTw_FlyTo) &&
(sKotakePtr->actionFunc == BossTw_FlyTo) &&
((sKoumePtr->actor.colChkInfo.health + sKotakePtr->actor.colChkInfo.health) >= 4)) {
BossTw_TwinrovaSetupMergeCS(this, globalCtx);
BossTw_SetupMergeCS(sKotakePtr, globalCtx);
BossTw_SetupMergeCS(sKoumePtr, globalCtx);
}
}
void BossTw_TwinrovaSetupMergeCS(BossTw* this, GlobalContext* globalCtx) {
this->actionFunc = BossTw_TwinrovaMergeCS;
this->csState2 = 0;
this->csState1 = 0;
}
void BossTw_TwinrovaMergeCS(BossTw* this, GlobalContext* globalCtx) {
s16 i;
Vec3f spB0;
Vec3f spA4;
Player* player = GET_PLAYER(globalCtx);
switch (this->csState2) {
case 0:
this->csState2 = 1;
func_80064520(globalCtx, &globalCtx->csCtx);
func_8002DF54(globalCtx, &this->actor, 0x39);
this->subCamId = Gameplay_CreateSubCamera(globalCtx);
Gameplay_ChangeCameraStatus(globalCtx, 0, CAM_STAT_WAIT);
Gameplay_ChangeCameraStatus(globalCtx, this->subCamId, CAM_STAT_ACTIVE);
this->subCamDist = 800.0f;
this->subCamYaw = M_PI;
sKoumePtr->actor.world.rot.x = 0;
sKoumePtr->actor.shape.rot.x = 0;
sKotakePtr->actor.world.rot.x = 0;
sKotakePtr->actor.shape.rot.x = 0;
this->workf[UNK_F9] = 0.0f;
this->workf[UNK_F10] = 0.0f;
this->workf[UNK_F11] = 600.0f;
Audio_QueueSeqCmd(0x1 << 28 | SEQ_PLAYER_BGM_MAIN << 24 | 0xC800FF);
this->work[CS_TIMER_2] = 0;
// fallthrough
case 1:
if (this->work[CS_TIMER_2] == 20) {
Message_StartTextbox(globalCtx, 0x6059, NULL);
}
if (this->work[CS_TIMER_2] == 80) {
Message_StartTextbox(globalCtx, 0x605A, NULL);
}
this->subCamAt.x = 0.0f;
this->subCamAt.y = 440.0f;
this->subCamAt.z = 0.0f;
spB0.x = 0.0f;
spB0.y = 0.0f;
spB0.z = this->subCamDist;
Matrix_RotateY(this->subCamYaw, MTXMODE_NEW);
Matrix_MultVec3f(&spB0, &spA4);
this->subCamEye.x = spA4.x;
this->subCamEye.y = 300.0f;
this->subCamEye.z = spA4.z;
Math_ApproachF(&this->subCamYaw, 0.3f, 0.02f, 0.03f);
Math_ApproachF(&this->subCamDist, 200.0f, 0.1f, 5.0f);
break;
case 2:
spB0.x = 0.0f;
spB0.y = 0.0f;
spB0.z = this->subCamDist;
Matrix_RotateY(this->subCamYaw, MTXMODE_NEW);
Matrix_MultVec3f(&spB0, &spA4);
this->subCamEye.x = spA4.x;
this->subCamEye.z = spA4.z;
Math_ApproachF(&this->subCamEye.y, 420.0f, 0.1f, this->subCamUpdateRate * 20.0f);
Math_ApproachF(&this->subCamAt.y, 470.0f, 0.1f, this->subCamUpdateRate * 6.0f);
Math_ApproachF(&this->subCamYaw, 0.3f, 0.02f, 0.03f);
Math_ApproachF(&this->subCamDist, 60.0f, 0.1f, this->subCamUpdateRate * 32.0f);
Math_ApproachF(&this->subCamUpdateRate, 1, 1, 0.1f);
break;
}
if (this->subCamId != 0) {
if (this->unk_5F9 == 0) {
Gameplay_CameraSetAtEye(globalCtx, this->subCamId, &this->subCamAt, &this->subCamEye);
} else {
Gameplay_CameraSetAtEye(globalCtx, this->subCamId, &this->subCamAt2, &this->subCamEye2);
}
}
switch (this->csState1) {
case 0:
Audio_PlayActorSound2(&sKotakePtr->actor, NA_SE_EN_TWINROBA_FLY - SFX_FLAG);
Audio_PlayActorSound2(&sKoumePtr->actor, NA_SE_EN_TWINROBA_FLY - SFX_FLAG);
spB0.x = this->workf[UNK_F11];
spB0.y = 400.0f;
spB0.z = 0.0f;
Matrix_RotateY(this->workf[UNK_F9], MTXMODE_NEW);
Matrix_MultVec3f(&spB0, &spA4);
sKoumePtr->actor.world.pos.x = spA4.x;
sKoumePtr->actor.world.pos.y = spA4.y;
sKoumePtr->actor.world.pos.z = spA4.z;
sKoumePtr->actor.shape.rot.y = (this->workf[UNK_F9] / M_PI) * 32768.0f;
sKotakePtr->actor.world.pos.x = -spA4.x;
sKotakePtr->actor.world.pos.y = spA4.y;
sKotakePtr->actor.world.pos.z = -spA4.z;
sKotakePtr->actor.shape.rot.y = ((this->workf[UNK_F9] / M_PI) * 32768.0f) + 32768.0f;
Math_ApproachF(&this->workf[UNK_F11], 0.0f, 0.1f, 7.0f);
this->workf[UNK_F9] -= this->workf[UNK_F10];
Math_ApproachF(&this->workf[UNK_F10], 0.5f, 1, 0.0039999997f);
if (this->workf[UNK_F11] < 10.0f) {
if (!this->work[PLAYED_CHRG_SFX]) {
Audio_PlayActorSound2(&sKoumePtr->actor, NA_SE_EN_TWINROBA_POWERUP);
this->work[PLAYED_CHRG_SFX] = true;
}
Math_ApproachF(&sKoumePtr->actor.scale.x, 0.005000001f, 1, 0.0003750001f);
for (i = 0; i < 4; i++) {
Vec3f pos;
f32 yOffset;
f32 xScale;
xScale = sKoumePtr->actor.scale.x * 3000.0f;
yOffset = Rand_CenteredFloat(xScale * 2.0f);
pos.x = 3000.0f;
pos.y = 400.0f + yOffset;
pos.z = 0.0f;
BossTw_AddMergeFlameEffect(globalCtx, &pos, Rand_ZeroFloat(5.0f) + 10.0f,
sqrtf(SQ(xScale) - SQ(yOffset)), Rand_ZeroFloat(1.99f));
}
if (sKoumePtr->actor.scale.x <= 0.0051f) {
Vec3f pos;
Vec3f velocity;
Vec3f accel;
this->actor.world.pos.y = 400.0f;
for (i = 0; i < 50; i++) {
pos = this->actor.world.pos;
velocity.x = Rand_CenteredFloat(20.0f);
velocity.y = Rand_CenteredFloat(20.0f);
velocity.z = Rand_CenteredFloat(20.0f);
pos.x += velocity.x;
pos.y += velocity.y;
pos.z += velocity.z;
accel.z = accel.y = accel.x = 0.0f;
BossTw_AddFlameEffect(globalCtx, &pos, &velocity, &accel, Rand_ZeroFloat(10.0f) + 25.0f,
velocity.x < 0.0f);
}
this->csState1 = 1;
this->visible = true;
this->actor.flags |= ACTOR_FLAG_0;
this->actor.shape.rot.y = 0;
BossTw_SetupWait(sKotakePtr, globalCtx);
BossTw_SetupWait(sKoumePtr, globalCtx);
Actor_SetScale(&this->actor, 0.0f);
Animation_MorphToPlayOnce(&this->skelAnime, &object_tw_Anim_038E2C, 0.0f);
this->workf[ANIM_SW_TGT] = Animation_GetLastFrame(&object_tw_Anim_038E2C);
this->timers[0] = 50;
func_8002DF54(globalCtx, &this->actor, 2);
Audio_PlayActorSound2(&this->actor, NA_SE_EN_TWINROBA_TRANSFORM);
Audio_QueueSeqCmd(SEQ_PLAYER_BGM_MAIN << 24 | NA_BGM_BOSS);
}
}
sKotakePtr->actor.scale.x = sKotakePtr->actor.scale.y = sKotakePtr->actor.scale.z =
sKoumePtr->actor.scale.y = sKoumePtr->actor.scale.z = sKoumePtr->actor.scale.x;
break;
case 1:
if (Animation_OnFrame(&this->skelAnime, this->workf[ANIM_SW_TGT])) {
Animation_MorphToLoop(&this->skelAnime, &object_tw_Anim_032BF8, -15.0f);
}
sEnvType = -1;
globalCtx->envCtx.unk_BD = 4;
Math_ApproachF(&globalCtx->envCtx.unk_D8, 1, 1, 0.1f);
// fallthrough
case 2:
SkelAnime_Update(&this->skelAnime);
Math_ApproachF(&this->actor.scale.x, 0.0069999993f, 1, 0.0006999999f);
this->actor.scale.y = this->actor.scale.z = this->actor.scale.x;
if (this->timers[0] == 1) {
this->csState2 = 2;
this->subCamUpdateRate = 0.0f;
this->timers[1] = 65;
this->timers[2] = 90;
this->timers[3] = 50;
player->actor.world.pos.x = 0.0f;
player->actor.world.pos.y = 240.0f;
player->actor.world.pos.z = 270.0f;
player->actor.world.rot.y = player->actor.shape.rot.y = -0x8000;
this->subCamEye2.x = 0.0f;
this->subCamEye2.y = 290.0f;
this->subCamEye2.z = 222.0f;
this->subCamAt2.x = player->actor.world.pos.x;
this->subCamAt2.y = player->actor.world.pos.y + 54.0f;
this->subCamAt2.z = player->actor.world.pos.z;
}
if (this->timers[3] == 19) {
func_8002DF54(globalCtx, &this->actor, 5);
}
if (this->timers[3] == 16) {
func_8002F7DC(&player->actor, player->ageProperties->unk_92 + NA_SE_VO_LI_SURPRISE);
}
if ((this->timers[3] != 0) && (this->timers[3] < 20)) {
this->unk_5F9 = 1;
Math_ApproachF(&this->subCamEye2.z, 242.0f, 0.2f, 100.0f);
} else {
this->unk_5F9 = 0;
}
if (this->timers[1] == 8) {
this->work[TW_BLINK_IDX] = 8;
func_80078884(NA_SE_EN_TWINROBA_YOUNG_WINK);
}
if (this->timers[2] == 4) {
sEnvType = 0;
globalCtx->envCtx.unk_BE = 5;
}
if (this->timers[2] == 1) {
Camera* cam = Gameplay_GetCamera(globalCtx, MAIN_CAM);
cam->eye = this->subCamEye;
cam->eyeNext = this->subCamEye;
cam->at = this->subCamAt;
func_800C08AC(globalCtx, this->subCamId, 0);
this->subCamId = 0;
this->csState2 = this->subCamId;
func_80064534(globalCtx, &globalCtx->csCtx);
func_8002DF54(globalCtx, &this->actor, 7);
this->work[TW_PLLR_IDX] = 0;
this->targetPos = sTwinrovaPillarPos[0];
BossTw_TwinrovaSetupFly(this, globalCtx);
}
break;
}
}
void BossTw_SetupDeathCS(BossTw* this, GlobalContext* globalCtx) {
this->actionFunc = BossTw_DeathCS;
Animation_MorphToLoop(&this->skelAnime, &object_tw_Anim_0004A4, -3.0f);
this->unk_5F8 = 0;
this->work[CS_TIMER_2] = Rand_ZeroFloat(20.0f);
}
void BossTw_DeathCS(BossTw* this, GlobalContext* globalCtx) {
if (this->timers[0] == 0) {
SkelAnime_Update(&this->skelAnime);
}
Math_ApproachS(&this->actor.shape.rot.y, this->work[YAW_TGT], 5, this->rotateSpeed);
Math_ApproachF(&this->rotateSpeed, 20480.0f, 1.0f, 1000.0f);
if (sTwinrovaPtr->work[CS_TIMER_2] > 140) {
Math_ApproachF(&this->fogR, 100.0f, 1.0f, 15.0f);
Math_ApproachF(&this->fogG, 255.0f, 1.0f, 15.0f);
Math_ApproachF(&this->fogB, 255.0f, 1.0f, 15.0f);
Math_ApproachF(&this->fogNear, 850.0f, 1.0f, 15.0f);
Math_ApproachF(&this->fogFar, 1099.0f, 1.0f, 15.0f);
}
}
void BossTw_SetupCSWait(BossTw* this, GlobalContext* globalCtx) {
this->actionFunc = BossTw_CSWait;
this->visible = false;
this->actor.world.pos.y = -2000.0f;
this->actor.flags &= ~ACTOR_FLAG_0;
}
/**
* Do nothing while waiting for the inital cutscene to start
*/
void BossTw_CSWait(BossTw* this, GlobalContext* globalCtx) {
}
void BossTw_TwinrovaSetupIntroCS(BossTw* this, GlobalContext* globalCtx) {
this->actionFunc = BossTw_TwinrovaIntroCS;
this->visible = false;
this->actor.world.pos.y = -2000.0f;
this->actor.flags &= ~ACTOR_FLAG_0;
}
void BossTw_TwinrovaIntroCS(BossTw* this, GlobalContext* globalCtx) {
u8 updateCam = 0;
s16 i;
Vec3f sp90;
Vec3f sp84;
Player* player = GET_PLAYER(globalCtx);
if (this->csSfxTimer > 220 && this->csSfxTimer < 630) {
func_80078884(NA_SE_EN_TWINROBA_UNARI - SFX_FLAG);
}
if (this->csSfxTimer == 180) {
func_80078914(&D_8094A7D0, NA_SE_EN_TWINROBA_LAUGH);
func_80078914(&D_8094A7D0, NA_SE_EN_TWINROBA_LAUGH2);
Audio_QueueSeqCmd(SEQ_PLAYER_BGM_MAIN << 24 | NA_BGM_KOTAKE_KOUME);
}
this->csSfxTimer++;
switch (this->csState2) {
case 0:
this->csSfxTimer = 0;
if (SQ(player->actor.world.pos.x) + SQ(player->actor.world.pos.z) < SQ(150.0f)) {
player->actor.world.pos.x = player->actor.world.pos.z = .0f;
this->csState2 = 1;
func_80064520(globalCtx, &globalCtx->csCtx);
func_8002DF54(globalCtx, &this->actor, 0x39);
this->subCamId = Gameplay_CreateSubCamera(globalCtx);
Gameplay_ChangeCameraStatus(globalCtx, 0, CAM_STAT_WAIT);
Gameplay_ChangeCameraStatus(globalCtx, this->subCamId, CAM_STAT_ACTIVE);
this->subCamEye.x = 0.0f;
this->subCamEye.y = 350;
this->subCamEye.z = 200;
this->subCamEyeTarget.x = 450;
this->subCamEyeTarget.y = 900;
this->subCamAt.x = 0;
this->subCamAt.y = 270;
this->subCamAt.z = 0;
this->subCamAtTarget.x = 0;
this->subCamAtTarget.y = 240;
this->subCamAtTarget.z = 140;
this->subCamEyeTarget.z = 530;
this->subCamEyeStep.x = fabsf(this->subCamEyeTarget.x - this->subCamEye.x);
this->subCamEyeStep.y = fabsf(this->subCamEyeTarget.y - this->subCamEye.y);
this->subCamEyeStep.z = fabsf(this->subCamEyeTarget.z - this->subCamEye.z);
this->subCamAtStep.x = fabsf(this->subCamAtTarget.x - this->subCamAt.x);
this->subCamAtStep.y = fabsf(this->subCamAtTarget.y - this->subCamAt.y);
this->subCamAtStep.z = fabsf(this->subCamAtTarget.z - this->subCamAt.z);
this->subCamDistStep = 0.05f;
this->work[CS_TIMER_1] = 0;
}
break;
case 1:
updateCam = 1;
if (this->work[CS_TIMER_1] == 30) {
Message_StartTextbox(globalCtx, 0x6048, NULL);
}
Math_ApproachF(&this->subCamUpdateRate, 0.01f, 1.0f, 0.0001f);
if (this->work[CS_TIMER_1] > 100) {
globalCtx->envCtx.unk_BD = 0;
Math_ApproachF(&globalCtx->envCtx.unk_D8, 1.0f, 1.0f, 0.03f);
}
if (this->work[CS_TIMER_1] == 180) {
func_80078884(NA_SE_EN_TWINROBA_APPEAR_MS);
}
if (this->work[CS_TIMER_1] > 180) {
this->spawnPortalScale = 0.05f;
Math_ApproachF(&this->spawnPortalAlpha, 255.0f, 1.0f, 5.f);
if (this->work[CS_TIMER_1] >= 236) {
this->csState2 = 2;
sKoumePtr->visible = 1;
Animation_MorphToLoop(&sKoumePtr->skelAnime, &object_tw_Anim_0004A4, 0.0f);
sKoumePtr->actor.world.pos.x = 0.0f;
sKoumePtr->actor.world.pos.y = 80.0f;
sKoumePtr->actor.world.pos.z = 600.0f;
sKoumePtr->actor.shape.rot.y = sKoumePtr->actor.world.rot.y = -0x8000;
this->subCamEye.x = -30;
this->subCamEye.y = 260;
this->subCamEye.z = 470;
this->subCamAt.x = 0.0F;
this->subCamAt.y = 270;
this->subCamAt.z = 600.0F;
this->work[CS_TIMER_1] = 0;
Actor_SetScale(&sKoumePtr->actor, 0.014999999f);
}
}
break;
case 2:
SkelAnime_Update(&sKoumePtr->skelAnime);
Math_ApproachF(&sKoumePtr->actor.world.pos.y, 240.0f, 0.05f, 5.0f);
this->subCamEye.x -= 0.2f;
this->subCamEye.z += 0.2f;
if (this->work[CS_TIMER_1] > 50) {
this->csState2 = 3;
this->subCamEyeTarget.x = -30;
this->subCamEyeTarget.y = 260;
this->subCamEyeTarget.z = 530;
this->subCamAtTarget.x = 0.0f;
this->subCamAtTarget.y = 265;
this->subCamAtTarget.z = 580;
this->subCamEyeStep.x = fabsf(this->subCamEyeTarget.x - this->subCamEye.x);
this->subCamEyeStep.y = fabsf(this->subCamEyeTarget.y - this->subCamEye.y);
this->subCamEyeStep.z = fabsf(this->subCamEyeTarget.z - this->subCamEye.z);
this->subCamAtStep.x = fabsf(this->subCamAtTarget.x - this->subCamAt.x);
this->subCamAtStep.y = fabsf(this->subCamAtTarget.y - this->subCamAt.y);
this->subCamAtStep.z = fabsf(this->subCamAtTarget.z - this->subCamAt.z);
this->subCamUpdateRate = 0;
this->subCamDistStep = 0.1f;
this->work[CS_TIMER_1] = 0;
}
break;
case 3:
SkelAnime_Update(&sKoumePtr->skelAnime);
updateCam = 1;
Math_ApproachF(&sKoumePtr->actor.world.pos.y, 240.0f, 0.05f, 5.0f);
Math_ApproachF(&this->subCamUpdateRate, 1.0f, 1.0f, 0.02f);
if (this->work[CS_TIMER_1] == 30) {
Message_StartTextbox(globalCtx, 0x6049, NULL);
}
if (this->work[CS_TIMER_1] > 80) {
this->csState2 = 4;
this->actor.speedXZ = 0;
this->subCamEyeTarget.x = -80.0f;
this->subCamEyeTarget.y = 260.0f;
this->subCamEyeTarget.z = 430.0f;
this->subCamAtTarget.x = sKoumePtr->actor.world.pos.x;
this->subCamAtTarget.y = sKoumePtr->actor.world.pos.y + 20.0f;
this->subCamAtTarget.z = sKoumePtr->actor.world.pos.z;
this->subCamEyeStep.x = fabsf(this->subCamEyeTarget.x - this->subCamEye.x);
this->subCamEyeStep.y = fabsf(this->subCamEyeTarget.y - this->subCamEye.y);
this->subCamEyeStep.z = fabsf(this->subCamEyeTarget.z - this->subCamEye.z);
this->subCamAtStep.x = fabsf(this->subCamAtTarget.x - this->subCamAt.x);
this->subCamAtStep.y = fabsf(this->subCamAtTarget.y - this->subCamAt.y);
this->subCamAtStep.z = fabsf(this->subCamAtTarget.z - this->subCamAt.z);
this->subCamUpdateRate = 0.0f;
this->subCamDistStep = 0.05f;
Animation_MorphToPlayOnce(&sKoumePtr->skelAnime, &object_tw_Anim_000AAC, 0.0f);
this->workf[ANIM_SW_TGT] = Animation_GetLastFrame(&object_tw_Anim_000AAC);
this->work[CS_TIMER_1] = 0;
}
break;
case 4:
updateCam = 1;
SkelAnime_Update(&sKoumePtr->skelAnime);
this->subCamAtTarget.y = 20.0f + sKoumePtr->actor.world.pos.y;
Math_ApproachF(&sKoumePtr->actor.world.pos.y, 350, 0.1f, this->actor.speedXZ);
Math_ApproachF(&this->actor.speedXZ, 9.0f, 1.0f, 0.9f);
Math_ApproachF(&this->subCamUpdateRate, 1.0f, 1.0f, 0.02f);
if (this->work[CS_TIMER_1] >= 30) {
if (this->work[CS_TIMER_1] < 45) {
globalCtx->envCtx.unk_BE = 0;
globalCtx->envCtx.unk_BD = 2;
globalCtx->envCtx.unk_D8 = 1.0f;
} else {
Math_ApproachZeroF(&globalCtx->envCtx.unk_D8, 1.0f, 0.1f);
}
if (this->work[CS_TIMER_1] == 30) {
for (i = 0; i < 50; i++) {
Vec3f pos;
Vec3f velocity;
pos.x = sKoumePtr->actor.world.pos.x + Rand_CenteredFloat(50.0f);
pos.y = sKoumePtr->actor.world.pos.y + Rand_CenteredFloat(50.0f);
pos.z = sKoumePtr->actor.world.pos.z + Rand_CenteredFloat(50.0f);
velocity.x = Rand_CenteredFloat(20.0f);
velocity.y = Rand_CenteredFloat(20.0f);
velocity.z = Rand_CenteredFloat(20.0f);
BossTw_AddFlameEffect(globalCtx, &pos, &velocity, &sZeroVector, Rand_ZeroFloat(10.0f) + 25.0f,
1);
}
Audio_PlayActorSound2(&sKoumePtr->actor, NA_SE_EN_TWINROBA_TRANSFORM);
globalCtx->envCtx.unk_D8 = 0;
}
if (this->work[CS_TIMER_1] >= 35) {
if (this->work[CS_TIMER_1] < 50) {
Math_ApproachF(&sKoumePtr->actor.scale.x,
((Math_SinS(this->work[CS_TIMER_1] * 0x4200) * 20.0f) / 10000.0f) + 0.024999999f,
1.0f, 0.005f);
} else {
if (this->work[CS_TIMER_1] == 50) {
Animation_MorphToPlayOnce(&sKoumePtr->skelAnime, &object_tw_Anim_0088C8, -5);
this->workf[ANIM_SW_TGT] = Animation_GetLastFrame(&object_tw_Anim_0088C8);
}
if (this->work[CS_TIMER_1] == 60) {
Audio_PlayActorSound2(&sKoumePtr->actor, NA_SE_EN_TWINROBA_LAUGH);
}
if (Animation_OnFrame(&sKoumePtr->skelAnime, this->workf[ANIM_SW_TGT])) {
Animation_MorphToLoop(&sKoumePtr->skelAnime, &object_tw_Anim_006F28, 0.f);
this->workf[ANIM_SW_TGT] = 1000.0f;
}
Math_ApproachF(&sKoumePtr->actor.scale.x, 0.024999999f, 0.1f, 0.005f);
}
Actor_SetScale(&sKoumePtr->actor, sKoumePtr->actor.scale.x);
sKoumePtr->actor.shape.rot.y = -0x8000;
sKoumePtr->unk_5F8 = 1;
if (this->work[CS_TIMER_1] == 0x64) {
this->csState2 = 10;
this->work[CS_TIMER_1] = 0;
this->subCamYawStep = 0.0f;
sKotakePtr->visible = 1;
Animation_MorphToLoop(&sKotakePtr->skelAnime, &object_tw_Anim_0004A4, 0.0f);
sKotakePtr->actor.world.pos.x = 0.0f;
sKotakePtr->actor.world.pos.y = 80.0f;
sKotakePtr->actor.world.pos.z = -600.0f;
sKotakePtr->actor.shape.rot.y = sKotakePtr->actor.world.rot.y = 0;
this->work[CS_TIMER_1] = 0;
this->subCamEye.x = -30.0f;
this->subCamEye.y = 260.0f;
this->subCamEye.z = -470.0f;
this->subCamAt.x = 0;
this->subCamAt.y = 270.0f;
this->subCamAt.z = -600.0f;
Actor_SetScale(&sKotakePtr->actor, 0.014999999f);
}
} else {
sKoumePtr->actor.shape.rot.y = sKoumePtr->actor.shape.rot.y + (s16)this->subCamYawStep;
}
} else {
if ((this->work[CS_TIMER_1] % 8) == 0) {
Audio_PlayActorSound2(&sKoumePtr->actor, NA_SE_EN_TWINROBA_ROLL);
}
sKoumePtr->actor.shape.rot.y = sKoumePtr->actor.shape.rot.y + (s16)this->subCamYawStep;
Math_ApproachF(&this->subCamYawStep, 12288.0f, 1.0f, 384.0f);
if (Animation_OnFrame(&sKoumePtr->skelAnime, this->workf[ANIM_SW_TGT])) {
Animation_MorphToLoop(&sKoumePtr->skelAnime, &object_tw_Anim_006F28, 0.0f);
this->workf[ANIM_SW_TGT] = 1000.0f;
}
}
break;
case 10:
SkelAnime_Update(&sKotakePtr->skelAnime);
Math_ApproachF(&sKotakePtr->actor.world.pos.y, 240.0f, 0.05f, 5.0f);
this->subCamEye.x -= 0.2f;
this->subCamEye.z -= 0.2f;
if (this->work[CS_TIMER_1] >= 0x33) {
this->csState2 = 11;
this->subCamEyeTarget.x = -30;
this->subCamEyeTarget.y = 260;
this->subCamEyeTarget.z = -530;
this->subCamAtTarget.x = 0;
this->subCamAtTarget.y = 265;
this->subCamAtTarget.z = -580;
this->subCamEyeStep.x = fabsf(this->subCamEyeTarget.x - this->subCamEye.x);
this->subCamEyeStep.y = fabsf(this->subCamEyeTarget.y - this->subCamEye.y);
this->subCamEyeStep.z = fabsf(this->subCamEyeTarget.z - this->subCamEye.z);
this->subCamAtStep.x = fabsf(this->subCamAtTarget.x - this->subCamAt.x);
this->subCamAtStep.y = fabsf(this->subCamAtTarget.y - this->subCamAt.y);
this->subCamAtStep.z = fabsf(this->subCamAtTarget.z - this->subCamAt.z);
this->subCamUpdateRate = 0;
this->subCamDistStep = 0.1f;
this->work[CS_TIMER_1] = 0;
}
break;
case 11:
SkelAnime_Update(&sKotakePtr->skelAnime);
updateCam = 1;
Math_ApproachF(&sKotakePtr->actor.world.pos.y, 240.0f, 0.05f, 5.0f);
Math_ApproachF(&this->subCamUpdateRate, 1.0f, 1.0f, 0.02f);
if (this->work[CS_TIMER_1] == 30) {
Message_StartTextbox(globalCtx, 0x604A, NULL);
}
if (this->work[CS_TIMER_1] > 80) {
this->csState2 = 12;
this->actor.speedXZ = 0;
this->subCamEyeTarget.y = 260.0f;
this->subCamEyeTarget.x = -80.0f;
this->subCamEyeTarget.z = -430.0f;
this->subCamAtTarget.x = sKotakePtr->actor.world.pos.x;
this->subCamAtTarget.y = sKotakePtr->actor.world.pos.y + 20.0f;
this->subCamAtTarget.z = sKotakePtr->actor.world.pos.z;
this->subCamEyeStep.x = fabsf(this->subCamEyeTarget.x - this->subCamEye.x);
this->subCamEyeStep.y = fabsf(this->subCamEyeTarget.y - this->subCamEye.y);
this->subCamEyeStep.z = fabsf(this->subCamEyeTarget.z - this->subCamEye.z);
this->subCamAtStep.x = fabsf(this->subCamAtTarget.x - this->subCamAt.x);
this->subCamAtStep.y = fabsf(this->subCamAtTarget.y - this->subCamAt.y);
this->subCamAtStep.z = fabsf(this->subCamAtTarget.z - this->subCamAt.z);
this->subCamUpdateRate = 0;
this->subCamDistStep = 0.05f;
Animation_MorphToPlayOnce(&sKotakePtr->skelAnime, &object_tw_Anim_000AAC, 0);
this->workf[ANIM_SW_TGT] = Animation_GetLastFrame(&object_tw_Anim_000AAC);
this->work[CS_TIMER_1] = 0;
}
break;
case 12:
updateCam = 1;
SkelAnime_Update(&sKotakePtr->skelAnime);
this->subCamAtTarget.y = sKotakePtr->actor.world.pos.y + 20.0f;
Math_ApproachF(&sKotakePtr->actor.world.pos.y, 350, 0.1f, this->actor.speedXZ);
Math_ApproachF(&this->actor.speedXZ, 9.0f, 1.0f, 0.9f);
Math_ApproachF(&this->subCamUpdateRate, 1.0f, 1.0f, 0.02f);
if (this->work[CS_TIMER_1] >= 30) {
if (this->work[CS_TIMER_1] < 45) {
globalCtx->envCtx.unk_BD = 3;
globalCtx->envCtx.unk_D8 = 1.0f;
} else {
Math_ApproachZeroF(&globalCtx->envCtx.unk_D8, 1.0f, 0.1f);
}
if (this->work[CS_TIMER_1] == 30) {
for (i = 0; i < 50; i++) {
Vec3f pos;
Vec3f velocity;
pos.x = sKotakePtr->actor.world.pos.x + Rand_CenteredFloat(50.0f);
pos.y = sKotakePtr->actor.world.pos.y + Rand_CenteredFloat(50.0f);
pos.z = sKotakePtr->actor.world.pos.z + Rand_CenteredFloat(50.0f);
velocity.x = Rand_CenteredFloat(20.0f);
velocity.y = Rand_CenteredFloat(20.0f);
velocity.z = Rand_CenteredFloat(20.0f);
BossTw_AddFlameEffect(globalCtx, &pos, &velocity, &sZeroVector, Rand_ZeroFloat(10.f) + 25.0f,
0);
}
Audio_PlayActorSound2(&sKotakePtr->actor, NA_SE_EN_TWINROBA_TRANSFORM);
globalCtx->envCtx.unk_D8 = 0.0f;
}
if (this->work[CS_TIMER_1] >= 35) {
if (this->work[CS_TIMER_1] < 50) {
Math_ApproachF(&sKotakePtr->actor.scale.x,
((Math_SinS(this->work[CS_TIMER_1] * 0x4200) * 20.0f) / 10000.0f) + 0.024999999f,
1.0f, 0.005f);
} else {
if (this->work[CS_TIMER_1] == 50) {
Animation_MorphToPlayOnce(&sKotakePtr->skelAnime, &object_tw_Anim_0088C8, -5.0f);
this->workf[ANIM_SW_TGT] = Animation_GetLastFrame(&object_tw_Anim_0088C8);
}
if (this->work[CS_TIMER_1] == 60) {
Audio_PlayActorSound2(&sKotakePtr->actor, NA_SE_EN_TWINROBA_LAUGH2);
}
if (Animation_OnFrame(&sKotakePtr->skelAnime, this->workf[ANIM_SW_TGT])) {
Animation_MorphToLoop(&sKotakePtr->skelAnime, &object_tw_Anim_006F28, 0.0f);
this->workf[ANIM_SW_TGT] = 1000.0f;
}
Math_ApproachF(&sKotakePtr->actor.scale.x, 0.024999999f, 0.1f, 0.005f);
}
Actor_SetScale(&sKotakePtr->actor, sKotakePtr->actor.scale.x);
sKotakePtr->actor.shape.rot.y = 0;
sKotakePtr->unk_5F8 = 1;
if (this->work[CS_TIMER_1] == 100) {
this->csState2 = 20;
this->work[CS_TIMER_1] = 0;
this->workf[UNK_F11] = 600.0f;
this->subCamEye.x = 800.0f;
this->subCamEye.y = 300.0f;
this->subCamEye.z = 0;
this->subCamAt.x = 0.0f;
this->subCamAt.y = 400.0f;
this->subCamAt.z = 0;
this->workf[UNK_F9] = -M_PI / 2.0f;
this->workf[UNK_F10] = 0.0f;
this->subCamEyeStep.x = 0.0f;
this->spawnPortalAlpha = 0.0f;
}
} else {
sKotakePtr->actor.shape.rot.y = sKotakePtr->actor.shape.rot.y + (s16)this->subCamYawStep;
}
} else {
if ((this->work[CS_TIMER_1] % 8) == 0) {
Audio_PlayActorSound2(&sKotakePtr->actor, NA_SE_EN_TWINROBA_ROLL);
}
sKotakePtr->actor.shape.rot.y = sKotakePtr->actor.shape.rot.y + (s16)this->subCamYawStep;
Math_ApproachF(&this->subCamYawStep, 12288.0f, 1.0f, 384.0f);
if (Animation_OnFrame(&sKotakePtr->skelAnime, this->workf[ANIM_SW_TGT])) {
Animation_MorphToLoop(&sKotakePtr->skelAnime, &object_tw_Anim_006F28, 0.0f);
this->workf[ANIM_SW_TGT] = 1000.0f;
}
}
break;
case 20:
if (this->work[CS_TIMER_1] > 20 && this->work[CS_TIMER_1] < 120) {
globalCtx->envCtx.unk_BD = 1;
Math_ApproachF(&globalCtx->envCtx.unk_D8, 1.0f, 1.0f, 0.015f);
}
if (this->work[CS_TIMER_1] == 90) {
Audio_QueueSeqCmd(0x1 << 28 | SEQ_PLAYER_BGM_MAIN << 24 | 0x5A00FF);
}
if (this->work[CS_TIMER_1] == 120) {
sEnvType = 0;
globalCtx->envCtx.unk_BE = 1;
globalCtx->envCtx.unk_BD = 1;
globalCtx->envCtx.unk_D8 = 0.0f;
TitleCard_InitBossName(globalCtx, &globalCtx->actorCtx.titleCtx, SEGMENTED_TO_VIRTUAL(gTwinrovaTitleCardTex), 160, 180, 128, 40, true);
gSaveContext.eventChkInf[7] |= 0x20;
Audio_QueueSeqCmd(SEQ_PLAYER_BGM_MAIN << 24 | NA_BGM_BOSS);
}
if (this->work[CS_TIMER_1] >= 160) {
if (this->work[CS_TIMER_1] == 160) {
this->subCamEyeStep.x = 0.0f;
}
Math_ApproachF(&this->subCamEye.x, 0.0f, 0.05f, this->subCamEyeStep.x * 0.5f);
Math_ApproachF(&this->subCamEye.z, 1000.0f, 0.05f, this->subCamEyeStep.x);
Math_ApproachF(&this->subCamEyeStep.x, 40.0f, 1.0f, 1);
} else {
Math_ApproachF(&this->subCamEye.x, 300.0f, 0.05f, this->subCamEyeStep.x);
Math_ApproachF(&this->subCamEyeStep.x, 5.0f, 1.0f, 0.5f);
}
if (this->work[CS_TIMER_1] < 200) {
Audio_PlayActorSound2(&sKoumePtr->actor, NA_SE_EN_TWINROBA_FLY - SFX_FLAG);
Audio_PlayActorSound2(&sKotakePtr->actor, NA_SE_EN_TWINROBA_FLY - SFX_FLAG);
sp90.x = this->workf[UNK_F11];
sp90.y = 400.0f;
sp90.z = 0.0f;
Matrix_RotateY(this->workf[UNK_F9], MTXMODE_NEW);
Matrix_MultVec3f(&sp90, &sp84);
sKoumePtr->actor.world.pos.x = sp84.x;
sKoumePtr->actor.world.pos.y = sp84.y;
sKoumePtr->actor.world.pos.z = sp84.z;
sKoumePtr->actor.world.rot.y = sKoumePtr->actor.shape.rot.y = (this->workf[UNK_F9] / M_PI) * 32768.0f;
sKotakePtr->actor.world.pos.x = -sp84.x;
sKotakePtr->actor.world.pos.y = sp84.y;
sKotakePtr->actor.world.pos.z = -sp84.z;
sKotakePtr->actor.shape.rot.y = sKotakePtr->actor.world.rot.y =
((this->workf[UNK_F9] / M_PI) * 32768.0f) + 32768.0f;
Math_ApproachF(&this->workf[UNK_F11], 80.0f, 0.1f, 5.0f);
this->workf[UNK_F9] -= this->workf[UNK_F10];
Math_ApproachF(&this->workf[UNK_F10], 0.19999999f, 1.0f, 0.0019999994f);
}
if (this->work[CS_TIMER_1] == 200) {
sKoumePtr->actionFunc = BossTw_FlyTo;
sKotakePtr->actionFunc = BossTw_FlyTo;
sKoumePtr->targetPos.x = 600.0f;
sKoumePtr->targetPos.y = 400.0f;
sKoumePtr->targetPos.z = 0.0f;
sKoumePtr->timers[0] = 100;
sKotakePtr->targetPos.x = -600.0f;
sKotakePtr->targetPos.y = 400.0f;
sKotakePtr->targetPos.z = 0.0f;
sKotakePtr->timers[0] = 100;
}
if (this->work[CS_TIMER_1] == 260) {
Camera* cam = Gameplay_GetCamera(globalCtx, MAIN_CAM);
cam->eye = this->subCamEye;
cam->eyeNext = this->subCamEye;
cam->at = this->subCamAt;
func_800C08AC(globalCtx, this->subCamId, 0);
this->subCamId = 0;
this->csState2 = this->subCamId;
func_80064534(globalCtx, &globalCtx->csCtx);
func_8002DF54(globalCtx, &this->actor, 7);
BossTw_SetupWait(this, globalCtx);
}
break;
}
if (this->subCamId != 0) {
if (updateCam) {
Math_ApproachF(&this->subCamEye.x, this->subCamEyeTarget.x, this->subCamDistStep,
this->subCamEyeStep.x * this->subCamUpdateRate);
Math_ApproachF(&this->subCamEye.y, this->subCamEyeTarget.y, this->subCamDistStep,
this->subCamEyeStep.y * this->subCamUpdateRate);
Math_ApproachF(&this->subCamEye.z, this->subCamEyeTarget.z, this->subCamDistStep,
this->subCamEyeStep.z * this->subCamUpdateRate);
Math_ApproachF(&this->subCamAt.x, this->subCamAtTarget.x, this->subCamDistStep,
this->subCamAtStep.x * this->subCamUpdateRate);
Math_ApproachF(&this->subCamAt.y, this->subCamAtTarget.y, this->subCamDistStep,
this->subCamAtStep.y * this->subCamUpdateRate);
Math_ApproachF(&this->subCamAt.z, this->subCamAtTarget.z, this->subCamDistStep,
this->subCamAtStep.z * this->subCamUpdateRate);
}
Gameplay_CameraSetAtEye(globalCtx, this->subCamId, &this->subCamAt, &this->subCamEye);
}
}
void BossTw_DeathBall(BossTw* this, GlobalContext* globalCtx) {
f32 xDiff;
f32 yDiff;
f32 zDiff;
s32 pad;
s16 i;
s16 yaw;
if ((this->work[CS_TIMER_1] % 16) == 0) {
Audio_PlayActorSound2(&this->actor, NA_SE_EN_TWINROBA_FB_FLY);
}
if (sTwinrovaPtr->csState2 < 2) {
if (this->timers[0] == 0) {
this->timers[0] = 20;
this->targetPos.x = Rand_CenteredFloat(100.0f) + sTwinrovaPtr->actor.world.pos.x;
this->targetPos.y = Rand_CenteredFloat(50.0f) + 400.0f;
this->targetPos.z = Rand_CenteredFloat(100.0f) + sTwinrovaPtr->actor.world.pos.z;
}
this->timers[1] = 10;
this->rotateSpeed = 8192.0f;
this->actor.speedXZ = 5.0f;
} else {
if (this->timers[1] == 9) {
this->targetPos.y = 413.0f;
this->actor.world.pos.z = 0.0f;
this->actor.world.pos.x = 0.0f;
for (i = 0; i < ARRAY_COUNT(this->blastTailPos); i++) {
this->blastTailPos[i] = this->actor.world.pos;
}
}
if (this->actor.params == 0x69) {
this->targetPos.x = sKoumePtr->actor.world.pos.x;
this->targetPos.z = sKoumePtr->actor.world.pos.z;
} else {
this->targetPos.x = sKotakePtr->actor.world.pos.x;
this->targetPos.z = sKotakePtr->actor.world.pos.z;
}
Math_ApproachF(&this->targetPos.y, 263.0f, 1.0f, 2.0f);
if (this->targetPos.y == 263.0f) {
Math_ApproachF(&this->actor.speedXZ, 0.0f, 1.0f, 0.2f);
if (sTwinrovaPtr->csState2 == 3) {
Actor_Kill(&this->actor);
}
}
}
xDiff = this->targetPos.x - this->actor.world.pos.x;
yDiff = this->targetPos.y - this->actor.world.pos.y;
zDiff = this->targetPos.z - this->actor.world.pos.z;
yaw = Math_FAtan2F(xDiff, zDiff) * (32768 / M_PI);
Math_ApproachS(&this->actor.world.rot.x, Math_FAtan2F(yDiff, sqrtf(SQ(xDiff) + SQ(zDiff))) * (32768 / M_PI), 5,
this->rotateSpeed);
Math_ApproachS(&this->actor.world.rot.y, yaw, 5, this->rotateSpeed);
func_8002D908(&this->actor);
func_8002D7EC(&this->actor);
}
void BossTw_TwinrovaSetupDeathCS(BossTw* this, GlobalContext* globalCtx) {
this->actionFunc = BossTw_TwinrovaDeathCS;
Animation_MorphToLoop(&this->skelAnime, &object_tw_Anim_024374, -3.0f);
this->actor.world.rot.y = this->actor.shape.rot.y;
this->actor.flags &= ~ACTOR_FLAG_0;
this->csState2 = this->csState1 = 0;
this->work[CS_TIMER_1] = this->work[CS_TIMER_2] = 0;
this->work[INVINC_TIMER] = 10000;
BossTw_SetupDeathCS(sKoumePtr, globalCtx);
BossTw_SetupDeathCS(sKotakePtr, globalCtx);
sKotakePtr->timers[0] = 8;
this->workf[UNK_F19] = 1.0f;
}
void BossTw_DeathCSMsgSfx(BossTw* this, GlobalContext* globalCtx) {
s32 pad;
s32 pad2;
s32 pad3;
s16 msgId2;
s16 msgId1;
u8 kotakeAnim;
u8 koumeAnim;
u8 sp35;
msgId2 = 0;
msgId1 = 0;
kotakeAnim = 0;
koumeAnim = 0;
sp35 = 0;
// Skip ahead to last part of the cutscene in rando
if (this->work[CS_TIMER_2] == 10 && gSaveContext.n64ddFlag) {
this->work[CS_TIMER_2] = 860;
}
if (this->work[CS_TIMER_2] == 80) {
koumeAnim = 1;
}
if (this->work[CS_TIMER_2] == 80) {
msgId2 = 0x604B;
sp35 = 50;
}
if (this->work[CS_TIMER_2] == 140) {
kotakeAnim = koumeAnim = 2;
}
if (this->work[CS_TIMER_2] == 170) {
kotakeAnim = 3;
sKotakePtr->work[YAW_TGT] = -0x4000;
sKotakePtr->rotateSpeed = 0.0f;
Audio_PlayActorSound2(&sKotakePtr->actor, NA_SE_EN_TWINROBA_SENSE);
msgId2 = 0x604C;
}
if (this->work[CS_TIMER_2] == 210) {
D_8094C874 = 30;
}
if (this->work[CS_TIMER_2] == 270) {
koumeAnim = 3;
sKoumePtr->work[YAW_TGT] = 0x4000;
sKoumePtr->rotateSpeed = 0.0f;
Audio_PlayActorSound2(&sKoumePtr->actor, NA_SE_EN_TWINROBA_SENSE);
}
if (this->work[CS_TIMER_2] == 290) {
msgId2 = 0x604D;
sp35 = 35;
}
if (this->work[CS_TIMER_2] == 350) {
koumeAnim = kotakeAnim = 2;
sKoumePtr->work[YAW_TGT] = sKotakePtr->work[YAW_TGT] = 0;
sKoumePtr->rotateSpeed = sKotakePtr->rotateSpeed = 0.0f;
}
if (this->work[CS_TIMER_2] == 380) {
koumeAnim = kotakeAnim = 3;
}
if (this->work[CS_TIMER_2] == 400) {
koumeAnim = kotakeAnim = 2;
}
if (this->work[CS_TIMER_2] == 430) {
koumeAnim = 4;
D_8094C874 = 435;
D_8094C878 = 1;
}
if (this->work[CS_TIMER_2] > 440 && this->work[CS_TIMER_2] < 860) {
func_80078884(NA_SE_EN_TWINROBA_FIGHT - SFX_FLAG);
}
if (this->work[CS_TIMER_2] == 430) {
msgId2 = 0x604E;
}
if (this->work[CS_TIMER_2] == 480) {
kotakeAnim = 4;
sKotakePtr->work[YAW_TGT] = -0x4000;
}
if (this->work[CS_TIMER_2] == 500) {
koumeAnim = 2;
}
if (this->work[CS_TIMER_2] == 480) {
msgId1 = 0x604F;
}
if (this->work[CS_TIMER_2] == 530) {
koumeAnim = 4;
sKoumePtr->work[YAW_TGT] = 0x4000;
D_8094C87A = 335;
D_8094C87E = 1;
}
if (this->work[CS_TIMER_2] == 530) {
msgId2 = 0x6050;
}
if (this->work[CS_TIMER_2] == 580) {
msgId1 = 0x6051;
}
if (this->work[CS_TIMER_2] == 620) {
msgId2 = 0x6052;
}
if (this->work[CS_TIMER_2] == 660) {
msgId1 = 0x6053;
}
if (this->work[CS_TIMER_2] == 700) {
msgId2 = 0x6054;
}
if (this->work[CS_TIMER_2] == 740) {
msgId1 = 0x6055;
}
if (this->work[CS_TIMER_2] == 780) {
msgId2 = 0x6056;
}
if (this->work[CS_TIMER_2] == 820) {
msgId1 = 0x6057;
Audio_QueueSeqCmd(0x1 << 28 | SEQ_PLAYER_BGM_MAIN << 24 | 0x5000FF);
}
if (this->work[CS_TIMER_2] == 860) {
koumeAnim = kotakeAnim = 3;
}
if (this->work[CS_TIMER_2] == 900) {
Audio_PlayActorSound2(&sKoumePtr->actor, NA_SE_EN_TWINROBA_DIE);
Audio_PlayActorSound2(&sKotakePtr->actor, NA_SE_EN_TWINROBA_DIE);
}
if (this->work[CS_TIMER_2] == 930) {
msgId2 = 0x6058;
}
if (msgId2 != 0) {
Message_StartTextbox(globalCtx, msgId2, NULL);
if (sp35) {
D_8094C876 = 10;
D_8094C874 = sp35;
D_8094C878 = 0;
}
}
if (msgId1 != 0) {
Message_StartTextbox(globalCtx, msgId1, NULL);
}
switch (kotakeAnim) {
case 1:
Animation_MorphToLoop(&sKotakePtr->skelAnime, &object_tw_Anim_00230C, -5.0f);
break;
case 2:
Animation_MorphToLoop(&sKotakePtr->skelAnime, &object_tw_Anim_001D10, -5.0f);
break;
case 3:
Animation_MorphToLoop(&sKotakePtr->skelAnime, &object_tw_Anim_0017E0, -5.0f);
break;
case 4:
Animation_MorphToLoop(&sKotakePtr->skelAnime, &object_tw_Anim_0012A4, -5.0f);
break;
}
switch (koumeAnim) {
case 1:
Animation_MorphToLoop(&sKoumePtr->skelAnime, &object_tw_Anim_00230C, -5.0f);
break;
case 2:
Animation_MorphToLoop(&sKoumePtr->skelAnime, &object_tw_Anim_001D10, -5.0f);
break;
case 3:
Animation_MorphToLoop(&sKoumePtr->skelAnime, &object_tw_Anim_0017E0, -5.0f);
break;
case 4:
Animation_MorphToLoop(&sKoumePtr->skelAnime, &object_tw_Anim_0012A4, -5.0f);
break;
}
if (this->work[CS_TIMER_2] >= 120 && this->work[CS_TIMER_2] < 500) {
Math_ApproachF(&this->workf[UNK_F18], 255.0f, 0.1f, 5.0f);
}
// Add seperate timings for the "beam" that opens and closes around the sisters
// Needed because we skip ahead in cutscene timer value so it never gets called otherwise
if (gSaveContext.n64ddFlag) {
if (this->work[CS_TIMER_2] < 900) {
Math_ApproachF(&this->workf[UNK_F18], 255.0f, 0.1f, 5.0f);
} else if (this->work[CS_TIMER_2] > 910) {
Math_ApproachF(&this->workf[UNK_F18], 0.0f, 1.0f, 3.0f);
}
}
if (this->work[CS_TIMER_2] >= 150) {
Math_ApproachF(&sKoumePtr->workf[UNK_F17], (Math_SinS(this->work[CS_TIMER_1] * 2000) * 0.05f) + 0.4f, 0.1f,
0.01f);
Math_ApproachF(&sKotakePtr->workf[UNK_F17], (Math_CosS(this->work[CS_TIMER_1] * 1700) * 0.05f) + 0.4f, 0.1f,
0.01f);
if (this->work[CS_TIMER_2] >= 880) {
Math_ApproachF(&sKotakePtr->actor.world.pos.y, 2000.0f, 1.0f, this->actor.speedXZ);
Math_ApproachF(&sKoumePtr->actor.world.pos.y, 2000.0f, 1.0f, this->actor.speedXZ);
Math_ApproachF(&this->actor.speedXZ, 10.0f, 1.0f, 0.25f);
if (this->work[CS_TIMER_2] >= 930) {
Math_ApproachF(&this->workf[UNK_F19], 5.0f, 1.0f, 0.05f);
Math_ApproachF(&this->workf[UNK_F18], 0.0f, 1.0f, 3.0f);
}
Audio_PlayActorSound2(&this->actor, NA_SE_EV_GOTO_HEAVEN - SFX_FLAG);
} else {
f32 yTarget = Math_CosS(this->work[CS_TIMER_2] * 1700) * 4.0f;
Math_ApproachF(&sKotakePtr->actor.world.pos.y, 20.0f + (263.0f + yTarget), 0.1f, this->actor.speedXZ);
yTarget = Math_SinS(this->work[CS_TIMER_2] * 1500) * 4.0f;
Math_ApproachF(&sKoumePtr->actor.world.pos.y, 20.0f + (263.0f + yTarget), 0.1f, this->actor.speedXZ);
Math_ApproachF(&this->actor.speedXZ, 1.0f, 1.0f, 0.05f);
}
}
}
void BossTw_TwinrovaDeathCS(BossTw* this, GlobalContext* globalCtx) {
s16 i;
Vec3f spD0;
Player* player = GET_PLAYER(globalCtx);
Camera* mainCam = Gameplay_GetCamera(globalCtx, MAIN_CAM);
SkelAnime_Update(&this->skelAnime);
this->work[UNK_S8] += 20;
if (this->work[UNK_S8] > 255) {
this->work[UNK_S8] = 255;
}
Math_ApproachF(&this->workf[UNK_F12], 0.0f, 1.0f, 0.05f);
this->unk_5F8 = 1;
switch (this->csState1) {
case 0:
if (this->work[CS_TIMER_1] == 15) {
Animation_MorphToPlayOnce(&this->skelAnime, &object_tw_Anim_0216DC, -3.0f);
}
if (this->work[CS_TIMER_1] >= 15) {
Math_ApproachF(&this->actor.world.pos.y, 400.0f, 0.05f, 10.0f);
}
if (this->work[CS_TIMER_1] >= 55) {
if (this->work[CS_TIMER_1] == 55) {
globalCtx->envCtx.unk_D8 = 0;
}
sEnvType = -1;
globalCtx->envCtx.unk_BE = 5;
globalCtx->envCtx.unk_BD = 0;
Math_ApproachF(&globalCtx->envCtx.unk_D8, 1.0f, 1.0f, 0.015f);
Math_ApproachF(&this->actor.scale.x, 0.00024999998f, 0.1f, 0.00005f);
this->actor.shape.rot.y += (s16)this->actor.speedXZ;
this->workf[UNK_F13] += this->actor.speedXZ;
if (this->workf[UNK_F13] > 65536.0f) {
this->workf[UNK_F13] -= 65536.0f;
Audio_PlayActorSound2(&this->actor, NA_SE_EN_TWINROBA_ROLL);
}
Math_ApproachF(&this->actor.speedXZ, 12288.0f, 1.0f, 256.0f);
if (this->work[CS_TIMER_1] == 135) {
Vec3f spBC;
Vec3f spB0;
Vec3f spA4 = { 0.0f, 0.0f, 0.0f };
func_80078884(NA_SE_EN_TWINROBA_TRANSFORM);
for (i = 0; i < 100; i++) {
spB0.x = Rand_CenteredFloat(5.0f);
spB0.y = Rand_CenteredFloat(5.0f);
spB0.z = Rand_CenteredFloat(5.0f);
spBC = this->actor.world.pos;
spBC.x += spB0.x;
spBC.y += spB0.y;
spBC.z += spB0.z;
BossTw_AddFlameEffect(globalCtx, &spBC, &spB0, &spA4, Rand_ZeroFloat(2.0f) + 5,
Rand_ZeroFloat(1.99f));
}
this->csState1 = 1;
this->visible = false;
this->actor.scale.x = 0.0f;
Actor_SpawnAsChild(&globalCtx->actorCtx, &this->actor, globalCtx, ACTOR_BOSS_TW,
this->actor.world.pos.x, this->actor.world.pos.y, this->actor.world.pos.z, 0, 0,
0, TW_DEATHBALL_KOUME);
Actor_SpawnAsChild(&globalCtx->actorCtx, &this->actor, globalCtx, ACTOR_BOSS_TW,
this->actor.world.pos.x, this->actor.world.pos.y, this->actor.world.pos.z, 0, 0,
0, TW_DEATHBALL_KOTAKE);
this->actor.flags &= ~ACTOR_FLAG_0;
}
}
Actor_SetScale(&this->actor, this->actor.scale.x);
break;
case 1:
break;
}
switch (this->csState2) {
case 0:
this->csState2 = 1;
func_80064520(globalCtx, &globalCtx->csCtx);
func_8002DF54(globalCtx, &this->actor, 8);
this->subCamId = Gameplay_CreateSubCamera(globalCtx);
Gameplay_ChangeCameraStatus(globalCtx, 0, CAM_STAT_WAIT);
Gameplay_ChangeCameraStatus(globalCtx, this->subCamId, CAM_STAT_ACTIVE);
this->subCamEye = mainCam->eye;
this->subCamAt = mainCam->at;
Audio_QueueSeqCmd(0x1 << 28 | SEQ_PLAYER_BGM_MAIN << 24 | 0x100FF);
break;
case 1:
spD0.x = Math_SinS(this->actor.world.rot.y) * 200.0f;
spD0.z = Math_CosS(this->actor.world.rot.y) * 200.0f;
Math_ApproachF(&this->subCamEye.x, spD0.x + this->actor.world.pos.x, 0.1f, 50.0f);
Math_ApproachF(&this->subCamEye.y, 300.0f, 0.1f, 50.0f);
Math_ApproachF(&this->subCamEye.z, spD0.z + this->actor.world.pos.z, 0.1f, 50.0f);
Math_ApproachF(&this->subCamAt.x, this->actor.world.pos.x, 0.1f, 50.0f);
Math_ApproachF(&this->subCamAt.y, this->actor.world.pos.y, 0.1f, 50.0f);
Math_ApproachF(&this->subCamAt.z, this->actor.world.pos.z, 0.1f, 50.0f);
if (this->work[CS_TIMER_1] == 170) {
this->csState2 = 2;
this->work[CS_TIMER_2] = 0;
this->subCamEye.z = 170.0f;
this->subCamDist = 170.0f;
this->subCamEye.x = 0.0f;
this->subCamAt.x = 0.0f;
this->subCamAt.z = 0.0f;
this->subCamEye.y = 260.0f;
player->actor.shape.rot.y = -0x8000;
player->actor.world.pos.x = -40.0f;
player->actor.world.pos.y = 240.0f;
player->actor.world.pos.z = 90.0f;
sKoumePtr->actor.world.pos.x = -37.0f;
sKotakePtr->actor.world.pos.x = 37.0f;
sKotakePtr->actor.world.pos.y = 263.0f;
sKoumePtr->actor.world.pos.y = sKotakePtr->actor.world.pos.y;
this->subCamAt.y = sKoumePtr->actor.world.pos.y + 17.0f;
sKotakePtr->actor.world.pos.z = 0.0f;
sKoumePtr->actor.world.pos.z = sKotakePtr->actor.world.pos.z;
sKoumePtr->work[YAW_TGT] = sKotakePtr->work[YAW_TGT] = sKoumePtr->actor.shape.rot.x =
sKotakePtr->actor.shape.rot.x = sKoumePtr->actor.shape.rot.y = sKotakePtr->actor.shape.rot.y = 0;
func_8002DF54(globalCtx, &sKoumePtr->actor, 1);
sKoumePtr->actor.flags |= ACTOR_FLAG_0;
}
break;
case 2:
if (this->work[CS_TIMER_2] == 100) {
Vec3f pos;
Vec3f velocity;
Vec3f accel = { 0.0f, 0.0f, 0.0f };
s32 zero = 0;
for (i = 0; i < 50; i++) {
velocity.x = Rand_CenteredFloat(3.0f);
velocity.y = Rand_CenteredFloat(3.0f);
velocity.z = Rand_CenteredFloat(3.0f);
pos = sKoumePtr->actor.world.pos;
pos.x += velocity.x * 2.0f;
pos.y += velocity.y * 2.0f;
pos.z += velocity.z * 2.0f;
BossTw_AddFlameEffect(globalCtx, &pos, &velocity, &accel, Rand_ZeroFloat(2.0f) + 5, 1);
// fake code needed to match, tricks the compiler into allocating more stack
if (zero) {
accel.x *= 2.0;
}
velocity.x = Rand_CenteredFloat(3.0f);
velocity.y = Rand_CenteredFloat(3.0f);
velocity.z = Rand_CenteredFloat(3.0f);
pos = sKotakePtr->actor.world.pos;
pos.x += velocity.x * 2.0f;
pos.y += velocity.y * 2.0f;
pos.z += velocity.z * 2.0f;
BossTw_AddFlameEffect(globalCtx, &pos, &velocity, &accel, Rand_ZeroFloat(2.0f) + 5, 0);
}
Actor_SetScale(&sKoumePtr->actor, 0.0f);
Actor_SetScale(&sKotakePtr->actor, 0.0f);
sKoumePtr->visible = 1;
sKotakePtr->visible = 1;
func_80078884(NA_SE_EN_TWINROBA_TRANSFORM);
Audio_QueueSeqCmd(SEQ_PLAYER_BGM_MAIN << 24 | NA_BGM_KOTAKE_KOUME);
this->csState2 = 3;
this->work[CS_TIMER_2] = 0;
this->subCamYaw = this->subCamYawStep = this->actor.speedXZ = this->subCamDistStep = 0.0f;
}
break;
case 3:
BossTw_DeathCSMsgSfx(this, globalCtx);
if (this->work[CS_TIMER_2] < 150) {
globalCtx->envCtx.unk_BE = 1;
globalCtx->envCtx.unk_BD = 0;
Math_ApproachZeroF(&globalCtx->envCtx.unk_D8, 1.0f, 0.1f);
} else {
globalCtx->envCtx.unk_BE = 1;
globalCtx->envCtx.unk_BD = 6;
Math_ApproachF(&globalCtx->envCtx.unk_D8, (Math_SinS(this->work[CS_TIMER_2] * 4096) / 4.0f) + 0.75f,
1.0f, 0.1f);
}
Math_ApproachF(&this->subCamAt.y, sKoumePtr->actor.world.pos.y + 17.0f, 0.05f, 10.0f);
if (this->work[CS_TIMER_2] >= 50) {
Math_ApproachF(&this->subCamDist, 110.0f, 0.05f, this->subCamDistStep);
Math_ApproachF(&this->subCamDistStep, 1.0f, 1.0f, 0.025f);
this->subCamEye.x = this->subCamDist * sinf(this->subCamYaw);
this->subCamEye.z = this->subCamDist * cosf(this->subCamYaw);
if (this->work[CS_TIMER_2] >= 151) {
this->subCamYaw += this->subCamYawStep;
if (this->work[CS_TIMER_2] >= 800) {
Math_ApproachF(&this->subCamYawStep, 0.0f, 1.0f, 0.0001f);
} else {
Math_ApproachF(&this->subCamYawStep, 0.015f, 1.0f, 0.0001f);
}
}
}
Math_ApproachF(&sKoumePtr->actor.scale.x, 0.009999999f, 0.1f, 0.001f);
Actor_SetScale(&sKoumePtr->actor, sKoumePtr->actor.scale.x);
Actor_SetScale(&sKotakePtr->actor, sKoumePtr->actor.scale.x);
if (this->work[CS_TIMER_2] >= 1020) {
mainCam = Gameplay_GetCamera(globalCtx, MAIN_CAM);
mainCam->eye = this->subCamEye;
mainCam->eyeNext = this->subCamEye;
mainCam->at = this->subCamAt;
func_800C08AC(globalCtx, this->subCamId, 0);
this->csState2 = 4;
this->subCamId = 0;
func_80064534(globalCtx, &globalCtx->csCtx);
func_8002DF54(globalCtx, &this->actor, 7);
Audio_QueueSeqCmd(SEQ_PLAYER_BGM_MAIN << 24 | NA_BGM_BOSS_CLEAR);
Actor_SpawnAsChild(&globalCtx->actorCtx, &this->actor, globalCtx, ACTOR_DOOR_WARP1, 600.0f, 230.0f,
0.0f, 0, 0, 0, WARP_DUNGEON_ADULT);
Actor_Spawn(&globalCtx->actorCtx, globalCtx, ACTOR_ITEM_B_HEART, -600.0f, 230.f, 0.0f, 0, 0, 0, 0);
this->actor.world.pos.y = -2000.0f;
this->workf[UNK_F18] = 0.0f;
sKoumePtr->visible = sKotakePtr->visible = false;
if (&this->subCamEye) {} // fixes regalloc, may be fake
Flags_SetClear(globalCtx, globalCtx->roomCtx.curRoom.num);
}
break;
case 4:
sEnvType = 0;
break;
}
if (this->subCamId) {
Gameplay_CameraSetAtEye(globalCtx, this->subCamId, &this->subCamAt, &this->subCamEye);
}
}
static s16 D_8094A900[] = {
0, 1, 2, 2, 1,
};
static s16 D_8094A90C[] = {
0, 1, 2, 2, 2, 2, 2, 2, 1,
};
void BossTw_Update(Actor* thisx, GlobalContext* globalCtx) {
BossTw* this = (BossTw*)thisx;
Player* player = GET_PLAYER(globalCtx);
s16 i;
s32 pad;
this->collider.base.colType = COLTYPE_HIT3;
Math_ApproachF(&this->fogR, globalCtx->lightCtx.fogColor[0], 1.0f, 10.0f);
Math_ApproachF(&this->fogG, globalCtx->lightCtx.fogColor[1], 1.0f, 10.0f);
Math_ApproachF(&this->fogB, globalCtx->lightCtx.fogColor[2], 1.0f, 10.0f);
Math_ApproachF(&this->fogNear, globalCtx->lightCtx.fogNear, 1.0f, 10.0f);
Math_ApproachF(&this->fogFar, 1000.0f, 1.0f, 10.0f);
this->work[CS_TIMER_1]++;
this->work[CS_TIMER_2]++;
this->work[TAIL_IDX]++;
if (this->work[TAIL_IDX] >= ARRAY_COUNT(this->blastTailPos)) {
this->work[TAIL_IDX] = 0;
}
this->blastTailPos[this->work[TAIL_IDX]] = this->actor.world.pos;
for (i = 0; i < 5; i++) {
if (this->timers[i] != 0) {
this->timers[i]--;
}
}
if (this->work[INVINC_TIMER] != 0) {
this->work[INVINC_TIMER]--;
}
if (this->work[FOG_TIMER] != 0) {
this->work[FOG_TIMER]--;
}
if (this->actionFunc == BossTw_FlyTo || this->actionFunc == BossTw_Spin ||
this->actionFunc == BossTw_TurnToPlayer) {
if ((s16)(player->actor.shape.rot.y - this->actor.yawTowardsPlayer + 0x8000) < 0x1000 &&
(s16)(player->actor.shape.rot.y - this->actor.yawTowardsPlayer + 0x8000) > -0x1000 && player->unk_A73) {
BossTw_SetupSpin(this, globalCtx);
}
}
this->actionFunc(this, globalCtx);
if (this->actionFunc != BossTw_Wait) {
this->collider.dim.radius = 45;
if (this->actionFunc == BossTw_Spin) {
this->collider.dim.radius *= 2;
}
this->collider.dim.height = 120;
this->collider.dim.yShift = -30;
if (this->work[INVINC_TIMER] == 0) {
if (this->collider.base.acFlags & AC_HIT) {
this->collider.base.acFlags &= ~AC_HIT;
}
Collider_UpdateCylinder(&this->actor, &this->collider);
CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, &this->collider.base);
CollisionCheck_SetAT(globalCtx, &globalCtx->colChkCtx, &this->collider.base);
}
if (this->actor.params == 0) {
this->workf[OUTR_CRWN_TX_X2] += 1.0f;
this->workf[OUTR_CRWN_TX_Y2] -= 7.0f;
this->workf[INNR_CRWN_TX_Y1] += 1.0f;
} else {
this->workf[OUTR_CRWN_TX_X2] += 0.0f;
this->workf[INNR_CRWN_TX_X2] += 0.0f;
this->workf[OUTR_CRWN_TX_Y2] += -15.0f;
this->workf[INNR_CRWN_TX_Y2] += -10.0f;
}
if (((this->work[CS_TIMER_2] % 32) == 0) && (Rand_ZeroOne() < 0.3f)) {
this->work[BLINK_IDX] = 4;
}
this->eyeTexIdx = D_8094A900[this->work[BLINK_IDX]];
if (this->work[BLINK_IDX] != 0) {
this->work[BLINK_IDX]--;
}
if (this->actionFunc != BossTw_MergeCS && this->unk_5F8 != 0) {
Vec3f pos;
Vec3f velocity = { 0.0f, 0.0f, 0.0f };
Vec3f accel = { 0.0f, 0.0f, 0.0f };
if (this->scepterAlpha > 0.0f) {
for (i = 0; i <= 0; i++) {
pos = this->scepterFlamePos[0];
pos.x += Rand_CenteredFloat(70.0f);
pos.y += Rand_CenteredFloat(70.0f);
pos.z += Rand_CenteredFloat(70.0f);
accel.y = 0.4f;
accel.x = Rand_CenteredFloat(0.5f);
accel.z = Rand_CenteredFloat(0.5f);
BossTw_AddDotEffect(globalCtx, &pos, &velocity, &accel, (s16)Rand_ZeroFloat(2.0f) + 8,
this->actor.params, 37);
}
}
for (i = 0; i <= 0; i++) {
pos = this->crownPos;
pos.x += Rand_CenteredFloat(70.0f);
pos.y += Rand_CenteredFloat(70.0f);
pos.z += Rand_CenteredFloat(70.0f);
accel.y = 0.4f;
accel.x = Rand_CenteredFloat(0.5f);
accel.z = Rand_CenteredFloat(0.5f);
BossTw_AddDotEffect(globalCtx, &pos, &velocity, &accel, (s16)Rand_ZeroFloat(2.0f) + 8,
this->actor.params, 37);
}
}
}
}
void BossTw_TwinrovaUpdate(Actor* thisx, GlobalContext* globalCtx2) {
s16 i;
GlobalContext* globalCtx = globalCtx2;
BossTw* this = (BossTw*)thisx;
Player* player = GET_PLAYER(globalCtx);
this->actor.flags &= ~ACTOR_FLAG_10;
this->unk_5F8 = 0;
this->collider.base.colType = COLTYPE_HIT3;
Math_ApproachF(&this->fogR, globalCtx->lightCtx.fogColor[0], 1.0f, 10.0f);
Math_ApproachF(&this->fogG, globalCtx->lightCtx.fogColor[1], 1.0f, 10.0f);
Math_ApproachF(&this->fogB, globalCtx->lightCtx.fogColor[2], 1.0f, 10.0f);
Math_ApproachF(&this->fogNear, globalCtx->lightCtx.fogNear, 1.0f, 10.0f);
Math_ApproachF(&this->fogFar, 1000.0f, 1.0f, 10.0f);
this->work[CS_TIMER_1]++;
this->work[CS_TIMER_2]++;
for (i = 0; i < 5; i++) {
if (this->timers[i] != 0) {
this->timers[i]--;
}
}
if (this->work[INVINC_TIMER] != 0) {
this->work[INVINC_TIMER]--;
}
if (this->work[FOG_TIMER] != 0) {
this->work[FOG_TIMER]--;
}
this->actionFunc(this, globalCtx);
if (this->actionFunc != BossTw_TwinrovaShootBlast && this->actionFunc != BossTw_TwinrovaChargeBlast &&
this->visible && this->unk_5F8 == 0 &&
(s16)(player->actor.shape.rot.y - this->actor.yawTowardsPlayer + 0x8000) < 0x1000 &&
(s16)(player->actor.shape.rot.y - this->actor.yawTowardsPlayer + 0x8000) > -0x1000 && player->unk_A73 != 0) {
BossTw_TwinrovaSetupSpin(this, globalCtx);
}
this->eyeTexIdx = D_8094A900[this->work[BLINK_IDX]];
if (this->work[BLINK_IDX] != 0) {
this->work[BLINK_IDX]--;
}
if ((this->work[CS_TIMER_2] % 32) == 0) {
if (this->actionFunc != BossTw_TwinrovaMergeCS) {
if (Rand_ZeroOne() < 0.3f) {
this->work[BLINK_IDX] = 4;
}
}
}
if (this->actionFunc == BossTw_TwinrovaMergeCS) {
this->leftEyeTexIdx = D_8094A90C[this->work[TW_BLINK_IDX]];
if (this->work[TW_BLINK_IDX] != 0) {
this->work[TW_BLINK_IDX]--;
}
} else {
if (this->actionFunc == BossTw_TwinrovaStun) {
this->eyeTexIdx = 1;
}
if (this->actionFunc == BossTw_TwinrovaDeathCS) {
this->eyeTexIdx = 2;
}
this->leftEyeTexIdx = this->eyeTexIdx;
}
if (this->visible && this->unk_5F8 == 0) {
Vec3f pos;
Vec3f velocity = { 0.0f, 0.0f, 0.0f };
Vec3f accel;
if (this->work[UNK_S8] != 0) {
this->work[UNK_S8] -= 20;
if (this->work[UNK_S8] < 0) {
this->work[UNK_S8] = 0;
}
}
Math_ApproachF(&this->workf[UNK_F12], 1.0f, 1.0f, 0.05f);
accel.y = 0.4f;
for (i = 0; i < 2; i++) {
pos = this->leftScepterPos;
pos.x += Rand_CenteredFloat(30.0f);
pos.y += Rand_CenteredFloat(30.0f);
pos.z += Rand_CenteredFloat(30.0f);
accel.x = Rand_CenteredFloat(0.5f);
accel.z = Rand_CenteredFloat(0.5f);
BossTw_AddDotEffect(globalCtx, &pos, &velocity, &accel, (s16)Rand_ZeroFloat(2.0f) + 7, 0, 75);
}
for (i = 0; i < 2; i++) {
pos = this->rightScepterPos;
pos.x += Rand_CenteredFloat(30.0f);
pos.y += Rand_CenteredFloat(30.0f);
pos.z += Rand_CenteredFloat(30.0f);
accel.x = Rand_CenteredFloat(0.5f);
accel.z = Rand_CenteredFloat(0.5f);
BossTw_AddDotEffect(globalCtx, &pos, &velocity, &accel, (s16)Rand_ZeroFloat(2.0f) + 7, 1, 75);
}
}
this->collider.dim.radius = 35;
if (this->actionFunc == BossTw_TwinrovaSpin) {
this->collider.dim.radius *= 2;
}
this->collider.dim.height = 150;
this->collider.dim.yShift = -60;
Collider_UpdateCylinder(&this->actor, &this->collider);
if (this->work[INVINC_TIMER] == 0) {
if (this->actionFunc != BossTw_TwinrovaStun) {
if (this->twinrovaStun != 0) {
this->twinrovaStun = 0;
this->work[FOG_TIMER] = 10;
BossTw_TwinrovaDamage(this, globalCtx, 0);
Audio_PlayActorSound2(&this->actor, NA_SE_EN_TWINROBA_YOUNG_DAMAGE);
} else if (this->collider.base.acFlags & AC_HIT) {
ColliderInfo* info = this->collider.info.acHitInfo;
this->collider.base.acFlags &= ~AC_HIT;
if (info->toucher.dmgFlags & (DMG_SLINGSHOT | DMG_ARROW)) {}
}
} else if (this->collider.base.acFlags & AC_HIT) {
u8 damage;
u8 swordDamage;
ColliderInfo* info = this->collider.info.acHitInfo;
this->collider.base.acFlags &= ~AC_HIT;
swordDamage = false;
damage = CollisionCheck_GetSwordDamage(info->toucher.dmgFlags);
if (damage == 0) {
damage = 2;
} else {
swordDamage = true;
}
if (!(info->toucher.dmgFlags & DMG_HOOKSHOT)) {
if (((s8)this->actor.colChkInfo.health < 3) && !swordDamage) {
damage = 0;
}
BossTw_TwinrovaDamage(this, globalCtx, damage);
}
}
}
CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, &this->collider.base);
osSyncPrintf("OooooooooooooooooooooooooooooooooCC\n");
CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->collider.base);
globalCtx->envCtx.unk_DC = 2;
switch (sEnvType) {
case 0:
Math_ApproachZeroF(&globalCtx->envCtx.unk_D8, 1.0f, 0.02f);
break;
case 1:
globalCtx->envCtx.unk_BD = 3;
Math_ApproachF(&globalCtx->envCtx.unk_D8, 0.5f, 1.0f, 0.05f);
break;
case 2:
globalCtx->envCtx.unk_BD = 2;
Math_ApproachF(&globalCtx->envCtx.unk_D8, (Math_SinS(this->work[CS_TIMER_1] * 0x3000) * 0.03f) + 0.5f, 1.0f,
0.05f);
break;
case 3:
globalCtx->envCtx.unk_BD = 3;
Math_ApproachF(&globalCtx->envCtx.unk_D8, 1.0f, 1.0f, 0.1f);
break;
case 4:
globalCtx->envCtx.unk_BD = 2;
Math_ApproachF(&globalCtx->envCtx.unk_D8, (Math_SinS(this->work[CS_TIMER_1] * 0x3E00) * 0.05f) + 0.95f,
1.0f, 0.1f);
break;
case 5:
globalCtx->envCtx.unk_BD = 0;
Math_ApproachF(&globalCtx->envCtx.unk_D8, 1.0f, 1.0f, 0.05f);
break;
case -1:
break;
}
BossTw_UpdateEffects(globalCtx);
if (sFreezeState == 1) {
sFreezeState = 2;
BossTw_AddPlayerFreezeEffect(globalCtx, NULL);
func_80078914(&player->actor.projectedPos, NA_SE_VO_LI_FREEZE);
func_80078914(&player->actor.projectedPos, NA_SE_PL_FREEZE);
if (sShieldFireCharge != 0) {
sShieldFireCharge = 4;
}
}
if (player->isBurning && sShieldIceCharge != 0) {
sShieldIceCharge = 4;
}
}
s32 BossTw_OverrideLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3f* pos, Vec3s* rot, void* thisx) {
BossTw* this = (BossTw*)thisx;
if (limbIndex == 21) {
if (this->unk_5F8 == 0) {
if (this->actor.params == 0) {
*dList = object_tw_DL_012CE0;
} else {
*dList = object_tw_DL_0134B8;
}
}
}
if (limbIndex == 14) {
if (this->actionFunc == BossTw_DeathCS) {
*dList = NULL;
} else if (this->scepterAlpha == 0.0f) {
if (this->actor.params == 0) {
*dList = object_tw_DL_012B38;
} else {
*dList = object_tw_DL_013310;
}
}
}
return false;
}
void BossTw_PostLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3s* rot, void* thisx) {
static Vec3f D_8094A944 = { 0.0f, 0.0f, 0.0f };
static Vec3f D_8094A950 = { 0.0f, 2000.0f, -2000.0f };
static Vec3f D_8094A95C[] = {
{ 0.0f, 0.0f, -10000.0f }, { 0.0f, 0.0f, -8000.0f }, { 0.0f, 0.0f, -9000.0f },
{ 0.0f, 0.0f, -11000.0f }, { 0.0f, 0.0f, -12000.0f },
};
BossTw* this = (BossTw*)thisx;
OPEN_DISPS(globalCtx->state.gfxCtx);
switch (limbIndex) {
case 21:
Matrix_MultVec3f(&D_8094A944, &this->actor.focus.pos);
Matrix_MultVec3f(&D_8094A950, &this->crownPos);
if (this->unk_5F8 != 0) {
gSPMatrix(POLY_XLU_DISP++, MATRIX_NEWMTX(globalCtx->state.gfxCtx),
G_MTX_LOAD | G_MTX_MODELVIEW | G_MTX_NOPUSH);
if (this->actor.params == 0) {
gSPDisplayList(POLY_XLU_DISP++, SEGMENTED_TO_VIRTUAL(object_tw_DL_013AE8));
} else {
gSPDisplayList(POLY_XLU_DISP++, SEGMENTED_TO_VIRTUAL(object_tw_DL_013D68));
}
}
break;
case 14:
Matrix_MultVec3f(&D_8094A95C[0], &this->scepterFlamePos[0]);
Matrix_MultVec3f(&D_8094A95C[1], &this->scepterFlamePos[1]);
Matrix_MultVec3f(&D_8094A95C[2], &this->scepterFlamePos[2]);
Matrix_MultVec3f(&D_8094A95C[3], &this->scepterFlamePos[3]);
Matrix_MultVec3f(&D_8094A95C[4], &this->scepterFlamePos[4]);
if (this->scepterAlpha > 0.0f) {
gSPMatrix(POLY_XLU_DISP++, MATRIX_NEWMTX(globalCtx->state.gfxCtx),
G_MTX_LOAD | G_MTX_MODELVIEW | G_MTX_NOPUSH);
if (this->actor.params == 0) {
gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 255, 225, 255, (s16)this->scepterAlpha);
gSPDisplayList(POLY_XLU_DISP++, SEGMENTED_TO_VIRTUAL(object_tw_DL_013E98));
gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 195, 225, 235, (s16)this->scepterAlpha);
gSPDisplayList(POLY_XLU_DISP++, SEGMENTED_TO_VIRTUAL(object_tw_DL_013F98));
} else {
gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 100, 20, 0, (s16)this->scepterAlpha);
gSPDisplayList(POLY_XLU_DISP++, SEGMENTED_TO_VIRTUAL(object_tw_DL_014070));
gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 255, 70, 0, (s16)this->scepterAlpha);
gSPDisplayList(POLY_XLU_DISP++, SEGMENTED_TO_VIRTUAL(object_tw_DL_014158));
}
}
break;
}
CLOSE_DISPS(globalCtx->state.gfxCtx);
}
void func_80941BC0(BossTw* this, GlobalContext* globalCtx) {
s32 pad;
OPEN_DISPS(globalCtx->state.gfxCtx);
Matrix_Push();
func_80093D84(globalCtx->state.gfxCtx);
Matrix_Translate(this->groundBlastPos2.x, this->groundBlastPos2.y, this->groundBlastPos2.z, MTXMODE_NEW);
Matrix_Scale(this->workf[UNK_F12], this->workf[UNK_F12], this->workf[UNK_F12], MTXMODE_APPLY);
gSPMatrix(POLY_XLU_DISP++, MATRIX_NEWMTX(globalCtx->state.gfxCtx),
G_MTX_LOAD | G_MTX_MODELVIEW | G_MTX_NOPUSH);
gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 255, 255, 255, (s16)this->workf[UNK_F11]);
gDPSetEnvColor(POLY_XLU_DISP++, 0, 40, 30, 80);
gSPDisplayList(POLY_XLU_DISP++, SEGMENTED_TO_VIRTUAL(object_tw_DL_01BC00));
gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 215, 215, 215, (s16)this->workf[UNK_F11] * this->workf[UNK_F14]);
gDPSetEnvColor(POLY_XLU_DISP++, 255, 255, 255, 128);
gSPSegment(POLY_XLU_DISP++, 8,
Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, 0, 0, 0x20, 0x40, 1, (u32)this->workf[UNK_F16] & 0x3F,
(this->work[CS_TIMER_2] * 4) & 0x3F, 0x10, 0x10));
Matrix_Push();
Matrix_RotateY(this->workf[UNK_F15], MTXMODE_APPLY);
gSPMatrix(POLY_XLU_DISP++, MATRIX_NEWMTX(globalCtx->state.gfxCtx),
G_MTX_LOAD | G_MTX_MODELVIEW | G_MTX_NOPUSH);
gSPDisplayList(POLY_XLU_DISP++, SEGMENTED_TO_VIRTUAL(object_tw_DL_01C1C0));
Matrix_Pop();
gSPMatrix(POLY_XLU_DISP++, MATRIX_NEWMTX(globalCtx->state.gfxCtx),
G_MTX_LOAD | G_MTX_MODELVIEW | G_MTX_NOPUSH);
gSPSegment(POLY_XLU_DISP++, 0xD,
Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, this->work[CS_TIMER_2] & 0x7F,
(this->work[CS_TIMER_2] * 8) & 0xFF, 0x20, 0x40, 1,
(-this->work[CS_TIMER_2] * 2) & 0x3F, 0, 0x10, 0x10));
gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 195, 225, 235, (s16)this->workf[UNK_F9]);
gDPSetEnvColor(POLY_XLU_DISP++, 255, 255, 255, 128);
gDPSetRenderMode(POLY_XLU_DISP++,
Z_CMP | IM_RD | CVG_DST_SAVE | ZMODE_DEC | FORCE_BL |
GBL_c1(G_BL_CLR_FOG, G_BL_A_SHADE, G_BL_CLR_IN, G_BL_1MA),
G_RM_ZB_OVL_SURF2);
gSPSetGeometryMode(POLY_XLU_DISP++, G_CULL_BACK | G_FOG);
gSPDisplayList(POLY_XLU_DISP++, SEGMENTED_TO_VIRTUAL(object_tw_DL_01A790));
Matrix_Pop();
CLOSE_DISPS(globalCtx->state.gfxCtx);
}
void func_80942180(BossTw* this, GlobalContext* globalCtx) {
s32 pad;
OPEN_DISPS(globalCtx->state.gfxCtx);
Matrix_Push();
func_80093D84(globalCtx->state.gfxCtx);
Matrix_Translate(this->groundBlastPos2.x, this->groundBlastPos2.y, this->groundBlastPos2.z, MTXMODE_NEW);
Matrix_Scale(this->workf[KM_GD_CRTR_SCL], this->workf[KM_GD_CRTR_SCL], this->workf[KM_GD_CRTR_SCL], MTXMODE_APPLY);
gSPSegment(POLY_XLU_DISP++, 8,
Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, (-this->work[CS_TIMER_1]) & 0x7F, 0, 0x20, 0x20, 1,
(this->work[CS_TIMER_1] * 2) & 0x7F, 0, 0x20, 0x20));
gSPMatrix(POLY_XLU_DISP++, MATRIX_NEWMTX(globalCtx->state.gfxCtx),
G_MTX_LOAD | G_MTX_MODELVIEW | G_MTX_NOPUSH);
gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 100, 40, 00, (s16)this->workf[KM_GRND_CRTR_A]);
gDPPipeSync(POLY_XLU_DISP++);
gDPSetEnvColor(POLY_XLU_DISP++, 255, 245, 255, 128);
gSPDisplayList(POLY_XLU_DISP++, SEGMENTED_TO_VIRTUAL(object_tw_DL_019D40));
Matrix_ReplaceRotation(&globalCtx->billboardMtxF);
gSPMatrix(POLY_XLU_DISP++, MATRIX_NEWMTX(globalCtx->state.gfxCtx),
G_MTX_LOAD | G_MTX_MODELVIEW | G_MTX_NOPUSH);
gSPSegment(POLY_XLU_DISP++, 8,
Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, this->work[CS_TIMER_1] & 0x7F,
(-this->work[CS_TIMER_1] * 6) & 0xFF, 0x20, 0x40, 1,
(this->work[CS_TIMER_1] * 2) & 0x7F, (-this->work[CS_TIMER_1] * 6) & 0xFF, 0x20, 0x40));
gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 80, 0, 0, (s16)this->workf[KM_GD_SMOKE_A]);
gDPPipeSync(POLY_XLU_DISP++);
gDPSetEnvColor(POLY_XLU_DISP++, 0, 0, 0, 100);
gSPDisplayList(POLY_XLU_DISP++, SEGMENTED_TO_VIRTUAL(object_tw_DL_018FC0));
gSPSegment(POLY_XLU_DISP++, 8,
Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, (-this->work[CS_TIMER_1] * 3) & 0x7F, 0, 0x20, 0x20, 1, 0,
(-this->work[CS_TIMER_1] * 10) & 0xFF, 0x20, 0x40));
gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 100, 50, 0, (s16)(this->workf[KM_GD_FLM_A] * 0.7f));
gDPPipeSync(POLY_XLU_DISP++);
gDPSetEnvColor(POLY_XLU_DISP++, 200, 235, 240, 128);
Matrix_Scale(this->workf[KM_GD_FLM_SCL], this->workf[KM_GD_FLM_SCL], this->workf[KM_GD_FLM_SCL], MTXMODE_APPLY);
gSPMatrix(POLY_XLU_DISP++, MATRIX_NEWMTX(globalCtx->state.gfxCtx),
G_MTX_LOAD | G_MTX_MODELVIEW | G_MTX_NOPUSH);
gSPDisplayList(POLY_XLU_DISP++, SEGMENTED_TO_VIRTUAL(object_tw_DL_019938));
Matrix_Pop();
CLOSE_DISPS(globalCtx->state.gfxCtx);
}
void func_809426F0(BossTw* this, GlobalContext* globalCtx) {
s32 pad;
s16 i;
static u32 epoch = 0;
epoch++;
OPEN_DISPS(globalCtx->state.gfxCtx);
gSPSegment(POLY_XLU_DISP++, 8,
Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, 0, (u8)(-this->work[CS_TIMER_2] * 15), 0x20, 0x40, 1, 0, 0,
0x40, 0x40));
Matrix_Push();
Matrix_Translate(0.0f, 0.0f, 5000.0f, MTXMODE_APPLY);
Matrix_Scale(this->spawnPortalScale / 2000.0f, this->spawnPortalScale / 2000.0f, this->spawnPortalScale / 2000.0f,
MTXMODE_APPLY);
Matrix_RotateZ(this->portalRotation, MTXMODE_APPLY);
gSPMatrix(POLY_XLU_DISP++, MATRIX_NEWMTX(globalCtx->state.gfxCtx),
G_MTX_LOAD | G_MTX_MODELVIEW | G_MTX_NOPUSH);
if (this->actor.params == 0) {
gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 135, 175, 165, (s16)this->spawnPortalAlpha);
gSPDisplayList(POLY_XLU_DISP++, SEGMENTED_TO_VIRTUAL(object_tw_DL_01CEE0));
} else {
gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 255, 255, 0, (s16)this->spawnPortalAlpha);
gSPDisplayList(POLY_XLU_DISP++, SEGMENTED_TO_VIRTUAL(object_tw_DL_01DBE8));
}
Matrix_Pop();
if (this->actor.params == 0) {
gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 195, 225, 235, (s16)this->flameAlpha);
gSPDisplayList(POLY_XLU_DISP++, SEGMENTED_TO_VIRTUAL(object_tw_DL_01A998));
} else {
gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 200, 20, 0, (s16)this->flameAlpha);
gDPSetEnvColor(POLY_XLU_DISP++, 255, 215, 255, 128);
}
for (i = 0; i < 8; i++) {
FrameInterpolation_RecordOpenChild("Twinrova 809426F0", epoch + i * 25);
Matrix_Push();
Matrix_Translate(0.0f, 0.0f, 5000.0f, MTXMODE_APPLY);
Matrix_RotateZ(((i * M_PI) * 2.0f * 0.125f) + this->flameRotation, MTXMODE_APPLY);
Matrix_Translate(0.0f, this->spawnPortalScale * 1.5f, 0.0f, MTXMODE_APPLY);
gSPSegment(POLY_XLU_DISP++, 8,
Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, ((this->work[CS_TIMER_2] * 3) + (i * 10)) & 0x7F,
(u8)((-this->work[CS_TIMER_2] * 15) + (i * 50)), 0x20, 0x40, 1, 0, 0, 0x20, 0x20));
Matrix_Scale(0.4f, 0.4f, 0.4f, MTXMODE_APPLY);
Matrix_ReplaceRotation(&globalCtx->billboardMtxF);
gSPMatrix(POLY_XLU_DISP++, MATRIX_NEWMTX(globalCtx->state.gfxCtx),
G_MTX_LOAD | G_MTX_MODELVIEW | G_MTX_NOPUSH);
gSPDisplayList(POLY_XLU_DISP++, SEGMENTED_TO_VIRTUAL(object_tw_DL_01A430));
Matrix_Pop();
FrameInterpolation_RecordCloseChild();
}
CLOSE_DISPS(globalCtx->state.gfxCtx);
}
void func_80942C70(Actor* thisx, GlobalContext* globalCtx) {
BossTw* this = (BossTw*)thisx;
s16 alpha;
OPEN_DISPS(globalCtx->state.gfxCtx);
if (this->beamDist != 0.0f) {
Matrix_Push();
gSPSegment(POLY_XLU_DISP++, 0xC,
Gfx_TexScroll(globalCtx->state.gfxCtx, 0, (u8)(this->work[CS_TIMER_1] * -0xF), 0x20, 0x40));
alpha = this->beamScale * 100.0f * 255.0f;
if (this->actor.params == 1) {
gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 255, 255, 60, alpha);
gDPSetEnvColor(POLY_XLU_DISP++, 255, 0, 0, 128);
} else {
gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 255, 255, 255, alpha);
gDPSetEnvColor(POLY_XLU_DISP++, 100, 100, 255, 128);
}
Matrix_Translate(this->beamOrigin.x, this->beamOrigin.y, this->beamOrigin.z, MTXMODE_NEW);
Matrix_RotateY(this->beamYaw, MTXMODE_APPLY);
Matrix_RotateX(this->beamPitch, MTXMODE_APPLY);
Matrix_RotateZ(this->beamRoll, MTXMODE_APPLY);
Matrix_Scale(this->beamScale, this->beamScale, (this->beamDist * 0.01f * 98.0f) / 20000.0f, MTXMODE_APPLY);
gSPMatrix(POLY_XLU_DISP++, MATRIX_NEWMTX(globalCtx->state.gfxCtx),
G_MTX_LOAD | G_MTX_MODELVIEW | G_MTX_NOPUSH);
gSPDisplayList(POLY_XLU_DISP++, SEGMENTED_TO_VIRTUAL(object_tw_DL_01DDF0));
if (this->beamReflectionDist > 10.0f) {
Matrix_Translate(this->beamReflectionOrigin.x, this->beamReflectionOrigin.y, this->beamReflectionOrigin.z,
MTXMODE_NEW);
Matrix_RotateY(this->beamReflectionYaw, MTXMODE_APPLY);
Matrix_RotateX(this->beamReflectionPitch, MTXMODE_APPLY);
Matrix_RotateZ(this->beamRoll, MTXMODE_APPLY);
Matrix_Scale(this->beamScale, this->beamScale, (this->beamReflectionDist * 0.01f * 100.0f) / 20000.0f,
MTXMODE_APPLY);
gSPMatrix(POLY_XLU_DISP++, MATRIX_NEWMTX(globalCtx->state.gfxCtx),
G_MTX_LOAD | G_MTX_MODELVIEW | G_MTX_NOPUSH);
gSPDisplayList(POLY_XLU_DISP++, SEGMENTED_TO_VIRTUAL(object_tw_DL_01DDF0));
}
Matrix_Pop();
}
CLOSE_DISPS(globalCtx->state.gfxCtx);
}
void func_80943028(Actor* thisx, GlobalContext* globalCtx) {
BossTw* this = (BossTw*)thisx;
OPEN_DISPS(globalCtx->state.gfxCtx);
Matrix_Push();
Matrix_Translate(this->actor.world.pos.x, this->actor.world.pos.y + 57.0f, this->actor.world.pos.z, MTXMODE_NEW);
Matrix_Scale(this->workf[UNK_F17], this->workf[UNK_F17], this->workf[UNK_F17], MTXMODE_APPLY);
gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 255, 255, 255, 255);
gSPMatrix(POLY_XLU_DISP++, MATRIX_NEWMTX(globalCtx->state.gfxCtx),
G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW);
gSPDisplayList(POLY_XLU_DISP++, SEGMENTED_TO_VIRTUAL(object_tw_DL_01F608));
func_80094044(globalCtx->state.gfxCtx);
gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 0, 0, 0, 200);
Matrix_Translate(this->actor.world.pos.x, 240.0f, this->actor.world.pos.z, MTXMODE_NEW);
Matrix_Scale((this->actor.scale.x * 4000.0f) / 100.0f, 1.0f, (this->actor.scale.x * 4000.0f) / 100.0f,
MTXMODE_APPLY);
gSPMatrix(POLY_XLU_DISP++, MATRIX_NEWMTX(globalCtx->state.gfxCtx),
G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW);
gSPDisplayList(POLY_XLU_DISP++, SEGMENTED_TO_VIRTUAL(gCircleShadowDL));
Matrix_Pop();
CLOSE_DISPS(globalCtx->state.gfxCtx);
}
static void* sEyeTextures[] = {
object_tw_Tex_00A438,
object_tw_Tex_00B238,
object_tw_Tex_00B638,
};
void BossTw_Draw(Actor* thisx, GlobalContext* globalCtx2) {
static Vec3f D_8094A9A4 = { 0.0f, 200.0f, 2000.0f };
GlobalContext* globalCtx = globalCtx2;
BossTw* this = (BossTw*)thisx;
Player* player = GET_PLAYER(globalCtx);
OPEN_DISPS(globalCtx->state.gfxCtx);
if (this->visible) {
gSPSegment(POLY_OPA_DISP++, 10, SEGMENTED_TO_VIRTUAL(sEyeTextures[this->eyeTexIdx]));
gSPSegment(POLY_XLU_DISP++, 10, SEGMENTED_TO_VIRTUAL(sEyeTextures[this->eyeTexIdx]));
gSPSegment(POLY_XLU_DISP++, 8,
Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, (s16)this->workf[OUTR_CRWN_TX_X1] & 0x7F,
(s16)this->workf[OUTR_CRWN_TX_Y1] & 0x7F, 0x20, 0x20, 1,
(s16)this->workf[OUTR_CRWN_TX_X2] & 0x7F, (s16)this->workf[OUTR_CRWN_TX_Y2] & 0xFF,
0x20, 0x40));
if (this->actor.params == TW_KOTAKE) {
gSPSegment(POLY_XLU_DISP++, 9,
Gfx_TexScroll(globalCtx->state.gfxCtx, (s16)this->workf[INNR_CRWN_TX_X1] & 0x7F,
(s16)this->workf[INNR_CRWN_TX_Y1] & 0xFF, 0x20, 0x40));
} else {
gSPSegment(POLY_XLU_DISP++, 9,
Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, (s16)this->workf[INNR_CRWN_TX_X1] & 0x7F,
(s16)this->workf[INNR_CRWN_TX_Y1] & 0x7F, 0x20, 0x20, 1,
(s16)this->workf[INNR_CRWN_TX_X2] & 0x7F,
(s16)this->workf[INNR_CRWN_TX_Y2] & 0xFF, 0x20, 0x40));
}
func_80093D18(globalCtx->state.gfxCtx);
func_80093D84(globalCtx->state.gfxCtx);
if (this->work[FOG_TIMER] & 2) {
POLY_OPA_DISP = Gfx_SetFog(POLY_OPA_DISP, 255, 50, 0, 0, 900, 1099);
} else {
POLY_OPA_DISP = Gfx_SetFog(POLY_OPA_DISP, (u32)this->fogR, (u32)this->fogG, (u32)this->fogB, 0,
this->fogNear, this->fogFar);
}
Matrix_Push();
SkelAnime_DrawFlexOpa(globalCtx, this->skelAnime.skeleton, this->skelAnime.jointTable,
this->skelAnime.dListCount, BossTw_OverrideLimbDraw, BossTw_PostLimbDraw, this);
Matrix_Pop();
POLY_OPA_DISP = Gameplay_SetFog(globalCtx, POLY_OPA_DISP);
}
if (this->actor.params == TW_KOTAKE) {
if (this->workf[UNK_F9] > 0.0f) {
if (this->workf[UNK_F11] > 0.0f) {
Vec3f diff;
diff.x = this->groundBlastPos2.x - player->actor.world.pos.x;
diff.y = this->groundBlastPos2.y - player->actor.world.pos.y;
diff.z = this->groundBlastPos2.z - player->actor.world.pos.z;
if ((fabsf(diff.y) < 10.0f) && (player->actor.bgCheckFlags & 1) &&
(sqrtf(SQ(diff.x) + SQ(diff.z)) < (this->workf[UNK_F12] * 4600.0f)) && (sFreezeState == 0) &&
(this->workf[UNK_F11] > 200.0f)) {
sFreezeState = 1;
sTwinrovaPtr->timers[2] = 100;
}
}
func_80941BC0(this, globalCtx);
}
} else {
func_80942180(this, globalCtx);
}
if (this->visible) {
if (this->actionFunc == BossTw_DeathCS) {
func_80943028(&this->actor, globalCtx);
} else {
func_809426F0(this, globalCtx);
Matrix_MultVec3f(&D_8094A9A4, &this->beamOrigin);
func_80942C70(&this->actor, globalCtx);
}
}
CLOSE_DISPS(globalCtx->state.gfxCtx);
}
void* D_8094A9B0[] = {
object_tw_Tex_02A9B0,
object_tw_Tex_02A070,
object_tw_Tex_02A470,
};
s32 BossTw_TwinrovaOverrideLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3f* pos, Vec3s* rot,
void* thisx) {
BossTw* this = (BossTw*)thisx;
OPEN_DISPS(globalCtx->state.gfxCtx);
switch (limbIndex) {
case 21:
gSPSegment(POLY_OPA_DISP++, 0xC,
Gfx_TexScroll(globalCtx->state.gfxCtx, 0, (s16)(f32)this->work[CS_TIMER_1], 8, 8));
gSPSegment(POLY_OPA_DISP++, 8, SEGMENTED_TO_VIRTUAL(D_8094A9B0[this->eyeTexIdx]));
gSPSegment(POLY_OPA_DISP++, 9, SEGMENTED_TO_VIRTUAL(D_8094A9B0[this->leftEyeTexIdx]));
gDPSetEnvColor(POLY_OPA_DISP++, 255, 255, 255, this->work[UNK_S8]);
break;
case 17:
case 41:
*dList = NULL;
gSPSegment(POLY_XLU_DISP++, 0xA,
Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, 0, 0, 0x20, 0x20, 1, 0,
-this->work[CS_TIMER_1] * 0xF, 0x20, 0x40));
break;
case 18:
case 42:
*dList = NULL;
gSPSegment(POLY_XLU_DISP++, 0xB,
Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, 0, 0, 0x20, 0x20, 1, 0,
-this->work[CS_TIMER_1] * 0xA, 0x20, 0x40));
break;
case 16:
case 32:
*dList = NULL;
gSPSegment(POLY_XLU_DISP++, 8,
Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, 0, 0, 0x20, 0x20, 1, this->work[CS_TIMER_1],
-this->work[CS_TIMER_1] * 7, 0x20, 0x40));
break;
case 15:
case 31:
*dList = NULL;
gSPSegment(POLY_XLU_DISP++, 9,
Gfx_TexScroll(globalCtx->state.gfxCtx, 0, this->work[CS_TIMER_1], 0x20, 0x40));
break;
case 19:
if (this->unk_5F8 != 0) {
*dList = object_tw_DL_02D940;
}
break;
case 20:
if (this->unk_5F8 != 0) {
*dList = object_tw_DL_02D890;
}
break;
}
if (this->unk_5F8 != 0 && ((limbIndex == 34) || (limbIndex == 40))) {
*dList = NULL;
}
CLOSE_DISPS(globalCtx->state.gfxCtx);
return false;
}
void BossTw_TwinrovaPostLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3s* rot, void* thisx) {
static Vec3f D_8094A9BC = { 0.0f, 0.0f, 0.0f };
static Vec3f D_8094A9C8 = { 0.0f, 2000.0f, -2000.0f };
static Vec3f D_8094A9D4 = { 13000.0f, 0.0f, 0.0f };
static Vec3f D_8094A9E0 = { 13000.0f, 0.0f, 0.0f };
BossTw* this = (BossTw*)thisx;
OPEN_DISPS(globalCtx->state.gfxCtx);
switch (limbIndex) {
case 34:
Matrix_MultVec3f(&D_8094A9D4, &this->leftScepterPos);
break;
case 40:
Matrix_MultVec3f(&D_8094A9E0, &this->rightScepterPos);
break;
case 21:
Matrix_MultVec3f(&D_8094A9BC, &this->actor.focus.pos);
Matrix_MultVec3f(&D_8094A9C8, &this->crownPos);
break;
case 15:
case 16:
case 17:
case 18:
case 31:
case 32:
case 41:
case 42:
Matrix_Push();
Matrix_Scale(this->workf[UNK_F12], this->workf[UNK_F12], this->workf[UNK_F12], MTXMODE_APPLY);
gSPMatrix(POLY_XLU_DISP++, MATRIX_NEWMTX(globalCtx->state.gfxCtx),
G_MTX_LOAD | G_MTX_MODELVIEW | G_MTX_NOPUSH);
Matrix_Pop();
gSPDisplayList(POLY_XLU_DISP++, *dList);
break;
}
CLOSE_DISPS(globalCtx->state.gfxCtx);
}
void BossTw_ShieldChargeDraw(BossTw* this, GlobalContext* globalCtx) {
s32 pad;
Player* player = GET_PLAYER(globalCtx);
s16 temp_t0;
s16 temp_a0;
OPEN_DISPS(globalCtx->state.gfxCtx);
Matrix_Push();
temp_t0 = sShieldFireCharge | sShieldIceCharge;
if (temp_t0 == 1) {
func_80078884(NA_SE_IT_SHIELD_CHARGE_LV1 & ~SFX_FLAG);
} else if (temp_t0 == 2) {
func_80078884(NA_SE_IT_SHIELD_CHARGE_LV2 & ~SFX_FLAG);
} else if (temp_t0 == 3) {
func_80078884(NA_SE_IT_SHIELD_CHARGE_LV3 & ~SFX_FLAG);
}
if (temp_t0 != 0 && temp_t0 < 4) {
Math_ApproachF(&D_8094C854, 255.0f, 1.0f, 20.0f);
if (temp_t0 == 3) {
temp_t0 *= 3;
}
} else if (temp_t0 == 0) {
D_8094C854 = 0.0f;
} else {
Math_ApproachF(&D_8094C854, 0.0f, 1.0f, 10.0f);
if (D_8094C854 == 0.0f) {
sShieldIceCharge = 0;
sShieldFireCharge = 0;
}
temp_t0 = 1;
}
if (Player_HasMirrorShieldEquipped(globalCtx)) {
if (temp_t0 != 0) {
Matrix_Mult(&player->shieldMf, MTXMODE_NEW);
Matrix_RotateX(M_PI / 2.0f, MTXMODE_APPLY);
gSPMatrix(POLY_XLU_DISP++, MATRIX_NEWMTX(globalCtx->state.gfxCtx),
G_MTX_LOAD | G_MTX_MODELVIEW | G_MTX_NOPUSH);
temp_a0 = (Math_SinS(this->work[CS_TIMER_1] * 2730 * temp_t0) * D_8094C854 * 0.5f) + (D_8094C854 * 0.5f);
if (sShieldFireCharge != 0) {
gDPSetEnvColor(POLY_XLU_DISP++, 255, 245, 255, temp_a0);
gSPDisplayList(POLY_XLU_DISP++, SEGMENTED_TO_VIRTUAL(object_tw_DL_01E0E0));
gSPSegment(POLY_XLU_DISP++, 8,
Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, (this->work[CS_TIMER_1] * 2) * temp_t0, 0, 0x20,
0x20, 1, (-this->work[CS_TIMER_1] * 2) * temp_t0, 0, 0x20, 0x20));
gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 100, 20, 0, (s16)D_8094C854);
gSPDisplayList(POLY_XLU_DISP++, SEGMENTED_TO_VIRTUAL(object_tw_DL_01E020));
} else {
gDPSetEnvColor(POLY_XLU_DISP++, 225, 255, 255, temp_a0);
gSPDisplayList(POLY_XLU_DISP++, SEGMENTED_TO_VIRTUAL(object_tw_DL_01E3A0));
gSPSegment(POLY_XLU_DISP++, 8,
Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, 0, (-this->work[CS_TIMER_1] * 5) * temp_t0,
0x20, 0x40, 1, (this->work[CS_TIMER_1] * 4) * temp_t0, 0, 0x20, 0x20));
gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 175, 205, 195, (s16)D_8094C854);
gSPDisplayList(POLY_XLU_DISP++, SEGMENTED_TO_VIRTUAL(object_tw_DL_01E2C0));
}
}
}
if (D_8094C86F != 0) {
f32 step = D_8094C872 > 0 ? 100.0f : 60.0f;
D_8094C86F--;
Math_ApproachF(&D_8094C858, 255.0f, 1.0f, step);
} else {
f32 step = D_8094C872 > 0 ? 40.0f : 20.0f;
Math_ApproachF(&D_8094C858, 0.0f, 1.0f, step);
}
if (Player_HasMirrorShieldEquipped(globalCtx) && D_8094C858 > 0.0f) {
f32 scale = D_8094C872 > 0 ? 1.3f : 1.0f;
Matrix_Mult(&player->shieldMf, MTXMODE_NEW);
Matrix_RotateX(M_PI / 2.0f, MTXMODE_APPLY);
Matrix_Scale(scale, scale, scale, MTXMODE_APPLY);
gSPMatrix(POLY_XLU_DISP++, MATRIX_NEWMTX(globalCtx->state.gfxCtx),
G_MTX_LOAD | G_MTX_MODELVIEW | G_MTX_NOPUSH);
if (sShieldFireCharge != 0) {
gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 255, 220, 20, (s16)D_8094C858);
gDPSetEnvColor(POLY_XLU_DISP++, 255, 0, 20, 110);
} else {
gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 255, 255, 255, (s16)D_8094C858);
gDPSetEnvColor(POLY_XLU_DISP++, 185, 225, 205, 150);
}
gSPSegment(POLY_XLU_DISP++, 8,
Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, 0, this->work[CS_TIMER_1] * D_8094C872, 0x20, 0x40, 1,
0, this->work[CS_TIMER_1] * D_8094C872, 0x20, 0x20));
gSPDisplayList(POLY_XLU_DISP++, SEGMENTED_TO_VIRTUAL(object_tw_DL_01E9F0));
}
Matrix_Pop();
CLOSE_DISPS(globalCtx->state.gfxCtx);
}
void BossTw_SpawnPortalDraw(BossTw* this, GlobalContext* globalCtx) {
s32 pad;
OPEN_DISPS(globalCtx->state.gfxCtx);
func_80093D84(globalCtx->state.gfxCtx);
gSPSegment(
POLY_XLU_DISP++, 8,
Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, 0, -this->work[CS_TIMER_1] * 15, 0x20, 0x40, 1, 0, 0, 0x40, 0x40));
Matrix_Push();
Matrix_Translate(0.0f, 232.0f, -600.0f, MTXMODE_NEW);
Matrix_Scale(this->spawnPortalScale, this->spawnPortalScale, this->spawnPortalScale, MTXMODE_APPLY);
gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 0, 0, 0, (s16)this->spawnPortalAlpha);
gSPMatrix(POLY_XLU_DISP++, MATRIX_NEWMTX(globalCtx->state.gfxCtx),
G_MTX_LOAD | G_MTX_MODELVIEW | G_MTX_NOPUSH);
gSPDisplayList(POLY_XLU_DISP++, SEGMENTED_TO_VIRTUAL(object_tw_DL_01EC68));
gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 135, 175, 165, (s16)this->spawnPortalAlpha);
Matrix_Translate(0.0f, 2.0f, 0.0f, MTXMODE_APPLY);
Matrix_RotateX(M_PI / 2.0f, MTXMODE_APPLY);
gSPMatrix(POLY_XLU_DISP++, MATRIX_NEWMTX(globalCtx->state.gfxCtx),
G_MTX_LOAD | G_MTX_MODELVIEW | G_MTX_NOPUSH);
gSPDisplayList(POLY_XLU_DISP++, SEGMENTED_TO_VIRTUAL(object_tw_DL_01CEE0));
Matrix_Translate(0.0f, 232.0f, 600.0f, MTXMODE_NEW);
Matrix_Scale(this->spawnPortalScale, this->spawnPortalScale, this->spawnPortalScale, MTXMODE_APPLY);
gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 0, 0, 0, (s16)this->spawnPortalAlpha);
gSPMatrix(POLY_XLU_DISP++, MATRIX_NEWMTX(globalCtx->state.gfxCtx),
G_MTX_LOAD | G_MTX_MODELVIEW | G_MTX_NOPUSH);
gSPDisplayList(POLY_XLU_DISP++, SEGMENTED_TO_VIRTUAL(object_tw_DL_01EC68));
gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 255, 255, 0, (s16)this->spawnPortalAlpha);
Matrix_Translate(0.0f, 2.0f, 0.0f, MTXMODE_APPLY);
Matrix_RotateX(M_PI / 2.0f, MTXMODE_APPLY);
gSPMatrix(POLY_XLU_DISP++, MATRIX_NEWMTX(globalCtx->state.gfxCtx),
G_MTX_LOAD | G_MTX_MODELVIEW | G_MTX_NOPUSH);
gSPDisplayList(POLY_XLU_DISP++, SEGMENTED_TO_VIRTUAL(object_tw_DL_01DBE8));
Matrix_Pop();
CLOSE_DISPS(globalCtx->state.gfxCtx);
}
void func_80944C50(BossTw* this, GlobalContext* globalCtx) {
s32 pad;
f32 scale;
OPEN_DISPS(globalCtx->state.gfxCtx);
Matrix_Push();
Matrix_Translate(0.0f, 750.0f, 0.0f, MTXMODE_NEW);
Matrix_Scale(0.35f, 0.35f, 0.35f, MTXMODE_APPLY);
Matrix_Push();
Matrix_Scale(this->workf[UNK_F19], this->workf[UNK_F19], this->workf[UNK_F19], MTXMODE_APPLY);
gSPMatrix(POLY_XLU_DISP++, MATRIX_NEWMTX(globalCtx->state.gfxCtx),
G_MTX_LOAD | G_MTX_MODELVIEW | G_MTX_NOPUSH);
gSPDisplayList(POLY_XLU_DISP++, SEGMENTED_TO_VIRTUAL(object_tw_DL_01F390));
Matrix_Pop();
gSPSegment(POLY_XLU_DISP++, 8,
Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, -sKoumePtr->work[CS_TIMER_1] * 2, 0, 0x20, 0x20, 1,
-sKoumePtr->work[CS_TIMER_1] * 2, 0, 0x20, 0x40));
gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 255, 255, 255, (s16)this->workf[UNK_F18] / 2);
gSPMatrix(POLY_XLU_DISP++, MATRIX_NEWMTX(globalCtx->state.gfxCtx),
G_MTX_LOAD | G_MTX_MODELVIEW | G_MTX_NOPUSH);
gSPDisplayList(POLY_XLU_DISP++, SEGMENTED_TO_VIRTUAL(object_tw_DL_01F238));
gSPSegment(POLY_XLU_DISP++, 8,
Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, -sKoumePtr->work[CS_TIMER_1] * 5,
-sKoumePtr->work[CS_TIMER_1] * 2, 0x20, 0x40, 1, 0, -sKoumePtr->work[CS_TIMER_1] * 2,
0x10, 0x10));
gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 255, 255, 255, (s16)(this->workf[UNK_F18] * 0.3f));
scale = this->workf[UNK_F18] / 150.0f;
scale = CLAMP_MAX(scale, 1.0f);
Matrix_Scale(scale, 1.0f, scale, MTXMODE_APPLY);
gSPMatrix(POLY_XLU_DISP++, MATRIX_NEWMTX(globalCtx->state.gfxCtx),
G_MTX_LOAD | G_MTX_MODELVIEW | G_MTX_NOPUSH);
gSPDisplayList(POLY_XLU_DISP++, SEGMENTED_TO_VIRTUAL(object_tw_DL_01EEB0));
Matrix_Pop();
CLOSE_DISPS(globalCtx->state.gfxCtx);
}
void BossTw_TwinrovaDraw(Actor* thisx, GlobalContext* globalCtx2) {
static Vec3f D_8094A9EC = { 0.0f, 200.0f, 2000.0f };
GlobalContext* globalCtx = globalCtx2;
BossTw* this = (BossTw*)thisx;
OPEN_DISPS(globalCtx->state.gfxCtx);
if (this->visible) {
func_80093D18(globalCtx->state.gfxCtx);
func_80093D84(globalCtx->state.gfxCtx);
POLY_OPA_DISP = (this->work[FOG_TIMER] & 2) ? Gfx_SetFog2(POLY_OPA_DISP, 255, 50, 0, 0, 900, 1099)
: Gfx_SetFog2(POLY_OPA_DISP, (u32)this->fogR, (u32)this->fogG,
(u32)this->fogB, 0, this->fogNear, this->fogFar);
Matrix_Push();
SkelAnime_DrawFlexOpa(globalCtx, this->skelAnime.skeleton, this->skelAnime.jointTable,
this->skelAnime.dListCount, BossTw_TwinrovaOverrideLimbDraw, BossTw_TwinrovaPostLimbDraw,
thisx);
Matrix_Pop();
Matrix_MultVec3f(&D_8094A9EC, &this->beamOrigin);
POLY_OPA_DISP = Gfx_SetFog2(POLY_OPA_DISP, globalCtx->lightCtx.fogColor[0], globalCtx->lightCtx.fogColor[1],
globalCtx->lightCtx.fogColor[2], 0, globalCtx->lightCtx.fogNear, 1000);
}
BossTw_DrawEffects(globalCtx);
BossTw_ShieldChargeDraw(this, globalCtx);
if (this->spawnPortalAlpha > 0.0f) {
BossTw_SpawnPortalDraw(this, globalCtx);
}
if (this->workf[UNK_F18] > 0.0f) {
func_80944C50(this, globalCtx);
}
CLOSE_DISPS(globalCtx->state.gfxCtx);
}
void BossTw_BlastFire(BossTw* this, GlobalContext* globalCtx) {
s16 i;
f32 xDiff;
f32 yDiff;
f32 zDiff;
f32 distXZ;
Player* player = GET_PLAYER(globalCtx);
Player* player2 = player;
switch (this->actor.params) {
case TW_FIRE_BLAST:
switch (this->csState1) {
case 0:
Actor_SetScale(&this->actor, 0.03f);
this->csState1 = 1;
xDiff = player->actor.world.pos.x - this->actor.world.pos.x;
yDiff = (player->actor.world.pos.y + 30.0f) - this->actor.world.pos.y;
zDiff = player->actor.world.pos.z - this->actor.world.pos.z;
// yaw
this->actor.world.rot.y = Math_FAtan2F(xDiff, zDiff) * (32768 / M_PI);
// pitch
distXZ = sqrtf(SQ(xDiff) + SQ(zDiff));
this->actor.world.rot.x = Math_FAtan2F(yDiff, distXZ) * (32768 / M_PI);
this->actor.speedXZ = 20.0f;
for (i = 0; i < 50; i++) {
this->blastTailPos[i] = this->actor.world.pos;
}
this->workf[TAIL_ALPHA] = 255.0f;
// fallthrough
case 1:
case 10:
this->blastActive = true;
if (this->timers[0] == 0) {
func_8002D908(&this->actor);
func_8002D7EC(&this->actor);
Audio_PlayActorSound2(&this->actor, NA_SE_EN_TWINROBA_SHOOT_FIRE & ~SFX_FLAG);
} else {
Vec3f velocity;
Vec3f velDir;
Vec3s blastDir;
s16 alpha;
this->actor.world.pos = player2->bodyPartsPos[15];
this->actor.world.pos.y = -2000.0f;
Matrix_MtxFToYXZRotS(&player2->shieldMf, &blastDir, 0);
blastDir.x = -blastDir.x;
blastDir.y = blastDir.y + 0x8000;
Math_ApproachS(&this->magicDir.x, blastDir.x, 0xA, 0x800);
Math_ApproachS(&this->magicDir.y, blastDir.y, 0xA, 0x800);
if (this->timers[0] == 50) {
D_8094C86F = 10;
D_8094C872 = 7;
globalCtx->envCtx.unk_D8 = 1.0f;
}
if (this->timers[0] <= 50) {
Audio_PlayActorSound2(&this->actor, NA_SE_EN_TWINROBA_SHOOT_FIRE & ~SFX_FLAG);
Audio_PlayActorSound2(&this->actor, NA_SE_EN_TWINROBA_REFL_FIRE & ~SFX_FLAG);
Matrix_RotateY((this->magicDir.y / 32678.0f) * M_PI, MTXMODE_NEW);
Matrix_RotateX((this->magicDir.x / 32678.0f) * M_PI, MTXMODE_APPLY);
velDir.x = 0.0f;
velDir.y = 0.0f;
velDir.z = 50.0f;
Matrix_MultVec3f(&velDir, &velocity);
alpha = this->timers[0] * 10;
alpha = CLAMP_MAX(alpha, 255);
BossTw_AddShieldBlastEffect(globalCtx, &player2->bodyPartsPos[15], &velocity, &sZeroVector,
10.0f, 80.0f, alpha, 1);
}
if (this->timers[0] == 1) {
sEnvType = 0;
sShieldFireCharge++;
Actor_Kill(&this->actor);
}
return;
}
this->groundBlastPos.y = BossTw_GetFloorY(&this->actor.world.pos);
if (this->groundBlastPos.y >= 0.0f) {
if (this->groundBlastPos.y != 35.0f) {
this->groundBlastPos.x = this->actor.world.pos.x;
this->groundBlastPos.z = this->actor.world.pos.z;
BossTw_SpawnGroundBlast(this, globalCtx, 1);
} else {
Vec3f velocity;
Vec3f accel;
for (i = 0; i < 50; i++) {
velocity.x = Rand_CenteredFloat(20.0f);
velocity.y = Rand_CenteredFloat(20.0f);
velocity.z = Rand_CenteredFloat(20.0f);
accel.x = 0.0f;
accel.y = 0.0f;
accel.z = 0.0f;
BossTw_AddFlameEffect(globalCtx, &this->actor.world.pos, &velocity, &accel,
Rand_ZeroFloat(10.0f) + 25.0f, this->blastType);
}
globalCtx->envCtx.unk_D8 = 0.5f;
}
this->csState1 = 2;
this->timers[0] = 20;
} else {
Vec3f pos;
Vec3f velocity = { 0.0f, 0.0f, 0.0f };
Vec3f accel = { 0.0f, 0.0f, 0.0f };
for (i = 0; i < 10; i++) {
pos = this->blastTailPos[(s16)Rand_ZeroFloat(29.9f)];
pos.x += Rand_CenteredFloat(40.0f);
pos.y += Rand_CenteredFloat(40.0f);
pos.z += Rand_CenteredFloat(40.0f);
accel.y = 0.4f;
accel.x = Rand_CenteredFloat(0.5f);
accel.z = Rand_CenteredFloat(0.5f);
BossTw_AddDotEffect(globalCtx, &pos, &velocity, &accel, (s16)Rand_ZeroFloat(2.0f) + 8, 1,
75);
}
}
break;
case 2:
Math_ApproachF(&this->workf[TAIL_ALPHA], 0.0f, 1.0f, 15.0f);
if (this->timers[0] == 0) {
Actor_Kill(&this->actor);
}
break;
}
break;
case TW_FIRE_BLAST_GROUND:
if (this->timers[0] != 0) {
if (this->timers[0] == 1) {
sEnvType = 0;
}
if (sGroundBlastType == 2) {
this->timers[0] = 0;
}
Audio_PlayActorSound2(&this->actor, NA_SE_EN_TWINROBA_FIRE_EXP - SFX_FLAG);
xDiff = sKoumePtr->groundBlastPos2.x - player->actor.world.pos.x;
yDiff = sKoumePtr->groundBlastPos2.y - player->actor.world.pos.y;
zDiff = sKoumePtr->groundBlastPos2.z - player->actor.world.pos.z;
if (!player->isBurning && (player->actor.bgCheckFlags & 1) && (fabsf(yDiff) < 10.0f) &&
(sqrtf(SQ(xDiff) + SQ(zDiff)) < (sKoumePtr->workf[UNK_F13] * 4550.0f))) {
s16 j;
for (j = 0; j < 18; j++) {
player->flameTimers[j] = Rand_S16Offset(0, 200);
}
player->isBurning = 1;
if (this->work[BURN_TMR] == 0) {
func_8002F7DC(&player->actor, player->ageProperties->unk_92 + NA_SE_VO_LI_DEMO_DAMAGE);
this->work[BURN_TMR] = 40;
}
sTwinrovaPtr->timers[2] = 100;
}
Math_ApproachF(&sKoumePtr->workf[UNK_F13], 0.04f, 0.1f, 0.002f);
break;
}
{
f32 sp4C = sGroundBlastType == 2 ? 3.0f : 1.0f;
Math_ApproachF(&sKoumePtr->workf[UNK_F9], 0.0f, 1.0f, 10.0f * sp4C);
Math_ApproachF(&sKoumePtr->workf[UNK_F12], 0.0f, 1.0f, 0.03f * sp4C);
Math_ApproachF(&sKoumePtr->workf[TAIL_ALPHA], 0.0f, 1.0f, 3.0f * sp4C);
Math_ApproachF(&sKoumePtr->workf[UNK_F11], 0.0f, 1.0f, 6.0f * sp4C);
}
if (sKoumePtr->workf[TAIL_ALPHA] <= 0.0f) {
Actor_Kill(&this->actor);
}
break;
}
}
void BossTw_BlastIce(BossTw* this, GlobalContext* globalCtx) {
s16 i;
f32 xDiff;
f32 yDiff;
f32 zDiff;
f32 xzDist;
Player* player = GET_PLAYER(globalCtx);
Player* player2 = player;
switch (this->actor.params) {
case TW_ICE_BLAST:
switch (this->csState1) {
case 0:
Actor_SetScale(&this->actor, 0.03f);
this->csState1 = 1;
xDiff = player->actor.world.pos.x - this->actor.world.pos.x;
yDiff = (player->actor.world.pos.y + 30.0f) - this->actor.world.pos.y;
zDiff = player->actor.world.pos.z - this->actor.world.pos.z;
this->actor.world.rot.y = Math_FAtan2F(xDiff, zDiff) * (32768 / M_PI);
xzDist = sqrtf(SQ(xDiff) + SQ(zDiff));
this->actor.world.rot.x = Math_FAtan2F(yDiff, xzDist) * (32768 / M_PI);
this->actor.speedXZ = 20.0f;
for (i = 0; i < 50; i++) {
this->blastTailPos[i] = this->actor.world.pos;
}
this->workf[TAIL_ALPHA] = 255.0f;
// fallthrough
case 1:
case 10:
this->blastActive = true;
if (this->timers[0] == 0) {
func_8002D908(&this->actor);
func_8002D7EC(&this->actor);
Audio_PlayActorSound2(&this->actor, NA_SE_EN_TWINROBA_SHOOT_FREEZE - SFX_FLAG);
} else {
Vec3f velocity;
Vec3f spF4;
Vec3s reflDir;
s16 alpha;
this->actor.world.pos = player2->bodyPartsPos[15];
this->actor.world.pos.y = -2000.0f;
Matrix_MtxFToYXZRotS(&player2->shieldMf, &reflDir, 0);
reflDir.x = -reflDir.x;
reflDir.y += 0x8000;
Math_ApproachS(&this->magicDir.x, reflDir.x, 0xA, 0x800);
Math_ApproachS(&this->magicDir.y, reflDir.y, 0xA, 0x800);
if (this->timers[0] == 50) {
D_8094C86F = 10;
D_8094C872 = 7;
globalCtx->envCtx.unk_D8 = 1.0f;
}
if (this->timers[0] <= 50) {
Audio_PlayActorSound2(&this->actor, NA_SE_EN_TWINROBA_SHOOT_FREEZE - SFX_FLAG);
Audio_PlayActorSound2(&this->actor, NA_SE_EN_TWINROBA_REFL_FREEZE - SFX_FLAG);
Matrix_RotateY((this->magicDir.y / 32678.0f) * M_PI, MTXMODE_NEW);
Matrix_RotateX((this->magicDir.x / 32678.0f) * M_PI, MTXMODE_APPLY);
spF4.x = 0.0f;
spF4.y = 0.0f;
spF4.z = 50.0f;
Matrix_MultVec3f(&spF4, &velocity);
alpha = this->timers[0] * 10;
alpha = CLAMP_MAX(alpha, 255);
BossTw_AddShieldBlastEffect(globalCtx, &player2->bodyPartsPos[15], &velocity, &sZeroVector,
10.0f, 80.0f, alpha, 0);
}
if (this->timers[0] == 1) {
sEnvType = 0;
sShieldIceCharge++;
Actor_Kill(&this->actor);
}
break;
}
this->groundBlastPos.y = BossTw_GetFloorY(&this->actor.world.pos);
if (this->groundBlastPos.y >= 0.0f) {
if (this->groundBlastPos.y != 35.0f) {
this->groundBlastPos.x = this->actor.world.pos.x;
this->groundBlastPos.z = this->actor.world.pos.z;
BossTw_SpawnGroundBlast(this, globalCtx, 0);
} else {
for (i = 0; i < 50; i++) {
Vec3f velocity;
Vec3f accel;
velocity.x = Rand_CenteredFloat(20.0f);
velocity.y = Rand_CenteredFloat(20.0f);
velocity.z = Rand_CenteredFloat(20.0f);
accel.x = 0.0f;
accel.y = 0.0f;
accel.z = 0.0f;
BossTw_AddFlameEffect(globalCtx, &this->actor.world.pos, &velocity, &accel,
Rand_ZeroFloat(10.0f) + 25.0f, this->blastType);
}
globalCtx->envCtx.unk_D8 = 0.5f;
}
this->csState1 = 2;
this->timers[0] = 20;
} else {
Vec3f pos;
Vec3f velocity = { 0.0f, 0.0f, 0.0f };
Vec3f accel = { 0.0f, 0.0f, 0.0f };
for (i = 0; i < 10; i++) {
pos = this->blastTailPos[(s16)Rand_ZeroFloat(29.9f)];
pos.x += Rand_CenteredFloat(40.0f);
pos.y += Rand_CenteredFloat(40.0f);
pos.z += Rand_CenteredFloat(40.0f);
accel.y = 0.4f;
accel.x = Rand_CenteredFloat(0.5f);
accel.z = Rand_CenteredFloat(0.5f);
BossTw_AddDotEffect(globalCtx, &pos, &velocity, &accel, ((s16)Rand_ZeroFloat(2.0f) + 8), 0,
75);
}
}
break;
case 2:
Math_ApproachF(&this->workf[TAIL_ALPHA], 0.0f, 1.0f, 15.0f);
if (this->timers[0] == 0) {
Actor_Kill(&this->actor);
}
break;
}
break;
case TW_ICE_BLAST_GROUND:
if (this->timers[0] != 0) {
if (this->timers[0] == 1) {
sEnvType = 0;
}
if (sGroundBlastType == 1) {
this->timers[0] = 0;
}
Audio_PlayActorSound2(&this->actor, NA_SE_EV_ICE_FREEZE - SFX_FLAG);
if (this->timers[0] > (sTwinrovaPtr->actionFunc == BossTw_Wait ? 70 : 20)) {
s32 pad;
Vec3f pos;
Vec3f velocity;
Vec3f accel;
pos.x = sKotakePtr->groundBlastPos2.x + Rand_CenteredFloat(320.0f);
pos.z = sKotakePtr->groundBlastPos2.z + Rand_CenteredFloat(320.0f);
pos.y = sKotakePtr->groundBlastPos2.y;
velocity.x = 0.0f;
velocity.y = 0.0f;
velocity.z = 0.0f;
accel.x = 0.0f;
accel.y = 0.13f;
accel.z = 0.0f;
BossTw_AddDmgCloud(globalCtx, 3, &pos, &velocity, &accel, Rand_ZeroFloat(5.0f) + 20.0f, 0, 0, 80);
velocity.x = Rand_CenteredFloat(10.0f);
velocity.z = Rand_CenteredFloat(10.0f);
velocity.y = Rand_ZeroFloat(3.0f) + 3.0f;
pos.x = sKotakePtr->groundBlastPos2.x + (velocity.x * 0.5f);
pos.z = sKotakePtr->groundBlastPos2.z + (velocity.z * 0.5f);
BossTw_AddDmgCloud(globalCtx, 3, &pos, &velocity, &accel, Rand_ZeroFloat(5.0f) + 15.0f, 255, 2,
130);
}
Math_ApproachF(&sKotakePtr->workf[UNK_F9], 80.0f, 1.0f, 3.0f);
Math_ApproachF(&sKotakePtr->workf[UNK_F11], 255.0f, 1.0f, 10.0f);
Math_ApproachF(&sKotakePtr->workf[UNK_F12], 0.04f, 0.1f, 0.002f);
Math_ApproachF(&sKotakePtr->workf[UNK_F16], 70.0f, 1.0f, 5.0f);
if ((this->timers[0] == 70) || (this->timers[0] == 30)) {
sKotakePtr->workf[UNK_F16] = 10.0f;
}
if ((this->timers[0] % 4) == 0) {
sKotakePtr->workf[UNK_F15] = (2.0f * (s16)Rand_ZeroFloat(9.9f) * M_PI) / 10.0f;
}
} else {
f32 sp80;
if (sGroundBlastType == 1) {
if (sKotakePtr->workf[UNK_F11] > 1.0f) {
for (i = 0; i < 3; i++) {
Vec3f pos;
Vec3f velocity;
Vec3f accel;
pos.x = Rand_CenteredFloat(280.0f) + sKotakePtr->groundBlastPos2.x;
pos.z = Rand_CenteredFloat(280.0f) + sKotakePtr->groundBlastPos2.z;
pos.y = sKotakePtr->groundBlastPos2.y + 30.0f;
velocity.x = 0.0f;
velocity.y = 0.0f;
velocity.z = 0.0f;
accel.x = 0.0f;
accel.y = 0.13f;
accel.z = 0.0f;
BossTw_AddDmgCloud(globalCtx, 3, &pos, &velocity, &accel, Rand_ZeroFloat(5.0f) + 20, 0, 0,
80);
}
}
sp80 = 3.0f;
} else {
sp80 = 1.0f;
}
Math_ApproachF(&sKotakePtr->workf[UNK_F14], 0.0f, 1.0f, 0.2f * sp80);
Math_ApproachF(&sKotakePtr->workf[UNK_F11], 0.0f, 1.0f, 5.0f * sp80);
Math_ApproachF(&sKotakePtr->workf[UNK_F9], 0.0f, 1.0f, sp80);
if (sKotakePtr->workf[UNK_F9] <= 0.0f) {
Actor_Kill(&this->actor);
}
}
break;
}
}
s32 BossTw_BlastShieldCheck(BossTw* this, GlobalContext* globalCtx) {
Player* player = GET_PLAYER(globalCtx);
s32 ret = false;
ColliderInfo* info;
if (this->csState1 == 1) {
if (this->collider.base.acFlags & AC_HIT) {
this->collider.base.acFlags &= ~AC_HIT;
this->collider.base.atFlags &= ~AT_HIT;
info = this->collider.info.acHitInfo;
if (info->toucher.dmgFlags & DMG_SHIELD) {
this->work[INVINC_TIMER] = 7;
globalCtx->envCtx.unk_D8 = 1.0f;
func_800AA000(0.0f, 100, 5, 4);
if (Player_HasMirrorShieldEquipped(globalCtx)) {
if (this->blastType == 1) {
if (sShieldIceCharge != 0) {
sShieldIceCharge = 0;
BossTw_AddShieldDeflectEffect(globalCtx, 10.0f, 1);
} else {
BossTw_AddShieldHitEffect(globalCtx, 10.0f, 1);
sShieldFireCharge++;
D_8094C86F = (sShieldFireCharge * 2) + 8;
D_8094C872 = -7;
}
} else {
if (sShieldFireCharge != 0) {
sShieldFireCharge = 0;
BossTw_AddShieldDeflectEffect(globalCtx, 10.0f, 0);
} else {
BossTw_AddShieldHitEffect(globalCtx, 10.0f, 0);
sShieldIceCharge++;
D_8094C86F = (sShieldIceCharge * 2) + 8;
D_8094C872 = -7;
}
}
if ((sShieldIceCharge >= 3) || (sShieldFireCharge >= 3)) {
this->timers[0] = 80;
this->csState1 = 10;
Matrix_MtxFToYXZRotS(&player->shieldMf, &this->magicDir, 0);
this->magicDir.y += 0x8000;
this->magicDir.x = -this->magicDir.x;
D_8094C86F = 8;
} else {
this->csState1 = 2;
this->timers[0] = 20;
sEnvType = 0;
}
} else {
BossTw_AddShieldDeflectEffect(globalCtx, 10.0f, this->blastType);
this->csState1 = 2;
this->timers[0] = 20;
sEnvType = 0;
sShieldIceCharge = 0;
sShieldFireCharge = 0;
func_80078884(NA_SE_IT_SHIELD_REFLECT_MG2);
}
ret = true;
}
}
}
return ret;
}
void BossTw_BlastUpdate(Actor* thisx, GlobalContext* globalCtx) {
BossTw* this = (BossTw*)thisx;
ColliderCylinder* collider;
s16 i;
this->work[CS_TIMER_1]++;
this->work[CS_TIMER_2]++;
this->work[TAIL_IDX]++;
if (this->work[TAIL_IDX] > 29) {
this->work[TAIL_IDX] = 0;
}
this->blastTailPos[this->work[TAIL_IDX]] = this->actor.world.pos;
this->actionFunc(this, globalCtx);
for (i = 0; i < 5; i++) {
if (this->timers[i] != 0) {
this->timers[i]--;
}
}
if (this->work[INVINC_TIMER] != 0) {
this->work[INVINC_TIMER]--;
}
if (this->work[BURN_TMR] != 0) {
this->work[BURN_TMR]--;
}
this->actor.focus.pos = this->actor.world.pos;
collider = &this->collider;
Collider_UpdateCylinder(&this->actor, collider);
if (this->blastActive && this->work[INVINC_TIMER] == 0 && !BossTw_BlastShieldCheck(this, globalCtx)) {
CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, &collider->base);
CollisionCheck_SetAT(globalCtx, &globalCtx->colChkCtx, &collider->base);
}
this->blastActive = false;
}
void BossTw_BlastDraw(Actor* thisx, GlobalContext* globalCtx2) {
GlobalContext* globalCtx = globalCtx2;
BossTw* this = (BossTw*)thisx;
f32 scaleFactor;
s16 tailIdx;
s16 i;
static u32 epoch = 0;
epoch++;
OPEN_DISPS(globalCtx->state.gfxCtx);
func_80093D84(globalCtx->state.gfxCtx);
switch (this->actor.params) {
case TW_FIRE_BLAST:
gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 200, 20, 0, (s8)this->workf[TAIL_ALPHA]);
gDPSetEnvColor(POLY_XLU_DISP++, 255, 215, 255, 128);
for (i = 9; i >= 0; i--) {
FrameInterpolation_RecordOpenChild("Twinrova Fire Blast", epoch + i * 25);
gSPSegment(POLY_XLU_DISP++, 8,
Gfx_TwoTexScroll(
globalCtx->state.gfxCtx, 0, ((this->work[CS_TIMER_1] * 3) + (i * 10)) & 0x7F,
((-this->work[CS_TIMER_1] * 15) + (i * 50)) & 0xFF, 0x20, 0x40, 1, 0, 0, 0x20, 0x20));
tailIdx = ((this->work[TAIL_IDX] - i) + 30) % 30;
Matrix_Translate(this->blastTailPos[tailIdx].x, this->blastTailPos[tailIdx].y,
this->blastTailPos[tailIdx].z, MTXMODE_NEW);
scaleFactor = 1.0f - (i * 0.09f);
Matrix_Scale(this->actor.scale.x * scaleFactor, this->actor.scale.y * scaleFactor,
this->actor.scale.z * scaleFactor, MTXMODE_APPLY);
Matrix_ReplaceRotation(&globalCtx->billboardMtxF);
gSPMatrix(POLY_XLU_DISP++, MATRIX_NEWMTX(globalCtx->state.gfxCtx),
G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW);
gSPDisplayList(POLY_XLU_DISP++, SEGMENTED_TO_VIRTUAL(object_tw_DL_01A430));
FrameInterpolation_RecordCloseChild();
}
break;
case TW_FIRE_BLAST_GROUND:
break;
case TW_ICE_BLAST:
gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 195, 225, 235, (s8)this->workf[TAIL_ALPHA]);
gSPDisplayList(POLY_XLU_DISP++, SEGMENTED_TO_VIRTUAL(object_tw_DL_01A998));
for (i = 9; i >= 0; i--) {
FrameInterpolation_RecordOpenChild("Twinrova Ice Blast", epoch + i * 25);
gSPSegment(POLY_XLU_DISP++, 8,
Gfx_TwoTexScroll(
globalCtx->state.gfxCtx, 0, ((this->work[CS_TIMER_1] * 3) + (i * 0xA)) & 0x7F,
(u8)((-this->work[CS_TIMER_1] * 0xF) + (i * 50)), 0x20, 0x40, 1, 0, 0, 0x20, 0x20));
tailIdx = ((this->work[TAIL_IDX] - i) + 30) % 30;
Matrix_Translate(this->blastTailPos[tailIdx].x, this->blastTailPos[tailIdx].y,
this->blastTailPos[tailIdx].z, MTXMODE_NEW);
scaleFactor = 1.0f - (i * 0.09f);
Matrix_Scale(this->actor.scale.x * scaleFactor, this->actor.scale.y * scaleFactor,
this->actor.scale.z * scaleFactor, MTXMODE_APPLY);
Matrix_ReplaceRotation(&globalCtx->billboardMtxF);
gSPMatrix(POLY_XLU_DISP++, MATRIX_NEWMTX(globalCtx->state.gfxCtx),
G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW);
gSPDisplayList(POLY_XLU_DISP++, SEGMENTED_TO_VIRTUAL(object_tw_DL_01AB00));
FrameInterpolation_RecordCloseChild();
}
break;
case TW_ICE_BLAST_GROUND:
break;
}
CLOSE_DISPS(globalCtx->state.gfxCtx);
}
void BossTw_DrawDeathBall(Actor* thisx, GlobalContext* globalCtx2) {
GlobalContext* globalCtx = globalCtx2;
BossTw* this = (BossTw*)thisx;
f32 scaleFactor;
s16 tailIdx;
s16 i;
static u32 epoch = 0;
epoch++;
OPEN_DISPS(globalCtx->state.gfxCtx);
func_80093D84(globalCtx->state.gfxCtx);
if (this->actor.params == TW_DEATHBALL_KOUME) {
gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 200, 20, 0, (s8)this->workf[TAIL_ALPHA]);
gDPSetEnvColor(POLY_XLU_DISP++, 255, 215, 255, 128);
for (i = 9; i >= 0; i--) {
FrameInterpolation_RecordOpenChild("Twinrova Death Ball 0", epoch + i * 25);
gSPSegment(POLY_XLU_DISP++, 8,
Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, (((this->work[CS_TIMER_1] * 3) + (i * 0xA))) & 0x7F,
(u8)((-this->work[CS_TIMER_1] * 0xF) + (i * 50)), 0x20, 0x40, 1, 0, 0, 0x20,
0x20));
tailIdx = ((this->work[TAIL_IDX] - i) + 30) % 30;
Matrix_Translate(this->blastTailPos[tailIdx].x, this->blastTailPos[tailIdx].y,
this->blastTailPos[tailIdx].z, MTXMODE_NEW);
scaleFactor = (1.0f - (i * 0.09f));
Matrix_Scale(this->actor.scale.x * scaleFactor, this->actor.scale.y * scaleFactor,
this->actor.scale.z * scaleFactor, MTXMODE_APPLY);
Matrix_ReplaceRotation(&globalCtx->billboardMtxF);
gSPMatrix(POLY_XLU_DISP++, MATRIX_NEWMTX(globalCtx->state.gfxCtx),
G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW);
gSPDisplayList(POLY_XLU_DISP++, SEGMENTED_TO_VIRTUAL(object_tw_DL_01A430));
FrameInterpolation_RecordCloseChild();
}
} else {
gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 195, 225, 235, (s8)this->workf[TAIL_ALPHA]);
gSPDisplayList(POLY_XLU_DISP++, SEGMENTED_TO_VIRTUAL(object_tw_DL_01A998));
for (i = 9; i >= 0; i--) {
FrameInterpolation_RecordOpenChild("Twinrova Death Ball 1", epoch + i * 25);
gSPSegment(POLY_XLU_DISP++, 8,
Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, (((this->work[CS_TIMER_1] * 3) + (i * 0xA))) & 0x7F,
(u8)((-this->work[CS_TIMER_1] * 0xF) + (i * 50)), 0x20, 0x40, 1, 0, 0, 0x20,
0x20));
tailIdx = ((this->work[TAIL_IDX] - i) + 30) % 30;
Matrix_Translate(this->blastTailPos[tailIdx].x, this->blastTailPos[tailIdx].y,
this->blastTailPos[tailIdx].z, MTXMODE_NEW);
scaleFactor = (1.0f - (i * 0.09f));
Matrix_Scale(this->actor.scale.x * scaleFactor, this->actor.scale.y * scaleFactor,
this->actor.scale.z * scaleFactor, MTXMODE_APPLY);
Matrix_ReplaceRotation(&globalCtx->billboardMtxF);
gSPMatrix(POLY_XLU_DISP++, MATRIX_NEWMTX(globalCtx->state.gfxCtx),
G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW);
gSPDisplayList(POLY_XLU_DISP++, SEGMENTED_TO_VIRTUAL(object_tw_DL_01AB00));
FrameInterpolation_RecordCloseChild();
}
}
CLOSE_DISPS(globalCtx->state.gfxCtx);
}
void BossTw_UpdateEffects(GlobalContext* globalCtx) {
static Color_RGB8 sDotColors[] = {
{ 255, 128, 0 }, { 255, 0, 0 }, { 255, 255, 0 }, { 255, 0, 0 },
{ 100, 100, 100 }, { 255, 255, 255 }, { 150, 150, 150 }, { 255, 255, 255 },
};
Vec3f sp11C;
BossTwEffect* eff = globalCtx->specialEffects;
Player* player = GET_PLAYER(globalCtx);
u8 sp113 = 0;
s16 i;
s16 j;
s16 colorIdx;
Vec3f off;
Vec3f spF4;
Vec3f spE8;
Vec3f spDC;
Vec3f spD0;
f32 phi_f22;
Vec3f spC0;
Vec3f spB4;
Vec3f spA8;
s16 spA6;
f32 phi_f0;
Actor* unk44;
for (i = 0; i < ARRAY_COUNT(sTwEffects); i++) {
if (eff->type != 0) {
eff->pos.x += eff->curSpeed.x;
eff->pos.y += eff->curSpeed.y;
eff->pos.z += eff->curSpeed.z;
eff->frame++;
eff->curSpeed.x += eff->accel.x;
eff->curSpeed.y += eff->accel.y;
eff->curSpeed.z += eff->accel.z;
if (eff->type == 1) {
colorIdx = eff->frame % 4;
if (eff->work[EFF_ARGS] == 0) {
colorIdx += 4;
}
eff->color.r = sDotColors[colorIdx].r;
eff->color.g = sDotColors[colorIdx].g;
eff->color.b = sDotColors[colorIdx].b;
eff->alpha -= 20;
if (eff->alpha <= 0) {
eff->alpha = 0;
eff->type = TWEFF_NONE;
}
} else if ((eff->type == 3) || (eff->type == 2)) {
if (eff->work[EFF_ARGS] == 2) {
eff->alpha -= 20;
if (eff->alpha <= 0) {
eff->alpha = 0;
eff->type = TWEFF_NONE;
}
} else if (eff->work[EFF_ARGS] == 0) {
eff->alpha += 10;
if (eff->alpha >= 100) {
eff->work[EFF_ARGS]++;
}
} else {
eff->alpha -= 3;
if (eff->alpha <= 0) {
eff->alpha = 0;
eff->type = TWEFF_NONE;
}
}
} else if (eff->type == TWEFF_FLAME) {
if (eff->work[EFF_UNKS1] != 0) {
eff->alpha = (eff->alpha - (i & 7)) - 0xD;
if (eff->alpha <= 0) {
eff->alpha = 0;
eff->type = TWEFF_NONE;
}
} else {
eff->alpha += 300;
if (eff->alpha >= 255) {
eff->alpha = 255;
eff->work[EFF_UNKS1]++;
}
}
} else if (eff->type == TWEFF_SHLD_BLST) {
D_8094C870 = 1;
eff->work[EFF_UNKS1]++;
if (eff->work[EFF_UNKS1] > 30) {
eff->alpha -= 10;
if (eff->alpha <= 0) {
eff->alpha = 0;
eff->type = TWEFF_NONE;
}
}
Math_ApproachF(&eff->workf[EFF_SCALE], eff->workf[EFF_DIST], 0.1f, 0.003f);
off.x = sTwinrovaPtr->actor.world.pos.x - eff->pos.x;
off.y = (sTwinrovaPtr->actor.world.pos.y - eff->pos.y) * 0.5f;
off.z = sTwinrovaPtr->actor.world.pos.z - eff->pos.z;
if (sTwinrovaPtr->actionFunc != BossTw_TwinrovaStun) {
if ((SQ(off.x) + SQ(off.y) + SQ(off.z)) < SQ(60.0f)) {
for (j = 0; j < 50; j++) {
spF4.x = sTwinrovaPtr->actor.world.pos.x + Rand_CenteredFloat(35.0f);
spF4.y = sTwinrovaPtr->actor.world.pos.y + Rand_CenteredFloat(70.0f);
spF4.z = sTwinrovaPtr->actor.world.pos.z + Rand_CenteredFloat(35.0f);
spE8.x = Rand_CenteredFloat(20.0f);
spE8.y = Rand_CenteredFloat(20.0f);
spE8.z = Rand_CenteredFloat(20.0f);
spDC.x = 0.0f;
spDC.y = 0.0f;
spDC.z = 0.0f;
BossTw_AddFlameEffect(globalCtx, &spF4, &spE8, &spDC, Rand_ZeroFloat(10.0f) + 25.0f,
eff->work[EFF_ARGS]);
}
sTwinrovaPtr->twinrovaStun = 1;
globalCtx->envCtx.unk_D8 = 1.0f;
eff->type = TWEFF_NONE;
}
}
} else if (eff->type == TWEFF_MERGEFLAME) {
sp11C.x = 0.0f;
sp11C.y = eff->pos.y;
sp11C.z = eff->workf[EFF_DIST];
Matrix_RotateY(sTwinrovaPtr->workf[UNK_F9] + eff->workf[EFF_ROLL], MTXMODE_NEW);
Matrix_MultVec3f(&sp11C, &eff->pos);
if (eff->work[EFF_UNKS1] != 0) {
eff->alpha -= 60;
if (eff->alpha <= 0) {
eff->alpha = 0;
eff->type = TWEFF_NONE;
}
} else {
eff->alpha += 60;
if (eff->alpha >= 255) {
eff->alpha = 255;
eff->work[EFF_UNKS1]++;
}
}
} else if (eff->type == TWEFF_SHLD_DEFL) {
eff->work[EFF_UNKS1]++;
sp11C.x = 0.0f;
sp11C.y = 0.0f;
sp11C.z = -eff->workf[EFF_DIST];
Matrix_RotateY((sShieldHitYaw / 32768.0f) * M_PI, MTXMODE_NEW);
Matrix_RotateX(-0.2f, MTXMODE_APPLY);
Matrix_RotateZ(eff->workf[EFF_ROLL], MTXMODE_APPLY);
Matrix_RotateY(eff->workf[EFF_YAW], MTXMODE_APPLY);
Matrix_MultVec3f(&sp11C, &eff->pos);
eff->pos.x += sShieldHitPos.x;
eff->pos.y += sShieldHitPos.y;
eff->pos.z += sShieldHitPos.z;
if (eff->work[EFF_UNKS1] < 10) {
Math_ApproachF(&eff->workf[EFF_DIST], 50.0f, 0.5f, 100.0f);
} else {
Math_ApproachF(&eff->workf[EFF_YAW], 0.0f, 0.5f, 10.0f);
Math_ApproachF(&eff->workf[EFF_DIST], 1000.0f, 1.0f, 10.0f);
if (eff->work[EFF_UNKS1] >= 0x10) {
if ((eff->work[EFF_UNKS1] == 16) && (sp113 == 0)) {
sp113 = 1;
spD0 = eff->pos;
if (eff->pos.y > 40.0f) {
spD0.y = 220.0f;
} else {
spD0.y = -50.0f;
}
sTwinrovaPtr->groundBlastPos.y = phi_f0 = BossTw_GetFloorY(&spD0);
if (phi_f0 >= 0.0f) {
if (sTwinrovaPtr->groundBlastPos.y != 35.0f) {
sTwinrovaPtr->groundBlastPos.x = eff->pos.x;
sTwinrovaPtr->groundBlastPos.z = eff->pos.z;
BossTw_SpawnGroundBlast(sTwinrovaPtr, globalCtx, eff->work[EFF_ARGS]);
}
}
}
eff->alpha -= 300;
if (eff->alpha <= 0) {
eff->alpha = 0;
eff->type = TWEFF_NONE;
}
}
}
BossTw_AddFlameEffect(globalCtx, &eff->pos, &sZeroVector, &sZeroVector, 10, eff->work[EFF_ARGS]);
} else if (eff->type == TWEFF_SHLD_HIT) {
eff->work[EFF_UNKS1]++;
sp11C.x = 0.0f;
sp11C.y = 0.0f;
sp11C.z = -eff->workf[EFF_DIST];
Matrix_RotateY((sShieldHitYaw / 32768.0f) * M_PI, MTXMODE_NEW);
Matrix_RotateX(-0.2f, MTXMODE_APPLY);
Matrix_RotateZ(eff->workf[EFF_ROLL], MTXMODE_APPLY);
Matrix_RotateY(eff->workf[EFF_YAW], MTXMODE_APPLY);
Matrix_MultVec3f(&sp11C, &eff->pos);
eff->pos.x += sShieldHitPos.x;
eff->pos.y += sShieldHitPos.y;
eff->pos.z += sShieldHitPos.z;
if (eff->work[EFF_UNKS1] < 5) {
Math_ApproachF(&eff->workf[EFF_DIST], 40.0f, 0.5f, 100.0f);
} else {
Math_ApproachF(&eff->workf[EFF_DIST], 0.0f, 0.2f, 5.0f);
if (eff->work[EFF_UNKS1] >= 11) {
eff->alpha -= 30;
if (eff->alpha <= 0) {
eff->alpha = 0;
eff->type = TWEFF_NONE;
}
}
}
BossTw_AddFlameEffect(globalCtx, &eff->pos, &sZeroVector, &sZeroVector, 10, eff->work[EFF_ARGS]);
} else if (eff->type == 4) {
if (eff->work[EFF_UNKS1] == 0) {
Math_ApproachF(&eff->workf[EFF_SCALE], eff->workf[EFF_DIST], 0.05f, 1.0f);
if (eff->frame >= 16) {
eff->alpha -= 10;
if (eff->alpha <= 0) {
eff->alpha = 0;
eff->type = TWEFF_NONE;
}
}
} else {
Math_ApproachF(&eff->workf[EFF_SCALE], eff->workf[EFF_DIST], 0.1f, 2.0f);
eff->alpha -= 15;
if (eff->alpha <= 0) {
eff->alpha = 0;
eff->type = TWEFF_NONE;
}
}
} else if (eff->type == TWEFF_PLYR_FRZ) {
if (eff->work[EFF_ARGS] < eff->frame) {
phi_f0 = 1.0f;
if (eff->target != NULL || sGroundBlastType == 1) {
phi_f0 *= 3.0f;
}
Math_ApproachF(&eff->workf[EFF_SCALE], 0.0f, 1.0f, 0.0005f * phi_f0);
if (eff->workf[EFF_SCALE] == 0.0f) {
eff->type = TWEFF_NONE;
if (eff->target == NULL) {
player->stateFlags2 &= ~0x8000;
sFreezeState = 0;
}
}
} else {
if (sGroundBlastType == 1) {
eff->frame = 100;
}
Math_ApproachF(&eff->workf[EFF_DIST], 0.8f, 0.2f, 0.04f);
if (eff->target == NULL) {
Math_ApproachF(&eff->workf[EFF_SCALE], 0.012f, 1.0f, 0.002f);
eff->workf[EFF_ROLL] += eff->workf[EFF_DIST];
if (eff->workf[EFF_ROLL] >= 0.8f) {
eff->workf[EFF_ROLL] -= 0.8f;
player->stateFlags2 |= 0x8000;
} else {
player->stateFlags2 &= ~0x8000;
}
if ((sKotakePtr->workf[UNK_F11] > 10.0f) && (sKotakePtr->workf[UNK_F11] < 200.0f)) {
eff->frame = 100;
}
if (!(globalCtx->gameplayFrames & 1)) {
globalCtx->damagePlayer(globalCtx, -1);
}
} else {
Math_ApproachF(&eff->workf[EFF_SCALE], 0.042f, 1.0f, 0.002f);
}
if ((eff->workf[EFF_DIST] > 0.4f) && ((eff->frame & 7) == 0)) {
spA6 = Rand_ZeroFloat(17.9f);
if (eff->target == NULL) {
spC0.x = player->bodyPartsPos[spA6].x + Rand_CenteredFloat(5.0f);
spC0.y = player->bodyPartsPos[spA6].y + Rand_CenteredFloat(5.0f);
spC0.z = player->bodyPartsPos[spA6].z + Rand_CenteredFloat(5.0f);
phi_f22 = 10.0f;
} else {
unk44 = eff->target;
spC0.x = unk44->world.pos.x + Rand_CenteredFloat(40.0f);
spC0.y = unk44->world.pos.y + Rand_CenteredFloat(40.0f);
spC0.z = unk44->world.pos.z + Rand_CenteredFloat(40.0f);
phi_f22 = 20.0f;
}
spB4.x = 0.0f;
spB4.y = 0.0f;
spB4.z = 0.0f;
spA8.x = 0.0f;
spA8.y = 0.1f;
spA8.z = 0.0f;
BossTw_AddDmgCloud(globalCtx, 3, &spC0, &spB4, &spA8, phi_f22 + Rand_ZeroFloat(phi_f22 * 0.5f),
0, 0, 150);
}
}
}
}
eff++;
}
}
static s32 sRandSeed0;
static s32 sRandSeed1;
static s32 sRandSeed2;
void BossTw_InitRand(s32 seed0, s32 seed1, s32 seed2) {
sRandSeed0 = seed0;
sRandSeed1 = seed1;
sRandSeed2 = seed2;
}
f32 BossTw_RandZeroOne(void) {
f32 rand;
// Wichmann-Hill algorithm
sRandSeed0 = (sRandSeed0 * 171) % 30269;
sRandSeed1 = (sRandSeed1 * 172) % 30307;
sRandSeed2 = (sRandSeed2 * 170) % 30323;
rand = (sRandSeed0 / 30269.0f) + (sRandSeed1 / 30307.0f) + (sRandSeed2 / 30323.0f);
while (rand >= 1.0f) {
rand -= 1.0f;
}
return fabsf(rand);
}
void BossTw_DrawEffects(GlobalContext* globalCtx) {
u8 sp18F = 0;
s16 i;
s16 j;
s32 pad;
Player* player = GET_PLAYER(globalCtx);
s16 phi_s4;
BossTwEffect* currentEffect = globalCtx->specialEffects;
BossTwEffect* effectHead;
GraphicsContext* gfxCtx = globalCtx->state.gfxCtx;
effectHead = currentEffect;
OPEN_DISPS(gfxCtx);
func_80093D84(globalCtx->state.gfxCtx);
for (i = 0; i < ARRAY_COUNT(sTwEffects); i++) {
if (currentEffect->type == 1) {
FrameInterpolation_RecordOpenChild(currentEffect, currentEffect->epoch);
if (sp18F == 0) {
gSPDisplayList(POLY_XLU_DISP++, object_tw_DL_01A528);
sp18F++;
}
gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, currentEffect->color.r, currentEffect->color.g,
currentEffect->color.b, currentEffect->alpha);
Matrix_Translate(currentEffect->pos.x, currentEffect->pos.y, currentEffect->pos.z, MTXMODE_NEW);
Matrix_ReplaceRotation(&globalCtx->billboardMtxF);
Matrix_Scale(currentEffect->workf[EFF_SCALE], currentEffect->workf[EFF_SCALE], 1.0f, MTXMODE_APPLY);
gSPMatrix(POLY_XLU_DISP++, MATRIX_NEWMTX(gfxCtx),
G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW);
gSPDisplayList(POLY_XLU_DISP++, object_tw_DL_01A5A8);
FrameInterpolation_RecordCloseChild();
}
currentEffect++;
}
sp18F = 0;
currentEffect = effectHead;
for (i = 0; i < ARRAY_COUNT(sTwEffects); i++) {
if (currentEffect->type == 3) {
FrameInterpolation_RecordOpenChild(currentEffect, currentEffect->epoch);
if (sp18F == 0) {
gSPDisplayList(POLY_XLU_DISP++, SEGMENTED_TO_VIRTUAL(object_tw_DL_01A998));
sp18F++;
}
gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 195, 225, 235, currentEffect->alpha);
gSPSegment(POLY_XLU_DISP++, 8,
Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, (currentEffect->frame * 3) & 0x7F,
(currentEffect->frame * 15) & 0xFF, 0x20, 0x40, 1, 0, 0, 0x20, 0x20));
Matrix_Translate(currentEffect->pos.x, currentEffect->pos.y, currentEffect->pos.z, MTXMODE_NEW);
Matrix_ReplaceRotation(&globalCtx->billboardMtxF);
Matrix_Scale(currentEffect->workf[EFF_SCALE], currentEffect->workf[EFF_SCALE], 1.0f, MTXMODE_APPLY);
gSPMatrix(POLY_XLU_DISP++, MATRIX_NEWMTX(gfxCtx),
G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW);
gSPDisplayList(POLY_XLU_DISP++, SEGMENTED_TO_VIRTUAL(object_tw_DL_01AB00));
FrameInterpolation_RecordCloseChild();
}
currentEffect++;
}
sp18F = 0;
currentEffect = effectHead;
for (i = 0; i < ARRAY_COUNT(sTwEffects); i++) {
if (currentEffect->type == 2) {
FrameInterpolation_RecordOpenChild(currentEffect, currentEffect->epoch);
if (sp18F == 0) {
gDPPipeSync(POLY_XLU_DISP++);
gDPSetEnvColor(POLY_XLU_DISP++, 255, 215, 255, 128);
sp18F++;
}
gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 200, 20, 0, currentEffect->alpha);
gSPSegment(POLY_XLU_DISP++, 8,
Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, (currentEffect->frame * 3) & 0x7F,
(currentEffect->frame * 15) & 0xFF, 0x20, 0x40, 1, 0, 0, 0x20, 0x20));
Matrix_Translate(currentEffect->pos.x, currentEffect->pos.y, currentEffect->pos.z, MTXMODE_NEW);
Matrix_ReplaceRotation(&globalCtx->billboardMtxF);
Matrix_Scale(currentEffect->workf[EFF_SCALE], currentEffect->workf[EFF_SCALE], 1.0f, MTXMODE_APPLY);
gSPMatrix(POLY_XLU_DISP++, MATRIX_NEWMTX(gfxCtx),
G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW);
gSPDisplayList(POLY_XLU_DISP++, SEGMENTED_TO_VIRTUAL(object_tw_DL_01A430));
FrameInterpolation_RecordCloseChild();
}
currentEffect++;
}
sp18F = 0;
currentEffect = effectHead;
for (i = 0; i < ARRAY_COUNT(sTwEffects); i++) {
if (currentEffect->type == 4) {
FrameInterpolation_RecordOpenChild(currentEffect, currentEffect->epoch);
if (sp18F == 0) {
sp18F++;
}
gSPSegment(POLY_XLU_DISP++, 0xD,
Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, currentEffect->frame & 0x7F,
(currentEffect->frame * 8) & 0xFF, 0x20, 0x40, 1,
(currentEffect->frame * -2) & 0x7F, 0, 0x10, 0x10));
if (currentEffect->work[EFF_ARGS] == 1) {
gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 255, 65, 0, currentEffect->alpha);
gDPPipeSync(POLY_XLU_DISP++);
gDPSetEnvColor(POLY_XLU_DISP++, 255, 255, 0, 128);
} else {
gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 195, 225, 235, currentEffect->alpha);
gDPSetEnvColor(POLY_XLU_DISP++, 255, 255, 255, 128);
}
Matrix_Translate(currentEffect->pos.x, currentEffect->pos.y, currentEffect->pos.z, MTXMODE_NEW);
Matrix_ReplaceRotation(&globalCtx->billboardMtxF);
if (currentEffect->work[EFF_UNKS1] == 0) {
Matrix_Translate(0.0f, 0.0f, 60.0f, MTXMODE_APPLY);
} else {
Matrix_Translate(0.0f, 0.0f, 0.0f, MTXMODE_APPLY);
}
Matrix_RotateZ(currentEffect->workf[EFF_ROLL], MTXMODE_APPLY);
Matrix_RotateX(M_PI / 2.0f, MTXMODE_APPLY);
Matrix_Scale(currentEffect->workf[EFF_SCALE], 1.0f, currentEffect->workf[EFF_SCALE], MTXMODE_APPLY);
gSPMatrix(POLY_XLU_DISP++, MATRIX_NEWMTX(gfxCtx),
G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW);
gDPSetRenderMode(POLY_XLU_DISP++, G_RM_PASS, G_RM_AA_ZB_XLU_SURF2);
gSPClearGeometryMode(POLY_XLU_DISP++, G_CULL_BACK | G_FOG);
gSPDisplayList(POLY_XLU_DISP++, SEGMENTED_TO_VIRTUAL(object_tw_DL_01A790));
FrameInterpolation_RecordCloseChild();
}
currentEffect++;
}
sp18F = 0;
currentEffect = effectHead;
for (i = 0; i < ARRAY_COUNT(sTwEffects); i++) {
Actor* actor;
Vec3f off;
if (currentEffect->type == TWEFF_PLYR_FRZ) {
FrameInterpolation_RecordOpenChild(currentEffect, currentEffect->epoch);
if (sp18F == 0) {
gSPDisplayList(POLY_XLU_DISP++, SEGMENTED_TO_VIRTUAL(object_tw_DL_01AA50));
gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 195, 225, 235, 255);
gSPSegment(POLY_XLU_DISP++, 8,
Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, 0, 0, 0x20, 0x40, 1, 0, 0, 0x20, 0x20));
sp18F++;
BossTw_InitRand(1, 0x71AC, 0x263A);
}
actor = currentEffect->target;
phi_s4 = actor == NULL ? 70 : 20;
for (j = 0; j < phi_s4; j++) {
off.x = (BossTw_RandZeroOne() - 0.5f) * 30.0f;
off.y = currentEffect->workf[EFF_DIST] * j;
off.z = (BossTw_RandZeroOne() - 0.5f) * 30.0f;
if (actor != NULL) {
Matrix_Translate(actor->world.pos.x + off.x, actor->world.pos.y + off.y, actor->world.pos.z + off.z,
MTXMODE_NEW);
} else {
Matrix_Translate(player->actor.world.pos.x + off.x, player->actor.world.pos.y + off.y,
player->actor.world.pos.z + off.z, MTXMODE_NEW);
}
Matrix_Scale(currentEffect->workf[EFF_SCALE], currentEffect->workf[EFF_SCALE],
currentEffect->workf[EFF_SCALE], MTXMODE_APPLY);
Matrix_RotateY(BossTw_RandZeroOne() * M_PI, MTXMODE_APPLY);
Matrix_RotateX((BossTw_RandZeroOne() - 0.5f) * M_PI * 0.5f, MTXMODE_APPLY);
gSPMatrix(POLY_XLU_DISP++, MATRIX_NEWMTX(gfxCtx),
G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW);
gSPDisplayList(POLY_XLU_DISP++, SEGMENTED_TO_VIRTUAL(object_tw_DL_01AB00));
}
FrameInterpolation_RecordCloseChild();
}
currentEffect++;
}
sp18F = 0;
currentEffect = effectHead;
for (i = 0; i < ARRAY_COUNT(sTwEffects); i++) {
if (currentEffect->type >= 6) {
FrameInterpolation_RecordOpenChild(currentEffect, currentEffect->epoch);
if (currentEffect->work[EFF_ARGS] == 0) {
gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 195, 225, 235, currentEffect->alpha);
gSPDisplayList(POLY_XLU_DISP++, SEGMENTED_TO_VIRTUAL(object_tw_DL_01A998));
} else {
gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 200, 20, 0, currentEffect->alpha);
gDPPipeSync(POLY_XLU_DISP++);
gDPSetEnvColor(POLY_XLU_DISP++, 255, 215, 255, 128);
}
gSPSegment(POLY_XLU_DISP++, 8,
Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, (currentEffect->frame * 3) & 0x7F,
(-currentEffect->frame * 15) & 0xFF, 0x20, 0x40, 1, 0, 0, 0x20, 0x20));
Matrix_Translate(currentEffect->pos.x, currentEffect->pos.y, currentEffect->pos.z, MTXMODE_NEW);
Matrix_ReplaceRotation(&globalCtx->billboardMtxF);
Matrix_Scale(currentEffect->workf[EFF_SCALE], currentEffect->workf[EFF_SCALE], 1.0f, MTXMODE_APPLY);
gSPMatrix(POLY_XLU_DISP++, MATRIX_NEWMTX(gfxCtx),
G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW);
if (currentEffect->work[EFF_ARGS] == 0) {
gSPDisplayList(POLY_XLU_DISP++, SEGMENTED_TO_VIRTUAL(object_tw_DL_01AB00));
} else {
gSPDisplayList(POLY_XLU_DISP++, SEGMENTED_TO_VIRTUAL(object_tw_DL_01A430));
}
FrameInterpolation_RecordCloseChild();
}
currentEffect++;
}
CLOSE_DISPS(gfxCtx);
}
void BossTw_TwinrovaSetupArriveAtTarget(BossTw* this, GlobalContext* globalCtx) {
this->actionFunc = BossTw_TwinrovaArriveAtTarget;
Animation_MorphToLoop(&this->skelAnime, &object_tw_Anim_032BF8, -3.0f);
this->work[CS_TIMER_1] = Rand_ZeroFloat(100.0f);
this->timers[1] = 25;
this->rotateSpeed = 0.0f;
}
void BossTw_TwinrovaArriveAtTarget(BossTw* this, GlobalContext* globalCtx) {
SkelAnime_Update(&this->skelAnime);
Math_ApproachF(&this->actor.world.pos.x, this->targetPos.x, 0.1f, fabsf(this->actor.velocity.x) * 1.5f);
Math_ApproachF(&this->actor.world.pos.y, this->targetPos.y, 0.1f, fabsf(this->actor.velocity.y) * 1.5f);
Math_ApproachF(&this->targetPos.y, 380.0f, 1.0f, 2.0f);
Math_ApproachF(&this->actor.world.pos.z, this->targetPos.z, 0.1f, fabsf(this->actor.velocity.z) * 1.5f);
if (this->timers[1] == 1) {
BossTw_TwinrovaSetupChargeBlast(this, globalCtx);
}
Math_ApproachS(&this->actor.shape.rot.y, this->actor.yawTowardsPlayer, 5, this->rotateSpeed);
Math_ApproachF(&this->rotateSpeed, 4096.0f, 1.0f, 350.0f);
}
void BossTw_TwinrovaSetupChargeBlast(BossTw* this, GlobalContext* globalCtx) {
this->actionFunc = BossTw_TwinrovaChargeBlast;
Animation_MorphToPlayOnce(&this->skelAnime, &object_tw_Anim_036FBC, -5.0f);
this->workf[ANIM_SW_TGT] = Animation_GetLastFrame(&object_tw_Anim_036FBC);
this->csState1 = 0;
}
void BossTw_TwinrovaChargeBlast(BossTw* this, GlobalContext* globalCtx) {
SkelAnime_Update(&this->skelAnime);
Math_ApproachF(&this->actor.world.pos.x, this->targetPos.x, 0.03f, fabsf(this->actor.velocity.x) * 1.5f);
Math_ApproachF(&this->actor.world.pos.y, this->targetPos.y, 0.03f, fabsf(this->actor.velocity.y) * 1.5f);
Math_ApproachF(&this->actor.world.pos.z, this->targetPos.z, 0.03f, fabsf(this->actor.velocity.z) * 1.5f);
Math_ApproachS(&this->actor.shape.rot.y, this->actor.yawTowardsPlayer, 5, 0x1000);
if (Animation_OnFrame(&this->skelAnime, this->workf[ANIM_SW_TGT])) {
if ((s8)this->actor.colChkInfo.health < 10) {
sTwinrovaBlastType = Rand_ZeroFloat(1.99f);
} else {
if (++sFixedBlatSeq >= 4) {
sFixedBlatSeq = 1;
sFixedBlastType = !sFixedBlastType;
}
sTwinrovaBlastType = sFixedBlastType;
}
BossTw_TwinrovaSetupShootBlast(this, globalCtx);
}
}
void BossTw_TwinrovaSetupShootBlast(BossTw* this, GlobalContext* globalCtx) {
this->actionFunc = BossTw_TwinrovaShootBlast;
if (sTwinrovaBlastType == 0) {
Animation_MorphToPlayOnce(&this->skelAnime, &object_tw_Anim_022700, 0.0f);
} else {
Animation_MorphToPlayOnce(&this->skelAnime, &object_tw_Anim_023750, 0.0f);
}
this->workf[ANIM_SW_TGT] = Animation_GetLastFrame(&object_tw_Anim_023750);
}
void BossTw_TwinrovaShootBlast(BossTw* this, GlobalContext* globalCtx) {
BossTw* twMagic;
Vec3f* magicSpawnPos;
s32 magicParams;
s16 i;
SkelAnime_Update(&this->skelAnime);
if (Animation_OnFrame(&this->skelAnime, 8.0f)) {
Audio_PlayActorSound2(&this->actor, NA_SE_EN_TWINROBA_THROW_MASIC);
Audio_PlayActorSound2(&this->actor, NA_SE_EN_TWINROBA_YOUNG_SHOOTVC);
}
if (Animation_OnFrame(&this->skelAnime, 12.0f)) {
if (sTwinrovaBlastType != 0) {
magicParams = TW_FIRE_BLAST;
magicSpawnPos = &this->rightScepterPos;
} else {
magicParams = TW_ICE_BLAST;
magicSpawnPos = &this->leftScepterPos;
}
twMagic =
(BossTw*)Actor_SpawnAsChild(&globalCtx->actorCtx, &this->actor, globalCtx, ACTOR_BOSS_TW, magicSpawnPos->x,
magicSpawnPos->y, magicSpawnPos->z, 0, 0, 0, magicParams);
if (twMagic != NULL) {
twMagic->blastType = magicParams == TW_ICE_BLAST ? 0 : 1;
}
sEnvType = twMagic->blastType + 1;
{
Vec3f velocity = { 0.0f, 0.0f, 0.0f };
Vec3f accel = { 0.0f, 0.0f, 0.0f };
for (i = 0; i < 100; i++) {
velocity.x = Rand_CenteredFloat(30.0f);
velocity.y = Rand_CenteredFloat(30.0f);
velocity.z = Rand_CenteredFloat(30.0f);
BossTw_AddDotEffect(globalCtx, magicSpawnPos, &velocity, &accel, (s16)Rand_ZeroFloat(2.0f) + 11,
twMagic->blastType, 75);
}
}
}
if (Animation_OnFrame(&this->skelAnime, this->workf[ANIM_SW_TGT])) {
BossTw_TwinrovaSetupDoneBlastShoot(this, globalCtx);
}
Math_ApproachS(&this->actor.shape.rot.y, this->actor.yawTowardsPlayer, 5, 0x1000);
}
void BossTw_TwinrovaSetupDoneBlastShoot(BossTw* this, GlobalContext* globalCtx) {
this->actionFunc = BossTw_TwinrovaDoneBlastShoot;
Animation_MorphToLoop(&this->skelAnime, &object_tw_Anim_032BF8, -10.0f);
this->timers[1] = 60;
}
void BossTw_TwinrovaDoneBlastShoot(BossTw* this, GlobalContext* globalCtx) {
SkelAnime_Update(&this->skelAnime);
if (this->timers[1] == 0 && D_8094C870 == 0) {
if (sTwinrovaPtr->timers[2] == 0) {
BossTw_TwinrovaSetupFly(this, globalCtx);
} else {
BossTw_TwinrovaSetupLaugh(this, globalCtx);
}
}
D_8094C870 = 0;
Math_ApproachS(&this->actor.shape.rot.y, this->actor.yawTowardsPlayer, 5, 0x1000);
}
void BossTw_TwinrovaDamage(BossTw* this, GlobalContext* globalCtx, u8 damage) {
if (this->actionFunc != BossTw_TwinrovaStun) {
Animation_MorphToPlayOnce(&this->skelAnime, &object_tw_Anim_0338F0, -15.0f);
this->timers[0] = 150;
this->timers[1] = 20;
this->csState1 = 0;
this->actor.velocity.y = 0.0f;
} else {
this->work[FOG_TIMER] = 10;
this->work[INVINC_TIMER] = 20;
Animation_MorphToPlayOnce(&this->skelAnime, &object_tw_Anim_024374, -3.0f);
this->workf[ANIM_SW_TGT] = Animation_GetLastFrame(&object_tw_Anim_024374);
this->csState1 = 1;
if ((s8)(this->actor.colChkInfo.health -= damage) < 0) {
this->actor.colChkInfo.health = 0;
}
if ((s8)this->actor.colChkInfo.health <= 0) {
BossTw_TwinrovaSetupDeathCS(this, globalCtx);
Enemy_StartFinishingBlow(globalCtx, &this->actor);
Audio_PlayActorSound2(&this->actor, NA_SE_EN_TWINROBA_YOUNG_DEAD);
return;
}
Audio_PlayActorSound2(&this->actor, NA_SE_EN_TWINROBA_YOUNG_DAMAGE2);
Audio_PlayActorSound2(&this->actor, NA_SE_EN_TWINROBA_CUTBODY);
}
this->actionFunc = BossTw_TwinrovaStun;
}
void BossTw_TwinrovaStun(BossTw* this, GlobalContext* globalCtx) {
s16 cloudType;
this->unk_5F8 = 1;
this->actor.flags |= ACTOR_FLAG_10;
cloudType = sTwinrovaBlastType == 0 ? 3 : 2;
if ((this->work[CS_TIMER_1] % 8) == 0) {
Vec3f pos;
Vec3f velocity;
Vec3f accel;
pos.x = this->actor.world.pos.x + Rand_CenteredFloat(20.0f);
pos.y = this->actor.world.pos.y + Rand_CenteredFloat(40.0f) + 20;
pos.z = this->actor.world.pos.z + Rand_CenteredFloat(20.0f);
velocity.x = 0.0f;
velocity.y = 0.0f;
velocity.z = 0.0f;
accel.x = 0.0f;
accel.y = 0.1f;
accel.z = 0.0f;
BossTw_AddDmgCloud(globalCtx, cloudType, &pos, &velocity, &accel, Rand_ZeroFloat(5.0f) + 10.0f, 0, 0, 150);
}
SkelAnime_Update(&this->skelAnime);
this->work[UNK_S8] += 20;
if (this->work[UNK_S8] > 255) {
this->work[UNK_S8] = 255;
}
Math_ApproachF(&this->workf[UNK_F12], 0.0f, 1.0f, 0.05f);
this->actor.world.pos.y += this->actor.velocity.y;
Math_ApproachF(&this->actor.velocity.y, -5.0f, 1.0f, 0.5f);
this->actor.world.pos.y -= 30.0f;
Actor_UpdateBgCheckInfo(globalCtx, &this->actor, 50.0f, 50.0f, 100.0f, 4);
this->actor.world.pos.y += 30.0f;
if (this->csState1 == 0) {
if (this->timers[1] == 0) {
this->csState1 = 1;
this->workf[ANIM_SW_TGT] = Animation_GetLastFrame(&object_tw_Anim_0343B4);
Animation_Change(&this->skelAnime, &object_tw_Anim_0343B4, 1.0f, 0.0f, this->workf[ANIM_SW_TGT], 3, 0.0f);
}
} else if (Animation_OnFrame(&this->skelAnime, this->workf[ANIM_SW_TGT])) {
this->workf[ANIM_SW_TGT] = 1000.0f;
Animation_MorphToLoop(&this->skelAnime, &object_tw_Anim_035030, 0.0f);
}
if (this->actor.bgCheckFlags & 1) {
this->actor.velocity.y = 0.0f;
}
if (this->timers[0] == 0) {
BossTw_TwinrovaSetupGetUp(this, globalCtx);
}
}
void BossTw_TwinrovaSetupGetUp(BossTw* this, GlobalContext* globalCtx) {
Animation_MorphToPlayOnce(&this->skelAnime, &object_tw_Anim_035988, 0.0f);
this->workf[ANIM_SW_TGT] = Animation_GetLastFrame(&object_tw_Anim_035988);
this->actionFunc = BossTw_TwinrovaGetUp;
this->timers[0] = 50;
}
void BossTw_TwinrovaGetUp(BossTw* this, GlobalContext* globalCtx) {
SkelAnime_Update(&this->skelAnime);
Math_ApproachF(&this->actor.world.pos.y, this->targetPos.y, 0.05f, 5.0f);
if (Animation_OnFrame(&this->skelAnime, this->workf[ANIM_SW_TGT])) {
this->workf[ANIM_SW_TGT] = 1000.0f;
Animation_MorphToLoop(&this->skelAnime, &object_tw_Anim_032BF8, 0.0f);
}
if (this->timers[0] == 0) {
BossTw_TwinrovaSetupFly(this, globalCtx);
}
}
void BossTw_TwinrovaSetupFly(BossTw* this, GlobalContext* globalCtx) {
f32 xDiff;
f32 zDiff;
f32 yDiff;
f32 xzDist;
Player* player = GET_PLAYER(globalCtx);
do {
this->work[TW_PLLR_IDX] += (s16)(((s16)Rand_ZeroFloat(2.99f)) + 1);
this->work[TW_PLLR_IDX] %= 4;
this->targetPos = sTwinrovaPillarPos[this->work[TW_PLLR_IDX]];
xDiff = this->targetPos.x - player->actor.world.pos.x;
zDiff = this->targetPos.z - player->actor.world.pos.z;
xzDist = SQ(xDiff) + SQ(zDiff);
} while (!(xzDist > SQ(300.0f)));
this->targetPos.y = 480.0f;
xDiff = this->targetPos.x - this->actor.world.pos.x;
yDiff = this->targetPos.y - this->actor.world.pos.y;
zDiff = this->targetPos.z - this->actor.world.pos.z;
this->actionFunc = BossTw_TwinrovaFly;
this->rotateSpeed = 0.0f;
this->actor.speedXZ = 0.0f;
this->actor.world.rot.y = Math_FAtan2F(xDiff, zDiff) * (32768 / M_PI);
xzDist = sqrtf(SQ(xDiff) + SQ(zDiff));
this->actor.world.rot.x = Math_FAtan2F(yDiff, xzDist) * (32768 / M_PI);
Animation_MorphToLoop(&this->skelAnime, &object_tw_Anim_032BF8, -10.0f);
}
void BossTw_TwinrovaFly(BossTw* this, GlobalContext* globalCtx) {
f32 xDiff;
f32 yDiff;
f32 zDiff;
s32 pad;
f32 yaw;
f32 xzDist;
Audio_PlayActorSound2(&this->actor, NA_SE_EN_TWINROBA_FLY - SFX_FLAG);
SkelAnime_Update(&this->skelAnime);
xDiff = this->targetPos.x - this->actor.world.pos.x;
yDiff = this->targetPos.y - this->actor.world.pos.y;
zDiff = this->targetPos.z - this->actor.world.pos.z;
// Convert from radians to degrees, then degrees to binary angle
yaw = (s16)(Math_FAtan2F(xDiff, zDiff) * ((180.0f / M_PI) * (65536.0f / 360.0f)));
xzDist = sqrtf(SQ(xDiff) + SQ(zDiff));
Math_ApproachS(&this->actor.world.rot.x,
(f32)(s16)(Math_FAtan2F(yDiff, xzDist) * ((180.0f / M_PI) * (65536.0f / 360.0f))), 0xA,
this->rotateSpeed);
Math_ApproachS(&this->actor.world.rot.y, yaw, 0xA, this->rotateSpeed);
Math_ApproachS(&this->actor.shape.rot.y, yaw, 0xA, this->rotateSpeed);
Math_ApproachF(&this->rotateSpeed, 2000.0f, 1.0f, 100.0f);
Math_ApproachF(&this->actor.speedXZ, 30.0f, 1.0f, 2.0f);
func_8002D908(&this->actor);
Math_ApproachF(&this->actor.world.pos.x, this->targetPos.x, 0.1f, fabsf(this->actor.velocity.x) * 1.5f);
Math_ApproachF(&this->actor.world.pos.y, this->targetPos.y, 0.1f, fabsf(this->actor.velocity.y) * 1.5f);
Math_ApproachF(&this->targetPos.y, 380.0f, 1.0f, 2.0f);
Math_ApproachF(&this->actor.world.pos.z, this->targetPos.z, 0.1f, fabsf(this->actor.velocity.z) * 1.5f);
if (xzDist < 200.0f) {
BossTw_TwinrovaSetupArriveAtTarget(this, globalCtx);
}
}
void BossTw_TwinrovaSetupSpin(BossTw* this, GlobalContext* globalCtx) {
this->actionFunc = BossTw_TwinrovaSpin;
Animation_MorphToLoop(&this->skelAnime, &object_tw_Anim_032BF8, 0.0f);
this->timers[0] = 20;
this->actor.speedXZ = 0.0f;
}
void BossTw_TwinrovaSpin(BossTw* this, GlobalContext* globalCtx) {
SkelAnime_Update(&this->skelAnime);
if (this->timers[0] != 0) {
this->collider.base.colType = COLTYPE_METAL;
this->actor.shape.rot.y -= 0x3000;
if ((this->timers[0] % 4) == 0) {
Audio_PlayActorSound2(&this->actor, NA_SE_EN_TWINROBA_ROLL);
}
} else {
BossTw_TwinrovaSetupFly(this, globalCtx);
}
}
void BossTw_TwinrovaSetupLaugh(BossTw* this, GlobalContext* globalCtx) {
this->actionFunc = BossTw_TwinrovaLaugh;
Animation_MorphToPlayOnce(&this->skelAnime, &object_tw_Anim_03A2D0, 0.0f);
this->workf[ANIM_SW_TGT] = Animation_GetLastFrame(&object_tw_Anim_03A2D0);
this->actor.speedXZ = 0.0f;
}
void BossTw_TwinrovaLaugh(BossTw* this, GlobalContext* globalCtx) {
SkelAnime_Update(&this->skelAnime);
if (Animation_OnFrame(&this->skelAnime, 10.0f)) {
Audio_PlayActorSound2(&this->actor, NA_SE_EN_TWINROBA_YOUNG_LAUGH);
}
if (Animation_OnFrame(&this->skelAnime, this->workf[ANIM_SW_TGT])) {
BossTw_TwinrovaSetupFly(this, globalCtx);
}
}
void BossTw_Reset(void) {
sTwInitalized = false;
memset(sTwEffects, 0, sizeof(sTwEffects));
}