Shipwright/soh/src/overlays/actors/ovl_Boss_Ganon/z_boss_ganon.c

5076 lines
188 KiB
C

#include "z_boss_ganon.h"
#include "overlays/ovl_Boss_Ganon/ovl_Boss_Ganon.h"
#include "overlays/actors/ovl_En_Ganon_Mant/z_en_ganon_mant.h"
#include "overlays/actors/ovl_En_Zl3/z_en_zl3.h"
#include "overlays/actors/ovl_Bg_Ganon_Otyuka/z_bg_ganon_otyuka.h"
#include "overlays/actors/ovl_En_Bom/z_en_bom.h"
#include "assets/objects/object_ganon/object_ganon.h"
#include "assets/objects/object_ganon_anime1/object_ganon_anime1.h"
#include "assets/objects/object_ganon_anime2/object_ganon_anime2.h"
#include "assets/scenes/dungeons/ganon_boss/ganon_boss_scene.h"
#include "soh/frame_interpolation.h"
#include <string.h>
#define FLAGS (ACTOR_FLAG_0 | ACTOR_FLAG_2 | ACTOR_FLAG_4 | ACTOR_FLAG_5)
void BossGanon_Init(Actor* thisx, GlobalContext* globalCtx);
void BossGanon_Destroy(Actor* thisx, GlobalContext* globalCtx);
void BossGanon_Update(Actor* thisx, GlobalContext* globalCtx);
void BossGanon_Draw(Actor* thisx, GlobalContext* globalCtx);
void func_808E1EB4(Actor* thisx, GlobalContext* globalCtx); // update
void func_808E2544(Actor* thisx, GlobalContext* globalCtx); // update
void BossGanon_LightBall_Update(Actor* thisx, GlobalContext* globalCtx);
void func_808E229C(Actor* thisx, GlobalContext* globalCtx); // draw
void func_808E324C(Actor* thisx, GlobalContext* globalCtx); // draw
void BossGanon_LightBall_Draw(Actor* thisx, GlobalContext* globalCtx);
void BossGanon_Reset(void);
void BossGanon_SetupIntroCutscene(BossGanon* this, GlobalContext* globalCtx);
void BossGanon_SetupTowerCutscene(BossGanon* this, GlobalContext* globalCtx);
void BossGanon_IntroCutscene(BossGanon* this, GlobalContext* globalCtx);
void BossGanon_DeathAndTowerCutscene(BossGanon* this, GlobalContext* globalCtx);
void BossGanon_Wait(BossGanon* this, GlobalContext* globalCtx);
void BossGanon_ChargeLightBall(BossGanon* this, GlobalContext* globalCtx);
void BossGanon_PlayTennis(BossGanon* this, GlobalContext* globalCtx);
void BossGanon_PoundFloor(BossGanon* this, GlobalContext* globalCtx);
void BossGanon_ChargeBigMagic(BossGanon* this, GlobalContext* globalCtx);
void BossGanon_Block(BossGanon* this, GlobalContext* globalCtx);
void BossGanon_HitByLightBall(BossGanon* this, GlobalContext* globalCtx);
void BossGanon_Vulnerable(BossGanon* this, GlobalContext* globalCtx);
void BossGanon_Damaged(BossGanon* this, GlobalContext* globalCtx);
void BossGanon_SetupWait(BossGanon* this, GlobalContext* globalCtx);
void BossGanon_SetupChargeLightBall(BossGanon* this, GlobalContext* globalCtx);
void BossGanon_SetupPlayTennis(BossGanon* this, GlobalContext* globalCtx);
void BossGanon_DrawEffects(GlobalContext* globalCtx);
void BossGanon_UpdateEffects(GlobalContext* globalCtx);
s32 BossGanon_CheckFallingPlatforms(BossGanon* this, GlobalContext* globalCtx, Vec3f* checkPos);
const ActorInit Boss_Ganon_InitVars = {
ACTOR_BOSS_GANON,
ACTORCAT_BOSS,
FLAGS,
OBJECT_GANON,
sizeof(BossGanon),
(ActorFunc)BossGanon_Init,
(ActorFunc)BossGanon_Destroy,
(ActorFunc)BossGanon_Update,
(ActorFunc)BossGanon_Draw,
(ActorResetFunc)BossGanon_Reset,
};
static ColliderCylinderInit sDorfCylinderInit = {
{
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, 0x10 },
{ 0xFFCFFFFE, 0x00, 0x00 },
TOUCH_ON | TOUCH_SFX_NORMAL,
BUMP_ON | BUMP_HOOKABLE,
OCELEM_ON,
},
{ 20, 80, -50, { 0, 0, 0 } },
};
static ColliderCylinderInit sLightBallCylinderInit = {
{
COLTYPE_NONE,
AT_ON | AT_TYPE_ENEMY,
AC_ON | AC_TYPE_PLAYER,
OC1_ON | OC1_TYPE_ALL,
OC2_TYPE_1,
COLSHAPE_CYLINDER,
},
{
ELEMTYPE_UNK6,
{ 0x00100700, 0x00, 0x08 },
{ 0x0D900740, 0x00, 0x00 },
TOUCH_ON | TOUCH_SFX_NORMAL,
BUMP_ON,
OCELEM_ON,
},
{ 20, 30, -15, { 0, 0, 0 } },
};
static u8 D_808E4C58[] = { 0, 12, 10, 12, 14, 16, 12, 14, 16, 12, 14, 16, 12, 14, 16, 10, 16, 14 };
static Vec3f sZeroVec = { 0.0f, 0.0f, 0.0f };
EnGanonMant* sBossGanonCape;
s32 sBossGanonSeed1;
s32 sBossGanonSeed3;
s32 sBossGanonSeed2;
BossGanon* sBossGanonGanondorf;
EnZl3* sBossGanonZelda;
GanondorfEffect sBossGanonEffectBuf[200];
void BossGanonEff_SpawnWindowShard(GlobalContext* globalCtx, Vec3f* pos, Vec3f* velocity, f32 scale) {
static Color_RGB8 shardColors[] = { { 255, 175, 85 }, { 155, 205, 155 }, { 155, 125, 55 } };
s16 i;
GanondorfEffect* eff = globalCtx->specialEffects;
Color_RGB8* color;
for (i = 0; i < 200; i++, eff++) {
if (eff->type == GDF_EFF_NONE) {
eff->type = GDF_EFF_WINDOW_SHARD;
eff->pos = *pos;
eff->velocity = *velocity;
eff->accel = sZeroVec;
eff->scale = scale;
eff->accel.y = -1.5f;
eff->unk_44 = Rand_ZeroFloat(6.28f);
eff->unk_48 = Rand_ZeroFloat(6.28f);
color = &shardColors[(s16)Rand_ZeroFloat(2.99f)];
eff->color.r = color->r;
eff->color.g = color->g;
eff->color.b = color->b;
eff->timer = (s16)Rand_ZeroFloat(20.0f);
eff->epoch++;
break;
}
}
}
void BossGanonEff_SpawnSparkle(GlobalContext* globalCtx, Vec3f* pos, Vec3f* velocity, Vec3f* accel, f32 scale,
s16 arg6) {
s16 i;
GanondorfEffect* eff = globalCtx->specialEffects;
for (i = 0; i < 150; i++, eff++) {
if (eff->type == GDF_EFF_NONE) {
eff->type = GDF_EFF_SPARKLE;
eff->pos = *pos;
eff->velocity = *velocity;
eff->accel = *accel;
eff->scale = scale / 1000.0f;
eff->unk_2E = (s16)Rand_ZeroFloat(100.0f) + 0xC8;
eff->unk_30 = arg6;
eff->timer = (s16)Rand_ZeroFloat(10.0f);
eff->epoch++;
break;
}
}
}
void BossGanonEff_SpawnLightRay(GlobalContext* globalCtx, Vec3f* pos, Vec3f* velocity, Vec3f* accel, f32 scale,
f32 arg5, s16 arg6) {
s16 i;
GanondorfEffect* eff = globalCtx->specialEffects;
for (i = 0; i < 150; i++, eff++) {
if (eff->type == GDF_EFF_NONE) {
eff->type = GDF_EFF_LIGHT_RAY;
eff->pos = *pos;
eff->velocity = *velocity;
eff->accel = *accel;
eff->scale = scale / 1000.0f;
eff->unk_38 = 1.0f;
eff->unk_40 = arg5;
eff->unk_2E = (s16)Rand_ZeroFloat(100.0f) + 0xC8;
eff->unk_30 = arg6;
eff->timer = (s16)Rand_ZeroFloat(10.0f);
eff->unk_48 = Math_Atan2F(eff->velocity.z, eff->velocity.x);
eff->unk_44 = -Math_Atan2F(sqrtf(SQXZ(eff->velocity)), eff->velocity.y);
eff->epoch++;
break;
}
}
}
void BossGanonEff_SpawnShock(GlobalContext* globalCtx, f32 scale, s16 shockType) {
s16 i;
GanondorfEffect* eff = globalCtx->specialEffects;
for (i = 0; i < 75; i++, eff++) {
if (eff->type == GDF_EFF_NONE) {
eff->type = GDF_EFF_SHOCK;
eff->pos = sZeroVec;
eff->pos.y = -2000.0f;
eff->velocity = sZeroVec;
eff->accel = sZeroVec;
eff->scale = scale / 1000.0f;
eff->unk_2E = shockType;
eff->timer = 0;
eff->epoch++;
break;
}
}
}
void BossGanonEff_SpawnLightning(GlobalContext* globalCtx, f32 scale, f32 arg2, f32 arg3) {
s16 i;
GanondorfEffect* eff = globalCtx->specialEffects;
for (i = 0; i < 150; i++, eff++) {
if (eff->type == GDF_EFF_NONE) {
eff->type = GDF_EFF_LIGHTNING;
eff->velocity = sZeroVec;
eff->accel = sZeroVec;
eff->unk_2E = 0;
eff->scale = scale;
eff->unk_48 = arg2;
eff->unk_3C = arg3;
eff->timer = 0;
eff->epoch++;
break;
}
}
}
void BossGanonEff_SpawnDustDark(GlobalContext* globalCtx, Vec3f* pos, f32 scale, f32 arg3) {
s16 i;
GanondorfEffect* eff = globalCtx->specialEffects;
for (i = 0; i < 150; i++, eff++) {
if (eff->type == GDF_EFF_NONE) {
eff->type = GDF_EFF_IMPACT_DUST_DARK;
eff->pos = *pos;
eff->velocity = sZeroVec;
eff->accel = sZeroVec;
eff->scale = scale;
eff->unk_40 = 1.0f;
eff->unk_38 = arg3;
eff->unk_30 = (s16)Rand_ZeroFloat(100.0f);
eff->unk_2E = eff->timer = eff->alpha = 0;
eff->epoch++;
break;
}
}
}
void BossGanonEff_SpawnDustLight(GlobalContext* globalCtx, Vec3f* pos, f32 scale, f32 arg3, s16 bufIndex) {
GanondorfEffect* effArr = globalCtx->specialEffects;
effArr[bufIndex].type = GDF_EFF_IMPACT_DUST_LIGHT;
effArr[bufIndex].pos = *pos;
effArr[bufIndex].velocity = sZeroVec;
effArr[bufIndex].accel = sZeroVec;
effArr[bufIndex].unk_40 = 1.0f;
effArr[bufIndex].scale = scale;
effArr[bufIndex].unk_38 = arg3;
effArr[bufIndex].unk_30 = Rand_ZeroFloat(100.0f);
effArr[bufIndex].unk_2E = effArr[bufIndex].timer = effArr[bufIndex].alpha = 0;
effArr[bufIndex].epoch++;
}
void BossGanonEff_SpawnShockwave(GlobalContext* globalCtx, Vec3f* pos, f32 scale, f32 arg3) {
s16 i;
GanondorfEffect* eff = globalCtx->specialEffects;
for (i = 0; i < 150; i++, eff++) {
if (eff->type == GDF_EFF_NONE) {
eff->type = GDF_EFF_SHOCKWAVE;
eff->pos = *pos;
eff->velocity = sZeroVec;
eff->accel = sZeroVec;
eff->alpha = 255;
eff->unk_40 = 0.6f;
eff->scale = scale;
eff->unk_38 = arg3;
eff->unk_30 = (s16)Rand_ZeroFloat(100.0f);
eff->unk_2E = eff->timer = 0;
eff->epoch++;
break;
}
}
}
void BossGanonEff_SpawnBlackDot(GlobalContext* globalCtx, Vec3f* pos, f32 scale) {
s16 i;
GanondorfEffect* eff = globalCtx->specialEffects;
for (i = 0; i < 150; i++, eff++) {
if (eff->type == GDF_EFF_NONE) {
eff->type = GDF_EFF_BLACK_DOT;
eff->pos = *pos;
eff->velocity = sZeroVec;
eff->accel = sZeroVec;
eff->unk_38 = 0.0f;
eff->scale = scale / 1000.0f;
eff->timer = 0;
eff->alpha = 0;
eff->unk_2E = 0;
eff->epoch++;
break;
}
}
}
void BossGanon_SetColliderPos(Vec3f* pos, ColliderCylinder* collider) {
collider->dim.pos.x = pos->x;
collider->dim.pos.y = pos->y;
collider->dim.pos.z = pos->z;
}
void BossGanon_SetAnimationObject(BossGanon* this, GlobalContext* globalCtx, s32 objectId) {
this->animBankIndex = Object_GetIndex(&globalCtx->objectCtx, objectId);
gSegments[6] = VIRTUAL_TO_PHYSICAL(globalCtx->objectCtx.status[this->animBankIndex].segment);
}
static InitChainEntry sInitChain[] = {
ICHAIN_U8(targetMode, 5, ICHAIN_CONTINUE),
ICHAIN_S8(naviEnemyId, 0x3D, ICHAIN_CONTINUE),
ICHAIN_F32_DIV1000(gravity, 0, ICHAIN_CONTINUE),
ICHAIN_F32(targetArrowOffset, 0, ICHAIN_STOP),
};
void BossGanon_Init(Actor* thisx, GlobalContext* globalCtx2) {
s16 i;
GlobalContext* globalCtx = globalCtx2;
BossGanon* this = (BossGanon*)thisx;
s32 cond;
f32 xDistFromPlayer;
f32 yDistFromPlayer;
f32 zDistFromPlayer;
Player* player = GET_PLAYER(globalCtx);
if (thisx->params < 0x64) {
Flags_SetSwitch(globalCtx, 0x14);
globalCtx->specialEffects = sBossGanonEffectBuf;
for (i = 0; i < ARRAY_COUNT(sBossGanonEffectBuf); i++) {
sBossGanonEffectBuf[i].type = GDF_EFF_NONE;
}
sBossGanonGanondorf = this;
thisx->colChkInfo.health = 40;
Actor_ProcessInitChain(thisx, sInitChain);
ActorShape_Init(&thisx->shape, 0, NULL, 0);
Actor_SetScale(thisx, 0.01f);
SkelAnime_InitFlex(globalCtx, &this->skelAnime, &gDorfSkel, NULL, NULL, NULL, 0);
Collider_InitCylinder(globalCtx, &this->collider);
Collider_SetCylinder(globalCtx, &this->collider, thisx, &sDorfCylinderInit);
if (thisx->params != 1) {
BossGanon_SetupIntroCutscene(this, globalCtx);
this->organAlpha = 255;
} else {
cond = Flags_GetSwitch(globalCtx, 0x37) &&
((globalCtx->sceneNum == SCENE_GANON_DEMO) || (globalCtx->sceneNum == SCENE_GANON_FINAL) ||
(globalCtx->sceneNum == SCENE_GANON_SONOGO) || (globalCtx->sceneNum == SCENE_GANONTIKA_SONOGO));
if (!cond) {
BossGanon_SetupTowerCutscene(this, globalCtx);
} else {
Actor_Kill(thisx);
return;
}
BossGanon_SetupTowerCutscene(this, globalCtx);
}
sBossGanonCape = (EnGanonMant*)Actor_SpawnAsChild(&globalCtx->actorCtx, thisx, globalCtx, ACTOR_EN_GANON_MANT, 0.0f,
0.0f, 0.0f, 0, 0, 0, 1);
Actor_ChangeCategory(globalCtx, &globalCtx->actorCtx, thisx, ACTORCAT_BOSS);
} else {
thisx->flags &= ~ACTOR_FLAG_0;
this->fwork[GDF_FWORK_1] = 255.0f;
if (thisx->params >= 0xC8) {
if (thisx->params == 0x12C) {
thisx->update = BossGanon_LightBall_Update;
thisx->draw = BossGanon_LightBall_Draw;
this->unk_1A8 = 2;
} else if (thisx->params == 0x190) {
thisx->update = BossGanon_LightBall_Update;
thisx->draw = BossGanon_LightBall_Draw;
this->unk_1A8 = 1;
} else if (thisx->params >= 0x104) {
// big magic light ball thrown
thisx->update = func_808E2544;
thisx->draw = func_808E324C;
this->unk_1C2 = 10;
this->unk_1A2 = 520 + (-thisx->params * 2);
for (i = 0; i < 15; i++) {
this->unk_2EC[i] = thisx->world.pos;
}
this->timers[1] = 3;
Collider_InitCylinder(globalCtx, &this->collider);
Collider_SetCylinder(globalCtx, &this->collider, thisx, &sLightBallCylinderInit);
} else if (thisx->params >= 0xFA) {
// big magic light ball charge
thisx->update = func_808E2544;
thisx->draw = func_808E324C;
this->unk_1A2 = Rand_ZeroFloat(10000.0f);
for (i = 0; i < 15; i++) {
this->unk_2EC[i] = thisx->world.pos;
}
this->fwork[GDF_FWORK_1] = 0;
} else {
thisx->update = func_808E1EB4;
thisx->draw = func_808E229C;
thisx->speedXZ = 11.0f;
if (thisx->params == 0xC8) {
this->timers[0] = 7;
} else {
this->timers[0] = (s16)Rand_ZeroFloat(3.0f) + 3;
}
for (i = 0; i < 15; i++) {
this->unk_2EC[i].y = 5000.0f;
}
}
} else {
// light ball (anything from 0x64 - 0xC7)
thisx->update = BossGanon_LightBall_Update;
thisx->draw = BossGanon_LightBall_Draw;
thisx->speedXZ = 12.0f;
xDistFromPlayer = player->actor.world.pos.x - thisx->world.pos.x;
yDistFromPlayer = (player->actor.world.pos.y + 30.0f) - thisx->world.pos.y;
zDistFromPlayer = player->actor.world.pos.z - thisx->world.pos.z;
thisx->world.rot.y = Math_Atan2S(zDistFromPlayer, xDistFromPlayer);
thisx->world.rot.x = Math_Atan2S(sqrtf(SQ(xDistFromPlayer) + SQ(zDistFromPlayer)), yDistFromPlayer);
if (Rand_ZeroOne() < 0) {
thisx->world.rot.y += (s16)Rand_CenteredFloat(5000.0f);
thisx->world.rot.x += (s16)Rand_CenteredFloat(5000.0f);
}
this->timers[1] = 3;
Collider_InitCylinder(globalCtx, &this->collider);
Collider_SetCylinder(globalCtx, &this->collider, thisx, &sLightBallCylinderInit);
}
}
}
void BossGanon_Destroy(Actor* thisx, GlobalContext* globalCtx) {
BossGanon* this = (BossGanon*)thisx;
if ((this->actor.params < 0xC8) || (this->actor.params >= 0x104)) {
Collider_DestroyCylinder(globalCtx, &this->collider);
}
if (this->actor.params < 0x64) {
SkelAnime_Free(&this->skelAnime, globalCtx);
}
}
void BossGanon_SetupIntroCutscene(BossGanon* this, GlobalContext* globalCtx) {
s32 pad;
s32 animBankIndex = Object_GetIndex(&globalCtx->objectCtx, OBJECT_GANON_ANIME2);
if (animBankIndex < 0) {
Actor_Kill(&this->actor);
return;
}
if (Object_IsLoaded(&globalCtx->objectCtx, animBankIndex)) {
this->actionFunc = BossGanon_IntroCutscene;
this->unk_198 = 1;
this->animBankIndex = animBankIndex;
gSegments[6] = VIRTUAL_TO_PHYSICAL(globalCtx->objectCtx.status[animBankIndex].segment);
Animation_MorphToLoop(&this->skelAnime, &object_ganon_anime2_Anim_005FFC, 0.0f);
} else {
this->actionFunc = BossGanon_SetupIntroCutscene;
}
}
typedef struct {
/* 0x00 */ Vec3s eye;
/* 0x06 */ Vec3s at;
} CutsceneCameraPosition; // size = 0x12
static CutsceneCameraPosition sIntroCsCameraPositions[] = {
{ { 0, 40, 0 }, { 0, 50, 430 } },
{ { -20, 30, 400 }, { 10, 55, 440 } },
{ { 0, 60, 300 }, { 0, 273, -150 } },
{ { 0, 180, -260 }, { 0, 155, -300 } },
{ { -30, 60, 440 }, { 20, 25, 390 } },
{ { -50, 140, -360 }, { 50, 92, -390 } },
{ { -10, 264, -121 }, { 5, 266, -160 } },
{ { -13, 200, -310 }, { 0, 125, -410 } },
{ { 0, 40, -50 }, { 0, 35, 230 } },
{ { 0, 140, -250 }, { 0, 115, -570 } },
{ { -410, 150, -130 }, { 50, 155, -170 } },
{ { 0, 130, -230 }, { 0, 125, -2000 } },
{ { -2, 147, -293 }, { -200, 345, -2000 } },
};
void BossGanon_SetIntroCsCamera(BossGanon* this, u8 camPosIndex) {
CutsceneCameraPosition* camPos = &sIntroCsCameraPositions[camPosIndex];
this->csCamEye.x = camPos->eye.x;
this->csCamEye.y = camPos->eye.y;
this->csCamEye.z = camPos->eye.z;
this->csCamAt.x = camPos->at.x;
this->csCamAt.y = camPos->at.y;
this->csCamAt.z = camPos->at.z;
}
void BossGanon_IntroCutscene(BossGanon* this, GlobalContext* globalCtx) {
u8 moveCam = false;
Player* player = GET_PLAYER(globalCtx);
s32 pad;
f32 sin;
f32 cos;
Camera* mainCam;
gSegments[6] = VIRTUAL_TO_PHYSICAL(globalCtx->objectCtx.status[this->animBankIndex].segment);
sBossGanonCape->backPush = -2.0f;
sBossGanonCape->backSwayMagnitude = 0.25f;
sBossGanonCape->sideSwayMagnitude = -1.0f;
sBossGanonCape->minDist = 0.0f;
this->csTimer++;
SkelAnime_Update(&this->skelAnime);
switch (this->csState) {
case 0:
player->actor.world.pos.x = 0.0f;
player->actor.world.pos.y = 0.0f;
player->actor.world.pos.z = 430.0f;
this->actor.world.pos.x = 0.0f;
this->actor.world.pos.y = 112.0f;
this->actor.world.pos.z = -333.0f;
this->actor.shape.yOffset = -7000.0f;
this->actor.shape.rot.y = 0;
func_80064520(globalCtx, &globalCtx->csCtx);
func_8002DF54(globalCtx, &this->actor, 8);
this->csCamIndex = Gameplay_CreateSubCamera(globalCtx);
Gameplay_ChangeCameraStatus(globalCtx, MAIN_CAM, CAM_STAT_WAIT);
Gameplay_ChangeCameraStatus(globalCtx, this->csCamIndex, CAM_STAT_ACTIVE);
this->csCamFov = 60.0f;
if (gSaveContext.eventChkInf[7] & 0x100 || gSaveContext.n64ddFlag) {
// watched cutscene already, skip most of it
this->csState = 17;
this->csTimer = 0;
player->actor.world.pos.z = 20.0f;
this->useOpenHand = false;
Animation_MorphToLoop(&this->skelAnime, &object_ganon_anime2_Anim_0089F8, -5.0f);
this->fwork[GDF_FWORK_1] = 1000.0f;
BossGanon_SetIntroCsCamera(this, 11);
this->unk_198 = 2;
this->timers[2] = 110;
gSaveContext.healthAccumulator = 0x140;
Audio_QueueSeqCmd(NA_BGM_STOP);
} else {
this->useOpenHand = true;
BossGanon_SetIntroCsCamera(this, 0);
this->csState = 1;
sBossGanonZelda = (EnZl3*)Actor_SpawnAsChild(&globalCtx->actorCtx, &this->actor, globalCtx, ACTOR_EN_ZL3, 0.0f,
220.0f, -150.0f, 0, 0, 0, 0x2000);
}
Actor_SpawnAsChild(&globalCtx->actorCtx, &this->actor, globalCtx, ACTOR_EN_GANON_ORGAN, 0.0f, 0.0f, 0.0f, 0,
0, 0, 1);
sBossGanonCape->minY = 57.0f;
// fallthrough
case 1:
this->envLightMode = 3;
if (this->csTimer == 70) {
this->csState = 2;
this->csTimer = 0;
}
break;
case 2:
BossGanon_SetIntroCsCamera(this, 1);
if (this->csTimer == 10) {
func_8002DF54(globalCtx, &this->actor, 5);
}
if (this->csTimer == 13) {
func_8002F7DC(&player->actor, player->ageProperties->unk_92 + NA_SE_VO_LI_SURPRISE);
}
if (this->csTimer != 35) {
break;
}
this->csState = 3;
this->csTimer = 0;
this->csCamEye.x = 0.0f;
this->csCamEye.y = 60.0f;
this->csCamEye.z = 300.0f;
this->csCamAt.x = 0.0f;
this->unk_704 = 1.2566371f;
// fallthrough
case 3:
this->envLightMode = 0;
globalCtx->envCtx.unk_D8 = 0.0f;
this->csCamAt.y = (sinf(this->unk_704) * 300.0f) + this->csCamEye.y;
this->csCamAt.z = (cosf(this->unk_704) * -300.0f) + this->csCamEye.z;
Math_ApproachF(&this->unk_704, 0.25f, 0.05f, this->csCamAtMaxStep.y);
Math_ApproachF(&this->csCamAtMaxStep.y, 0.01f, 1.0f, 0.0001f);
if (this->csTimer != 200) {
break;
}
func_8002DF54(globalCtx, &this->actor, 8);
this->csState = 4;
BossGanon_SetIntroCsCamera(this, 2);
this->csTimer = 0;
// fallthrough
case 4:
if ((this->csTimer == 0) || (this->csTimer == 10) || (this->csTimer == 20)) {
this->csCamEye.y += 68.0f;
this->csCamEye.z -= 142.0f;
}
if (this->csTimer >= 20) {
this->envLightMode = 4;
} else {
this->envLightMode = 35;
}
if (this->csTimer == 60) {
BossGanon_SetIntroCsCamera(this, 1);
this->csState = 5;
this->csTimer = 0;
}
break;
case 5:
this->envLightMode = 5;
if (this->csTimer < 50) {
globalCtx->envCtx.unk_D8 = 1.0f;
}
if (this->csTimer == 10) {
func_8002DF54(globalCtx, &this->actor, 0x4B);
}
if (this->csTimer == 70) {
BossGanon_SetIntroCsCamera(this, 3);
this->csState = 6;
this->csTimer = 0;
this->envLightMode = 3;
}
break;
case 6:
this->envLightMode = 3;
if (this->csTimer != 30) {
break;
}
this->csState = 7;
this->csTimer = 0;
BossGanon_SetIntroCsCamera(this, 4);
this->triforceType = GDF_TRIFORCE_PLAYER;
this->fwork[GDF_TRIFORCE_SCALE] = 10.0f;
this->fwork[GDF_TRIFORCE_PRIM_A] = 0.0f;
this->fwork[GDF_TRIFORCE_PRIM_B] = 255.0f;
this->fwork[GDF_TRIFORCE_ENV_G] = 100.0f;
func_80078884(NA_SE_EV_TRIFORCE_MARK);
globalCtx->envCtx.unk_D8 = 0.0f;
// fallthrough
case 7:
this->envLightMode = 6;
// fade in links triforce
Math_ApproachF(&this->fwork[GDF_TRIFORCE_PRIM_A], 255.0f, 1.0f, 10.0f);
Math_ApproachF(&this->fwork[GDF_TRIFORCE_SCALE], 0.4f, 1.0f, 0.3f);
Math_ApproachF(&this->fwork[GDF_TRIFORCE_PRIM_B], 170.0f, 1.0f, 2.55f);
Math_ApproachF(&this->fwork[GDF_TRIFORCE_ENV_G], 200.0f, 1.0f, 3.0f);
if (this->csTimer >= 30) {
this->envLightMode = 65;
}
if (this->csTimer == 30) {
globalCtx->envCtx.unk_D8 = 1.0f;
}
BossGanon_SetIntroCsCamera(this, 4);
this->csCamEye.x += 5.0f;
this->csCamEye.z += -10.0f;
this->csCamAt.x += 18.0f;
if (this->csTimer == 60) {
this->csState = 8;
this->csTimer = 0;
}
break;
case 8:
this->envLightMode = 3;
BossGanon_SetIntroCsCamera(this, 5);
if (this->csTimer != 30) {
break;
}
this->csState = 9;
this->csTimer = 0;
func_8002DF54(globalCtx, &this->actor, 8);
sBossGanonZelda->unk_3C8 = 0;
this->triforceType = GDF_TRIFORCE_ZELDA;
this->fwork[GDF_TRIFORCE_SCALE] = 10.0f;
this->fwork[GDF_TRIFORCE_PRIM_A] = 0.0f;
this->fwork[GDF_TRIFORCE_PRIM_B] = 255.0f;
this->fwork[GDF_TRIFORCE_ENV_G] = 100.0f;
func_80078884(NA_SE_EV_TRIFORCE_MARK);
globalCtx->envCtx.unk_D8 = 0.0f;
// fallthrough
case 9:
this->envLightMode = 7;
BossGanon_SetIntroCsCamera(this, 6);
// fade in zeldas triforce
Math_ApproachF(&this->fwork[GDF_TRIFORCE_PRIM_A], 255.0f, 1.0f, 10.0f);
Math_ApproachF(&this->fwork[GDF_TRIFORCE_SCALE], 0.4f, 1.0f, 0.3f);
Math_ApproachF(&this->fwork[GDF_TRIFORCE_PRIM_B], 170.0f, 1.0f, 2.55f);
Math_ApproachF(&this->fwork[GDF_TRIFORCE_ENV_G], 200.0f, 1.0f, 3.0f);
if (this->csTimer == 30) {
sBossGanonZelda->unk_3C8 = 1;
}
if (this->csTimer >= 32) {
this->envLightMode = 75;
}
if (this->csTimer == 32) {
globalCtx->envCtx.unk_D8 = 1.0f;
}
if (this->csTimer == 50) {
this->csState = 10;
this->csTimer = 0;
}
break;
case 10: // top view of playing the organ
this->envLightMode = 3;
BossGanon_SetIntroCsCamera(this, 7);
if (this->csTimer == 40) {
this->csState = 11;
this->csTimer = 0;
this->fwork[GDF_TRIFORCE_PRIM_A] = 0.0f;
}
break;
case 11: // link is healed
this->envLightMode = 3;
BossGanon_SetIntroCsCamera(this, 8);
player->actor.world.pos.z = 20.0f;
if (this->csTimer == 20) {
func_8002DF54(globalCtx, &this->actor, 0x17);
Interface_ChangeAlpha(11); // show hearts only
}
if (this->csTimer == 25) {
gSaveContext.healthAccumulator = 0x140;
}
if (this->csTimer == 100) {
Interface_ChangeAlpha(1);
}
if (this->csTimer == 120) {
this->csState = 12;
this->csTimer = 0;
}
break;
case 12: // first dialogue, ganondorf facing away from link
this->envLightMode = 3;
BossGanon_SetIntroCsCamera(this, 9);
if (this->csTimer == 30) {
Audio_QueueSeqCmd(0x100100FF);
this->fwork[GDF_FWORK_1] = Animation_GetLastFrame(&object_ganon_anime2_Anim_004F64);
Animation_MorphToPlayOnce(&this->skelAnime, &object_ganon_anime2_Anim_004F64, -5.0f);
}
if ((this->csTimer > 30) && Animation_OnFrame(&this->skelAnime, this->fwork[GDF_FWORK_1])) {
Animation_MorphToLoop(&this->skelAnime, &object_ganon_anime2_Anim_006AF4, 0.0f);
this->fwork[GDF_FWORK_1] = 1000.0f;
}
if (this->csTimer == 80) {
Message_StartTextbox(globalCtx, 0x70C8, NULL);
}
if ((this->csTimer > 180) && (Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_NONE)) {
this->csState = 15;
this->csTimer = 0;
this->useOpenHand = false;
}
break;
case 15: // side view of all 3 of them
this->envLightMode = 0;
globalCtx->envCtx.unk_D8 = 0.0f;
BossGanon_SetIntroCsCamera(this, 10);
if (this->csTimer == 30) {
Message_StartTextbox(globalCtx, 0x70C9, NULL);
}
if ((this->csTimer > 100) && (Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_NONE)) {
this->csState = 16;
this->csTimer = 0;
BossGanon_SetIntroCsCamera(this, 11);
this->unk_198 = 2;
sBossGanonZelda->unk_3C8 = 2;
this->timers[2] = 110;
this->envLightMode = 3;
}
break;
case 16:
this->envLightMode = 3;
if (this->csTimer <= 20) {
if (this->csTimer == 20) {
Animation_MorphToPlayOnce(&this->skelAnime, &object_ganon_anime2_Anim_004304, -5.0f);
this->fwork[GDF_FWORK_1] = Animation_GetLastFrame(&object_ganon_anime2_Anim_004304);
}
} else if (Animation_OnFrame(&this->skelAnime, this->fwork[GDF_FWORK_1])) {
Message_StartTextbox(globalCtx, 0x70CA, NULL);
Animation_MorphToLoop(&this->skelAnime, &object_ganon_anime2_Anim_0089F8, -5.0f);
this->fwork[GDF_FWORK_1] = 1000.0f;
}
if ((this->csTimer > 100) && (Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_NONE)) {
this->csState = 17;
this->csTimer = 0;
}
break;
case 17: // turns around
this->envLightMode = 3;
if (this->csTimer == 20) {
Animation_MorphToPlayOnce(&this->skelAnime, &object_ganon_anime2_Anim_001F58, -5.0f);
this->fwork[GDF_FWORK_1] = Animation_GetLastFrame(&object_ganon_anime2_Anim_001F58);
}
if (this->csTimer > 10) {
if (this->csTimer == 62) {
sBossGanonCape->attachRightArmTimer = 20.0f;
}
if (this->csTimer == 57) {
Audio_PlayActorSound2(&this->actor, NA_SE_EV_GANON_MANTLE);
}
Math_ApproachF(&this->csCamFov, 110.0f, 0.1f, this->csCamMaxStepScale * 2.0f);
Math_ApproachF(&this->csCamEye.z, -290.0f, 0.1f, this->csCamMaxStepScale * 2.4f);
Math_ApproachF(&this->csCamMaxStepScale, 0.75f, 1.0f, 0.05f);
if (this->csTimer == 70) {
this->csState = 18;
this->csTimer = 0;
this->csCamFov = 60.0f;
BossGanon_SetIntroCsCamera(this, 12);
if (!gSaveContext.n64ddFlag) {
Message_StartTextbox(globalCtx, 0x70CB, NULL);
}
}
}
break;
case 18: // last dialog before triforce
this->envLightMode = 3;
BossGanon_SetIntroCsCamera(this, 12);
this->csCamEye.y += -6.0f;
this->csCamEye.z += 6.0f;
if (Animation_OnFrame(&this->skelAnime, this->fwork[GDF_FWORK_1] - 5.0f)) {
Animation_MorphToLoop(&this->skelAnime, &object_ganon_anime2_Anim_003018, -5.0f);
this->fwork[GDF_FWORK_1] = 1000.0f;
}
if ((this->csTimer <= 50) || (Message_GetState(&globalCtx->msgCtx) != TEXT_STATE_NONE)) {
break;
}
this->csState = 19;
this->csTimer = 0;
Message_StartTextbox(globalCtx, 0x70CC, NULL);
Animation_MorphToPlayOnce(&this->skelAnime, &object_ganon_anime2_Anim_007268, -5.0f);
this->triforceType = GDF_TRIFORCE_DORF;
this->fwork[GDF_TRIFORCE_SCALE] = 10.0f;
this->fwork[GDF_TRIFORCE_PRIM_A] = 0.0f;
this->fwork[GDF_TRIFORCE_PRIM_B] = 255.0f;
this->fwork[GDF_TRIFORCE_ENV_G] = 100.0f;
globalCtx->envCtx.unk_D8 = 0.0f;
// fallthrough
case 19: // show triforce
this->envLightMode = 8;
if (this->csTimer >= 60) {
this->envLightMode = 9;
if (this->csTimer == 60) {
globalCtx->envCtx.unk_D8 = 1.0f;
}
}
BossGanon_SetIntroCsCamera(this, 12);
this->csCamEye.y += -6.0f;
this->csCamEye.z += 6.0f;
if (this->csTimer >= 30) {
if (this->csTimer == 30) {
func_80078884(NA_SE_EV_TRIFORCE_MARK);
}
// fade in ganondorf's triforce
Math_ApproachF(&this->fwork[GDF_TRIFORCE_PRIM_A], 255.0f, 1.0f, 10.0f);
Math_ApproachF(&this->fwork[GDF_TRIFORCE_SCALE], 0.6f, 1.0f, 0.3f);
Math_ApproachF(&this->fwork[GDF_TRIFORCE_PRIM_B], 170.0f, 1.0f, 2.55f);
Math_ApproachF(&this->fwork[GDF_TRIFORCE_ENV_G], 200.0f, 1.0f, 3.0f);
}
if (this->csTimer == 17) {
Animation_MorphToLoop(&this->skelAnime, &object_ganon_anime2_Anim_007A64, -5.0f);
}
if ((this->csTimer > 80) && (Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_NONE)) {
this->csState = 20;
this->csTimer = 0;
this->csCamTargetEye.x = this->csCamEye.x - 50.0f;
this->csCamTargetEye.y = this->csCamEye.y - 100.0f;
this->csCamTargetEye.z = this->csCamEye.z + 400.0f;
this->csCamEyeMaxStep.x = 50.0f;
this->csCamEyeMaxStep.y = 100.0f;
this->csCamEyeMaxStep.z = 400.0f;
this->csCamAtMaxStep.x = 400.0f;
this->csCamMaxStepScale = 0.0f;
this->csCamTargetAt.x = this->csCamAt.x + 400.0f;
this->csCamTargetAt.y = this->csCamAt.y;
this->csCamTargetAt.z = this->csCamAt.z;
this->csCamMovementScale = 0.2f;
this->fwork[GDF_VORTEX_ALPHA] = 0.0f;
this->fwork[GDF_VORTEX_SCALE] = 0.1f;
Audio_PlayActorSound2(&this->actor, NA_SE_EN_GANON_DARKWAVE);
}
break;
case 20: // zoom cam out
this->envLightMode = 10;
moveCam = true;
Math_ApproachF(&this->csCamMaxStepScale, 0.15f, 1.0f, 0.015f);
if (this->csTimer <= 40) {
Math_ApproachF(&this->fwork[GDF_VORTEX_ALPHA], 255.0f, 1.0f, 6.5f);
Math_ApproachF(&this->fwork[GDF_VORTEX_SCALE], 0.2f, 1.0f, 0.025f);
}
if (this->csTimer > 20) {
Audio_PlayActorSound2(&this->actor, NA_SE_EN_GANON_DARKWAVE_M - SFX_FLAG);
}
if (this->csTimer > 20) {
BossGanonEff_SpawnShock(globalCtx, 700.0f, GDF_SHOCK_PLAYER_PURPLE);
BossGanonEff_SpawnShock(globalCtx, 700.0f, GDF_SHOCK_PLAYER_PURPLE);
}
if (this->csTimer == 30) {
func_8002DF54(globalCtx, &this->actor, 0x4A);
}
if (this->csTimer <= 50) {
break;
}
this->csState = 21;
this->csTimer = 0;
this->fwork[GDF_TRIFORCE_PRIM_A] = 0.0f;
this->fwork[GDF_VORTEX_SCALE] = 0.16f;
goto skip_sound_and_fx;
case 21: // purple vortex
this->envLightMode = 11;
Audio_PlayActorSound2(&this->actor, NA_SE_EN_GANON_DARKWAVE_M - SFX_FLAG);
BossGanonEff_SpawnShock(globalCtx, 700.0f, GDF_SHOCK_PLAYER_PURPLE);
BossGanonEff_SpawnShock(globalCtx, 700.0f, GDF_SHOCK_PLAYER_PURPLE);
skip_sound_and_fx:
this->csCamEye.x = -30.0f;
this->csCamEye.y = 37.0f;
this->csCamEye.z = -30.0f;
this->csCamAt.x = -10.0f;
this->csCamAt.y = 45.0f;
this->csCamAt.z = 0.0f;
if (this->csTimer == 13) {
Message_StartTextbox(globalCtx, 0x70CD, NULL);
}
if ((this->csTimer <= 120) || (Message_GetState(&globalCtx->msgCtx) != TEXT_STATE_NONE)) {
break;
}
this->csState = 22;
this->csTimer = 0;
this->timers[2] = 30;
this->organAlpha = 254;
this->csCamAt.x = this->unk_1FC.x - 10.0f;
this->csCamAt.y = this->unk_1FC.y + 30.0f;
this->csCamAt.z = this->unk_1FC.z;
this->fwork[GDF_VORTEX_ALPHA] = 255.0f;
this->fwork[GDF_VORTEX_SCALE] = 0.2f;
// fallthrough
case 22: // start floating, show title card, start fight
if (this->csTimer > 30) {
this->envLightMode = 0;
} else {
this->envLightMode = 12;
}
Math_ApproachZeroF(&this->fwork[GDF_VORTEX_ALPHA], 1.0f, 10.0f);
this->csCamEye.x = -30.0f;
this->csCamEye.y = 137.0f;
this->csCamEye.z = -110.0f;
Math_ApproachF(&this->csCamAt.y, this->unk_1FC.y + 30.0f, 0.1f, 20.0f);
Math_ApproachF(&this->csCamAt.x, this->unk_1FC.x - 10.0f, 0.1f, 5.0f);
if (this->csTimer == 20) {
BossGanon_SetAnimationObject(this, globalCtx, OBJECT_GANON_ANIME1);
Animation_MorphToPlayOnce(&this->skelAnime, &gDorfGetUp3Anim, 0.0f);
SkelAnime_Update(&this->skelAnime);
this->actor.shape.yOffset = 0.0f;
sBossGanonCape->attachShouldersTimer = 18.0f;
Audio_PlayActorSound2(&this->actor, NA_SE_EV_GANON_MANTLE);
this->unk_198 = 0;
Audio_QueueSeqCmd(SEQ_PLAYER_BGM_MAIN << 24 | NA_BGM_GANONDORF_BOSS);
}
if (this->csTimer == 50) {
gSegments[6] = VIRTUAL_TO_PHYSICAL(
globalCtx->objectCtx.status[Object_GetIndex(&globalCtx->objectCtx, OBJECT_GANON)].segment);
if (!(gSaveContext.eventChkInf[7] & 0x100)) {
TitleCard_InitBossName(globalCtx, &globalCtx->actorCtx.titleCtx,
SEGMENTED_TO_VIRTUAL(gDorfTitleCardTex), 160, 180, 128, 40, false);
}
gSaveContext.eventChkInf[7] |= 0x100;
}
if (this->csTimer >= 20) {
this->legSwayEnabled = true;
Audio_PlayActorSound2(&this->actor, NA_SE_EN_GANON_FLOAT - SFX_FLAG);
Math_ApproachF(&this->actor.world.pos.y, 228.0f, 0.05f, 2.0f);
Math_ApproachF(&this->actor.world.pos.z, -230.0f, 0.05f, 4.0f);
sBossGanonCape->backPush = -3.0f;
sBossGanonCape->backSwayMagnitude = 0.25f;
sBossGanonCape->sideSwayMagnitude = -3.0f;
sin = Math_SinS(this->csTimer * 1500);
this->actor.velocity.y = this->fwork[GDF_FWORK_0] * sin * 0.04f;
this->actor.world.pos.y += this->actor.velocity.y;
cos = Math_CosS(this->csTimer * 1800);
this->actor.world.pos.x = this->fwork[GDF_FWORK_0] * cos * 0.5f;
this->actor.velocity.x = this->actor.world.pos.x - this->actor.prevPos.x;
Math_ApproachF(&this->fwork[GDF_FWORK_0], 50.0f, 1.0f, 1.0f);
}
if (this->csTimer > 30) {
this->organAlpha -= 5;
if (this->organAlpha < 0) {
this->organAlpha = 0;
}
}
if (this->csTimer == 120) {
mainCam = Gameplay_GetCamera(globalCtx, MAIN_CAM);
mainCam->eye = this->csCamEye;
mainCam->eyeNext = this->csCamEye;
mainCam->at = this->csCamAt;
func_800C08AC(globalCtx, this->csCamIndex, 0);
this->csState = this->csCamIndex = 0;
func_80064534(globalCtx, &globalCtx->csCtx);
func_8002DF54(globalCtx, &this->actor, 7);
BossGanon_SetupWait(this, globalCtx);
}
if (sBossGanonZelda != NULL) {
sBossGanonZelda->actor.world.pos.x = 0.0f;
sBossGanonZelda->actor.world.pos.y = 350.0f;
sBossGanonZelda->actor.world.pos.z = 0.0f;
}
}
if (this->csCamIndex != 0) {
if (moveCam) {
Math_ApproachF(&this->csCamEye.x, this->csCamTargetEye.x, this->csCamMovementScale,
this->csCamEyeMaxStep.x * this->csCamMaxStepScale);
Math_ApproachF(&this->csCamEye.y, this->csCamTargetEye.y, this->csCamMovementScale,
this->csCamEyeMaxStep.y * this->csCamMaxStepScale);
Math_ApproachF(&this->csCamEye.z, this->csCamTargetEye.z, this->csCamMovementScale,
this->csCamEyeMaxStep.z * this->csCamMaxStepScale);
Math_ApproachF(&this->csCamAt.x, this->csCamTargetAt.x, this->csCamMovementScale,
this->csCamAtMaxStep.x * this->csCamMaxStepScale);
Math_ApproachF(&this->csCamAt.y, this->csCamTargetAt.y, this->csCamMovementScale,
this->csCamAtMaxStep.y * this->csCamMaxStepScale);
Math_ApproachF(&this->csCamAt.z, this->csCamTargetAt.z, this->csCamMovementScale,
this->csCamAtMaxStep.z * this->csCamMaxStepScale);
}
Gameplay_CameraSetAtEye(globalCtx, this->csCamIndex, &this->csCamAt, &this->csCamEye);
Gameplay_CameraSetFov(globalCtx, this->csCamIndex, this->csCamFov);
}
}
void BossGanon_SetupDeathCutscene(BossGanon* this, GlobalContext* globalCtx) {
s32 pad;
s32 animBankIndex = Object_GetIndex(&globalCtx->objectCtx, OBJECT_GANON_ANIME2);
if (Object_IsLoaded(&globalCtx->objectCtx, animBankIndex)) {
this->actionFunc = BossGanon_DeathAndTowerCutscene;
this->csTimer = this->csState = 0;
this->unk_198 = 1;
this->animBankIndex = animBankIndex;
gSegments[6] = VIRTUAL_TO_PHYSICAL(globalCtx->objectCtx.status[animBankIndex].segment);
Animation_MorphToPlayOnce(&this->skelAnime, &object_ganon_anime2_Anim_00EA00, 0.0f);
this->fwork[GDF_FWORK_1] = Animation_GetLastFrame(&object_ganon_anime2_Anim_00EA00);
this->unk_508 = 0.0f;
}
}
void BossGanon_SetupTowerCutscene(BossGanon* this, GlobalContext* globalCtx) {
s32 pad;
s32 animBankIndex = Object_GetIndex(&globalCtx->objectCtx, OBJECT_GANON_ANIME2);
if (Object_IsLoaded(&globalCtx->objectCtx, animBankIndex)) {
this->animBankIndex = animBankIndex;
gSegments[6] = VIRTUAL_TO_PHYSICAL(globalCtx->objectCtx.status[animBankIndex].segment);
Animation_MorphToPlayOnce(&this->skelAnime, &object_ganon_anime2_Anim_00EA00, 0.0f);
this->fwork[GDF_FWORK_1] = Animation_GetLastFrame(&object_ganon_anime2_Anim_00EA00);
this->actionFunc = BossGanon_DeathAndTowerCutscene;
this->csTimer = 0;
this->csState = 100;
this->unk_198 = 1;
gSaveContext.magic = gSaveContext.unk_13F4;
gSaveContext.health = gSaveContext.healthCapacity;
} else {
this->actionFunc = BossGanon_SetupTowerCutscene;
}
}
void BossGanon_ShatterWindows(u8 windowShatterState) {
s16 i;
u8* tex1 = ResourceMgr_LoadTexByName(SEGMENTED_TO_VIRTUAL(ganon_boss_sceneTex_006C18));
u8* tex2 = ResourceMgr_LoadTexByName(SEGMENTED_TO_VIRTUAL(ganon_boss_sceneTex_007418));
for (i = 0; i < 2048; i++) {
if ((tex1[i] != 0) && (Rand_ZeroOne() < 0.03f)) {
if ((((u8*)gDorfWindowShatterTemplateTex)[i] == 0) || (windowShatterState == GDF_WINDOW_SHATTER_FULL)) {
tex1[i] = tex2[i] = 1;
}
}
}
}
void BossGanon_DeathAndTowerCutscene(BossGanon* this, GlobalContext* globalCtx) {
static Color_RGBA8 bloodPrimColor = { 0, 120, 0, 255 };
static Color_RGBA8 bloodEnvColor = { 0, 120, 0, 255 };
if(CVar_GetS32("gRedGanonBlood", 0)) {
bloodPrimColor.r = 120;
bloodPrimColor.g = 0;
bloodEnvColor.r = 120;
bloodEnvColor.g = 0;
}
s16 i;
u8 moveCam = false;
Player* player = GET_PLAYER(globalCtx);
s16 pad;
Vec3f sp98;
Vec3f sp8C;
Vec3f sp80;
Vec3f sp74;
Camera* mainCam;
Vec3f sp64;
gSegments[6] = VIRTUAL_TO_PHYSICAL(globalCtx->objectCtx.status[this->animBankIndex].segment);
this->csTimer++;
SkelAnime_Update(&this->skelAnime);
switch (this->csState) {
case 0:
func_80064520(globalCtx, &globalCtx->csCtx);
func_8002DF54(globalCtx, &this->actor, 8);
this->csCamIndex = Gameplay_CreateSubCamera(globalCtx);
Gameplay_ChangeCameraStatus(globalCtx, MAIN_CAM, CAM_STAT_WAIT);
Gameplay_ChangeCameraStatus(globalCtx, this->csCamIndex, CAM_STAT_ACTIVE);
this->actor.world.pos.x = 0.0f;
this->actor.world.pos.y = 70.0f;
this->actor.world.pos.z = -80.0f;
this->actor.shape.yOffset = -7000.0f;
this->actor.shape.rot.y = 0;
this->csState = 1;
this->csTimer = 0;
this->useOpenHand = true;
// fallthrough
case 1:
player->actor.shape.rot.y = -0x8000;
player->actor.world.pos.x = -10.0f;
player->actor.world.pos.y = 0.0f;
player->actor.world.pos.z = 115.0f;
this->envLightMode = 13;
if (this->csTimer < 30) {
globalCtx->envCtx.unk_D8 = 0.0f;
}
if (this->csTimer >= 2) {
globalCtx->envCtx.fillScreen = false;
}
this->csCamEye.x = -50.0f;
this->csCamEye.z = -50.0f;
this->csCamEye.y = 50.0f;
this->csCamAt.x = this->unk_1FC.x;
this->csCamAt.y = this->unk_1FC.y + 30.0f;
this->csCamAt.z = this->unk_1FC.z;
if (Animation_OnFrame(&this->skelAnime, this->fwork[GDF_FWORK_1])) {
Animation_MorphToLoop(&this->skelAnime, &object_ganon_anime2_Anim_00F19C, 0.0f);
this->csState = 2;
this->csTimer = 0;
}
break;
case 2:
this->csCamEye.x = -100.0f;
this->csCamEye.y = 20.0f;
this->csCamEye.z = -130.0f;
this->envLightMode = 13;
this->csCamAt.x = this->unk_1FC.x;
this->csCamAt.y = this->unk_1FC.y;
this->csCamAt.z = this->unk_1FC.z + 40.0f;
if (this->csTimer >= 30) {
this->csState = 3;
this->csTimer = 0;
Message_StartTextbox(globalCtx, 0x70CE, NULL);
this->fwork[GDF_FWORK_1] = 1000.0f;
}
if ((this->unk_1A2 % 32) == 0) {
Audio_PlayActorSound2(&this->actor, NA_SE_EN_GANON_BREATH);
}
break;
case 3:
this->envLightMode = 14;
if ((this->fwork[GDF_FWORK_1] > 100.0f) && ((this->unk_1A2 % 32) == 0)) {
Audio_PlayActorSound2(&this->actor, NA_SE_EN_GANON_BREATH);
}
this->csCamEye.x = 7.0f;
this->csCamEye.y = 52.0f;
this->csCamEye.z = -15.0f;
this->csCamAt.x = this->unk_1FC.x - 5.0f;
this->csCamAt.y = this->unk_1FC.y + 30.0f - 10.0f;
this->csCamAt.z = this->unk_1FC.z;
if ((this->fwork[GDF_FWORK_1] > 100.0f) && (this->csTimer > 100) &&
(Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_NONE)) {
Animation_MorphToPlayOnce(&this->skelAnime, &object_ganon_anime2_Anim_00B668, 0.0f);
this->fwork[GDF_FWORK_1] = Animation_GetLastFrame(&object_ganon_anime2_Anim_00B668);
Audio_PlayActorSound2(&this->actor, NA_SE_EN_GANON_TOKETU);
} else {
if (Animation_OnFrame(&this->skelAnime, this->fwork[GDF_FWORK_1] - 16.0f)) {
for (i = 0; i < 40; i++) {
sp98.x = Rand_CenteredFloat(5.0f);
sp98.y = Rand_CenteredFloat(1.5f) + 1.0f;
sp98.z = Rand_ZeroFloat(5.0f) + 2.0f;
sp8C.x = 0.0f;
sp8C.y = -1.0f;
sp8C.z = 0.0f;
sp80.x = this->unk_208.x;
sp80.y = this->unk_208.y - 10.0f;
sp80.z = this->unk_208.z;
func_8002836C(globalCtx, &sp80, &sp98, &sp8C, &bloodPrimColor, &bloodEnvColor,
(s16)Rand_ZeroFloat(50.0f) + 50, 0, 17);
}
}
if (Animation_OnFrame(&this->skelAnime, this->fwork[GDF_FWORK_1])) {
Animation_MorphToLoop(&this->skelAnime, &object_ganon_anime2_Anim_00BE38, 0.0f);
this->csState = 4;
this->csTimer = 0;
}
}
break;
case 4:
this->envLightMode = 14;
if (this->csTimer == 30) {
Message_StartTextbox(globalCtx, 0x70CF, NULL);
this->csState = 5;
this->csTimer = 0;
}
break;
case 5:
this->envLightMode = 14;
if ((this->csTimer > 70) && (Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_NONE)) {
this->csState = 6;
this->csTimer = 0;
Animation_MorphToPlayOnce(&this->skelAnime, &object_ganon_anime2_Anim_010298, 0.0f);
this->fwork[GDF_FWORK_1] = Animation_GetLastFrame(&object_ganon_anime2_Anim_010298);
this->csCamMovementScale = 0.05f;
this->csCamMaxStepScale = 0.0f;
this->csCamTargetEye.x = 7.0f;
this->csCamTargetEye.y = 12.0f;
this->csCamTargetEye.z = 70.0f;
this->csCamTargetAt.x = this->unk_1FC.x - 5.0f;
this->csCamTargetAt.y = (this->unk_1FC.y + 30.0f) - 10.0f;
this->csCamTargetAt.z = this->unk_1FC.z;
this->csCamEyeMaxStep.x = fabsf(this->csCamEye.x - this->csCamTargetEye.x);
this->csCamEyeMaxStep.y = fabsf(this->csCamEye.y - this->csCamTargetEye.y);
this->csCamEyeMaxStep.z = fabsf(this->csCamEye.z - this->csCamTargetEye.z);
this->csCamAtMaxStep.x = fabsf(this->csCamAt.x - this->csCamTargetAt.x);
this->csCamAtMaxStep.y = fabsf(this->csCamAt.y - this->csCamTargetAt.y);
this->csCamAtMaxStep.z = fabsf(this->csCamAt.z - this->csCamTargetAt.z);
Audio_PlayActorSound2(&this->actor, NA_SE_EN_GANON_CASBREAK);
}
break;
case 6:
this->envLightMode = 14;
moveCam = true;
Math_ApproachF(&this->csCamMaxStepScale, 0.2f, 1.0f, 0.01f);
if (Animation_OnFrame(&this->skelAnime, this->fwork[GDF_FWORK_1])) {
Animation_MorphToLoop(&this->skelAnime, &object_ganon_anime2_Anim_010514, 0.0f);
this->csState = 7;
this->csTimer = 0;
this->unk_2E8 = 0;
this->envLightMode = 15;
this->unk_508 = 0.0f;
this->fwork[GDF_FWORK_1] = 1000.0f;
globalCtx->envCtx.unk_D8 = 0.0f;
}
break;
case 7:
if (this->csTimer < 10) {
globalCtx->envCtx.unk_D8 = 0.0f;
}
if (this->csTimer == 30) {
this->csState = 8;
this->csTimer = 0;
this->unk_70C = 0.0f;
}
goto skip_cam_and_quake;
case 8:
this->csCamEye.x = -60.0f;
this->csCamEye.y = 80.0f;
this->csCamEye.z = -130.0f;
this->csCamAt.x = 0.0f;
this->csCamAt.y = 0.0f;
this->csCamAt.z = 70.0f;
this->unk_70C = Math_SinS(this->csTimer * 0x6300) * 0.2f;
func_80078884(NA_SE_EV_EARTHQUAKE - SFX_FLAG);
skip_cam_and_quake:
this->envLightMode = 15;
Audio_PlayActorSound2(&this->actor, NA_SE_EN_GANON_BODY_SPARK - SFX_FLAG);
for (i = 1; i < 15; i++) {
this->unk_4E4[i] = 0xA;
}
this->unk_2E6 = 20000;
Math_ApproachF(&this->unk_508, 5.0f, 0.05f, 0.1f);
if (this->csTimer == 30) {
this->csState = 9;
this->csTimer = 0;
this->csCamEye.x = -30.0f;
this->csCamEye.y = 40.0f;
this->csCamEye.z = 60.0f;
this->csCamAt.x = 492.0f;
this->csCamAt.y = 43.0f;
this->csCamAt.z = 580.0f;
this->csCamMaxStepScale = 0.0f;
this->unk_710 = 10.0f;
}
break;
case 9:
Audio_PlayActorSound2(&this->actor, NA_SE_EN_GANON_BODY_SPARK - SFX_FLAG);
if (this->csTimer == 2) {
func_8002DF54(globalCtx, &this->actor, 0x39);
}
if (this->csTimer > 50) {
Math_ApproachZeroF(&this->unk_710, 1.0f, 0.2f);
Math_ApproachF(&this->csCamEye.x, 270.0f, 0.05f, this->csCamMaxStepScale * 30.0f);
Math_ApproachF(&this->csCamEye.z, 260.0f, 0.05f, this->csCamMaxStepScale * 20.0f);
Math_ApproachF(&this->csCamAt.y, 103.0f, 0.05f, this->csCamMaxStepScale * 6.0f);
Math_ApproachF(&this->csCamAt.z, 280.0f, 0.05f, this->csCamMaxStepScale * 20.0f);
Math_ApproachF(&this->csCamMaxStepScale, 1.0f, 1.0f, 0.01f);
}
this->unk_70C = Math_SinS(this->csTimer * 0x6300) * this->unk_710;
func_80078884(NA_SE_EV_EARTHQUAKE - SFX_FLAG);
if (this->csTimer < 100) {
this->windowShatterState = GDF_WINDOW_SHATTER_PARTIAL;
this->envLightMode = 15;
} else {
this->envLightMode = 16;
this->windowShatterState = GDF_WINDOW_SHATTER_FULL;
}
if (this->csTimer >= 130) {
Math_ApproachF(&this->whiteFillAlpha, 255.0f, 1.0f, 5.0f);
}
if (this->csTimer == 180) {
globalCtx->sceneLoadFlag = 0x14;
if (gSaveContext.n64ddFlag && Randomizer_GetSettingValue(RSK_SKIP_TOWER_ESCAPE)) {
Flags_SetEventChkInf(0xC7);
globalCtx->nextEntranceIndex = 0x517;
}
else {
globalCtx->nextEntranceIndex = 0x43F;
}
globalCtx->fadeTransition = 5;
}
break;
case 100:
func_80064520(globalCtx, &globalCtx->csCtx);
func_8002DF54(globalCtx, &this->actor, 8);
this->csCamIndex = Gameplay_CreateSubCamera(globalCtx);
Gameplay_ChangeCameraStatus(globalCtx, MAIN_CAM, CAM_STAT_WAIT);
Gameplay_ChangeCameraStatus(globalCtx, this->csCamIndex, CAM_STAT_ACTIVE);
Animation_MorphToPlayOnce(&this->skelAnime, &object_ganon_anime2_Anim_00ADDC, 0.0f);
this->fwork[1] = Animation_GetLastFrame(&object_ganon_anime2_Anim_00EA00);
this->csState = 101;
this->skelAnime.playSpeed = 0.0f;
sBossGanonZelda = (EnZl3*)Actor_SpawnAsChild(&globalCtx->actorCtx, &this->actor, globalCtx, ACTOR_EN_ZL3, 0.0f,
6000.0f, 0.0f, 0, 0, 0, 0x2000);
player->actor.world.pos.x = -472.0f;
player->actor.world.pos.y = 4102.0f;
player->actor.world.pos.z = -130.0f;
player->actor.shape.rot.y = -0x8000;
this->actor.world.pos.x = -472.0f;
this->actor.world.pos.y = 4172.0f;
this->actor.world.pos.z = -400.0f;
this->actor.shape.yOffset = -7000.0f;
this->actor.shape.rot.y = 0;
this->csCamEye.x = this->csCamAt.x = -472.0f;
this->csCamEye.y = this->csCamAt.y = 4152.0f;
this->csCamEye.z = -160.0f;
this->csCamAt.z = -100.0f;
sBossGanonCape->backPush = -2.0f;
sBossGanonCape->backSwayMagnitude = 0.25f;
sBossGanonCape->sideSwayMagnitude = -1.0f;
sBossGanonCape->minDist = 0.0f;
sBossGanonCape->minY = 4104.0f;
sBossGanonCape->tearTimer = 20;
this->whiteFillAlpha = 255.0f;
globalCtx->envCtx.unk_D8 = 1.0f;
// fallthrough
case 101:
player->actor.world.pos.y = 4102.0f;
Math_ApproachZeroF(&this->whiteFillAlpha, 1.0f, 5.0f);
if (this->csTimer > 40) {
Math_ApproachF(&this->csCamEye.z, -520.0f, 0.1f, this->csCamMaxStepScale);
Math_ApproachF(&this->csCamMaxStepScale, 5.0f, 1.0f, 0.1f);
if (this->csTimer == 150) {
this->skelAnime.playSpeed = 1.0f;
}
if (this->csTimer == 160) {
Audio_PlayActorSound2(&this->actor, NA_SE_PL_BOUND_NOWEAPON);
}
if (this->csTimer == 187) {
Audio_PlayActorSound2(&this->actor, NA_SE_PL_BODY_HIT);
}
if (this->csTimer == 180) {
Audio_PlayActorSound2(&this->actor, NA_SE_EV_GANON_MANTLE);
}
if (this->csTimer == 190) {
sp74 = this->actor.world.pos;
sp74.y = 4102.0f;
BossGanonEff_SpawnDustDark(globalCtx, &sp74, 0.2f, 0.7f);
}
if (this->csTimer == 230) {
this->csState = 102;
this->csTimer = 0;
}
}
break;
case 102:
player->actor.world.pos.y = 4102.0f;
this->csCamEye.x = -442.0f;
this->csCamEye.y = 4152.0f;
this->csCamEye.z = -135.0f;
this->csCamAt.x = -472.0f;
this->csCamAt.y = 4152.0f;
this->csCamAt.z = -135.0f;
if (this->csTimer == 5) {
func_8002DF54(globalCtx, &this->actor, 0x4C);
}
if (this->csTimer == 70) {
func_8002DF54(globalCtx, &this->actor, 0x4D);
}
if (this->csTimer == 90) {
this->csState = 103;
this->csTimer = 0;
sBossGanonZelda->actor.world.pos.x = -472.0f;
sBossGanonZelda->actor.world.pos.y = 4352.0f;
sBossGanonZelda->actor.world.pos.z = -200.0f;
sBossGanonZelda->unk_3C8 = 3;
}
break;
case 103:
Audio_PlayActorSound2(&sBossGanonZelda->actor, NA_SE_EV_DOWN_TO_GROUND - SFX_FLAG);
Math_ApproachF(&sBossGanonZelda->actor.world.pos.y, 4102.0f, 0.05f, 1.5f);
this->csCamEye.x = -242.0f;
this->csCamEye.y = 4122.0f;
this->csCamEye.z = -190.0f;
this->csCamAt.x = sBossGanonZelda->actor.world.pos.x;
this->csCamAt.y = sBossGanonZelda->actor.world.pos.y + 40.0f + 5.0f;
this->csCamAt.z = sBossGanonZelda->actor.world.pos.z;
if (this->csTimer == 200) {
sBossGanonZelda->actor.world.pos.y = 4102.0f;
this->csState = 104;
this->csTimer = 0;
} else {
break;
}
// fallthrough
case 104:
this->csCamEye.x = -432.0f;
this->csCamEye.y = 4147.0f;
this->csCamEye.z = -200.0f;
this->csCamAt.x = sBossGanonZelda->actor.world.pos.x;
this->csCamAt.y = sBossGanonZelda->actor.world.pos.y + 40.0f + 5.0f;
this->csCamAt.z = sBossGanonZelda->actor.world.pos.z;
if (this->csTimer >= 10) {
Math_ApproachZeroF(&globalCtx->envCtx.unk_D8, 1.0f, 0.05f);
}
if (this->csTimer == 10) {
sBossGanonZelda->unk_3C8 = 8;
}
if (this->csTimer == 50) {
sBossGanonZelda->unk_3C8 = 4;
}
if (this->csTimer == 100) {
this->csState = 105;
this->csTimer = 0;
}
break;
case 105:
this->csCamEye.x = -450.0f;
this->csCamEye.y = 4154.0f;
this->csCamEye.z = -182.0f;
this->csCamAt.x = sBossGanonZelda->actor.world.pos.x - 5.0f;
this->csCamAt.y = sBossGanonZelda->actor.world.pos.y + 40.0f + 5.0f;
this->csCamAt.z = sBossGanonZelda->actor.world.pos.z - 25.0f;
if (this->csTimer == 10) {
Message_StartTextbox(globalCtx, 0x70D0, NULL);
}
if ((this->csTimer > 100) && (Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_NONE)) {
this->csState = 1055;
this->csTimer = 0;
}
break;
case 1055:
this->unk_70C = Math_SinS(this->csTimer * 0x6300) * 0.3f;
func_80078884(NA_SE_EV_EARTHQUAKE - SFX_FLAG);
if (this->csTimer == 20) {
sBossGanonZelda->unk_3C8 = 5;
func_8002DF54(globalCtx, &this->actor, 0x39);
}
if (this->csTimer == 40) {
this->csState = 1056;
this->csTimer = 0;
}
break;
case 1056:
this->unk_70C = Math_SinS(this->csTimer * 0x6300) * 0.3f;
func_80078884(NA_SE_EV_EARTHQUAKE - SFX_FLAG);
this->csCamEye.x = -503.0f;
this->csCamEye.y = 4128.0f;
this->csCamEye.z = -162.0f;
this->csCamAt.x = -416.0f;
this->csCamAt.y = 4181.0f;
this->csCamAt.z = -75.0f;
if (this->csTimer > 40) {
this->csState = 1057;
this->csTimer = 0;
}
break;
case 1057:
this->unk_70C = Math_SinS(this->csTimer * 0x6300) * (50.0f * this->csCamMovementScale);
func_80078884(NA_SE_EV_EARTHQUAKE - SFX_FLAG);
Math_ApproachF(&this->csCamEye.x, -1200.0f, 0.1f, this->csCamMovementScale * 697.0f);
Math_ApproachF(&this->csCamEye.y, 4241.0f, 0.1f, this->csCamMovementScale * 113.0f);
Math_ApproachF(&this->csCamEye.z, -1048.0f, 0.1f, this->csCamMovementScale * 886.0f);
Math_ApproachF(&this->csCamMovementScale, 0.05f, 1.0f, 0.001f);
if (this->csTimer > 80) {
this->csState = 106;
this->csTimer = 60;
}
break;
case 106:
this->csCamEye.x = -450.0f;
this->csCamEye.y = 4154.0f;
this->csCamEye.z = -182.0f;
this->csCamAt.x = sBossGanonZelda->actor.world.pos.x - 5.0f;
this->csCamAt.y = sBossGanonZelda->actor.world.pos.y + 40.0f + 5.0f;
this->csCamAt.z = sBossGanonZelda->actor.world.pos.z - 25.0f;
this->unk_70C = Math_SinS(this->csTimer * 0x6300) * 0.3f;
func_80078884(NA_SE_EV_EARTHQUAKE - SFX_FLAG);
if (this->csTimer == 70) {
sBossGanonZelda->unk_3C8 = 6;
}
if (this->csTimer == 90) {
Message_StartTextbox(globalCtx, 0x70D1, NULL);
}
if ((this->csTimer > 150) && (Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_NONE)) {
this->csState = 107;
this->csTimer = 0;
Message_StartTextbox(globalCtx, 0x70D2, NULL);
func_8002DF54(globalCtx, &this->actor, 0x39);
}
break;
case 107:
this->unk_70C = Math_SinS(this->csTimer * 0x6300) * 0.8f;
func_80078884(NA_SE_EV_EARTHQUAKE - SFX_FLAG);
this->csCamEye.x = -380.0f;
this->csCamEye.y = 4154.0f;
this->csCamEye.z = -242.0f;
this->csCamAt.x = (sBossGanonZelda->actor.world.pos.x - 5.0f) - 30.0f;
this->csCamAt.y = (sBossGanonZelda->actor.world.pos.y + 40.0f + 5.0f) - 20.0f;
this->csCamAt.z = (sBossGanonZelda->actor.world.pos.z - 25.0f) + 80.0f;
if ((this->csTimer > 50) && (Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_NONE)) {
sBossGanonZelda->unk_3C8 = 7;
this->csState = 108;
this->csTimer = 0;
}
break;
case 108:
this->unk_70C = Math_SinS(this->csTimer * 0x6300) * 0.8f;
func_80078884(NA_SE_EV_EARTHQUAKE - SFX_FLAG);
this->csCamAt.x = (sBossGanonZelda->actor.world.pos.x - 5.0f) - 30.0f;
this->csCamAt.y = (sBossGanonZelda->actor.world.pos.y + 40.0f + 5.0f) - 20.0f;
this->csCamAt.z = (sBossGanonZelda->actor.world.pos.z - 25.0f) + 80.0f;
if (this->csTimer > 50) {
mainCam = Gameplay_GetCamera(globalCtx, MAIN_CAM);
mainCam->eye = this->csCamEye;
mainCam->eyeNext = this->csCamEye;
mainCam->at = this->csCamAt;
func_800C08AC(globalCtx, this->csCamIndex, 0);
this->csState = 109;
this->csCamIndex = 0;
func_80064534(globalCtx, &globalCtx->csCtx);
func_8002DF54(globalCtx, &this->actor, 7);
Flags_SetSwitch(globalCtx, 0x37);
}
break;
case 109:
func_80078884(NA_SE_EV_EARTHQUAKE - SFX_FLAG);
break;
}
if (this->csState >= 100) {
this->envLightMode = 20;
}
if (this->csCamIndex != 0) {
if (moveCam) {
Math_ApproachF(&this->csCamEye.x, this->csCamTargetEye.x, this->csCamMovementScale,
this->csCamEyeMaxStep.x * this->csCamMaxStepScale);
Math_ApproachF(&this->csCamEye.y, this->csCamTargetEye.y, this->csCamMovementScale,
this->csCamEyeMaxStep.y * this->csCamMaxStepScale);
Math_ApproachF(&this->csCamEye.z, this->csCamTargetEye.z, this->csCamMovementScale,
this->csCamEyeMaxStep.z * this->csCamMaxStepScale);
Math_ApproachF(&this->csCamAt.x, this->csCamTargetAt.x, this->csCamMovementScale,
this->csCamAtMaxStep.x * this->csCamMaxStepScale);
Math_ApproachF(&this->csCamAt.y, this->csCamTargetAt.y, this->csCamMovementScale,
this->csCamAtMaxStep.y * this->csCamMaxStepScale);
Math_ApproachF(&this->csCamAt.z, this->csCamTargetAt.z, this->csCamMovementScale,
this->csCamAtMaxStep.z * this->csCamMaxStepScale);
}
sp64 = this->csCamAt;
sp64.y += this->unk_70C;
Gameplay_CameraSetAtEye(globalCtx, this->csCamIndex, &sp64, &this->csCamEye);
}
}
void BossGanon_SetupPoundFloor(BossGanon* this, GlobalContext* globalCtx) {
this->unk_1C2 = 0;
this->timers[0] = 40;
this->actionFunc = BossGanon_PoundFloor;
this->actor.velocity.x = 0.0f;
this->actor.velocity.y = 0.0f;
this->fwork[GDF_CENTER_POS] = 100.0f;
}
void BossGanon_PoundFloor(BossGanon* this, GlobalContext* globalCtx) {
s16 i;
f32 heightTarget;
f32 targetPosX;
f32 targetPosZ;
Vec3f sp6C;
Vec3f sp60;
Vec3f sp54;
Vec3f sp48;
SkelAnime_Update(&this->skelAnime);
switch (this->unk_1C2) {
case 0:
targetPosX = Math_SinS(this->unk_1A2 * 1280);
targetPosX = targetPosX * this->fwork[GDF_CENTER_POS];
targetPosZ = Math_CosS(this->unk_1A2 * 1792);
targetPosZ = targetPosZ * this->fwork[GDF_CENTER_POS];
Math_ApproachF(&this->actor.world.pos.x, targetPosX, 0.05f, this->fwork[GDF_FWORK_0]);
Math_ApproachF(&this->actor.world.pos.z, targetPosZ, 0.05f, this->fwork[GDF_FWORK_0]);
Math_ApproachF(&this->fwork[GDF_CENTER_POS], 0.0f, 1, 1.5f);
if (this->timers[0] == 5) {
Audio_PlayActorSound2(&this->actor, NA_SE_EN_GANON_HIT_GND);
}
if (this->timers[0] < 14) {
heightTarget = 250.0f;
this->unk_258 += (Rand_ZeroFloat(M_PI / 2) + (M_PI / 2));
Math_ApproachF(&this->handLightBallScale, 7.0f, 0.5f, 1.0f);
this->envLightMode = 1;
} else {
heightTarget = 200.0f;
}
Math_ApproachF(&this->actor.world.pos.y, heightTarget, 0.1f, this->actor.velocity.y);
Math_ApproachF(&this->actor.velocity.y, 20.0f, 1.0f, 1.0f);
if (this->timers[0] == 14) {
this->fwork[GDF_FWORK_1] = Animation_GetLastFrame(&gDorfPoundAnim);
Animation_MorphToPlayOnce(&this->skelAnime, &gDorfPoundAnim, 0.0f);
this->actor.velocity.y = 0.0f;
}
if (this->timers[0] == 0) {
this->unk_1C2 = 1;
this->actor.velocity.y = 0.0f;
}
break;
case 1:
sBossGanonCape->gravity = -1.0f;
this->envLightMode = 1;
Math_ApproachF(&this->actor.velocity.y, -50.0f, 1.0f, 10.0f);
this->actor.world.pos.y += this->actor.velocity.y;
if (this->actor.world.pos.y < 60.0f) {
this->actor.world.pos.y = 60.0f;
this->unk_1C2 = 2;
this->timers[0] = 10;
func_80033E88(&this->actor, globalCtx, 0xA, 0x14); // rumble
this->unk_19C = 35;
this->unk_19E = 0;
Audio_PlayActorSound2(&this->actor, NA_SE_EN_GANON_HIT_GND_IMP);
this->handLightBallScale = 0.0f;
sp60 = this->unk_260;
sp60.y = 0.0f;
for (i = 0; i < 80; i++) {
sp6C.x = Rand_CenteredFloat(25.0f);
sp6C.y = Rand_ZeroFloat(17.0f);
sp6C.z = Rand_CenteredFloat(25.0f);
BossGanonEff_SpawnLightRay(globalCtx, &sp60, &sp6C, &sZeroVec, Rand_ZeroFloat(300.0f) + 500.0f,
13.0f, 0x1E);
}
}
break;
case 2:
this->envLightMode = 1;
if (this->timers[0] == 0) {
this->fwork[GDF_FWORK_1] = Animation_GetLastFrame(&gDorfPoundEndAnim);
Animation_MorphToPlayOnce(&this->skelAnime, &gDorfPoundEndAnim, 0.0f);
this->unk_1C2 = 3;
this->unk_19F = 1;
this->actor.velocity.y = 0.0f;
}
break;
case 3:
Math_ApproachF(&this->actor.world.pos.y, 150.0f, 0.1f, this->actor.velocity.y);
Math_ApproachF(&this->actor.velocity.y, 20.0f, 1.0f, 1.0f);
if (Animation_OnFrame(&this->skelAnime, this->fwork[GDF_FWORK_1])) {
this->fwork[GDF_FWORK_1] = Animation_GetLastFrame(&gDorfGetUp3Anim);
Animation_MorphToPlayOnce(&this->skelAnime, &gDorfGetUp3Anim, 0.0f);
SkelAnime_Update(&this->skelAnime);
sBossGanonCape->attachShouldersTimer = 18.0f;
Audio_PlayActorSound2(&this->actor, NA_SE_EV_GANON_MANTLE);
this->unk_1C2 = 4;
}
break;
case 4:
Math_ApproachF(&this->actor.world.pos.y, 150.0f, 0.1f, this->actor.velocity.y);
Math_ApproachF(&this->actor.velocity.y, 20.0f, 1.0f, 1.0f);
if (Animation_OnFrame(&this->skelAnime, this->fwork[GDF_FWORK_1])) {
BossGanon_SetupWait(this, globalCtx);
}
break;
}
if ((this->unk_19C == 35) || (this->unk_19C == 30) || (this->unk_19C == 25)) {
sp54 = this->actor.world.pos;
sp54.y = 0.0f;
BossGanonEff_SpawnDustLight(globalCtx, &sp54, 0, 3.0f, this->unk_19C - 25);
}
if (this->unk_19C == 35) {
sp48 = this->actor.world.pos;
sp48.y = 0.0f;
BossGanonEff_SpawnShockwave(globalCtx, &sp48, 0, 3.0f);
}
}
void BossGanon_SetupChargeBigMagic(BossGanon* this, GlobalContext* globalCtx) {
this->unk_1C2 = 0;
this->timers[0] = 30;
this->actor.velocity.x = 0.0f;
this->actor.velocity.y = 0.0f;
this->fwork[GDF_CENTER_POS] = 100.0f;
this->unk_1AA = Rand_ZeroFloat(20000.0f);
this->unk_1AC = 0;
this->actionFunc = BossGanon_ChargeBigMagic;
}
void BossGanon_ChargeBigMagic(BossGanon* this, GlobalContext* globalCtx) {
s32 pad;
f32 targetPosX;
f32 targetPosZ;
Vec3f sp80;
Vec3f sp74;
Vec3f sp68;
s16 i;
SkelAnime_Update(&this->skelAnime);
targetPosX = Math_SinS(this->unk_1A2 * 1280);
targetPosX = targetPosX * this->fwork[GDF_CENTER_POS];
targetPosZ = Math_CosS(this->unk_1A2 * 1792);
targetPosZ = targetPosZ * this->fwork[GDF_CENTER_POS];
Math_ApproachF(&this->actor.world.pos.x, targetPosX, 0.05f, this->fwork[GDF_FWORK_0]);
Math_ApproachF(&this->actor.world.pos.z, targetPosZ, 0.05, this->fwork[GDF_FWORK_0]);
Math_ApproachF(&this->fwork[GDF_CENTER_POS], 0.0f, 1.0f, 1.5f);
Math_ApproachF(&this->actor.world.pos.y, 200.0f, 0.05f, this->actor.velocity.y);
Math_ApproachF(&this->actor.velocity.y, 20.0f, 1.0f, 1.0f);
switch (this->unk_1C2) {
case 0:
if (this->timers[0] == 0) {
this->fwork[GDF_FWORK_1] = Animation_GetLastFrame(&gDorfBigMagicChargeStartAnim);
Animation_MorphToPlayOnce(&this->skelAnime, &gDorfBigMagicChargeStartAnim, 0.0f);
this->unk_1C2 = 1;
}
break;
case 1:
if (Animation_OnFrame(&this->skelAnime, this->fwork[GDF_FWORK_1])) {
this->fwork[GDF_FWORK_1] = Animation_GetLastFrame(&gDorfBigMagicChargeHoldAnim);
Animation_MorphToLoop(&this->skelAnime, &gDorfBigMagicChargeHoldAnim, 0.0f);
this->unk_1C2 = 2;
this->timers[0] = 100;
}
break;
case 2:
this->envLightMode = 2;
Audio_PlayActorSound2(&this->actor, NA_SE_EN_GANON_CHARGE_MASIC - SFX_FLAG);
this->unk_278.x = this->unk_2EC[0].x;
this->unk_278.y = this->unk_2EC[0].y + 50.0f + 30.0f;
this->unk_278.z = this->unk_2EC[0].z;
Math_ApproachF(&this->unk_284, 0.25f, 0.1f, 0.006f);
Math_ApproachF(&this->unk_288, 255.0f, 1.0f, 255.0f);
Math_ApproachF(&this->unk_28C, 0.25f, 0.1f, 0.006f);
if ((this->timers[0] > 20) && (this->timers[0] < 60)) {
Math_ApproachF(&this->unk_290, 255.0f, 1.0f, 15.0f);
}
if (this->timers[0] == 0) {
this->fwork[GDF_FWORK_1] = Animation_GetLastFrame(&gDorfBigMagicWindupAnim);
Animation_MorphToPlayOnce(&this->skelAnime, &gDorfBigMagicWindupAnim, 0.0f);
this->unk_1C2 = 3;
this->timers[0] = 6;
this->timers[1] = 15;
Audio_PlayActorSound2(&this->actor, NA_SE_EN_GANON_DARKWAVE);
break;
}
Math_ApproachS(&this->actor.shape.rot.y, this->actor.yawTowardsPlayer, 5, 0x3E8);
if (this->timers[0] < -4) {
for (i = 0; i < ARRAY_COUNT(this->unk_294); i++) {
Math_ApproachF(&this->unk_294[i], 0.0f, 1.0f, 40.0f);
}
} else if ((this->timers[0] >= 7) && (this->timers[0] < 26)) {
if (this->unk_1AC < ARRAY_COUNT(this->unk_294)) {
this->unk_1AC++;
}
for (i = 0; i < this->unk_1AC; i++) {
Math_ApproachF(&this->unk_294[i], 200.0f, 1.0f, 40.0f);
}
}
if (this->timers[0] <= 30) {
Math_ApproachF(&this->unk_284, 0.4f, 0.5f, 0.017f);
this->unk_28C = this->unk_284;
}
if (this->timers[0] <= 30) {
Math_ApproachF(&this->unk_2D0, 45.0f, 0.1f, 10.0f);
this->lensFlareTimer = 1;
this->lensFlareMode = 2;
gCustomLensFlarePos = this->unk_278;
}
if (this->timers[0] == 47) {
this->unk_274 = 1;
}
if (this->timers[0] == 46) {
this->unk_274 = 2;
}
if (this->timers[0] == 45) {
this->unk_274 = 3;
}
if (this->timers[0] == 44) {
this->unk_274 = 4;
}
if (this->timers[0] == 43) {
this->unk_274 = 5;
}
if (this->timers[0] == 42) {
this->unk_274 = 6;
}
if (this->timers[0] > 30) {
sp74.x = 0.0f;
sp74.y = Rand_ZeroFloat(10.0f) + 150.0f;
sp74.z = 0.0f;
Matrix_RotateY(BINANG_TO_RAD(this->actor.yawTowardsPlayer), MTXMODE_NEW);
Matrix_RotateZ(Rand_ZeroFloat(65536.0f), MTXMODE_APPLY);
Matrix_MultVec3f(&sp74, &sp68);
sp80.x = this->unk_278.x + sp68.x;
sp80.y = this->unk_278.y + sp68.y;
sp80.z = this->unk_278.z + sp68.z;
BossGanonEff_SpawnBlackDot(globalCtx, &sp80, 20.0f);
}
break;
case 3:
this->envLightMode = 2;
for (i = 0; i < ARRAY_COUNT(this->unk_294); i++) {
Math_ApproachF(&this->unk_294[i], 0.0f, 1.0f, 40.0f);
}
if (this->timers[0] == 1) {
sBossGanonCape->attachLeftArmTimer = 15.0f;
Audio_PlayActorSound2(&this->actor, NA_SE_EV_GANON_MANTLE);
}
if (this->timers[0] == 0) {
Math_ApproachZeroF(&this->unk_284, 1.0f, 0.08f);
this->unk_28C = this->unk_284;
Math_ApproachZeroF(&this->unk_2D0, 1.0f, 10.0f);
Math_ApproachF(&this->unk_278.x, this->unk_1FC.x, 0.5f, 30.0f);
Math_ApproachF(&this->unk_278.y, this->unk_1FC.y, 0.5f, 30.0f);
Math_ApproachF(&this->unk_278.z, this->unk_1FC.z, 0.5f, 30.0f);
}
if (this->timers[1] == 0) {
this->fwork[GDF_FWORK_1] = Animation_GetLastFrame(&gDorfBigMagicThrowAnim);
Animation_MorphToLoop(&this->skelAnime, &gDorfBigMagicThrowAnim, 0.0f);
this->unk_1C2 = 4;
this->unk_288 = 0.0f;
this->unk_290 = 0.0f;
this->unk_284 = 0.0f;
this->unk_28C = 0.0f;
}
break;
case 4:
this->envLightMode = 2;
if (Animation_OnFrame(&this->skelAnime, 5.0f)) {
for (i = 0; i < 5; i++) {
Actor_SpawnAsChild(&globalCtx->actorCtx, &this->actor, globalCtx, ACTOR_BOSS_GANON, this->unk_1FC.x,
this->unk_1FC.y, this->unk_1FC.z, 0, this->actor.yawTowardsPlayer, 0, 0x104 + i);
}
Audio_PlayActorSound2(&this->actor, NA_SE_EN_GANON_BIGMASIC);
Audio_PlayActorSound2(&this->actor, NA_SE_EN_GANON_THROW_BIG);
}
if (Animation_OnFrame(&this->skelAnime, 3.0f)) {
sBossGanonCape->attachShouldersTimer = 26.0f;
Audio_PlayActorSound2(&this->actor, NA_SE_EV_GANON_MANTLE);
}
if (Animation_OnFrame(&this->skelAnime, this->fwork[GDF_FWORK_1])) {
this->fwork[GDF_FWORK_1] = Animation_GetLastFrame(&gDorfBigMagicThrowEndAnim);
Animation_MorphToLoop(&this->skelAnime, &gDorfBigMagicThrowEndAnim, 0.0f);
this->unk_1C2 = 5;
}
break;
case 5:
this->envLightMode = 2;
if (Animation_OnFrame(&this->skelAnime, this->fwork[GDF_FWORK_1])) {
BossGanon_SetupWait(this, globalCtx);
}
break;
}
}
void BossGanon_SetupWait(BossGanon* this, GlobalContext* globalCtx) {
BossGanon_SetAnimationObject(this, globalCtx, OBJECT_GANON_ANIME1);
Animation_MorphToLoop(&this->skelAnime, &gDorfFloatAnim, -10.0f);
this->actionFunc = BossGanon_Wait;
this->fwork[GDF_FWORK_0] = 0.0f;
this->timers[0] = (s16)Rand_ZeroFloat(64.0f) + 30;
this->unk_1C2 = 0;
sBossGanonCape->minY = 2.0f;
}
void BossGanon_Wait(BossGanon* this, GlobalContext* globalCtx) {
f32 sin;
s32 pad;
f32 cos;
Player* player = GET_PLAYER(globalCtx);
this->legSwayEnabled = true;
sBossGanonCape->backPush = -3.0f;
sBossGanonCape->backSwayMagnitude = 0.25f;
sBossGanonCape->sideSwayMagnitude = -3.0f;
sBossGanonCape->minDist = 20.0f;
SkelAnime_Update(&this->skelAnime);
if ((this->unk_1C2 == 0) && !(player->actor.world.pos.y < 0.0f)) {
if (!(player->stateFlags1 & 0x2000) && (fabsf(player->actor.world.pos.x) < 110.0f) &&
(fabsf(player->actor.world.pos.z) < 110.0f)) {
BossGanon_SetupPoundFloor(this, globalCtx);
} else if ((this->timers[0] == 0) && !(player->stateFlags1 & 0x2000)) {
this->timers[0] = (s16)Rand_ZeroFloat(30.0f) + 30;
if ((s8)this->actor.colChkInfo.health >= 20) {
BossGanon_SetupChargeLightBall(this, globalCtx);
} else if (Rand_ZeroOne() >= 0.5f) {
if ((Rand_ZeroOne() >= 0.5f) || (this->actor.xzDistToPlayer > 350.0f)) {
BossGanon_SetupChargeBigMagic(this, globalCtx);
} else {
BossGanon_SetupPoundFloor(this, globalCtx);
}
} else {
BossGanon_SetupChargeLightBall(this, globalCtx);
}
}
}
sin = Math_SinS(this->unk_1A2 * 1280) * 100.0f;
cos = Math_CosS(this->unk_1A2 * 1792) * 100.0f;
Math_ApproachF(&this->actor.world.pos.x, sin, 0.05f, this->fwork[GDF_FWORK_0]);
Math_ApproachF(&this->actor.world.pos.y, 150.0f, 0.05f, this->fwork[GDF_FWORK_0] * 0.2f);
Math_ApproachF(&this->actor.world.pos.z, cos, 0.05f, this->fwork[GDF_FWORK_0]);
Math_ApproachF(&this->fwork[GDF_FWORK_0], 50.0f, 1.0f, 0.5f);
this->actor.velocity.x = this->actor.world.pos.x - this->actor.prevPos.x;
this->actor.velocity.z = this->actor.world.pos.z - this->actor.prevPos.z;
sin = Math_SinS(this->unk_1A2 * 1500);
this->actor.velocity.y = this->fwork[GDF_FWORK_0] * sin * 0.04f;
this->actor.world.pos.y += this->actor.velocity.y;
Math_ApproachS(&this->actor.shape.rot.y, this->actor.yawTowardsPlayer, 5, 0xBB8);
func_80078914(&this->actor.projectedPos, NA_SE_EN_FANTOM_FLOAT - SFX_FLAG);
}
void BossGanon_SetupChargeLightBall(BossGanon* this, GlobalContext* globalCtx) {
BossGanon_SetAnimationObject(this, globalCtx, OBJECT_GANON_ANIME1);
this->fwork[GDF_FWORK_1] = Animation_GetLastFrame(&gDorfChargeLightBallAnim);
Animation_MorphToPlayOnce(&this->skelAnime, &gDorfChargeLightBallAnim, -3.0f);
this->actionFunc = BossGanon_ChargeLightBall;
this->timers[0] = 25;
}
void BossGanon_ChargeLightBall(BossGanon* this, GlobalContext* globalCtx) {
SkelAnime_Update(&this->skelAnime);
sBossGanonCape->backPush = -3.0f;
sBossGanonCape->backSwayMagnitude = 1.25f;
sBossGanonCape->sideSwayMagnitude = -2.0f;
sBossGanonCape->minDist = 10.0f;
if (this->timers[0] < 17) {
this->envLightMode = 1;
}
if (this->timers[0] == 17) {
this->unk_26C = 10;
this->unk_270 = Rand_ZeroFloat(M_PI);
Audio_PlayActorSound2(&this->actor, NA_SE_EN_GANON_SPARK);
}
if (this->timers[0] < 10) {
this->unk_258 += (Rand_ZeroFloat(M_PI / 2) + (M_PI / 2));
Math_ApproachF(&this->handLightBallScale, 10.0f, 0.5f, 1.25f);
if (this->timers[0] == 0) {
BossGanon_SetupPlayTennis(this, globalCtx);
}
}
Math_ApproachS(&this->actor.shape.rot.y, this->actor.yawTowardsPlayer, 5, 0x7D0);
this->actor.world.pos.x += this->actor.velocity.x;
this->actor.world.pos.z += this->actor.velocity.z;
Math_ApproachZeroF(&this->actor.velocity.x, 1.0f, 0.5f);
Math_ApproachZeroF(&this->actor.velocity.z, 1.0f, 0.5f);
this->actor.velocity.y = Math_SinS(this->unk_1A2 * 1500) * 2.0f;
this->actor.world.pos.y += this->actor.velocity.y;
}
void BossGanon_SetupPlayTennis(BossGanon* this, GlobalContext* globalCtx) {
BossGanon_SetAnimationObject(this, globalCtx, OBJECT_GANON_ANIME1);
this->fwork[GDF_FWORK_1] = Animation_GetLastFrame(&gDorfThrowAnim);
Animation_MorphToPlayOnce(&this->skelAnime, &gDorfThrowAnim, 0.0f);
this->actionFunc = BossGanon_PlayTennis;
}
void BossGanon_PlayTennis(BossGanon* this, GlobalContext* globalCtx) {
static AnimationHeader* volleyAnims[] = { &gDorfVolleyLeftAnim, &gDorfVolleyRightAnim };
static s16 capeRightArmDurations[] = { 26, 20 };
s16 rand;
SkelAnime_Update(&this->skelAnime);
Math_ApproachZeroF(&this->handLightBallScale, 1.0f, 0.2f);
switch (this->unk_1C2) {
case 0:
this->envLightMode = 1;
if (Animation_OnFrame(&this->skelAnime, this->fwork[GDF_FWORK_1])) {
this->unk_1C2 = 1;
Animation_MorphToLoop(&this->skelAnime, &gDorfFloatAnim, 0.0f);
}
if (this->skelAnime.curFrame <= 12.0f) {
this->lensFlareTimer = 2;
this->lensFlareMode = 2;
gCustomLensFlarePos = this->unk_260;
}
if (Animation_OnFrame(&this->skelAnime, 12.0f)) {
this->handLightBallScale = 0.0f;
}
if (Animation_OnFrame(&this->skelAnime, 11.0f)) {
this->unk_25C = 1;
Audio_PlayActorSound2(&this->actor, NA_SE_EN_GANON_THROW);
Audio_PlayActorSound2(&this->actor, NA_SE_EN_GANON_THROW_MASIC);
Actor_SpawnAsChild(&globalCtx->actorCtx, &this->actor, globalCtx, ACTOR_BOSS_GANON, this->unk_260.x,
this->unk_260.y, this->unk_260.z, 0, 0, 0, 0x64);
}
break;
case 1:
if (this->startVolley) {
rand = Rand_ZeroOne() * 1.99f;
this->fwork[GDF_FWORK_1] = Animation_GetLastFrame(volleyAnims[rand]);
Animation_MorphToPlayOnce(&this->skelAnime, volleyAnims[rand], 0.0f);
sBossGanonCape->attachRightArmTimer = capeRightArmDurations[rand];
Audio_PlayActorSound2(&this->actor, NA_SE_EV_GANON_MANTLE);
this->startVolley = false;
}
break;
}
Math_ApproachS(&this->actor.shape.rot.y, this->actor.yawTowardsPlayer, 5, 0x7D0);
this->actor.world.pos.x += this->actor.velocity.x;
this->actor.world.pos.z += this->actor.velocity.z;
Math_SmoothStepToF(&this->actor.velocity.x, 0.0f, 1.0f, 0.5f, 0.0f);
Math_SmoothStepToF(&this->actor.velocity.z, 0.0f, 1.0f, 0.5f, 0.0f);
this->actor.velocity.y = Math_SinS(this->unk_1A2 * 1500) * 2.0f;
this->actor.world.pos.y += this->actor.velocity.y;
}
void BossGanon_SetupBlock(BossGanon* this, GlobalContext* globalCtx) {
if ((this->actionFunc != BossGanon_Block) || (this->unk_1C2 != 0)) {
BossGanon_SetAnimationObject(this, globalCtx, OBJECT_GANON_ANIME1);
this->fwork[GDF_FWORK_1] = Animation_GetLastFrame(&gDorfBlockAnim);
Animation_MorphToPlayOnce(&this->skelAnime, &gDorfBlockAnim, 0.0f);
this->actionFunc = BossGanon_Block;
}
this->unk_1C2 = 0;
sBossGanonCape->attachLeftArmTimer = this->timers[0] = 10;
Audio_PlayActorSound2(&this->actor, NA_SE_EV_GANON_MANTLE);
this->handLightBallScale = 0.0f;
}
void BossGanon_Block(BossGanon* this, GlobalContext* globalCtx) {
this->collider.base.colType = 9;
SkelAnime_Update(&this->skelAnime);
sBossGanonCape->backPush = -9.0f;
sBossGanonCape->backSwayMagnitude = 0.25f;
sBossGanonCape->sideSwayMagnitude = -2.0f;
sBossGanonCape->minDist = 13.0f;
if (this->unk_1C2 == 0) {
if (this->timers[0] == 0) {
this->unk_1C2 = 1;
Animation_MorphToPlayOnce(&this->skelAnime, &gDorfBlockReleaseAnim, 0.0f);
this->fwork[GDF_FWORK_1] = Animation_GetLastFrame(&gDorfBlockReleaseAnim);
SkelAnime_Update(&this->skelAnime);
sBossGanonCape->attachShouldersTimer = 15.0f;
Audio_PlayActorSound2(&this->actor, NA_SE_EV_GANON_MANTLE);
}
} else {
sBossGanonCape->sideSwayMagnitude = -13.0f;
if (Animation_OnFrame(&this->skelAnime, this->fwork[GDF_FWORK_1])) {
BossGanon_SetupWait(this, globalCtx);
}
}
this->actor.world.pos.x += this->actor.velocity.x;
this->actor.world.pos.y += this->actor.velocity.y;
this->actor.world.pos.z += this->actor.velocity.z;
Math_ApproachZeroF(&this->actor.velocity.x, 1.0f, 0.5f);
Math_ApproachZeroF(&this->actor.velocity.y, 1.0f, 0.5f);
Math_ApproachZeroF(&this->actor.velocity.z, 1.0f, 0.5f);
}
void BossGanon_SetupHitByLightBall(BossGanon* this, GlobalContext* globalCtx) {
s16 i;
BossGanon_SetAnimationObject(this, globalCtx, OBJECT_GANON_ANIME1);
this->fwork[GDF_FWORK_1] = Animation_GetLastFrame(&gDorfBigMagicHitAnim);
Animation_MorphToPlayOnce(&this->skelAnime, &gDorfBigMagicHitAnim, 0);
this->timers[0] = 70;
sBossGanonCape->attachRightArmTimer = sBossGanonCape->attachLeftArmTimer = 0;
for (i = 1; i < 15; i++) {
this->unk_4E4[i] = D_808E4C58[i];
}
this->unk_2E6 = 80;
this->unk_2E8 = 0;
this->actionFunc = BossGanon_HitByLightBall;
this->actor.velocity.x = this->actor.velocity.z = 0.0f;
this->unk_1C2 = 0;
this->unk_1A6 = 15;
this->unk_508 = 6.0f;
}
void BossGanon_HitByLightBall(BossGanon* this, GlobalContext* globalCtx) {
s16 i;
Vec3f sp50;
SkelAnime_Update(&this->skelAnime);
if (this->unk_1C2 == 0) {
BossGanonEff_SpawnShock(globalCtx, 1500.0f, GDF_SHOCK_DORF_YELLOW);
if (Animation_OnFrame(&this->skelAnime, this->fwork[GDF_FWORK_1])) {
this->fwork[GDF_FWORK_1] = Animation_GetLastFrame(&gDorfLightArrowWaitAnim);
Animation_MorphToLoop(&this->skelAnime, &gDorfLightArrowWaitAnim, 0.0f);
this->unk_1C2 = 1;
}
} else if (this->unk_1C2 == 1) {
BossGanonEff_SpawnShock(globalCtx, 1000.0f, GDF_SHOCK_DORF_YELLOW);
if (this->timers[0] == 0) {
this->fwork[GDF_FWORK_1] = Animation_GetLastFrame(&gDorfGetUp3Anim);
Animation_MorphToPlayOnce(&this->skelAnime, &gDorfGetUp3Anim, 0.0f);
this->unk_1C2 = 2;
SkelAnime_Update(&this->skelAnime);
sBossGanonCape->attachShouldersTimer = 18.0f;
Audio_PlayActorSound2(&this->actor, NA_SE_EV_GANON_MANTLE);
Audio_PlayActorSound2(&this->actor, NA_SE_EN_GANON_RESTORE);
this->timers[2] = 130;
}
} else {
if (Animation_OnFrame(&this->skelAnime, 7.0f)) {
for (i = 0; i < 100; i++) {
sp50.x = Rand_CenteredFloat(25.0f);
sp50.y = Rand_CenteredFloat(25.0f);
sp50.z = Rand_CenteredFloat(25.0f);
BossGanonEff_SpawnSparkle(globalCtx, &this->unk_1FC, &sp50, &sZeroVec, Rand_ZeroFloat(200.0f) + 500.0f,
0x14);
}
Audio_PlayActorSound2(&this->actor, NA_SE_PL_WALK_WATER2);
}
if (Animation_OnFrame(&this->skelAnime, this->fwork[GDF_FWORK_1])) {
BossGanon_SetupWait(this, globalCtx);
}
}
this->actor.velocity.y = Math_SinS(this->unk_1A2 * 1500) * 2.0f;
this->actor.world.pos.y += this->actor.velocity.y;
}
void BossGanon_SetupVulnerable(BossGanon* this, GlobalContext* globalCtx) {
s16 i;
if (this->actionFunc != BossGanon_Vulnerable) {
BossGanon_SetAnimationObject(this, globalCtx, OBJECT_GANON_ANIME1);
this->fwork[GDF_FWORK_1] = Animation_GetLastFrame(&gDorfLightArrowHitAnim);
Animation_MorphToPlayOnce(&this->skelAnime, &gDorfLightArrowHitAnim, 0.0f);
sBossGanonCape->attachRightArmTimer = sBossGanonCape->attachLeftArmTimer = 0;
this->actionFunc = BossGanon_Vulnerable;
this->actor.velocity.x = 0.0f;
this->actor.velocity.y = 0.0f;
this->actor.velocity.z = 0.0f;
this->unk_1C2 = 0;
sBossGanonCape->backPush = -4.0f;
sBossGanonCape->backSwayMagnitude = 0.75f;
sBossGanonCape->sideSwayMagnitude = -3.0f;
sBossGanonCape->minDist = 20.0f;
for (i = 0; i < 10; i++) {
Actor_SpawnAsChild(&globalCtx->actorCtx, &this->actor, globalCtx, ACTOR_BOSS_GANON, this->unk_1FC.x,
this->unk_1FC.y, this->unk_1FC.z, Rand_CenteredFloat(0x8000),
(s16)Rand_CenteredFloat(0x8000) + this->actor.yawTowardsPlayer, 0, 0xC8 + i);
}
this->unk_1A4 = 0;
this->unk_288 = 0.0f;
this->unk_290 = 0.0f;
this->unk_284 = 0.0f;
this->unk_28C = 0.0f;
}
}
void BossGanon_Vulnerable(BossGanon* this, GlobalContext* globalCtx) {
s16 i;
Vec3f sp40;
if (this->timers[3] == 0) {
this->actor.flags |= ACTOR_FLAG_0;
}
SkelAnime_Update(&this->skelAnime);
this->envLightMode = 1;
this->actor.world.pos.y += this->actor.velocity.y;
if (this->unk_1A4 < 0x28) {
Math_ApproachF(&this->unk_508, 4.0f, 0.1f, 0.1f);
}
if ((this->unk_1A4 >= 0x28) && (this->unk_1A4 < 0x37)) {
Math_ApproachF(&this->unk_508, 0.0f, 1.0f, 0.5f);
}
if (this->unk_1A4 >= 0x37) {
Math_ApproachF(&this->unk_508, 5.0f, 0.1f, 0.15f);
this->shockGlow = true;
}
switch (this->unk_1C2) {
case 0:
if (Animation_OnFrame(&this->skelAnime, this->fwork[GDF_FWORK_1])) {
this->unk_1C2 = 1;
this->fwork[GDF_FWORK_1] = Animation_GetLastFrame(&gDorfLightEnergyHitAnim);
Animation_MorphToPlayOnce(&this->skelAnime, &gDorfLightEnergyHitAnim, 0.0f);
}
break;
case 1:
if (Animation_OnFrame(&this->skelAnime, this->fwork[GDF_FWORK_1])) {
this->unk_1C2 = 2;
this->fwork[GDF_FWORK_1] = Animation_GetLastFrame(&gDorfKneelVulnerableAnim);
Animation_MorphToPlayOnce(&this->skelAnime, &gDorfKneelVulnerableAnim, 0.0f);
}
break;
case 2:
sBossGanonCape->minDist = 0.0f;
this->actor.velocity.y = this->actor.velocity.y - 0.5f;
if (this->actor.world.pos.y < 40.0f) {
this->actor.world.pos.y = 40.0f;
this->actor.velocity.y = 0.0f;
this->unk_1C2 = 3;
this->fwork[GDF_FWORK_1] = Animation_GetLastFrame(&gDorfLandAnim);
Animation_MorphToPlayOnce(&this->skelAnime, &gDorfLandAnim, 0.0f);
this->timers[0] = 70;
this->actor.flags |= ACTOR_FLAG_10;
}
break;
case 3:
if (this->timers[0] == 68) {
this->unk_19F = 1;
}
if (Animation_OnFrame(&this->skelAnime, this->fwork[GDF_FWORK_1])) {
this->unk_1C2 = 4;
this->fwork[GDF_FWORK_1] = Animation_GetLastFrame(&gDorfVulnerableAnim);
Animation_MorphToLoop(&this->skelAnime, &gDorfVulnerableAnim, 0.0f);
}
break;
case 4:
if (Animation_OnFrame(&this->skelAnime, 5.0f)) {
Audio_PlayActorSound2(&this->actor, NA_SE_EN_GANON_DOWN);
}
if (this->timers[0] == 0) {
this->unk_1C2 = 5;
this->fwork[GDF_FWORK_1] = Animation_GetLastFrame(&gDorfGetUp1Anim);
Animation_MorphToPlayOnce(&this->skelAnime, &gDorfGetUp1Anim, 0.0f);
this->unk_2D4 = 80;
for (i = 1; i < 15; i++) {
this->unk_4E4[i] = Rand_ZeroFloat(10.0f);
}
this->unk_2E6 = 80;
this->unk_2E8 = 0;
this->actor.flags &= ~ACTOR_FLAG_10;
}
break;
case 5:
BossGanonEff_SpawnShock(globalCtx, 1000.0f, GDF_SHOCK_DORF_YELLOW);
if (Animation_OnFrame(&this->skelAnime, this->fwork[GDF_FWORK_1])) {
this->unk_1C2 = 6;
this->fwork[GDF_FWORK_1] = Animation_GetLastFrame(&gDorfGetUp2Anim);
Animation_MorphToPlayOnce(&this->skelAnime, &gDorfGetUp2Anim, 0.0f);
sBossGanonCape->minDist = 20.0f;
this->unk_19F = 1;
}
break;
case 6:
this->envLightMode = 0;
Math_ApproachF(&this->actor.world.pos.y, 200.0f, 0.1f, 1000.0f);
if (Animation_OnFrame(&this->skelAnime, this->fwork[GDF_FWORK_1])) {
this->unk_1C2 = 7;
}
break;
case 7:
this->envLightMode = 0;
Math_ApproachF(&this->actor.world.pos.y, 150.0f, 0.05f, 30.0f);
this->fwork[GDF_FWORK_1] = Animation_GetLastFrame(&gDorfGetUp3Anim);
Animation_MorphToPlayOnce(&this->skelAnime, &gDorfGetUp3Anim, 0.0f);
this->unk_1C2 = 8;
SkelAnime_Update(&this->skelAnime);
sBossGanonCape->attachShouldersTimer = 18.0f;
Audio_PlayActorSound2(&this->actor, NA_SE_EV_GANON_MANTLE);
Audio_PlayActorSound2(&this->actor, NA_SE_EN_GANON_RESTORE);
break;
case 8:
this->envLightMode = 0;
if (Animation_OnFrame(&this->skelAnime, 7.0f)) {
for (i = 0; i < 100; i++) {
sp40.x = Rand_CenteredFloat(25.0f);
sp40.y = Rand_CenteredFloat(25.0f);
sp40.z = Rand_CenteredFloat(25.0f);
BossGanonEff_SpawnSparkle(globalCtx, &this->unk_1FC, &sp40, &sZeroVec,
Rand_ZeroFloat(200.0f) + 500.0f, 0x14);
}
Audio_PlayActorSound2(&this->actor, NA_SE_PL_WALK_WATER2);
this->timers[3] = 50;
}
if (Animation_OnFrame(&this->skelAnime, this->fwork[GDF_FWORK_1])) {
BossGanon_SetupWait(this, globalCtx);
}
break;
}
}
void BossGanon_SetupDamaged(BossGanon* this, GlobalContext* globalCtx) {
BossGanon_SetAnimationObject(this, globalCtx, OBJECT_GANON_ANIME1);
this->fwork[GDF_FWORK_1] = Animation_GetLastFrame(&gDorfDamageAnim);
Animation_MorphToPlayOnce(&this->skelAnime, &gDorfDamageAnim, 0.0f);
this->actionFunc = BossGanon_Damaged;
}
void BossGanon_Damaged(BossGanon* this, GlobalContext* globalCtx) {
this->actor.flags |= ACTOR_FLAG_0;
SkelAnime_Update(&this->skelAnime);
if (this->unk_1A4 >= 0x37) {
Math_ApproachF(&this->unk_508, 5.0f, 0.1f, 0.15f);
this->shockGlow = true;
}
if (Animation_OnFrame(&this->skelAnime, this->fwork[GDF_FWORK_1])) {
this->actionFunc = BossGanon_Vulnerable;
this->unk_1C2 = 4;
this->fwork[GDF_FWORK_1] = Animation_GetLastFrame(&gDorfVulnerableAnim);
Animation_MorphToLoop(&this->skelAnime, &gDorfVulnerableAnim, 0.0f);
}
}
void BossGanon_UpdateDamage(BossGanon* this, GlobalContext* globalCtx) {
s16 i;
s16 j;
ColliderInfo* acHitInfo;
if (this->collider.base.acFlags & 2) {
this->unk_2D4 = 2;
this->collider.base.acFlags &= ~2;
acHitInfo = this->collider.info.acHitInfo;
if ((this->actionFunc == BossGanon_HitByLightBall) || (this->actionFunc == BossGanon_ChargeBigMagic)) {
if (acHitInfo->toucher.dmgFlags & 0x2000) {
BossGanon_SetupVulnerable(this, globalCtx);
this->timers[2] = 0;
Audio_PlayActorSound2(&this->actor, NA_SE_EN_GANON_DAMAGE1);
this->unk_1A6 = 15;
}
} else if ((this->actionFunc == BossGanon_Vulnerable) && (this->unk_1C2 >= 3)) {
if (!(acHitInfo->toucher.dmgFlags & 0x80)) {
u8 hitWithSword = false;
u8 damage;
Vec3f sp50;
u32 flags;
for (i = 0; i < 30; i++) {
sp50.x = Rand_CenteredFloat(20.0f);
sp50.y = Rand_CenteredFloat(20.0f);
sp50.z = Rand_CenteredFloat(20.0f);
BossGanonEff_SpawnSparkle(globalCtx, &this->unk_1FC, &sp50, &sZeroVec,
Rand_ZeroFloat(200.0f) + 500.0f, 0x1E);
}
damage = flags = CollisionCheck_GetSwordDamage(acHitInfo->toucher.dmgFlags);
if (flags == 0) {
damage = 2;
} else {
hitWithSword = true;
}
if (((s8)this->actor.colChkInfo.health >= 3) || hitWithSword) {
this->actor.colChkInfo.health -= damage;
}
for (i = 0; i < ARRAY_COUNT(sBossGanonCape->strands); i++) {
for (j = 1; j < 12; j++) {
sBossGanonCape->strands[i].velocities[j].x = Rand_CenteredFloat(15.0f);
sBossGanonCape->strands[i].velocities[j].z = Rand_CenteredFloat(15.0f);
}
}
if ((s8)this->actor.colChkInfo.health <= 0) {
BossGanon_SetupDeathCutscene(this, globalCtx);
Audio_PlayActorSound2(&this->actor, NA_SE_EN_GANON_DEAD);
Audio_PlayActorSound2(&this->actor, NA_SE_EN_GANON_DD_THUNDER);
func_80078914(&sZeroVec, NA_SE_EN_LAST_DAMAGE);
Audio_QueueSeqCmd(0x100100FF);
this->screenFlashTimer = 4;
} else {
Audio_PlayActorSound2(&this->actor, NA_SE_EN_GANON_DAMAGE2);
Audio_PlayActorSound2(&this->actor, NA_SE_EN_GANON_CUTBODY);
BossGanon_SetupDamaged(this, globalCtx);
this->unk_1A6 = 15;
sBossGanonCape->tearTimer = 1;
}
}
} else if (acHitInfo->toucher.dmgFlags & 0x1F8A4) {
Audio_PlayActorSound2(&this->actor, 0);
for (i = 0; i < ARRAY_COUNT(sBossGanonCape->strands); i++) {
for (j = 1; j < 12; j++) {
sBossGanonCape->strands[i].velocities[j].x = Rand_CenteredFloat(15.0f);
sBossGanonCape->strands[i].velocities[j].z = Rand_CenteredFloat(15.0f);
}
}
}
}
}
static f32 D_808E4D44[] = {
1.0f, 3.0f, 0.0f, 7.0f, 13.0f, 4.0f, 6.0f, 11.0f, 5.0f, 2.0f, 8.0f, 14.0f, 10.0f, 12.0f, 9.0f,
};
void BossGanon_Update(Actor* thisx, GlobalContext* globalCtx2) {
BossGanon* this = (BossGanon*)thisx;
GlobalContext* globalCtx = globalCtx2;
f32 legRotX;
f32 legRotY;
f32 legRotZ;
Player* player = GET_PLAYER(globalCtx);
s16 i;
f32 sin;
f32 cos;
Vec3f shardPos;
Vec3f shardVel;
Vec3f spE8;
s16 i2;
s16 j;
Vec3f spD8;
Vec3f platformCheckPos;
Actor* explosive;
Vec3f spBC;
Vec3f spB0;
Vec3f platCheckPosBomb;
Actor* prop;
BgGanonOtyuka* platform;
f32 targetLensFlareScale;
f32 xOffset;
f32 zOffset;
if ((this->actionFunc != BossGanon_IntroCutscene) && (this->actionFunc != BossGanon_DeathAndTowerCutscene)) {
BossGanon_SetAnimationObject(this, globalCtx, OBJECT_GANON_ANIME1);
} else {
gSegments[6] = VIRTUAL_TO_PHYSICAL(globalCtx->objectCtx.status[this->animBankIndex].segment);
}
if (this->windowShatterState != GDF_WINDOW_SHATTER_OFF) {
BossGanon_ShatterWindows(this->windowShatterState);
shardVel.y = 0.0f;
for (i = 0; i < 10; i++) {
shardPos.y = Rand_ZeroFloat(240.0f) + 20.0f;
if (Rand_ZeroOne() < 0.5f) {
shardPos.x = 463;
shardPos.z = Rand_ZeroFloat(463.0f);
shardVel.x = Rand_ZeroFloat(2.0f);
shardVel.z = Rand_ZeroFloat(1.0f);
} else {
shardPos.z = 463;
shardPos.x = Rand_ZeroFloat(463.0f);
shardVel.z = Rand_ZeroFloat(2.0f);
shardVel.x = Rand_ZeroFloat(1.0f);
}
BossGanonEff_SpawnWindowShard(globalCtx, &shardPos, &shardVel, Rand_ZeroFloat(0.075f) + 0.08f);
}
}
this->collider.base.colType = 3;
sBossGanonCape->gravity = -3.0f;
this->shockGlow = false;
this->actor.flags &= ~ACTOR_FLAG_0;
this->unk_1A2++;
this->unk_1A4++;
// block players attack if hes shooting something
if ((this->actionFunc == BossGanon_Wait) || (this->actionFunc == BossGanon_Block)) {
if (player->unk_A73 != 0) {
BossGanon_SetupBlock(this, globalCtx);
}
}
this->actionFunc(this, globalCtx);
for (i = 0; i < ARRAY_COUNT(this->timers); i++) {
if (this->timers[i] != 0) {
this->timers[i]--;
}
}
if (this->unk_1A6 != 0) {
this->unk_1A6--;
}
if (this->unk_2D4 != 0) {
this->unk_2D4--;
}
if (this->unk_2E8 != 0) {
this->unk_2E8--;
}
if (this->unk_2E6 != 0) {
this->unk_2E6--;
}
if (this->unk_19C != 0) {
this->unk_19C--;
}
if (this->csState == 0) {
BossGanon_UpdateDamage(this, globalCtx);
BossGanon_SetColliderPos(&this->unk_1FC, &this->collider);
CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->collider.base);
if (this->unk_2D4 == 0) {
CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, &this->collider.base);
if ((this->actionFunc != BossGanon_HitByLightBall) && (this->actionFunc != BossGanon_Vulnerable) &&
(this->actionFunc != BossGanon_Damaged)) {
CollisionCheck_SetAT(globalCtx, &globalCtx->colChkCtx, &this->collider.base);
}
}
}
if (this->legSwayEnabled) {
sin = Math_SinS(-this->actor.shape.rot.y);
cos = Math_CosS(-this->actor.shape.rot.y);
legRotX = ((this->actor.velocity.z * sin) + (cos * this->actor.velocity.x)) * 300.0f;
legRotY = ((-sin * this->actor.velocity.x) + (cos * this->actor.velocity.z)) * 300.0f;
legRotZ = (Math_SinS(this->unk_1A2 * 2268) * -500.0f) - 500.0f;
} else {
legRotY = legRotX = legRotZ = 0.0f;
}
this->legSwayEnabled = false;
Math_SmoothStepToF(&this->legRot.x, legRotX, 1.0f, 600.0f, 0.0f);
Math_SmoothStepToF(&this->legRot.y, legRotY, 1.0f, 600.0f, 0.0f);
Math_SmoothStepToF(&this->legRot.z, legRotZ, 1.0f, 100.0f, 0.0f);
if (this->timers[2] == 1) {
Audio_PlayActorSound2(&this->actor, NA_SE_EN_GANON_LAUGH);
}
if (this->timers[2] == 100) {
Audio_PlayActorSound2(&this->actor, NA_SE_EN_FANTOM_ST_LAUGH);
this->timers[2] = 0;
}
if ((this->unk_2E6 != 0) || (this->unk_2E8 != 0)) {
for (i = 1; i < ARRAY_COUNT(this->unk_49C); i++) {
if (this->unk_4E4[i] != 0) {
this->unk_4E4[i]--;
Math_ApproachF(&this->unk_49C[i], this->unk_508, 1.0f, 2.0f);
} else {
Math_ApproachZeroF(&this->unk_49C[i], 1.0f, 0.2f);
}
}
// player hit, spawn shock and play sound
if (this->unk_2E8 != 0) {
func_80078914(&player->actor.projectedPos, NA_SE_PL_SPARK - SFX_FLAG);
BossGanonEff_SpawnShock(globalCtx, 700.0f, GDF_SHOCK_PLAYER_YELLOW);
}
}
if (this->unk_19F != 0) {
this->unk_19F = 0;
spE8 = this->actor.world.pos;
spE8.y = 0.0f;
BossGanonEff_SpawnDustDark(globalCtx, &spE8, 0.2, 0.7f);
BossGanonEff_SpawnDustDark(globalCtx, &spE8, 0.3f, 0.8f);
}
if (this->unk_26C != 0) {
this->unk_26C--;
if (this->unk_26C == 0) {
BossGanonEff_SpawnLightning(globalCtx, 1.0f, 0.0f, 0.0f);
}
BossGanonEff_SpawnLightning(globalCtx, 1.0f, D_808E4D44[this->unk_26C] * (M_PI / 5) + this->unk_270,
Rand_CenteredFloat(M_PI / 5) + (M_PI / 2));
}
// see if light ball hit and should knock platform down?
if ((this->unk_19C != 0) && (this->unk_19E < 4)) {
if ((this->unk_19A == 0) && (this->unk_19C == 20)) {
this->unk_19A = 1;
platformCheckPos.x = -180.0f;
platformCheckPos.y = 0.0f;
for (i2 = 0; i2 < 4; i2++) {
for (j = 0, platformCheckPos.z = -180.0f; j < 4; j++) {
BossGanon_CheckFallingPlatforms(this, globalCtx, &platformCheckPos);
platformCheckPos.z += 120.0f;
}
platformCheckPos.x += 120.0f;
}
} else if (this->unk_19C < 30) {
spD8.x = 0.0f;
spD8.y = 0.0f;
spD8.z = 15.0f * (30.0f - this->unk_19C);
Matrix_RotateY(Rand_ZeroFloat(6.2831855f), MTXMODE_NEW);
Matrix_MultVec3f(&spD8, &platformCheckPos);
this->unk_19E += BossGanon_CheckFallingPlatforms(this, globalCtx, &platformCheckPos);
}
}
// see if a bomb exploded near a group of platforms and if they should fall
explosive = globalCtx->actorCtx.actorLists[ACTORCAT_EXPLOSIVE].head;
while (explosive != NULL) {
if (explosive->params != BOMB_EXPLOSION) {
explosive = explosive->next;
} else {
for (i = 0; i < 8; i++) {
spBC.x = 0.0f;
spBC.y = 0.0f;
spBC.z = 60.0f;
Matrix_RotateY(i * (M_PI / 4), MTXMODE_NEW);
Matrix_MultVec3f(&spBC, &spB0);
platCheckPosBomb.x = explosive->world.pos.x + spB0.x;
platCheckPosBomb.y = explosive->world.pos.y;
platCheckPosBomb.z = explosive->world.pos.z + spB0.z;
BossGanon_CheckFallingPlatforms(this, globalCtx, &platCheckPosBomb);
}
explosive = explosive->next;
}
}
BossGanon_UpdateEffects(globalCtx);
prop = globalCtx->actorCtx.actorLists[ACTORCAT_PROP].head;
// if a platform is lit up, change the room lighting
while (prop != NULL) {
if (prop->id != ACTOR_BG_GANON_OTYUKA) {
prop = prop->next;
} else {
platform = (BgGanonOtyuka*)prop;
if (platform->flashState != 0) {
this->envLightMode = 1;
break;
}
prop = prop->next;
}
}
globalCtx->envCtx.unk_BF = 0;
globalCtx->envCtx.unk_BE = 0;
globalCtx->envCtx.unk_DC = 2;
switch (this->envLightMode) {
case -1:
break;
case 0:
Math_ApproachF(&globalCtx->envCtx.unk_D8, 0.0f, 1.0f, 0.02f);
break;
case 1:
globalCtx->envCtx.unk_BD = 1;
Math_ApproachF(&globalCtx->envCtx.unk_D8, 1.0f, 1.0f, 0.1f);
break;
case 2:
globalCtx->envCtx.unk_BD = 1;
Math_ApproachF(&globalCtx->envCtx.unk_D8, 1.0f, 1.0f, 0.02f);
break;
case 3:
globalCtx->envCtx.unk_BD = 3;
globalCtx->envCtx.unk_D8 = 1.0f;
break;
case 35:
globalCtx->envCtx.unk_BD = 0;
globalCtx->envCtx.unk_D8 = 1.0f;
break;
case 4:
globalCtx->envCtx.unk_BD = 4;
globalCtx->envCtx.unk_D8 = 1.0f;
break;
case 5:
globalCtx->envCtx.unk_BE = 5;
globalCtx->envCtx.unk_BD = 3;
Math_ApproachZeroF(&globalCtx->envCtx.unk_D8, 1.0f, 0.075f);
break;
case 6:
globalCtx->envCtx.unk_BE = 5;
globalCtx->envCtx.unk_D8 = 0.0f;
break;
case 65:
globalCtx->envCtx.unk_BE = 3;
globalCtx->envCtx.unk_BD = 6;
Math_ApproachZeroF(&globalCtx->envCtx.unk_D8, 1.0f, 0.05f);
break;
case 7:
globalCtx->envCtx.unk_BE = 7;
globalCtx->envCtx.unk_D8 = 0.0f;
break;
case 75:
globalCtx->envCtx.unk_BE = 4;
globalCtx->envCtx.unk_BD = 8;
Math_ApproachZeroF(&globalCtx->envCtx.unk_D8, 1.0f, 0.05f);
break;
case 8:
globalCtx->envCtx.unk_BE = 3;
globalCtx->envCtx.unk_BD = 9;
Math_ApproachF(&globalCtx->envCtx.unk_D8, 1.0f, 1.0f, 0.05f);
break;
case 9:
globalCtx->envCtx.unk_BE = 3;
globalCtx->envCtx.unk_BD = 0xA;
Math_ApproachZeroF(&globalCtx->envCtx.unk_D8, 1.0f, 0.05f);
break;
case 10:
globalCtx->envCtx.unk_BE = 3;
globalCtx->envCtx.unk_BD = 0xB;
Math_ApproachF(&globalCtx->envCtx.unk_D8, 1.0f, 1.0f, 0.05f);
this->unk_1A4 = 0;
break;
case 11:
globalCtx->envCtx.unk_BE = 0xC;
globalCtx->envCtx.unk_BD = 0xB;
Math_ApproachF(&globalCtx->envCtx.unk_D8, (Math_CosS(this->unk_1A4 * 0x1800) * 0.5f) + 0.5f, 1.0f, 1.0f);
break;
case 12:
globalCtx->envCtx.unk_BE = 0xC;
globalCtx->envCtx.unk_BD = 3;
Math_ApproachF(&globalCtx->envCtx.unk_D8, 1.0f, 1.0f, 0.05f);
break;
case 13:
globalCtx->envCtx.unk_BD = 0xD;
Math_ApproachF(&globalCtx->envCtx.unk_D8, 1.0f, 1.0f, 0.025f);
break;
case 14:
globalCtx->envCtx.unk_BD = 0xE;
globalCtx->envCtx.unk_D8 = 1.0f;
break;
case 15:
globalCtx->envCtx.unk_BE = 0xE;
globalCtx->envCtx.unk_BD = 0xF;
Math_ApproachF(&globalCtx->envCtx.unk_D8, 1.0f, 1.0f, 0.01f);
break;
case 16:
globalCtx->envCtx.unk_BE = 0x10;
globalCtx->envCtx.unk_BD = 0xF;
Math_ApproachZeroF(&globalCtx->envCtx.unk_D8, 1.0f, 0.05f);
break;
case 20:
globalCtx->envCtx.unk_BE = 2;
globalCtx->envCtx.unk_BD = 1;
break;
default:
break;
}
this->envLightMode = 0;
if (this->whiteFillAlpha != 0) {
globalCtx->envCtx.screenFillColor[3] = (s8)(u8)this->whiteFillAlpha;
globalCtx->envCtx.screenFillColor[0] = globalCtx->envCtx.screenFillColor[1] =
globalCtx->envCtx.screenFillColor[2] = 255;
globalCtx->envCtx.fillScreen = true;
} else if (this->screenFlashTimer != 0) {
globalCtx->envCtx.fillScreen = true;
globalCtx->envCtx.screenFillColor[0] = globalCtx->envCtx.screenFillColor[1] =
globalCtx->envCtx.screenFillColor[2] = 255;
globalCtx->envCtx.screenFillColor[3] = ((this->screenFlashTimer % 2) != 0) ? 100 : 0;
this->screenFlashTimer--;
} else {
globalCtx->envCtx.fillScreen = globalCtx->envCtx.screenFillColor[3] = 0;
}
if (this->lensFlareTimer != 0) {
this->lensFlareTimer--;
if (this->lensFlareMode == 1) {
targetLensFlareScale = 40.0f;
} else if (this->lensFlareMode == 4) {
targetLensFlareScale = 25.0f;
} else {
targetLensFlareScale = 10.0f;
}
Math_ApproachF(&this->lensFlareScale, targetLensFlareScale, 0.3f, 10.0f);
} else {
Math_ApproachZeroF(&this->lensFlareScale, 1.0f, 5.0f);
if (this->lensFlareScale == 0.0f) {
this->lensFlareMode = 0;
}
}
if (this->lensFlareMode != 0) {
gCustomLensFlareOn = true;
if (this->lensFlareMode == 1) {
gCustomLensFlarePos = this->actor.world.pos;
}
gLensFlareScale = this->lensFlareScale;
gLensFlareColorIntensity = 10.0f;
gLensFlareScreenFillAlpha = 0;
} else {
gCustomLensFlareOn = false;
}
if (this->unk_274 != 0) {
i = this->unk_274 - 1;
this->unk_278.x = this->unk_2EC[0].x;
this->unk_278.y = this->unk_2EC[0].y + 50.0f + 30.0f;
this->unk_278.z = this->unk_2EC[0].z;
xOffset = (sinf(i * 1.2566371f) * 600.0f);
zOffset = (cosf(i * 1.2566371f) * 600.0f);
// 5 or 6 light balls that go into the charge. not the same as the ones that he throws
Actor_SpawnAsChild(&globalCtx->actorCtx, &this->actor, globalCtx, ACTOR_BOSS_GANON, this->unk_1FC.x + xOffset,
this->unk_1FC.y, this->unk_1FC.z + zOffset, 0, (s16)(i * 13107.2f) + 0x6000, 0, 0xFA + i);
this->unk_274 = 0;
}
}
s32 BossGanon_OverrideLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3f* pos, Vec3s* rot,
void* thisx) {
BossGanon* this = (BossGanon*)thisx;
switch (limbIndex) {
case 10:
if (this->useOpenHand) {
*dList = gDorfOpenHandDL;
}
break;
case 20:
rot->y += this->legRot.x + this->legRot.z;
rot->z += this->legRot.y;
break;
case 21:
if (this->legRot.y > 0.0f) {
rot->z += this->legRot.y;
}
break;
case 22:
rot->y += this->legRot.x + this->legRot.z;
rot->z += this->legRot.y;
break;
case 23:
rot->y += this->legRot.x - this->legRot.z;
rot->z += this->legRot.y;
break;
case 24:
if (this->legRot.y > 0.0f) {
rot->z += this->legRot.y;
}
break;
case 25:
rot->y += this->legRot.x - this->legRot.z;
rot->z += this->legRot.y;
break;
default:
break;
}
return 0;
}
void BossGanon_PostLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3s* rot, void* thisx) {
static s8 bodyPartLimbMap[] = {
-1, -1, 1, -1, 3, 4, 5, -1, 6, 7, 8, -1, -1, -1, -1, -1, -1, -1, -1, 2, 12, 13, 14, 9, 10, 11, -1, -1, -1, -1,
};
static Vec3f D_808E4DA0 = { -500.0f, 200.0f, -300.0f };
static Vec3f D_808E4DAC = { -500.0f, 200.0f, 300.0f };
static Vec3f D_808E4DB8 = { 0.0f, 0.0f, 0.0f };
static Vec3f D_808E4DC4 = { 0.0f, 0.0f, 0.0f };
static Vec3f D_808E4DD0 = { 0.0f, 0.0f, 0.0f };
static Vec3f D_808E4DDC = { 1300.0f, 0.0f, 0.0f };
static Vec3f D_808E4DE8 = { 600.0f, 420.0f, 100.0f };
s8 bodyPart;
BossGanon* this = (BossGanon*)thisx;
bodyPart = bodyPartLimbMap[limbIndex];
if (bodyPart >= 0) {
Matrix_MultVec3f(&D_808E4DB8, &this->unk_2EC[bodyPart]);
}
if (limbIndex == 2) {
Matrix_MultVec3f(&D_808E4DB8, &this->unk_1FC);
} else if (limbIndex == 19) {
Matrix_MultVec3f(&D_808E4DB8, &this->actor.focus.pos);
} else if (limbIndex == 11) {
OPEN_DISPS(globalCtx->state.gfxCtx);
Matrix_MultVec3f(&D_808E4DB8, &this->unk_208);
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_ganon_DL_00BE90));
CLOSE_DISPS(globalCtx->state.gfxCtx);
} else if (limbIndex == 6) {
Matrix_MultVec3f(&D_808E4DC4, &this->unk_238);
} else if (limbIndex == 10) {
Matrix_MultVec3f(&D_808E4DD0, &this->unk_22C);
if (this->unk_25C == 0) {
Matrix_MultVec3f(&D_808E4DDC, &this->unk_260);
}
this->unk_25C = 0;
if (this->triforceType == GDF_TRIFORCE_DORF) {
Matrix_MultVec3f(&D_808E4DE8, &this->triforcePos);
}
} else if (limbIndex == 4) {
Vec3f sp28 = D_808E4DA0;
if (this->unk_198 == 1) {
sp28.x += -300.0f;
sp28.y += -300.0f;
sp28.z += 700.0f;
} else if (this->unk_198 == 2) {
sp28.x += -300.0f;
sp28.z += 700.0f;
}
Matrix_MultVec3f(&sp28, &this->unk_220);
} else if (limbIndex == 8) {
Vec3f sp1C = D_808E4DAC;
if (this->unk_198 == 1) {
sp1C.x += -300.0f;
sp1C.y += -300.0f;
sp1C.z += -700.0f;
} else if (this->unk_198 == 2) {
sp1C.x += -300.0f;
sp1C.y += 100.0f;
sp1C.z += -700.0f;
}
Matrix_MultVec3f(&sp1C, &this->unk_214);
}
}
void BossGanon_InitRand(s32 seedInit0, s32 seedInit1, s32 seedInit2) {
sBossGanonSeed1 = seedInit0;
sBossGanonSeed2 = seedInit1;
sBossGanonSeed3 = seedInit2;
}
f32 BossGanon_RandZeroOne(void) {
// Wichmann-Hill algorithm
f32 randFloat;
sBossGanonSeed1 = (sBossGanonSeed1 * 171) % 30269;
sBossGanonSeed2 = (sBossGanonSeed2 * 172) % 30307;
sBossGanonSeed3 = (sBossGanonSeed3 * 170) % 30323;
randFloat = (sBossGanonSeed1 / 30269.0f) + (sBossGanonSeed2 / 30307.0f) + (sBossGanonSeed3 / 30323.0f);
while (randFloat >= 1.0f) {
randFloat -= 1.0f;
}
return fabsf(randFloat);
}
void BossGanon_DrawShock(BossGanon* this, GlobalContext* globalCtx) {
s32 pad;
GraphicsContext* gfxCtx = globalCtx->state.gfxCtx;
s16 i;
static u32 epoch = 0;
epoch++;
OPEN_DISPS(gfxCtx);
if ((this->unk_2E8 != 0) || (this->unk_2E6 != 0)) {
func_80093D84(globalCtx->state.gfxCtx);
gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 255, 255, 255, 255);
gDPSetEnvColor(POLY_XLU_DISP++, 255, 255, 0, 0);
gSPDisplayList(POLY_XLU_DISP++, gDorfLightBallMaterialDL);
if (this->unk_2E8 != 0) {
Player* player = GET_PLAYER(globalCtx);
for (i = 0; i < ARRAY_COUNT(player->bodyPartsPos); i++) {
FrameInterpolation_RecordOpenChild("Ganondorf Shock 0", epoch + i * 25);
Matrix_Translate(player->bodyPartsPos[i].x, player->bodyPartsPos[i].y, player->bodyPartsPos[i].z,
MTXMODE_NEW);
Matrix_ReplaceRotation(&globalCtx->billboardMtxF);
Matrix_Scale(this->unk_49C[i], this->unk_49C[i], this->unk_49C[i], MTXMODE_APPLY);
Matrix_RotateZ(Rand_CenteredFloat(M_PI), MTXMODE_APPLY);
gSPMatrix(POLY_XLU_DISP++, MATRIX_NEWMTX(gfxCtx),
G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW);
gSPDisplayList(POLY_XLU_DISP++, gDorfSquareDL);
FrameInterpolation_RecordCloseChild();
}
} else {
for (i = 1; i < 15; i++) {
FrameInterpolation_RecordOpenChild("Ganondorf Shock 1", epoch + i * 25);
Matrix_Translate(this->unk_2EC[i].x, this->unk_2EC[i].y, this->unk_2EC[i].z, MTXMODE_NEW);
Matrix_ReplaceRotation(&globalCtx->billboardMtxF);
Matrix_Scale(this->unk_49C[i], this->unk_49C[i], this->unk_49C[i], MTXMODE_APPLY);
if (!this->shockGlow) {
Matrix_RotateZ(Rand_CenteredFloat(M_PI), MTXMODE_APPLY);
}
gSPMatrix(POLY_XLU_DISP++, MATRIX_NEWMTX(gfxCtx),
G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW);
if (this->shockGlow) {
gSPSegment(POLY_XLU_DISP++, 0x08,
Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, 0, 0, 32, 64, 1, 0,
(this->unk_1A2 + i) * -15, 32, 64));
gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 200, 255, 170, 255);
gDPSetEnvColor(POLY_XLU_DISP++, 255, 255, 0, 128);
gSPDisplayList(POLY_XLU_DISP++, gDorfShockGlowDL);
} else {
gSPDisplayList(POLY_XLU_DISP++, gDorfSquareDL);
}
FrameInterpolation_RecordCloseChild();
}
}
}
CLOSE_DISPS(gfxCtx);
}
void BossGanon_DrawHandLightBall(BossGanon* this, GlobalContext* globalCtx) {
s32 pad;
GraphicsContext* gfxCtx = globalCtx->state.gfxCtx;
s32 alpha;
OPEN_DISPS(gfxCtx);
if (this->handLightBallScale > 0.0f) {
func_80093D84(globalCtx->state.gfxCtx);
gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 255, 255, 255, 255);
if ((this->unk_1A2 % 2) != 0) {
gDPSetEnvColor(POLY_XLU_DISP++, 255, 255, 0, 0);
} else {
gDPSetEnvColor(POLY_XLU_DISP++, 100, 255, 0, 0);
}
gSPDisplayList(POLY_XLU_DISP++, gDorfLightBallMaterialDL);
Matrix_Translate(this->unk_260.x, this->unk_260.y, this->unk_260.z, MTXMODE_NEW);
Matrix_ReplaceRotation(&globalCtx->billboardMtxF);
Matrix_Scale(this->handLightBallScale, this->handLightBallScale, this->handLightBallScale, MTXMODE_APPLY);
Matrix_RotateZ(this->unk_258, 1);
gSPMatrix(POLY_XLU_DISP++, MATRIX_NEWMTX(gfxCtx),
G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW);
gSPDisplayList(POLY_XLU_DISP++, gDorfSquareDL);
alpha = ((this->unk_1A2 % 2) != 0) ? 100 : 80;
gDPPipeSync(POLY_XLU_DISP++);
gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 255, 255, 155, alpha);
Matrix_Translate(this->unk_260.x, 0.0f, this->unk_260.z, MTXMODE_NEW);
Matrix_Scale(this->handLightBallScale * 0.75f, 1.0f, this->handLightBallScale * 0.75f, MTXMODE_APPLY);
gSPMatrix(POLY_XLU_DISP++, MATRIX_NEWMTX(globalCtx->state.gfxCtx),
G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW);
gSPDisplayList(POLY_XLU_DISP++, gDorfLightCoreDL);
}
CLOSE_DISPS(gfxCtx);
}
void BossGanon_DrawBigMagicCharge(BossGanon* this, GlobalContext* globalCtx) {
s32 pad;
f32 yRot;
GraphicsContext* gfxCtx = globalCtx->state.gfxCtx;
s16 i;
static u32 epoch = 0;
epoch++;
OPEN_DISPS(gfxCtx);
if (this->unk_284 > 0.0f) {
func_80093D84(globalCtx->state.gfxCtx);
// light flecks
gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 255, 255, 170, (s8)this->unk_290);
gDPSetEnvColor(POLY_XLU_DISP++, 200, 255, 0, 128);
gSPSegment(POLY_XLU_DISP++, 0x08,
Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, this->unk_1A2 * -2, 0, 0x40, 0x40, 1, 0,
this->unk_1A2 * 0xA, 0x40, 0x40));
Matrix_Translate(this->unk_278.x, this->unk_278.y, this->unk_278.z, MTXMODE_NEW);
Matrix_ReplaceRotation(&globalCtx->billboardMtxF);
Matrix_Scale(this->unk_28C, this->unk_28C, this->unk_28C, MTXMODE_APPLY);
gSPMatrix(POLY_XLU_DISP++, MATRIX_NEWMTX(gfxCtx),
G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW);
gSPDisplayList(POLY_XLU_DISP++, gDorfLightFlecksDL);
// background circle texture
Matrix_Translate(this->unk_278.x, this->unk_278.y, this->unk_278.z, MTXMODE_NEW);
Matrix_ReplaceRotation(&globalCtx->billboardMtxF);
Matrix_Scale(this->unk_284, this->unk_284, this->unk_284, MTXMODE_APPLY);
gSPMatrix(POLY_XLU_DISP++, MATRIX_NEWMTX(gfxCtx),
G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW);
gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 255, 0, 100, (s8)this->unk_288);
gSPSegment(
POLY_XLU_DISP++, 0x09,
Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, 0, 0, 0x20, 0x20, 1, 0, this->unk_1A2 * -4, 0x20, 0x20));
gSPDisplayList(POLY_XLU_DISP++, gDorfBigMagicBGCircleDL);
// yellow background dot
gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 150, 170, 0, (s8)this->unk_288);
gSPSegment(POLY_XLU_DISP++, 0x0A,
Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, 0, 0, 0x20, 0x20, 1, this->unk_1A2 * 2,
this->unk_1A2 * -0x14, 0x40, 0x40));
gSPDisplayList(POLY_XLU_DISP++, gDorfDotDL);
// light ball material
gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 255, 255, 255, 255);
gDPSetEnvColor(POLY_XLU_DISP++, 255, 255, 100, 0);
gSPDisplayList(POLY_XLU_DISP++, gDorfLightBallMaterialDL);
// light ball geometry
Matrix_Translate(this->unk_278.x, this->unk_278.y, this->unk_278.z, MTXMODE_NEW);
Matrix_ReplaceRotation(&globalCtx->billboardMtxF);
Matrix_Scale(this->unk_2D0, this->unk_2D0, this->unk_2D0, MTXMODE_APPLY);
Matrix_RotateZ((this->unk_1A2 * 10.0f) / 1000.0f, MTXMODE_APPLY);
gSPMatrix(POLY_XLU_DISP++, MATRIX_NEWMTX(gfxCtx),
G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW);
gSPDisplayList(POLY_XLU_DISP++, gDorfSquareDL);
BossGanon_InitRand(this->unk_1AA + 1, 0x71AC, 0x263A);
Matrix_Translate(this->unk_278.x, this->unk_278.y, this->unk_278.z, MTXMODE_NEW);
Matrix_RotateY((this->unk_1A2 * 10.0f) / 1000.0f, MTXMODE_APPLY);
gDPSetEnvColor(POLY_XLU_DISP++, 200, 255, 0, 0);
yRot = BINANG_TO_RAD(this->actor.yawTowardsPlayer);
for (i = 0; i < this->unk_1AC; i++) {
FrameInterpolation_RecordOpenChild("Ganondorf Big Magic", epoch + i * 25);
f32 xzRot = (BossGanon_RandZeroOne() - 0.5f) * M_PI * 1.5f;
gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 255, 255, 255, (s8)this->unk_294[i]);
Matrix_Push();
Matrix_RotateY(xzRot + yRot, MTXMODE_APPLY);
Matrix_RotateX((BossGanon_RandZeroOne() - 0.5f) * M_PI, MTXMODE_APPLY);
Matrix_RotateZ(xzRot, MTXMODE_APPLY);
Matrix_Translate(0.0f, 0.0f, 50.0f, MTXMODE_APPLY);
Matrix_Scale(4.0f, 4.0f, 1.0f, MTXMODE_APPLY);
gSPMatrix(POLY_XLU_DISP++, MATRIX_NEWMTX(gfxCtx),
G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW);
gSPDisplayList(POLY_XLU_DISP++, gDorfLightRayTriDL);
Matrix_Pop();
FrameInterpolation_RecordCloseChild();
}
}
CLOSE_DISPS(gfxCtx);
}
void BossGanon_DrawTriforce(BossGanon* this, GlobalContext* globalCtx) {
s32 pad;
if (this->fwork[GDF_TRIFORCE_PRIM_A] > 0.0f) {
OPEN_DISPS(globalCtx->state.gfxCtx);
Matrix_Push();
gDPPipeSync(POLY_XLU_DISP++);
gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 255, 255, (u8)this->fwork[GDF_TRIFORCE_PRIM_B],
(s8)this->fwork[GDF_TRIFORCE_PRIM_A]);
gDPSetEnvColor(POLY_XLU_DISP++, 255, (u8)this->fwork[GDF_TRIFORCE_ENV_G], 0, 128);
if (this->triforceType == GDF_TRIFORCE_PLAYER) {
Player* player = GET_PLAYER(globalCtx);
this->triforcePos = player->bodyPartsPos[12];
this->triforcePos.x += -0.6f;
this->triforcePos.y += 3.0f;
this->triforcePos.z += -2.0f;
} else if (this->triforceType == GDF_TRIFORCE_ZELDA) {
this->triforcePos = sBossGanonZelda->unk_31C;
this->triforcePos.y += 1.8f;
this->triforcePos.z += 4.0f;
}
Matrix_Translate(this->triforcePos.x, this->triforcePos.y, this->triforcePos.z, MTXMODE_NEW);
if (this->triforceType == GDF_TRIFORCE_PLAYER) {
Matrix_RotateX(-1.4f, MTXMODE_APPLY);
Matrix_RotateZ(4.0f, MTXMODE_APPLY);
} else if (this->triforceType == GDF_TRIFORCE_ZELDA) {
Matrix_RotateY(1.5f, 1);
Matrix_RotateX(1.1f, 1);
Matrix_RotateZ(-0.99999994f, MTXMODE_APPLY);
} else {
Matrix_ReplaceRotation(&globalCtx->billboardMtxF);
}
Matrix_Scale(this->fwork[GDF_TRIFORCE_SCALE], this->fwork[GDF_TRIFORCE_SCALE], 1.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(gDorfTriforceDL));
Matrix_Pop();
CLOSE_DISPS(globalCtx->state.gfxCtx);
}
}
void BossGanon_DrawDarkVortex(BossGanon* this, GlobalContext* globalCtx) {
s32 pad;
if (this->fwork[GDF_VORTEX_ALPHA] > 0.0f) {
OPEN_DISPS(globalCtx->state.gfxCtx);
Matrix_Push();
gDPPipeSync(POLY_XLU_DISP++);
gSPSegment(POLY_XLU_DISP++, 0x08,
Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, this->unk_1A2 * -8, 0, 0x20, 0x40, 1,
this->unk_1A2 * -4, this->unk_1A2 * -8, 0x20, 0x20));
gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 100, 0, 200, (s8)this->fwork[GDF_VORTEX_ALPHA]);
gDPSetEnvColor(POLY_XLU_DISP++, 130, 0, 0, 128);
if (this->csState != 21) {
Matrix_Translate(0.0f, 105.0f, -400.0f, MTXMODE_NEW);
Matrix_RotateX(M_PI / 2, MTXMODE_APPLY);
} else {
Matrix_Translate(-50.0f, 50.0f, -150.0f, MTXMODE_NEW);
Matrix_RotateY(M_PI / 10, MTXMODE_APPLY);
Matrix_RotateX(M_PI / 2, MTXMODE_APPLY);
}
Matrix_Scale(this->fwork[GDF_VORTEX_SCALE], this->fwork[GDF_VORTEX_SCALE], this->fwork[GDF_VORTEX_SCALE],
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(gDorfVortexDL));
Matrix_Pop();
CLOSE_DISPS(globalCtx->state.gfxCtx);
}
}
void func_808E0254(BossGanon* this, u8* tex, f32 arg2) {
static s16 D_808E4DF4[] = { 1, 2, 3, 3, 2, 1 };
static s16 D_808E4E00[] = { 2, 3, 4, 4, 4, 3, 2 };
static s16 D_808E4E10[] = { 2, 3, 4, 4, 4, 4, 3, 2 };
static s16 D_808E4E20[] = { 2, 4, 5, 5, 6, 6, 6, 6, 5, 5, 4, 2 };
static s16 D_808E4E38[] = { 1, -1, 1, 1, 3, 4, 1, 6, 7, 2, 9, 10, 2, 12, 13 };
static u8 D_808E4E58[] = { 3, 2, 2, 1, 3, 3, 1, 3, 3, 1, 0, 3, 1, 0, 3 };
s16 baseX;
s16 index;
s16 i;
s16 baseY;
s16 x;
s16 addY;
f32 lerpX;
s16 y;
f32 lerpY;
f32 lerpZ;
Vec3f sp68;
Vec3f sp5C;
for (i = 0; i < 15; i++) {
if (arg2 == 0.0f || (y = D_808E4E38[i]) >= 0) {
if (arg2 > 0.0f) {
lerpX = this->unk_2EC[i].x + (this->unk_2EC[y].x - this->unk_2EC[i].x) * arg2;
lerpY = this->unk_2EC[i].y + (this->unk_2EC[y].y - this->unk_2EC[i].y) * arg2;
lerpZ = this->unk_2EC[i].z + (this->unk_2EC[y].z - this->unk_2EC[i].z) * arg2;
sp68.x = lerpX - this->actor.world.pos.x;
sp68.y = lerpY - this->actor.world.pos.y + 76 + 30 + 30;
sp68.z = lerpZ - this->actor.world.pos.z;
} else {
sp68.x = this->unk_2EC[i].x - this->actor.world.pos.x;
sp68.y = this->unk_2EC[i].y - this->actor.world.pos.y + 76 + 30 + 30;
sp68.z = this->unk_2EC[i].z - this->actor.world.pos.z;
}
Matrix_MultVec3f(&sp68, &sp5C);
sp5C.x *= 0.4f;
sp5C.y *= 0.4f;
baseX = (s16)(sp5C.x + 32.0f);
baseY = (s16)sp5C.y * 64;
if (D_808E4E58[i] == 2) {
for (y = 0, addY = -0x180; y < 12; y++, addY += 0x40) {
for (x = -D_808E4E20[y]; x < D_808E4E20[y]; x++) {
index = baseX + x + baseY + addY;
if ((index >= 0) && (index < 0x1000)) {
tex[index] = 255;
}
}
}
} else if (D_808E4E58[i] == 1) {
for (y = 0, addY = -0x100; y < 8; y++, addY += 0x40) {
for (x = -D_808E4E10[y]; x < D_808E4E10[y]; x++) {
index = baseX + x + baseY + addY;
if ((index >= 0) && (index < 0x1000)) {
tex[index] = 255;
}
}
}
} else if (D_808E4E58[i] == 0) {
for (y = 0, addY = -0xC0; y < 7; y++, addY += 0x40) {
for (x = -D_808E4E00[y]; x < D_808E4E00[y] - 1; x++) {
index = baseX + x + baseY + addY;
if ((index >= 0) && (index < 0x1000)) {
tex[index] = 255;
}
}
}
} else {
for (y = 0, addY = -0x80; y < 6; y++, addY += 0x40) {
for (x = -D_808E4DF4[y]; x < D_808E4DF4[y] - 1; x++) {
index = baseX + x + baseY + addY;
if ((index >= 0) && (index < 0x1000)) {
tex[index] = 255;
}
}
}
}
}
}
}
void BossGanon_GenShadowTexture(u8* tex, BossGanon* this, GlobalContext* globalCtx) {
s16 addY;
s16 baseX;
s16 baseY;
s16 i;
s16 j;
s16 y;
s16 x;
s16 index;
Vec3f sp7C;
Vec3f sp70;
s32* ptr = (s32*)tex;
for (i = 0; i < 64 * 64 / 4; i++, ptr++) {
*ptr = 0;
}
Matrix_RotateX(1.0f, MTXMODE_NEW);
for (i = 0; i <= 5; i++) {
func_808E0254(this, tex, i / 5.0f);
}
for (i = 0; i < 12; i++) {
for (j = 0; j < 12; j++) {
sp7C.x = sBossGanonCape->strands[i].joints[j].x - this->actor.world.pos.x;
sp7C.y = sBossGanonCape->strands[i].joints[j].y - this->actor.world.pos.y + 76.0f + 100.0f + 30.0f;
sp7C.z = sBossGanonCape->strands[i].joints[j].z - this->actor.world.pos.z;
Matrix_MultVec3f(&sp7C, &sp70);
sp70.x = sp70.x * 0.28f;
sp70.y = sp70.y * 0.28f;
baseX = (s32)(sp70.x + 32.0f);
baseY = (s16)sp70.y * 0x40;
if (!sBossGanonCape->strands[i].torn[j]) {
for (y = -1, addY = -0x40; y <= 1; y++, addY += 0x40) {
for (x = -3; x <= 3; x++) {
index = baseX + x + baseY + addY;
if (0 <= index && index < 0x1000) {
tex[index] = 255;
}
}
}
} else {
for (y = -1, addY = -0x40; y <= 1; y++, addY += 0x40) {
for (x = -1; x <= 1; x++) {
index = baseX + x + baseY + addY;
if (0 <= index && index < 0x1000) {
tex[index] = 255;
}
}
}
}
}
}
}
void BossGanon_DrawShadowTexture(void* tex, BossGanon* this, GlobalContext* globalCtx) {
s32 pad;
f32 zOffset;
GraphicsContext* gfxCtx = globalCtx->state.gfxCtx;
OPEN_DISPS(gfxCtx);
func_80093D18(globalCtx->state.gfxCtx);
gDPSetPrimColor(POLY_OPA_DISP++, 0, 0, 0, 0, 0, 50);
gDPSetEnvColor(POLY_OPA_DISP++, 0, 0, 0, 0);
if (this->csState < 100) {
zOffset = (((((this->actor.world.pos.y - 10) + 70.0f) * -5.0f) / 10.0f) + 10.0f);
Matrix_Translate(this->actor.world.pos.x, 0.0f, this->actor.world.pos.z + zOffset, MTXMODE_NEW);
} else {
Matrix_Translate(this->actor.world.pos.x, 4102.0f, this->actor.world.pos.z - 20.0f, MTXMODE_NEW);
}
Matrix_Scale(0.95000005f, 1.0f, 0.95000005f, MTXMODE_APPLY);
gSPMatrix(POLY_OPA_DISP++, MATRIX_NEWMTX(globalCtx->state.gfxCtx),
G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW);
gSPDisplayList(POLY_OPA_DISP++, gDorfShadowSetupDL);
gDPLoadTextureBlock(POLY_OPA_DISP++, tex, G_IM_FMT_I, G_IM_SIZ_8b, 64, 64, 0, G_TX_NOMIRROR | G_TX_CLAMP,
G_TX_NOMIRROR | G_TX_CLAMP, 6, 6, G_TX_NOLOD, G_TX_NOLOD);
gSPDisplayList(POLY_OPA_DISP++, gDorfShadowModelDL);
CLOSE_DISPS(gfxCtx);
}
void BossGanon_Draw(Actor* thisx, GlobalContext* globalCtx) {
s32 i;
BossGanon* this = (BossGanon*)thisx;
void* shadowTex;
shadowTex = Graph_Alloc(globalCtx->state.gfxCtx, 64 * 64);
OPEN_DISPS(globalCtx->state.gfxCtx);
func_80093D18(globalCtx->state.gfxCtx);
func_80093D84(globalCtx->state.gfxCtx);
if ((this->unk_1A6 & 2) != 0) {
POLY_OPA_DISP = Gfx_SetFog(POLY_OPA_DISP, 255, 50, 0, 0, 900, 1099);
}
gSPSegment(POLY_XLU_DISP++, 0x08, SEGMENTED_TO_VIRTUAL(gDorfEyeTex));
SkelAnime_DrawFlexOpa(globalCtx, this->skelAnime.skeleton, this->skelAnime.jointTable, this->skelAnime.dListCount,
BossGanon_OverrideLimbDraw, BossGanon_PostLimbDraw, &this->actor);
this->unk_2EC[0].x = this->unk_2EC[1].x;
this->unk_2EC[0].y = this->unk_2EC[1].y + 30.0f;
this->unk_2EC[0].z = this->unk_2EC[1].z;
POLY_OPA_DISP = Gameplay_SetFog(globalCtx, POLY_OPA_DISP);
BossGanon_DrawEffects(globalCtx);
sBossGanonCape->actor.world.pos = this->actor.world.pos;
sBossGanonCape->rightForearmPos = this->unk_214;
sBossGanonCape->leftForearmPos = this->unk_220;
sBossGanonCape->rightShoulderPos = this->unk_22C;
sBossGanonCape->leftShoulderPos = this->unk_238;
BossGanon_DrawShock(this, globalCtx);
BossGanon_DrawHandLightBall(this, globalCtx);
BossGanon_DrawBigMagicCharge(this, globalCtx);
BossGanon_DrawTriforce(this, globalCtx);
BossGanon_DrawDarkVortex(this, globalCtx);
BossGanon_GenShadowTexture(shadowTex, this, globalCtx);
BossGanon_DrawShadowTexture(shadowTex, this, globalCtx);
CLOSE_DISPS(globalCtx->state.gfxCtx);
}
s32 BossGanon_CheckFallingPlatforms(BossGanon* this, GlobalContext* globalCtx, Vec3f* checkPos) {
Actor* prop = globalCtx->actorCtx.actorLists[ACTORCAT_PROP].head;
while (prop != NULL) {
if (((BossGanon*)prop == this) || (prop->id != ACTOR_BG_GANON_OTYUKA)) {
prop = prop->next;
} else {
BgGanonOtyuka* platform = (BgGanonOtyuka*)prop;
f32 xDiff = platform->dyna.actor.world.pos.x - checkPos->x;
f32 yDiff = platform->dyna.actor.world.pos.y - checkPos->y;
f32 zDiff = platform->dyna.actor.world.pos.z - checkPos->z;
if ((fabsf(xDiff) < 60.0f) && (yDiff < 20.0f) && (yDiff > -20.0f) && (fabsf(zDiff) < 60.0f)) {
platform->isFalling = true;
platform->visibleSides = OTYUKA_SIDE_ALL;
return 1;
} else {
prop = prop->next;
}
}
}
return 0;
}
void BossGanon_LightBall_Update(Actor* thisx, GlobalContext* globalCtx2) {
u8 hitWithBottle;
s16 i;
s16 spBA = 0;
Vec3f spAC;
Vec3f spA0;
Vec3f sp94;
BossGanon* this = (BossGanon*)thisx;
GlobalContext* globalCtx = globalCtx2;
f32 xDistFromLink;
f32 yDistFromLink;
f32 zDistFromLink;
f32 minReflectDist;
f32 xDistFromGanondorf;
f32 yDistFromGanondorf;
f32 zDistFromGanondorf;
Player* player = GET_PLAYER(globalCtx);
s32 pad;
BossGanon* ganondorf = (BossGanon*)this->actor.parent;
s32 pad1;
this->unk_1A2++;
ganondorf->envLightMode = 1;
if (this->unk_1A8 != 0) {
if (this->unk_1A8 == 2) {
Math_ApproachZeroF(&this->fwork[GDF_FWORK_1], 1.0f, 10.0f);
Math_ApproachF(&this->actor.scale.x, 30.0f, 0.5f, 100.0f);
} else {
this->actor.shape.rot.y += 0x1000;
ganondorf->lensFlareTimer = 1;
gCustomLensFlarePos = this->actor.world.pos;
Math_ApproachZeroF(&this->fwork[GDF_FWORK_1], 1.0f, 30.0f);
Math_ApproachF(&this->actor.scale.x, 20.0f, 0.5f, 100.0f);
this->fwork[GDF_FWORK_0] += ((M_PI / 2) + Rand_ZeroFloat(M_PI / 4));
}
Actor_SetScale(&this->actor, this->actor.scale.x);
if (this->fwork[GDF_FWORK_1] == 0.0f) {
Actor_Kill(&this->actor);
}
} else {
Audio_PlayActorSound2(&this->actor, NA_SE_EN_GANON_FIRE - SFX_FLAG);
if ((this->unk_1A2 % 2) != 0) {
Actor_SetScale(&this->actor, 6.0f);
} else {
Actor_SetScale(&this->actor, 5.25f);
}
this->actor.shape.rot.z += (s16)(Rand_ZeroOne() * 20000.0f) + 0x4000;
for (i = 0; i < ARRAY_COUNT(this->timers); i++) {
if (this->timers[i] != 0) {
this->timers[i]--;
}
}
xDistFromGanondorf = ganondorf->unk_1FC.x - this->actor.world.pos.x;
yDistFromGanondorf = ganondorf->unk_1FC.y - this->actor.world.pos.y;
zDistFromGanondorf = ganondorf->unk_1FC.z - this->actor.world.pos.z;
xDistFromLink = player->actor.world.pos.x - this->actor.world.pos.x;
yDistFromLink = (player->actor.world.pos.y + 40.0f) - this->actor.world.pos.y;
zDistFromLink = player->actor.world.pos.z - this->actor.world.pos.z;
func_8002D908(&this->actor);
func_8002D7EC(&this->actor);
switch (this->unk_1C2) {
case 0:
if ((player->stateFlags1 & 2) &&
(ABS((s16)(player->actor.shape.rot.y - (s16)(ganondorf->actor.yawTowardsPlayer + 0x8000))) <
0x2000) &&
(sqrtf(SQ(xDistFromLink) + SQ(yDistFromLink) + SQ(zDistFromLink)) <= 25.0f)) {
hitWithBottle = true;
} else {
hitWithBottle = false;
}
if ((this->collider.base.acFlags & 2) || hitWithBottle) {
ColliderInfo* acHitInfo = this->collider.info.acHitInfo;
this->collider.base.acFlags &= ~2;
if ((hitWithBottle == false) && (acHitInfo->toucher.dmgFlags & 0x100000)) {
spBA = 2;
Audio_PlaySoundGeneral(NA_SE_IT_SHIELD_REFLECT_MG, &player->actor.projectedPos, 4, &D_801333E0,
&D_801333E0, &D_801333E8);
func_800AA000(this->actor.xyzDistToPlayerSq, 0xFF, 0x14, 0x96);
} else {
spBA = 1;
this->actor.world.rot.y = Math_Atan2S(zDistFromGanondorf, xDistFromGanondorf);
this->actor.world.rot.x =
Math_Atan2S(sqrtf(SQ(xDistFromGanondorf) + SQ(zDistFromGanondorf)), yDistFromGanondorf);
this->unk_1A4++;
this->timers[1] = 2;
Audio_PlaySoundGeneral(NA_SE_IT_SWORD_REFLECT_MG, &player->actor.projectedPos, 4, &D_801333E0,
&D_801333E0, &D_801333E8);
func_800AA000(this->actor.xyzDistToPlayerSq, 0xB4, 0x14, 0x64);
if (hitWithBottle == false) {
// if ganondorf is 250 units away from link, at least 3 volleys are required
if ((ganondorf->actor.xyzDistToPlayerSq > 62500.0f) && (this->unk_1A4 < 3)) {
this->unk_1C2 = 1;
} else if (Rand_ZeroOne() < 0.7f) {
this->unk_1C2 = 1;
} else {
this->unk_1C2 = 3;
}
// if a spin attack is used
if (player->swordAnimation >= 0x18) {
this->actor.speedXZ = 20.0f;
}
break;
} else {
if (Rand_ZeroOne() < 0.9f) {
this->unk_1C2 = 1;
} else {
this->unk_1C2 = 3;
}
}
}
} else {
if (sqrtf(SQ(xDistFromLink) + SQ(yDistFromLink) + SQ(zDistFromLink)) <= 25.0f) {
spBA = 5;
func_8002F6D4(globalCtx, &this->actor, 3.0f, this->actor.world.rot.y, 0.0f, 0x30);
SoundSource_PlaySfxAtFixedWorldPos(globalCtx, &this->actor.world.pos, 40,
NA_SE_EN_GANON_HIT_THUNDER);
ganondorf->timers[2] = 20;
for (i = 0; i < ARRAY_COUNT(ganondorf->unk_4E4); i++) {
ganondorf->unk_4E4[i] = D_808E4C58[i];
}
ganondorf->unk_2E6 = 0;
ganondorf->unk_2E8 = 60;
ganondorf->unk_508 = 4.0f;
}
}
break;
case 1:
if ((ganondorf->actionFunc == BossGanon_PlayTennis) && (ganondorf->unk_1C2 == 1)) {
minReflectDist = (this->actor.speedXZ >= 19.0f) ? 250.0f : 170.0f;
if (sqrtf(SQ(xDistFromGanondorf) + SQ(yDistFromGanondorf) + SQ(zDistFromGanondorf)) <
minReflectDist) {
ganondorf->startVolley = true;
this->timers[0] = 8;
this->unk_1C2 = 2;
}
}
break;
case 2:
if (this->timers[0] == 1) {
spBA = 1;
this->actor.world.rot.y = Math_Atan2S(zDistFromLink, xDistFromLink);
this->actor.world.rot.x = Math_Atan2S(sqrtf(SQ(xDistFromLink) + SQ(zDistFromLink)), yDistFromLink);
this->timers[1] = 2;
Audio_PlayActorSound2(&this->actor, NA_SE_IT_SWORD_REFLECT_MG);
Audio_PlayActorSound2(&this->actor, NA_SE_EN_GANON_AT_RETURN);
this->unk_1C2 = 0;
break;
}
// fallthrough
case 4:
if (sqrtf(SQ(xDistFromGanondorf) + SQ(yDistFromGanondorf) + SQ(zDistFromGanondorf)) < 30.0f) {
spBA = 3;
SoundSource_PlaySfxAtFixedWorldPos(globalCtx, &this->actor.world.pos, 40, NA_SE_EN_GANON_DAMAGE1);
SoundSource_PlaySfxAtFixedWorldPos(globalCtx, &this->actor.world.pos, 40,
NA_SE_EN_GANON_HIT_THUNDER);
}
break;
case 3:
if (sqrtf(SQ(xDistFromGanondorf) + SQ(yDistFromGanondorf) + SQ(zDistFromGanondorf)) < 100.0f) {
ganondorf->startVolley = true;
this->unk_1C2 = 4;
}
break;
}
Collider_UpdateCylinder(&this->actor, &this->collider);
if (this->timers[1] == 0) {
CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, &this->collider.base);
}
for (i = 0; i < 2; i++) {
spA0.x = spA0.z = 0.0f;
spA0.y = 0.2f;
spAC.x = spAC.y = spAC.z = 0.0f;
sp94.x = Rand_CenteredFloat(30.0f) + this->actor.world.pos.x;
sp94.y = Rand_CenteredFloat(30.0f) + this->actor.world.pos.y;
sp94.z = Rand_CenteredFloat(30.0f) + this->actor.world.pos.z;
BossGanonEff_SpawnSparkle(globalCtx, &sp94, &spAC, &spA0, Rand_ZeroFloat(500.0f) + 700.0f, 0x1E);
}
if (this->actor.world.pos.y < 10.0f) {
Actor_UpdateBgCheckInfo(globalCtx, &this->actor, 0.0f, 20.0f, 20.0f, 4);
}
if ((fabsf(this->actor.world.pos.x) > 465.0f) || (this->actor.world.pos.y > 500.0f) ||
(fabsf(this->actor.world.pos.z) > 465.0f)) {
spBA = 4;
}
if ((spBA != 0) || (this->actor.bgCheckFlags & 1)) {
f32 sp58;
f32 sp54;
f32 phi_f20;
s16 sp4E;
if (spBA == 1) {
sp58 = Rand_ZeroFloat(100.0f) + 300.0f;
sp54 = 10.0f;
phi_f20 = 25.0f;
sp4E = 40;
} else {
sp58 = Rand_ZeroFloat(200.0f) + 500.0f;
sp54 = 15.0f;
phi_f20 = 30.0f;
sp4E = 70;
SoundSource_PlaySfxAtFixedWorldPos(globalCtx, &this->actor.world.pos, 80, NA_SE_EN_GANON_HIT_THUNDER);
}
for (i = 0; i < sp4E; i++) {
if (spBA != 0) {
spAC.x = Rand_CenteredFloat(phi_f20);
spAC.y = Rand_CenteredFloat(phi_f20);
spAC.z = Rand_CenteredFloat(phi_f20);
} else {
spAC.x = Rand_CenteredFloat(phi_f20);
spAC.y = Rand_ZeroFloat(25.0f);
spAC.z = Rand_CenteredFloat(phi_f20);
}
BossGanonEff_SpawnLightRay(globalCtx, &this->actor.world.pos, &spAC, &sZeroVec, sp58, sp54, 0x1E);
}
if (spBA != 1) {
this->unk_1A8 = 1;
if (spBA == 0) {
BossGanon_CheckFallingPlatforms(this, globalCtx, &this->actor.world.pos);
}
if (spBA == 3) {
BossGanon_SetupHitByLightBall(ganondorf, globalCtx);
} else if (ganondorf->actionFunc == BossGanon_PlayTennis) {
BossGanon_SetupWait(ganondorf, globalCtx);
if (spBA == 5) {
ganondorf->timers[0] = 125;
}
}
}
}
}
}
void BossGanon_LightBall_Draw(Actor* thisx, GlobalContext* globalCtx) {
BossGanon* this = (BossGanon*)thisx;
s16 i;
f32 alpha;
s32 pad;
static u32 epoch = 0;
epoch++;
OPEN_DISPS(globalCtx->state.gfxCtx);
func_80093D84(globalCtx->state.gfxCtx);
alpha = ((this->unk_1A2 % 2) != 0) ? this->fwork[GDF_FWORK_1] * 0.4f : this->fwork[GDF_FWORK_1] * 0.35f;
gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 255, 255, 155, (s8)alpha);
Matrix_Push();
Matrix_Translate(this->actor.world.pos.x, this->actor.floorHeight, this->actor.world.pos.z, MTXMODE_NEW);
Matrix_Scale(this->actor.scale.x * 0.75f, 1.0f, this->actor.scale.z * 0.75f, MTXMODE_APPLY);
gSPMatrix(POLY_XLU_DISP++, MATRIX_NEWMTX(globalCtx->state.gfxCtx),
G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW);
gSPDisplayList(POLY_XLU_DISP++, gDorfLightCoreDL);
Matrix_Pop();
gSPDisplayList(POLY_XLU_DISP++, gDorfLightBallMaterialDL);
gDPPipeSync(POLY_XLU_DISP++);
gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 255, 255, 255, (s8)this->fwork[GDF_FWORK_1]);
gDPSetEnvColor(POLY_XLU_DISP++, 255, 255, 0, 0);
if (this->unk_1A8 == 1) {
for (i = 0; i < 8; i++) {
FrameInterpolation_RecordOpenChild("Ganondorf Light Ball 0", epoch + i * 25);
Matrix_Push();
Matrix_RotateY(i * (M_PI / 8), MTXMODE_APPLY);
Matrix_RotateZ(this->fwork[GDF_FWORK_0], MTXMODE_APPLY);
gSPMatrix(POLY_XLU_DISP++, MATRIX_NEWMTX(globalCtx->state.gfxCtx),
G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW);
gSPDisplayList(POLY_XLU_DISP++, gDorfSquareDL);
Matrix_Pop();
FrameInterpolation_RecordCloseChild();
}
} else if (this->unk_1A8 == 0) {
Matrix_ReplaceRotation(&globalCtx->billboardMtxF);
Matrix_RotateZ((this->actor.shape.rot.z / 32768.0f) * 3.1416f, MTXMODE_APPLY);
gSPMatrix(POLY_XLU_DISP++, MATRIX_NEWMTX(globalCtx->state.gfxCtx),
G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW);
gSPDisplayList(POLY_XLU_DISP++, gDorfSquareDL);
}
CLOSE_DISPS(globalCtx->state.gfxCtx);
}
void func_808E1EB4(Actor* thisx, GlobalContext* globalCtx2) {
s16 i;
BossGanon* this = (BossGanon*)thisx;
GlobalContext* globalCtx = globalCtx2;
BossGanon* dorf = (BossGanon*)this->actor.parent;
f32 xDiff;
f32 yDiff;
f32 zDiff;
f32 xzDist;
s16 xRotTarget;
s16 yRotTarget;
Vec3f vel;
Vec3f accel;
this->unk_1A2++;
dorf->envLightMode = 1;
Actor_SetScale(&this->actor, 6.0f);
this->actor.shape.rot.z += ((s16)(Rand_ZeroOne() * 20000.0f) + 0x4000);
for (i = 0; i < ARRAY_COUNT(this->timers); i++) {
if (this->timers[i] != 0) {
this->timers[i]--;
}
}
func_8002D908(&this->actor);
func_8002D7EC(&this->actor);
this->unk_1A6++;
if (this->unk_1A6 >= 15) {
this->unk_1A6 = 0;
}
this->unk_2EC[this->unk_1A6] = this->actor.world.pos;
if (this->unk_1C2 == 0) {
if (this->timers[0] == 0) {
this->unk_1C2 = 1;
}
} else if (this->unk_1C2 == 1) {
xDiff = dorf->unk_1FC.x - this->actor.world.pos.x;
yDiff = dorf->unk_1FC.y - this->actor.world.pos.y;
zDiff = dorf->unk_1FC.z - this->actor.world.pos.z;
yRotTarget = RADF_TO_BINANG(Math_FAtan2F(xDiff, zDiff));
xzDist = sqrtf(SQ(xDiff) + SQ(zDiff));
xRotTarget = RADF_TO_BINANG(Math_FAtan2F(yDiff, xzDist));
Math_ApproachS(&this->actor.world.rot.x, xRotTarget, 1, 0x1000);
Math_ApproachS(&this->actor.world.rot.y, yRotTarget, 1, 0x1000);
if (sqrtf(SQ(xDiff) + SQ(zDiff) + SQ(yDiff)) < 40.0f) {
this->unk_1C2 = 2;
this->timers[0] = 30;
this->actor.speedXZ = 0.0f;
if (this->actor.params == 0xC8) {
func_80078884(NA_SE_EN_GANON_DAMAGE2);
func_80078884(NA_SE_EN_GANON_DD_THUNDER);
for (i = 0; i < 150; i++) {
vel.x = Rand_CenteredFloat(25.0f);
vel.y = Rand_CenteredFloat(25.0f);
vel.z = Rand_CenteredFloat(25.0f);
accel.x = vel.x * -0.03f;
accel.y = vel.y * -0.03f;
accel.z = vel.z * -0.03f;
BossGanonEff_SpawnLightRay(globalCtx, &dorf->unk_1FC, &vel, &accel,
Rand_ZeroFloat(500.0f) + 1000.0f, 15.0f, 0x14);
}
for (i = 1; i < 15; i++) {
dorf->unk_4E4[i] = 1000;
}
dorf->unk_2E6 = 1000;
dorf->unk_2E8 = 0;
dorf->screenFlashTimer = 4;
dorf->lensFlareTimer = 10;
dorf->lensFlareMode = 1;
dorf->unk_508 = 10.0f;
Actor_SpawnAsChild(&globalCtx->actorCtx, &dorf->actor, globalCtx, ACTOR_BOSS_GANON, dorf->unk_1FC.x,
dorf->unk_1FC.y, dorf->unk_1FC.z, 0, 0, 0, 0x12C);
}
this->actor.world.pos.y = 5000.0f;
}
} else if (this->timers[0] == 0) {
Actor_Kill(&this->actor);
}
}
void func_808E229C(Actor* thisx, GlobalContext* globalCtx2) {
BossGanon* this = (BossGanon*)thisx;
GlobalContext* globalCtx = globalCtx2;
s16 i;
s32 temp;
OPEN_DISPS(globalCtx->state.gfxCtx);
func_80093D84(globalCtx->state.gfxCtx);
gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 255, 255, 255, 255);
gDPSetEnvColor(POLY_XLU_DISP++, 255, 255, 0, 0);
gSPDisplayList(POLY_XLU_DISP++, gDorfLightBallMaterialDL);
for (i = 9; i >= 0; i--) {
temp = (s16)(((this->unk_1A6 - i) + 0xF) % 15);
Matrix_Translate(this->unk_2EC[temp].x, this->unk_2EC[temp].y, this->unk_2EC[temp].z, MTXMODE_NEW);
Matrix_Scale(this->actor.scale.x * (1.0f - (i * 0.07000001f)), this->actor.scale.y * (1.0f - (i * 0.07000001f)),
this->actor.scale.z * (1.0f - (i * 0.07000001f)), MTXMODE_APPLY);
Matrix_ReplaceRotation(&globalCtx->billboardMtxF);
Matrix_RotateZ(((2.0f * (i * M_PI)) / 10.0f) + BINANG_TO_RAD(this->actor.shape.rot.z), MTXMODE_APPLY);
gSPMatrix(POLY_XLU_DISP++, MATRIX_NEWMTX(globalCtx->state.gfxCtx),
G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW);
gSPDisplayList(POLY_XLU_DISP++, gDorfSquareDL);
}
CLOSE_DISPS(globalCtx->state.gfxCtx);
}
void func_808E2544(Actor* thisx, GlobalContext* globalCtx) {
u8 numEffects = 0;
s16 xRot;
f32 xDiff;
f32 yDiff;
f32 zDiff;
f32 xzDist;
f32 new_var;
f32 sp84;
s16 i;
s16 sp80;
BossGanon* this = (BossGanon*)thisx;
BossGanon* dorf = (BossGanon*)this->actor.parent;
s32 pad;
Player* player = GET_PLAYER(globalCtx);
ColliderInfo* acHitInfo;
Vec3f sp60;
this->unk_1A2++;
Actor_SetScale(&this->actor, 0.01f);
for (i = 0; i < ARRAY_COUNT(this->timers); i++) {
if (this->timers[i] != 0) {
this->timers[i]--;
}
}
func_8002D908(&this->actor);
func_8002D7EC(&this->actor);
this->unk_1A6++;
if (this->unk_1A6 >= 15) {
this->unk_1A6 = 0;
}
this->unk_2EC[this->unk_1A6] = this->actor.world.pos;
this->unk_3C4[this->unk_1A6].x = BINANG_TO_RAD(this->actor.world.rot.x);
this->unk_3C4[this->unk_1A6].y = BINANG_TO_RAD(this->actor.world.rot.y);
switch (this->unk_1C2) {
case 0:
this->actor.speedXZ = 40.0f;
Math_ApproachF(&this->fwork[1], 255.0f, 1.0f, 40.0f);
xDiff = dorf->unk_278.x - this->actor.world.pos.x;
yDiff = dorf->unk_278.y - this->actor.world.pos.y;
zDiff = dorf->unk_278.z - this->actor.world.pos.z;
sp80 = RADF_TO_BINANG(Math_FAtan2F(xDiff, zDiff));
xzDist = sqrtf(SQ(xDiff) + SQ(zDiff));
xRot = RADF_TO_BINANG(Math_FAtan2F(yDiff, xzDist));
sp84 = (xzDist * 700.0f) / 10.0f;
if (sp84 > 6144.0f) {
sp84 = 6144.0f;
}
xRot += (Math_CosS(this->unk_1A2 * 0x2200) * sp84);
this->actor.world.rot.x = xRot;
Math_ApproachS(&this->actor.shape.rot.y, sp80, 1, this->csCamMaxStepScale);
Math_ApproachF(&this->csCamMaxStepScale, 4096.0f, 1.0f, 256.0f);
this->actor.world.rot.y = (Math_SinS(this->unk_1A2 * 0x1A00) * sp84) + this->actor.shape.rot.y;
if (sqrtf(SQ(xDiff) + SQ(zDiff) + SQ(yDiff)) < 45.0f) {
this->unk_1C2 = 1;
this->actor.speedXZ = 0.0f;
}
break;
case 1:
Math_ApproachZeroF(&this->fwork[1], 1.0f, 40.0f);
if (this->fwork[1] == 0.0f) {
Actor_Kill(&this->actor);
}
break;
case 10:
this->unk_1C2 = 0xB;
this->timers[0] = 14;
this->collider.dim.radius = 15;
this->collider.dim.height = 20;
this->collider.dim.yShift = -10;
this->actor.speedXZ = 20.0f;
this->fwork[1] = 255.0f;
this->unk_1F0 = player->actor.world.pos;
new_var = this->unk_1F0.x - this->actor.world.pos.x;
this->actor.shape.rot.y = RADF_TO_BINANG(Math_FAtan2F(new_var, this->unk_1F0.z - this->actor.world.pos.z)) +
(this->actor.params << 0xD) - 0x20C000;
// fallthrough
case 11:
if (this->timers[0] != 0) {
this->unk_1F0 = player->actor.world.pos;
xDiff = this->unk_1F0.x - this->actor.world.pos.x;
yDiff = (this->unk_1F0.y + 30.0f) - this->actor.world.pos.y;
zDiff = this->unk_1F0.z - this->actor.world.pos.z;
sp80 = RADF_TO_BINANG(Math_FAtan2F(xDiff, zDiff));
this->actor.shape.rot.x = RADF_TO_BINANG(Math_FAtan2F(yDiff, sqrtf(SQ(xDiff) + SQ(zDiff))));
Math_ApproachS(&this->actor.shape.rot.y, sp80, 1, this->csCamMaxStepScale);
Math_ApproachF(&this->csCamMaxStepScale, 4096.0f, 1.0f, 256.0f);
}
sp84 = (sqrtf(this->actor.xyzDistToPlayerSq) * 200.0f) / 10.0f;
if (sp84 > 13824.0f) {
sp84 = 13824.0f;
}
this->actor.world.rot.x = (Math_CosS(this->unk_1A2 * 0x3400) * sp84 * 0.1f) + this->actor.shape.rot.x;
this->actor.world.rot.y = (Math_SinS(this->unk_1A2 * 0x1A00) * sp84) + this->actor.shape.rot.y;
if ((player->swordState != 0) && (player->swordAnimation >= 0x18) && (this->actor.xzDistToPlayer < 80.0f)) {
this->unk_1C2 = 0xC;
this->actor.speedXZ = -30.0f;
func_8002D908(&this->actor);
func_8002D7EC(&this->actor);
this->unk_1F0 = dorf->unk_1FC;
numEffects = 10;
break;
}
if (this->collider.base.acFlags & 2) {
acHitInfo = this->collider.info.acHitInfo;
this->collider.base.acFlags &= ~2;
if (!(acHitInfo->toucher.dmgFlags & 0x100000) || Player_HasMirrorShieldEquipped(globalCtx)) {
func_800AA000(this->actor.xyzDistToPlayerSq, 0xB4, 0x14, 0x64);
this->unk_1C2 = 0xC;
this->actor.speedXZ = -30.0f;
func_8002D908(&this->actor);
func_8002D7EC(&this->actor);
this->unk_1F0.x = Rand_CenteredFloat(700.0f) + dorf->unk_1FC.x;
this->unk_1F0.y = Rand_CenteredFloat(200.0f) + dorf->unk_1FC.y;
this->unk_1F0.z = Rand_CenteredFloat(700.0f) + dorf->unk_1FC.z;
this->unk_1F0.x = this->unk_1F0.x + ((this->unk_1F0.x - this->actor.world.pos.x) * 100.0f);
this->unk_1F0.y = this->unk_1F0.y + ((this->unk_1F0.y - this->actor.world.pos.y) * 100.0f);
this->unk_1F0.z = this->unk_1F0.z + ((this->unk_1F0.z - this->actor.world.pos.z) * 100.0f);
numEffects = 10;
break;
}
}
Collider_UpdateCylinder(&this->actor, &this->collider);
if (this->timers[1] == 0) {
CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, &this->collider.base);
}
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;
if (sqrtf(SQ(xDiff) + SQ(zDiff) + SQ(yDiff)) < 30.0f) {
this->unk_1C2 = 1;
this->actor.speedXZ = 0.0f;
if (dorf->timers[2] == 0) {
func_8002F6D4(globalCtx, &this->actor, 3.0f, this->actor.world.rot.y, 0.0f, 0x50);
SoundSource_PlaySfxAtFixedWorldPos(globalCtx, &this->actor.world.pos, 40,
NA_SE_EN_GANON_HIT_THUNDER);
dorf->timers[2] = 20;
for (i = 0; i < ARRAY_COUNT(this->unk_4E4); i++) {
dorf->unk_4E4[i] = D_808E4C58[i];
}
dorf->unk_2E6 = 0;
dorf->unk_2E8 = 60;
dorf->unk_508 = 4.0f;
numEffects = 40;
}
}
break;
case 12:
this->actor.speedXZ = 20.0f;
xDiff = this->unk_1F0.x - this->actor.world.pos.x;
yDiff = this->unk_1F0.y - this->actor.world.pos.y;
zDiff = this->unk_1F0.z - this->actor.world.pos.z;
sp80 = RADF_TO_BINANG(Math_FAtan2F(xDiff, zDiff));
xzDist = sqrtf(SQ(xDiff) + SQ(zDiff));
xRot = RADF_TO_BINANG(Math_FAtan2F(yDiff, xzDist));
sp84 = (xzDist * 700.0f) / 10.0f;
if (sp84 > 6144.0f) {
sp84 = 6144.0f;
}
sp80 += Math_SinS(this->unk_1A2 * 0x2200) * sp84;
xRot += Math_CosS(this->unk_1A2 * 0x1800) * sp84;
this->actor.world.rot.x = xRot;
this->actor.world.rot.y = sp80;
xDiff = dorf->unk_1FC.x - this->actor.world.pos.x;
yDiff = dorf->unk_1FC.y - this->actor.world.pos.y;
zDiff = dorf->unk_1FC.z - this->actor.world.pos.z;
if (sqrtf(SQ(xDiff) + SQ(zDiff) + SQ(yDiff)) < 45.0f) {
BossGanon_SetupHitByLightBall(dorf, globalCtx);
this->timers[0] = 150;
numEffects = 40;
this->unk_1C2 = 1;
this->actor.speedXZ = 0.0f;
}
break;
}
if (this->unk_1C2 >= 0xB) {
xzDist = (this->unk_1C2 == 0xC) ? -65.0f : 0.0f;
if ((fabsf(this->actor.world.pos.x) > (465.0f + xzDist)) ||
(fabsf(this->actor.world.pos.z) > (465.0f + xzDist)) || ((this->actor.world.pos.y < 0.0f)) ||
(this->actor.world.pos.y > 450.0f)) {
this->unk_1C2 = 1;
this->actor.speedXZ = 0.0f;
numEffects = 10;
BossGanon_CheckFallingPlatforms(this, globalCtx, &this->actor.world.pos);
Actor_SpawnAsChild(&globalCtx->actorCtx, &dorf->actor, globalCtx, ACTOR_BOSS_GANON, this->actor.world.pos.x,
this->actor.world.pos.y, this->actor.world.pos.z, 0, 0, 0, 0x190);
}
}
if (numEffects) {
SoundSource_PlaySfxAtFixedWorldPos(globalCtx, &this->actor.world.pos, 80, NA_SE_EN_FANTOM_THUNDER);
for (i = 0; i < numEffects; i++) {
sp60.x = Rand_CenteredFloat(30.0f);
sp60.y = Rand_CenteredFloat(30.0f);
sp60.z = Rand_CenteredFloat(30.0);
BossGanonEff_SpawnLightRay(globalCtx, &this->actor.world.pos, &sp60, &sZeroVec,
Rand_ZeroFloat(200.0f) + 500.0f, 15.0f, 0x1E);
}
}
}
static Gfx* sBigMagicLightStreakDLists[] = {
gDorfLightStreak12DL, gDorfLightStreak11DL, gDorfLightStreak10DL, gDorfLightStreak9DL,
gDorfLightStreak8DL, gDorfLightStreak7DL, gDorfLightStreak6DL, gDorfLightStreak5DL,
gDorfLightStreak4DL, gDorfLightStreak3DL, gDorfLightStreak2DL, gDorfLightStreak1DL,
};
void func_808E324C(Actor* thisx, GlobalContext* globalCtx) {
BossGanon* this = (BossGanon*)thisx;
Mtx* mtx;
s16 i;
s32 temp;
mtx = Graph_Alloc(globalCtx->state.gfxCtx, 12 * sizeof(Mtx));
OPEN_DISPS(globalCtx->state.gfxCtx);
func_80093D84(globalCtx->state.gfxCtx);
gDPSetPrimColor(POLY_XLU_DISP++, 0, 0x80, 255, 255, 255, (s8)this->fwork[GDF_FWORK_1]);
gDPSetEnvColor(POLY_XLU_DISP++, 150, 255, 0, 128);
gSPSegment(POLY_XLU_DISP++, 0x0D, mtx);
for (i = 0; i < 12; i++) {
temp = (s16)(((this->unk_1A6 - i) + 0xF) % 15);
Matrix_Translate(this->unk_2EC[temp].x, this->unk_2EC[temp].y, this->unk_2EC[temp].z, MTXMODE_NEW);
Matrix_RotateY(this->unk_3C4[temp].y, MTXMODE_APPLY);
Matrix_RotateX(-this->unk_3C4[temp].x, MTXMODE_APPLY);
Matrix_Scale(this->actor.scale.x, this->actor.scale.y, this->actor.scale.z, MTXMODE_APPLY);
Matrix_RotateY(M_PI / 2, MTXMODE_APPLY);
MATRIX_TOMTX(mtx);
gSPMatrix(POLY_XLU_DISP++, mtx, G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW);
gSPDisplayList(POLY_XLU_DISP++, sBigMagicLightStreakDLists[i]);
mtx++;
};
Matrix_Translate(this->actor.world.pos.x, this->actor.world.pos.y, this->actor.world.pos.z, MTXMODE_NEW);
Matrix_ReplaceRotation(&globalCtx->billboardMtxF);
Matrix_Scale(10.0f, 10.0f, 10.0f, MTXMODE_APPLY);
Matrix_RotateZ(Rand_CenteredFloat(M_PI), MTXMODE_APPLY);
gSPMatrix(POLY_XLU_DISP++, MATRIX_NEWMTX(globalCtx->state.gfxCtx),
G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW);
gSPDisplayList(POLY_XLU_DISP++, gDorfLightBallMaterialDL);
gSPDisplayList(POLY_XLU_DISP++, gDorfSquareDL);
CLOSE_DISPS(globalCtx->state.gfxCtx);
}
void BossGanon_UpdateEffects(GlobalContext* globalCtx) {
Player* player = GET_PLAYER(globalCtx);
GanondorfEffect* eff = globalCtx->specialEffects;
s16 i;
s32 pad;
f32 xDiff;
f32 yDiff;
f32 zDiff;
f32 yRot;
f32 xRot;
Vec3f spA0;
s16 bodyPart;
f32 distToPlayer;
s32 pad2;
s32 pad3;
spA0.x = 0.0f;
spA0.y = 0.0f;
for (i = 0; i < ARRAY_COUNT(sBossGanonEffectBuf); i++, eff++) {
if (eff->type != GDF_EFF_NONE) {
eff->pos.x += eff->velocity.x;
eff->pos.y += eff->velocity.y;
eff->pos.z += eff->velocity.z;
eff->timer++;
eff->velocity.x += eff->accel.x;
eff->velocity.y += eff->accel.y;
eff->velocity.z += eff->accel.z;
if (eff->type == GDF_EFF_WINDOW_SHARD) {
eff->unk_44 += 0.3f;
eff->unk_48 += 0.5f;
if (eff->pos.y < 0.0f) {
eff->type = GDF_EFF_NONE;
}
} else if (eff->type == GDF_EFF_SPARKLE) {
eff->unk_3C += Rand_ZeroFloat(M_PI / 2) + M_PI / 2;
eff->unk_2E -= eff->unk_30;
if (eff->unk_2E <= 0) {
eff->unk_2E = 0;
eff->type = GDF_EFF_NONE;
}
eff->alpha = eff->unk_2E;
if (eff->alpha > 255) {
eff->alpha = 255;
}
} else if (eff->type == GDF_EFF_BLACK_DOT) {
xDiff = sBossGanonGanondorf->unk_278.x - eff->pos.x;
yDiff = sBossGanonGanondorf->unk_278.y - eff->pos.y;
zDiff = sBossGanonGanondorf->unk_278.z - eff->pos.z;
yRot = Math_FAtan2F(xDiff, zDiff);
xRot = -Math_FAtan2F(yDiff, sqrtf(SQ(xDiff) + SQ(zDiff)));
spA0.z = eff->unk_38;
Matrix_RotateY(yRot, MTXMODE_NEW);
Matrix_RotateX(xRot, MTXMODE_APPLY);
Matrix_MultVec3f(&spA0, &eff->velocity);
Math_ApproachF(&eff->unk_38, 10.0f, 1.0f, 0.5f);
eff->alpha += 10;
if (eff->alpha > 255) {
eff->alpha = 255;
}
if ((sqrtf(SQ(xDiff) + SQ(yDiff) + SQ(zDiff)) < 20.0f) || (eff->timer > 70)) {
eff->type = GDF_EFF_NONE;
}
} else if (eff->type == GDF_EFF_LIGHT_RAY) {
eff->unk_3C += Rand_ZeroFloat(M_PI / 2) + M_PI / 2;
eff->unk_2E -= eff->unk_30;
if (eff->unk_2E <= 0) {
eff->unk_2E = 0;
eff->type = GDF_EFF_NONE;
}
eff->alpha = eff->unk_2E;
if (eff->alpha > 255) {
eff->alpha = 255;
}
Math_ApproachF(&eff->unk_38, eff->unk_40, 1.0f, (eff->unk_40 / 15.0f) * 4.0f);
} else if (eff->type == GDF_EFF_SHOCK) {
if (eff->unk_2E == GDF_SHOCK_DORF_YELLOW) {
bodyPart = (s16)Rand_ZeroFloat(13.9f) + 1;
eff->pos.x = sBossGanonGanondorf->unk_2EC[bodyPart].x + Rand_CenteredFloat(20.0f);
eff->pos.y = sBossGanonGanondorf->unk_2EC[bodyPart].y + Rand_CenteredFloat(20.0f);
eff->pos.z = sBossGanonGanondorf->unk_2EC[bodyPart].z + Rand_CenteredFloat(20.0f);
} else {
bodyPart = (s16)Rand_ZeroFloat(17.9f);
eff->pos.x = player->bodyPartsPos[bodyPart].x + Rand_CenteredFloat(10.0f);
eff->pos.y = player->bodyPartsPos[bodyPart].y + Rand_CenteredFloat(15.0f);
eff->pos.z = player->bodyPartsPos[bodyPart].z + Rand_CenteredFloat(10.0f);
}
eff->unk_3C += (Rand_ZeroFloat(M_PI / 2) + M_PI / 2);
if (eff->timer > 20) {
eff->type = GDF_EFF_NONE;
}
} else if (eff->type == GDF_EFF_LIGHTNING) {
if (eff->unk_3C == 0.0f) {
eff->unk_44 = BINANG_TO_RAD(Camera_GetInputDirYaw(Gameplay_GetCamera(globalCtx, MAIN_CAM)));
} else {
eff->unk_44 = M_PI / 2;
}
if (eff->timer > 12) {
eff->type = GDF_EFF_NONE;
}
} else if (eff->type == GDF_EFF_IMPACT_DUST_DARK) {
eff->unk_30++; // unused
if (eff->unk_2E == 0) {
eff->alpha += 26;
if (eff->alpha > 255) {
eff->alpha = 255;
eff->unk_2E = 1;
}
} else if (eff->unk_2E == 1) {
eff->unk_2E = 2;
} else if (eff->unk_2E == 2) {
eff->alpha -= 26;
if (eff->alpha < 0) {
eff->alpha = 0;
eff->type = GDF_EFF_NONE;
}
}
Math_ApproachF(&eff->scale, eff->unk_38, 1.0f, 0.01f);
Math_ApproachF(&eff->unk_40, 4.0f, 1.0f, 0.15f);
} else if (eff->type == GDF_EFF_IMPACT_DUST_LIGHT) {
if (i == 0) {
func_80078884(NA_SE_EN_GANON_WAVE_GND - SFX_FLAG);
}
eff->unk_30++; // unused
if (eff->unk_2E == 0) {
eff->alpha += 100;
if (eff->alpha > 255) {
eff->alpha = 255;
eff->unk_2E = 1;
}
} else if (eff->unk_2E == 1) {
if (eff->timer >= 20) {
eff->unk_2E = 2;
}
} else if (eff->unk_2E == 2) {
eff->alpha -= 30;
if (eff->alpha < 0) {
eff->alpha = 0;
eff->type = GDF_EFF_NONE;
}
}
Math_ApproachF(&eff->scale, eff->unk_38, 1.0f, 0.1f);
Math_ApproachF(&eff->unk_40, 1.0f, 1.0f, 0.15f);
} else if (eff->type == GDF_EFF_SHOCKWAVE) {
eff->unk_30++; // unused
eff->alpha -= 30;
if (eff->alpha < 0) {
eff->alpha = 0;
eff->type = GDF_EFF_NONE;
}
Math_ApproachF(&eff->scale, eff->unk_38, 1.0f, 0.13f);
if ((eff->timer < 150) && (fabsf(player->actor.world.pos.y) < 5.0f)) {
distToPlayer =
sqrtf(SQ(eff->pos.x - player->actor.world.pos.x) + SQ(eff->pos.z - player->actor.world.pos.z));
if (((eff->scale * 150.0f) < distToPlayer) && (distToPlayer < (eff->scale * 300.0f))) {
eff->timer = 150;
func_8002F6D4(globalCtx, &sBossGanonGanondorf->actor, 7.0f, sBossGanonGanondorf->actor.yawTowardsPlayer, 0.0f,
0x20);
}
}
}
}
}
}
static void* sLightningTextures[] = {
gDorfLightning1Tex, gDorfLightning1Tex, gDorfLightning2Tex, gDorfLightning3Tex, gDorfLightning4Tex,
gDorfLightning5Tex, gDorfLightning6Tex, gDorfLightning7Tex, gDorfLightning8Tex, gDorfLightning9Tex,
gDorfLightning10Tex, gDorfLightning11Tex, gDorfLightning12Tex,
};
static u8 sLightningPrimColors[] = {
0, 0, 0, 255, 255, 255, 231, 250, 231, 208, 245, 208, 185, 240, 185, 162, 235, 162, 139, 230,
139, 115, 225, 115, 92, 220, 92, 69, 215, 69, 46, 210, 46, 23, 205, 23, 0, 200, 0,
};
static u8 sLightningEnvColors[] = {
0, 0, 0, 255, 255, 0, 240, 231, 23, 226, 208, 46, 212, 185, 69, 198, 162, 92,
184, 139, 115, 170, 115, 139, 156, 92, 162, 142, 69, 185, 128, 46, 208, 114, 23, 231,
100, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
};
void BossGanon_DrawEffects(GlobalContext* globalCtx) {
u8 flag = 0;
s16 i;
s32 pad;
GraphicsContext* gfxCtx = globalCtx->state.gfxCtx;
GanondorfEffect* eff = globalCtx->specialEffects;
GanondorfEffect* effFirst = eff;
OPEN_DISPS(gfxCtx);
func_80093D84(globalCtx->state.gfxCtx);
for (i = 0; i < 200; i++, eff++) {
if (eff->type == GDF_EFF_WINDOW_SHARD) {
FrameInterpolation_RecordOpenChild(eff, eff->epoch);
gDPPipeSync(POLY_OPA_DISP++);
if (flag == 0) {
gSPDisplayList(POLY_OPA_DISP++, gDorfWindowShardMaterialDL);
flag++;
}
if ((eff->timer & 7) != 0) {
gDPSetPrimColor(POLY_OPA_DISP++, 0, 0, eff->color.r, eff->color.g, eff->color.b, 255);
} else {
gDPSetPrimColor(POLY_OPA_DISP++, 0, 0, 255, 255, 255, 255);
}
Matrix_Translate(eff->pos.x, eff->pos.y, eff->pos.z, MTXMODE_NEW);
Matrix_Scale(eff->scale, eff->scale, eff->scale, MTXMODE_APPLY);
Matrix_RotateY(eff->unk_48, MTXMODE_APPLY);
Matrix_RotateX(eff->unk_44, MTXMODE_APPLY);
gSPMatrix(POLY_OPA_DISP++, MATRIX_NEWMTX(gfxCtx),
G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW);
gSPDisplayList(POLY_OPA_DISP++, gDorfWindowShardModelDL);
FrameInterpolation_RecordCloseChild();
}
}
eff = effFirst;
flag = 0;
for (i = 0; i < 150; i++, eff++) {
if (eff->type == GDF_EFF_SPARKLE) {
FrameInterpolation_RecordOpenChild(eff, eff->epoch);
gDPPipeSync(POLY_XLU_DISP++);
if (flag == 0) {
gDPSetEnvColor(POLY_XLU_DISP++, 255, 255, 0, 0);
gSPDisplayList(POLY_XLU_DISP++, gDorfLightBallMaterialDL);
flag++;
}
gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 255, 255, 255, eff->alpha);
Matrix_Translate(eff->pos.x, eff->pos.y, eff->pos.z, MTXMODE_NEW);
Matrix_ReplaceRotation(&globalCtx->billboardMtxF);
Matrix_Scale(eff->scale, eff->scale, 1.0f, MTXMODE_APPLY);
Matrix_RotateZ(eff->unk_3C, MTXMODE_APPLY);
gSPMatrix(POLY_XLU_DISP++, MATRIX_NEWMTX(gfxCtx),
G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW);
gSPDisplayList(POLY_XLU_DISP++, gDorfSquareDL);
FrameInterpolation_RecordCloseChild();
}
}
eff = effFirst;
flag = 0;
for (i = 0; i < 150; i++, eff++) {
if (eff->type == GDF_EFF_LIGHT_RAY) {
FrameInterpolation_RecordOpenChild(eff, eff->epoch);
gDPPipeSync(POLY_XLU_DISP++);
if (flag == 0) {
gDPSetEnvColor(POLY_XLU_DISP++, 255, 255, 0, 0);
gSPDisplayList(POLY_XLU_DISP++, gDorfLightBallMaterialDL);
flag++;
}
gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 255, 255, 255, eff->alpha);
Matrix_Translate(eff->pos.x, eff->pos.y, eff->pos.z, MTXMODE_NEW);
Matrix_RotateY(eff->unk_48, MTXMODE_APPLY);
Matrix_RotateX(eff->unk_44, MTXMODE_APPLY);
Matrix_RotateZ(eff->unk_3C, MTXMODE_APPLY);
Matrix_Scale(eff->scale, eff->scale, eff->unk_38 * eff->scale, MTXMODE_APPLY);
Matrix_RotateX(M_PI / 2, MTXMODE_APPLY);
gSPMatrix(POLY_XLU_DISP++, MATRIX_NEWMTX(gfxCtx),
G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW);
gSPDisplayList(POLY_XLU_DISP++, gDorfSquareDL);
FrameInterpolation_RecordCloseChild();
}
}
eff = effFirst;
flag = 0;
for (i = 0; i < 150; i++, eff++) {
if (eff->type == GDF_EFF_SHOCK) {
FrameInterpolation_RecordOpenChild(eff, eff->epoch);
if (flag == 0) {
gDPPipeSync(POLY_XLU_DISP++);
if (eff->unk_2E == GDF_SHOCK_PLAYER_PURPLE) {
gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 100, 0, 200, 255);
gDPSetEnvColor(POLY_XLU_DISP++, 130, 0, 0, 0);
} else { // GDF_SHOCK_DORF_YELLOW or GDF_SHOCK_PLAYER_YELLOW
gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 255, 255, 255, 255);
gDPSetEnvColor(POLY_XLU_DISP++, 255, 255, 0, 0);
}
flag++;
}
Matrix_Translate(eff->pos.x, eff->pos.y, eff->pos.z, MTXMODE_NEW);
Matrix_Scale(eff->scale, eff->scale, 1.0f, MTXMODE_APPLY);
Matrix_RotateX(eff->unk_3C * 1.3f, MTXMODE_APPLY);
Matrix_RotateZ(eff->unk_3C, MTXMODE_APPLY);
gSPMatrix(POLY_XLU_DISP++, MATRIX_NEWMTX(gfxCtx),
G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW);
gSPDisplayList(POLY_XLU_DISP++, gDorfShockDL);
FrameInterpolation_RecordCloseChild();
}
}
eff = effFirst;
for (i = 0; i < 150; i++, eff++) {
if (eff->type == GDF_EFF_LIGHTNING) {
FrameInterpolation_RecordOpenChild(eff, eff->epoch);
gDPPipeSync(POLY_XLU_DISP++);
gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, sLightningPrimColors[(eff->timer * 3) + 0],
sLightningPrimColors[(eff->timer * 3) + 1], sLightningPrimColors[(eff->timer * 3) + 2],
255);
gDPSetEnvColor(POLY_XLU_DISP++, sLightningEnvColors[(eff->timer * 3) + 0],
sLightningEnvColors[(eff->timer * 3) + 1], sLightningEnvColors[(eff->timer * 3) + 2], 0);
Matrix_Translate(sBossGanonGanondorf->unk_260.x, sBossGanonGanondorf->unk_260.y, sBossGanonGanondorf->unk_260.z, MTXMODE_NEW);
Matrix_RotateY(eff->unk_48, MTXMODE_APPLY);
Matrix_RotateZ(eff->unk_3C, MTXMODE_APPLY);
Matrix_Scale(eff->scale, eff->scale, eff->scale, MTXMODE_APPLY);
Matrix_RotateY(eff->unk_44, MTXMODE_APPLY);
gSPMatrix(POLY_XLU_DISP++, MATRIX_NEWMTX(gfxCtx),
G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW);
gSPSegment(POLY_XLU_DISP++, 0x08, SEGMENTED_TO_VIRTUAL(sLightningTextures[eff->timer]));
gSPDisplayList(POLY_XLU_DISP++, gDorfLightningDL);
FrameInterpolation_RecordCloseChild();
}
}
eff = effFirst;
for (i = 0; i < 150; i++, eff++) {
if (eff->type == GDF_EFF_IMPACT_DUST_DARK) {
FrameInterpolation_RecordOpenChild(eff, eff->epoch);
gDPPipeSync(POLY_XLU_DISP++);
gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 0, 0, 0, eff->alpha);
gDPSetEnvColor(POLY_XLU_DISP++, 100, 70, 0, 128);
gSPSegment(POLY_XLU_DISP++, 0x08,
Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, eff->timer * 4, 0, 32, 64, 1, eff->timer * 2,
eff->timer * -20, 32, 32));
Matrix_Translate(eff->pos.x, eff->pos.y, eff->pos.z, MTXMODE_NEW);
Matrix_Scale(eff->scale, eff->unk_40 * eff->scale, eff->scale, MTXMODE_APPLY);
gSPMatrix(POLY_XLU_DISP++, MATRIX_NEWMTX(gfxCtx),
G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW);
gSPDisplayList(POLY_XLU_DISP++, gDorfImpactDarkDL);
FrameInterpolation_RecordCloseChild();
}
}
eff = effFirst;
for (i = 0; i < 150; i++, eff++) {
if (eff->type == GDF_EFF_IMPACT_DUST_LIGHT) {
FrameInterpolation_RecordOpenChild(eff, eff->epoch);
gDPPipeSync(POLY_XLU_DISP++);
gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 255, 255, 255, eff->alpha);
gDPSetEnvColor(POLY_XLU_DISP++, 200, 100, 0, 128);
gSPSegment(POLY_XLU_DISP++, 0x08,
Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, eff->timer * 4, 0, 32, 64, 1, eff->timer * 2,
eff->timer * -20, 32, 32));
Matrix_Translate(eff->pos.x, eff->pos.y, eff->pos.z, MTXMODE_NEW);
Matrix_Scale(eff->scale, eff->unk_40 * eff->scale, eff->scale, MTXMODE_APPLY);
gSPMatrix(POLY_XLU_DISP++, MATRIX_NEWMTX(gfxCtx),
G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW);
gSPDisplayList(POLY_XLU_DISP++, gDorfImpactLightDL);
FrameInterpolation_RecordCloseChild();
}
}
eff = effFirst;
for (i = 0; i < 150; i++, eff++) {
if (eff->type == GDF_EFF_SHOCKWAVE) {
FrameInterpolation_RecordOpenChild(eff, eff->epoch);
gDPPipeSync(POLY_XLU_DISP++);
gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 255, 255, 170, eff->alpha);
gDPSetEnvColor(POLY_XLU_DISP++, 150, 255, 0, 128);
gSPSegment(POLY_XLU_DISP++, 0x08,
Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, (eff->timer * 100), 0, 64, 32, 1,
(eff->timer * 100), 0, 64, 32));
Matrix_Translate(eff->pos.x, eff->pos.y, eff->pos.z, MTXMODE_NEW);
Matrix_Scale((eff->scale * 200.0f) / 1500.0f, (eff->unk_40 * 200.0f) / 1500.0f,
(eff->scale * 200.0f) / 1500.0f, MTXMODE_APPLY);
gSPMatrix(POLY_XLU_DISP++, MATRIX_NEWMTX(gfxCtx),
G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW);
gSPDisplayList(POLY_XLU_DISP++, gDorfShockwaveDL);
FrameInterpolation_RecordCloseChild();
}
}
eff = effFirst;
for (i = 0; i < 150; i++, eff++) {
if (eff->type == GDF_EFF_BLACK_DOT) {
FrameInterpolation_RecordOpenChild(eff, eff->epoch);
gDPPipeSync(POLY_XLU_DISP++);
gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 150, 170, 0, eff->alpha);
gDPSetEnvColor(POLY_XLU_DISP++, 255, 255, 255, 128);
gSPSegment(POLY_XLU_DISP++, 0x0A,
Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, 0, 0, 32, 32, 1, eff->timer * 2, eff->timer * -20,
64, 64));
Matrix_Translate(eff->pos.x, eff->pos.y, eff->pos.z, MTXMODE_NEW);
Matrix_ReplaceRotation(&globalCtx->billboardMtxF);
Matrix_Scale(eff->scale, 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++, gDorfDotDL);
FrameInterpolation_RecordCloseChild();
}
}
CLOSE_DISPS(gfxCtx);
}
#include "overlays/ovl_Boss_Ganon/ovl_Boss_Ganon.h"
void BossGanon_Reset(void) {
sBossGanonSeed1 = 0;
sBossGanonSeed2 = 0;
sBossGanonSeed3 = 0;
sBossGanonGanondorf = NULL;
sBossGanonZelda = NULL;
sBossGanonCape = NULL;
memset(sBossGanonEffectBuf, 0, sizeof(sBossGanonEffectBuf));
}