/* * File: z_boss_ganondrof.c * Overlay: ovl_Boss_Ganondrof * Description: Phantom Ganon */ #include "z_boss_ganondrof.h" #include "objects/object_gnd/object_gnd.h" #include "overlays/actors/ovl_En_fHG/z_en_fhg.h" #include "overlays/actors/ovl_En_Fhg_Fire/z_en_fhg_fire.h" #include "overlays/effects/ovl_Effect_Ss_Fhg_Flash/z_eff_ss_fhg_flash.h" #include "overlays/effects/ovl_Effect_Ss_Hahen/z_eff_ss_hahen.h" #include "overlays/actors/ovl_Door_Warp1/z_door_warp1.h" #define FLAGS (ACTOR_FLAG_0 | ACTOR_FLAG_2 | ACTOR_FLAG_4 | ACTOR_FLAG_5) typedef enum { /* 0 */ NOT_DEAD, /* 1 */ DEATH_START, /* 2 */ DEATH_THROES, /* 3 */ DEATH_WARP, /* 4 */ DEATH_SCREAM, /* 5 */ DEATH_DISINTEGRATE, /* 6 */ DEATH_FINISH } BossGanondrofDeathState; typedef enum { /* 0 */ THROW_NORMAL, /* 1 */ THROW_SLOW } BossGanondrofThrowAction; typedef enum { /* 0 */ STUNNED_FALL, /* 1 */ STUNNED_GROUND } BossGanondrofStunnedAction; typedef enum { /* 0 */ CHARGE_WINDUP, /* 1 */ CHARGE_START, /* 2 */ CHARGE_ATTACK, /* 3 */ CHARGE_FINISH } BossGanondrofChargeAction; typedef enum { /* 0 */ DEATH_SPASM, /* 1 */ DEATH_LIMP, /* 2 */ DEATH_HUNCHED } BossGanondrofDeathAction; void BossGanondrof_Init(Actor* thisx, PlayState* play); void BossGanondrof_Destroy(Actor* thisx, PlayState* play); void BossGanondrof_Update(Actor* thisx, PlayState* play); void BossGanondrof_Draw(Actor* thisx, PlayState* play); void BossGanondrof_SetupIntro(BossGanondrof* this, PlayState* play); void BossGanondrof_Intro(BossGanondrof* this, PlayState* play); void BossGanondrof_SetupPaintings(BossGanondrof* this); void BossGanondrof_Paintings(BossGanondrof* this, PlayState* play); void BossGanondrof_SetupNeutral(BossGanondrof* this, f32 arg1); void BossGanondrof_Neutral(BossGanondrof* this, PlayState* play); void BossGanondrof_SetupThrow(BossGanondrof* this, PlayState* play); void BossGanondrof_Throw(BossGanondrof* this, PlayState* play); void BossGanondrof_SetupBlock(BossGanondrof* this, PlayState* play); void BossGanondrof_Block(BossGanondrof* this, PlayState* play); void BossGanondrof_SetupReturn(BossGanondrof* this, PlayState* play); void BossGanondrof_Return(BossGanondrof* this, PlayState* play); void BossGanondrof_SetupCharge(BossGanondrof* this, PlayState* play); void BossGanondrof_Charge(BossGanondrof* this, PlayState* play); void BossGanondrof_Stunned(BossGanondrof* this, PlayState* play); void BossGanondrof_Death(BossGanondrof* this, PlayState* play); const ActorInit Boss_Ganondrof_InitVars = { ACTOR_BOSS_GANONDROF, ACTORCAT_BOSS, FLAGS, OBJECT_GND, sizeof(BossGanondrof), (ActorFunc)BossGanondrof_Init, (ActorFunc)BossGanondrof_Destroy, (ActorFunc)BossGanondrof_Update, (ActorFunc)BossGanondrof_Draw, NULL, }; static ColliderCylinderInit sCylinderInitBody = { { 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, }, { 30, 90, -50, { 0, 0, 0 } }, }; static ColliderCylinderInit sCylinderInitSpear = { { 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, 0x30 }, { 0xFFCFFFFF, 0x00, 0x00 }, TOUCH_ON | TOUCH_SFX_NORMAL, BUMP_ON, OCELEM_ON, }, { 20, 30, -20, { 0, 0, 0 } }, }; // clang-format off static u8 sDecayMaskHigh[16 * 16] = { 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1, 1,0,1,1,0,0,0,0,1,1,1,1,1,1,0,1, 1,0,1,1,1,0,0,0,0,1,1,1,1,1,0,1, 1,0,0,1,1,1,1,0,0,0,1,1,1,0,0,1, 1,0,0,1,1,1,1,1,0,0,0,1,1,0,0,1, 1,0,1,1,1,1,0,0,0,0,0,1,0,0,0,1, 1,1,1,1,1,1,1,1,0,0,0,0,1,1,0,1, 1,0,1,1,1,1,1,0,0,0,0,1,1,1,0,1, 1,0,0,1,1,1,0,0,0,1,1,1,1,1,0,1, 1,0,0,0,0,0,0,0,0,0,1,1,1,1,0,1, 1,0,0,0,1,1,0,0,0,1,1,1,1,1,1,1, 1,0,0,1,1,1,1,0,1,1,1,1,1,1,1,1, 1,0,1,1,1,1,1,0,0,1,1,1,1,1,0,1, 1,1,1,1,1,1,1,0,0,1,1,1,0,0,0,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, }; static u8 sDecayMaskLow[16 * 16] = { 1,1,1,0,1,0,0,1,0,0,1,1,1,1,1,1, 0,0,0,0,0,0,0,0,1,1,1,0,0,1,1,0, 1,0,1,1,0,0,0,0,0,1,1,0,0,1,0,0, 1,0,0,1,1,0,0,0,0,0,1,1,1,0,0,0, 0,0,0,1,1,1,0,0,0,0,0,1,1,0,0,1, 0,0,0,1,0,0,1,0,0,0,0,0,1,0,0,1, 1,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0, 1,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0, 1,0,0,0,1,0,0,0,0,0,0,0,1,0,0,1, 0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,1, 0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,1, 1,0,0,0,0,1,0,0,0,0,0,1,1,1,0,0, 1,0,0,0,0,1,0,0,0,0,1,0,1,1,0,0, 0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0, 1,0,0,0,1,0,1,0,0,0,1,1,0,0,0,1, 1,0,0,1,1,1,0,0,0,1,1,1,0,0,0,1, }; static u8 sDecayMaskTotal[16 * 16] = { 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, }; // clang-format on static u8 sDecayTex[16 * 16] = { { 0 } }; // These are Phantom Ganon's body textures, but I don't know which is which. static void* sLimbTex_rgba16_8x8[] = { gPhantomGanonLimbTex_00A800, gPhantomGanonLimbTex_00AE80, gPhantomGanonLimbTex_00AF00, gPhantomGanonLimbTex_00C180, gPhantomGanonLimbTex_00C400, }; static void* sLimbTex_rgba16_16x8[] = { gPhantomGanonLimbTex_00B980, gPhantomGanonLimbTex_00C480, gPhantomGanonLimbTex_00BC80, gPhantomGanonLimbTex_00BD80, gPhantomGanonLimbTex_00C080, }; static void* sLimbTex_rgba16_16x16[] = { gPhantomGanonLimbTex_00C200, gPhantomGanonLimbTex_00A000, gPhantomGanonLimbTex_00A200, gPhantomGanonLimbTex_00A400, gPhantomGanonLimbTex_00A600, gPhantomGanonLimbTex_00A880, gPhantomGanonLimbTex_00B780, gPhantomGanonLimbTex_00BA80, gPhantomGanonLimbTex_00BE80, }; static void* sLimbTex_rgba16_16x32[] = { gPhantomGanonLimbTex_00AA80, gPhantomGanonLimbTex_00AF80 }; static void* sMouthTex_ci8_16x16[] = { gPhantomGanonMouthTex, gPhantomGanonSmileTex }; static InitChainEntry sInitChain[] = { ICHAIN_U8(targetMode, 5, ICHAIN_CONTINUE), ICHAIN_S8(naviEnemyId, 0x2B, ICHAIN_CONTINUE), ICHAIN_F32_DIV1000(gravity, 0, ICHAIN_CONTINUE), ICHAIN_F32(targetArrowOffset, 0, ICHAIN_STOP), }; static Vec3f sAudioVec = { 0.0f, 0.0f, 50.0f }; void BossGanondrof_ClearPixels(u8* mask, s16 index) { if (mask[index]) { sDecayTex[index] = 1; } } void BossGanondrof_SetColliderPos(Vec3f* pos, ColliderCylinder* collider) { collider->dim.pos.x = pos->x; collider->dim.pos.y = pos->y; collider->dim.pos.z = pos->z; } void BossGanondrof_Init(Actor* thisx, PlayState* play) { s32 pad; BossGanondrof* this = (BossGanondrof*)thisx; Actor_ProcessInitChain(&this->actor, sInitChain); ActorShape_Init(&this->actor.shape, 0.0f, NULL, 0.0f); Actor_SetScale(&this->actor, 0.01f); SkelAnime_Init(play, &this->skelAnime, &gPhantomGanonSkel, &gPhantomGanonRideAnim, NULL, NULL, 0); if (this->actor.params < GND_FAKE_BOSS) { this->actor.params = GND_REAL_BOSS; this->actor.colChkInfo.health = 30; this->lightNode = LightContext_InsertLight(play, &play->lightCtx, &this->lightInfo); Lights_PointNoGlowSetInfo(&this->lightInfo, this->actor.world.pos.x, this->actor.world.pos.y, this->actor.world.pos.z, 255, 255, 255, 255); BossGanondrof_SetupIntro(this, play); } else { BossGanondrof_SetupPaintings(this); } Collider_InitCylinder(play, &this->colliderBody); Collider_InitCylinder(play, &this->colliderSpear); Collider_SetCylinder(play, &this->colliderBody, &this->actor, &sCylinderInitBody); Collider_SetCylinder(play, &this->colliderSpear, &this->actor, &sCylinderInitSpear); this->actor.flags &= ~ACTOR_FLAG_0; if (Flags_GetClear(play, play->roomCtx.curRoom.num)) { Actor_Kill(&this->actor); Actor_Spawn(&play->actorCtx, play, ACTOR_DOOR_WARP1, GND_BOSSROOM_CENTER_X, GND_BOSSROOM_CENTER_Y, GND_BOSSROOM_CENTER_Z, 0, 0, 0, WARP_DUNGEON_ADULT, true); Actor_Spawn(&play->actorCtx, play, ACTOR_ITEM_B_HEART, 200.0f + GND_BOSSROOM_CENTER_X, GND_BOSSROOM_CENTER_Y, GND_BOSSROOM_CENTER_Z, 0, 0, 0, 0, true); } else { Actor_SpawnAsChild(&play->actorCtx, &this->actor, play, ACTOR_EN_FHG, this->actor.world.pos.x, this->actor.world.pos.y, this->actor.world.pos.z, 0, 0, 0, this->actor.params); } for (int i = 0; i < ARRAY_COUNT(sDecayTex); i++) { sDecayTex[i] = 0; } for (int i = 0; i < 5; i++) { Gfx_RegisterBlendedTexture(sLimbTex_rgba16_8x8[i], sDecayTex, NULL); Gfx_RegisterBlendedTexture(sLimbTex_rgba16_16x8[i], sDecayTex, NULL); } for (int i = 0; i < ARRAY_COUNT(sLimbTex_rgba16_16x16); i++) { Gfx_RegisterBlendedTexture(sLimbTex_rgba16_16x16[i], sDecayTex, NULL); } for (int i = 0; i < ARRAY_COUNT(sLimbTex_rgba16_16x32); i++) { Gfx_RegisterBlendedTexture(sLimbTex_rgba16_16x32[i], sDecayTex, NULL); } Gfx_RegisterBlendedTexture(gPhantomGanonLimbTex_00B380, sDecayTex, NULL); Gfx_RegisterBlendedTexture(gPhantomGanonEyeTex, sDecayTex, NULL); for (int i = 0; i < ARRAY_COUNT(sMouthTex_ci8_16x16); i++) { Gfx_RegisterBlendedTexture(sMouthTex_ci8_16x16[i], sDecayTex, NULL); } } void BossGanondrof_Destroy(Actor* thisx, PlayState* play) { s32 pad; BossGanondrof* this = (BossGanondrof*)thisx; osSyncPrintf("DT1\n"); SkelAnime_Free(&this->skelAnime, play); Collider_DestroyCylinder(play, &this->colliderBody); Collider_DestroyCylinder(play, &this->colliderSpear); if (this->actor.params == GND_REAL_BOSS) { LightContext_RemoveLight(play, &play->lightCtx, this->lightNode); } osSyncPrintf("DT2\n"); } void BossGanondrof_SetupIntro(BossGanondrof* this, PlayState* play) { Animation_PlayLoop(&this->skelAnime, &gPhantomGanonRidePoseAnim); this->actionFunc = BossGanondrof_Intro; this->work[GND_MASK_OFF] = true; } void BossGanondrof_Intro(BossGanondrof* this, PlayState* play) { s16 i; s32 pad; EnfHG* horse = (EnfHG*)this->actor.child; SkelAnime_Update(&this->skelAnime); this->actor.world.pos = horse->actor.world.pos; this->actor.shape.rot.y = this->actor.world.rot.y = horse->actor.world.rot.y; osSyncPrintf("SW %d------------------------------------------------\n", horse->bossGndSignal); if ((this->timers[1] != 0) && (this->timers[1] < 25)) { Vec3f pos; Vec3f vel = { 0.0f, 0.0f, 0.0f }; Vec3f accel = { 0.0f, 0.0f, 0.0f }; pos.x = this->bodyPartsPos[14].x + Rand_CenteredFloat(10.0f); pos.y = this->bodyPartsPos[14].y + Rand_ZeroFloat(-5.0f); pos.z = this->bodyPartsPos[14].z + Rand_CenteredFloat(10.0f) + 5.0f; accel.y = 0.03f; EffectSsKFire_Spawn(play, &pos, &vel, &accel, (s16)Rand_ZeroFloat(10.0f) + 5, 0); } if (this->timers[1] == 20) { this->work[GND_MASK_OFF] = false; } if (this->timers[1] == 30) { func_80078914(&sAudioVec, NA_SE_EN_FANTOM_TRANSFORM); } if (horse->bossGndSignal == FHG_LIGHTNING) { Animation_Change(&this->skelAnime, &gPhantomGanonMaskOnAnim, 0.5f, 0.0f, Animation_GetLastFrame(&gPhantomGanonMaskOnAnim), ANIMMODE_ONCE_INTERP, 0.0f); this->timers[1] = 40; } if (horse->bossGndSignal == FHG_REAR) { Animation_MorphToPlayOnce(&this->skelAnime, &gPhantomGanonHorseRearingAnim, -3.0f); } if (horse->bossGndSignal == FHG_RIDE) { Animation_MorphToLoop(&this->skelAnime, &gPhantomGanonRidePoseAnim, -13.0f); } if (horse->bossGndSignal == FHG_SPUR) { EnfHG* horseTemp; Animation_MorphToPlayOnce(&this->skelAnime, &gPhantomGanonRideSpearRaiseAnim, -7.0f); horseTemp = (EnfHG*)this->actor.child; Actor_SpawnAsChild(&play->actorCtx, &this->actor, play, ACTOR_EN_FHG_FIRE, this->spearTip.x, this->spearTip.y, this->spearTip.z, 50, FHGFIRE_LIGHT_GREEN, 0, FHGFIRE_SPEAR_LIGHT); this->actor.child = &horseTemp->actor; } if (horse->bossGndSignal == FHG_FINISH) { Animation_MorphToPlayOnce(&this->skelAnime, &gPhantomGanonRideSpearResetAnim, -5.0f); } switch (this->work[GND_EYE_STATE]) { case GND_EYESTATE_FADE: this->fwork[GND_EYE_ALPHA] += 40.0f; if (this->fwork[GND_EYE_ALPHA] >= 255.0f) { this->fwork[GND_EYE_ALPHA] = 255.0f; } break; case GND_EYESTATE_BRIGHTEN: this->fwork[GND_EYE_BRIGHTNESS] += 20.0f; if (this->fwork[GND_EYE_BRIGHTNESS] > 255.0f) { this->fwork[GND_EYE_BRIGHTNESS] = 255.0f; } break; } this->armRotY = Math_SinS(this->work[GND_VARIANCE_TIMER] * 0x6E8) * 0; this->armRotZ = Math_CosS(this->work[GND_VARIANCE_TIMER] * 0x8DC) * 300.0f; for (i = 0; i < 30; i++) { this->rideRotY[i] = Math_SinS(this->work[GND_VARIANCE_TIMER] * ((i * 50) + 0x7B0)) * 100.0f; this->rideRotZ[i] = Math_CosS(this->work[GND_VARIANCE_TIMER] * ((i * 50) + 0x8DC)) * 100.0f; } if (horse->bossGndSignal == FHG_START_FIGHT) { BossGanondrof_SetupPaintings(this); for (i = 0; i < 30; i++) { this->rideRotY[i] = this->rideRotZ[i] = 0.0f; } } horse->bossGndSignal = FHG_NO_SIGNAL; } void BossGanondrof_SetupPaintings(BossGanondrof* this) { Animation_MorphToLoop(&this->skelAnime, &gPhantomGanonRideAnim, -5.0f); this->actionFunc = BossGanondrof_Paintings; } void BossGanondrof_Paintings(BossGanondrof* this, PlayState* play) { EnfHG* horse = (EnfHG*)this->actor.child; osSyncPrintf("RUN 1\n"); SkelAnime_Update(&this->skelAnime); osSyncPrintf("RUN 2\n"); if (horse->bossGndSignal == FHG_RAISE_SPEAR) { EnfHG* horseTemp; Animation_MorphToPlayOnce(&this->skelAnime, &gPhantomGanonRideSpearRaiseAnim, -2.0f); this->actor.flags |= ACTOR_FLAG_0; horseTemp = (EnfHG*)this->actor.child; Actor_SpawnAsChild(&play->actorCtx, &this->actor, play, ACTOR_EN_FHG_FIRE, this->spearTip.x, this->spearTip.y, this->spearTip.z, 30, FHGFIRE_LIGHT_GREEN, 0, FHGFIRE_SPEAR_LIGHT); this->actor.child = &horseTemp->actor; } else if (horse->bossGndSignal == FHG_LIGHTNING) { Animation_MorphToPlayOnce(&this->skelAnime, &gPhantomGanonRideSpearStrikeAnim, -2.0f); } else if (horse->bossGndSignal == FHG_RESET) { Animation_MorphToPlayOnce(&this->skelAnime, &gPhantomGanonRideSpearResetAnim, -2.0f); } else if (horse->bossGndSignal == FHG_RIDE) { Animation_MorphToLoop(&this->skelAnime, &gPhantomGanonRideAnim, -2.0f); this->actor.flags &= ~ACTOR_FLAG_0; } osSyncPrintf("RUN 3\n"); this->actor.world.pos = horse->actor.world.pos; this->actor.world.pos.y = horse->actor.world.pos.y; this->actor.shape.rot.y = this->actor.world.rot.y = horse->actor.world.rot.y; if (this->flyMode != GND_FLY_PAINTING) { BossGanondrof_SetupNeutral(this, -20.0f); this->timers[0] = 100; this->colliderBody.dim.radius = 20; this->colliderBody.dim.height = 60; this->colliderBody.dim.yShift = -33; Audio_PlayActorSound2(&this->actor, NA_SE_EN_FANTOM_LAUGH); this->actor.naviEnemyId = 0x1A; } else { horse->bossGndSignal = FHG_NO_SIGNAL; this->actor.scale.x = horse->actor.scale.x / 1.15f; this->actor.scale.y = horse->actor.scale.y / 1.15f; this->actor.scale.z = horse->actor.scale.z / 1.15f; osSyncPrintf("RUN 4\n"); } } void BossGanondrof_SetupNeutral(BossGanondrof* this, f32 arg1) { Animation_MorphToLoop(&this->skelAnime, &gPhantomGanonNeutralAnim, arg1); this->actionFunc = BossGanondrof_Neutral; this->actor.flags |= ACTOR_FLAG_0; this->fwork[GND_FLOAT_SPEED] = 0.0f; this->timers[0] = (s16)(Rand_ZeroOne() * 64.0f) + 30; } void BossGanondrof_Neutral(BossGanondrof* this, PlayState* play) { f32 targetX; f32 targetY; f32 targetZ; Player* player = GET_PLAYER(play); Actor* playerx = &player->actor; Actor* thisx = &this->actor; f32 rand01; s16 i; SkelAnime_Update(&this->skelAnime); switch (this->flyMode) { case GND_FLY_NEUTRAL: if (this->timers[0] == 0) { this->timers[0] = (s16)(Rand_ZeroOne() * 64.0f) + 30; rand01 = Rand_ZeroOne(); if (thisx->colChkInfo.health < 5) { if (rand01 < 0.25f) { BossGanondrof_SetupThrow(this, play); } else if (rand01 >= 0.8f) { this->flyMode = GND_FLY_CHARGE; this->timers[0] = 60; this->fwork[GND_FLOAT_SPEED] = 0.0f; Audio_PlayActorSound2(thisx, NA_SE_EN_FANTOM_LAUGH); } else { this->flyMode = GND_FLY_VOLLEY; this->timers[0] = 60; this->fwork[GND_FLOAT_SPEED] = 0.0f; Audio_PlayActorSound2(thisx, NA_SE_EN_FANTOM_LAUGH); } } else if ((rand01 < 0.5f) || (this->work[GND_THROW_COUNT] < 5)) { BossGanondrof_SetupThrow(this, play); } else { this->flyMode = GND_FLY_VOLLEY; this->timers[0] = 60; this->fwork[GND_FLOAT_SPEED] = 0.0f; Audio_PlayActorSound2(thisx, NA_SE_EN_FANTOM_LAUGH); } } if (this->timers[1] != 0) { targetX = GND_BOSSROOM_CENTER_X; targetZ = GND_BOSSROOM_CENTER_Z; } else { targetX = playerx->world.pos.x + (180.0f * Math_SinS(playerx->shape.rot.y)); targetZ = playerx->world.pos.z + (180.0f * Math_CosS(playerx->shape.rot.y)); if (sqrtf(SQ(targetX - GND_BOSSROOM_CENTER_X) + SQ(targetZ - GND_BOSSROOM_CENTER_Z)) > 280.0f) { this->timers[1] = 50; this->fwork[GND_FLOAT_SPEED] = 0.0f; } } targetY = playerx->world.pos.y + 100.0f + 0.0f; targetX += Math_SinS(this->work[GND_VARIANCE_TIMER] * 0x500) * 100.0f; targetZ += Math_CosS(this->work[GND_VARIANCE_TIMER] * 0x700) * 100.0f; break; case GND_FLY_VOLLEY: targetX = GND_BOSSROOM_CENTER_X - 14.0f; targetZ = GND_BOSSROOM_CENTER_Z + 265.0f; targetY = playerx->world.pos.y + 100.0f + 100.0f; targetX += Math_SinS(this->work[GND_VARIANCE_TIMER] * 0x500) * 100.0f; targetZ += Math_CosS(this->work[GND_VARIANCE_TIMER] * 0x700) * 100.0f; if (this->timers[0] == 0) { this->flyMode = GND_FLY_RETURN; this->returnSuccess = false; BossGanondrof_SetupThrow(this, play); this->timers[0] = 80; } break; case GND_FLY_RETURN: targetX = GND_BOSSROOM_CENTER_X - 14.0f; targetZ = GND_BOSSROOM_CENTER_Z + 265.0f; targetY = playerx->world.pos.y + 100.0f + 100.0f; targetX += Math_SinS(this->work[GND_VARIANCE_TIMER] * 0x500) * 50.0f; targetZ += Math_CosS(this->work[GND_VARIANCE_TIMER] * 0x700) * 50.0f; if (this->returnSuccess) { this->returnSuccess = false; BossGanondrof_SetupReturn(this, play); this->timers[0] = 80; } if (this->timers[0] == 0) { this->flyMode = GND_FLY_NEUTRAL; } break; case GND_FLY_CHARGE: targetX = GND_BOSSROOM_CENTER_X - 14.0f; targetZ = GND_BOSSROOM_CENTER_Z + 215.0f; targetY = playerx->world.pos.y + 100.0f + 50.0f; targetX += Math_SinS(this->work[GND_VARIANCE_TIMER] * 0x500) * 100.0f; targetZ += Math_CosS(this->work[GND_VARIANCE_TIMER] * 0x700) * 100.0f; if (this->timers[0] == 0) { BossGanondrof_SetupCharge(this, play); } break; } Math_ApproachF(&thisx->world.pos.x, targetX, 0.05f, this->fwork[GND_FLOAT_SPEED]); if (this->timers[2] != 0) { Math_ApproachF(&thisx->world.pos.y, targetY + 100.0f, 0.1f, 50.0f); } else { Math_ApproachF(&thisx->world.pos.y, targetY, 0.05f, 10.0f); } Math_ApproachF(&thisx->world.pos.z, targetZ, 0.05f, this->fwork[GND_FLOAT_SPEED]); Math_ApproachF(&this->fwork[GND_FLOAT_SPEED], 50.0f, 1.0f, 0.5f); thisx->velocity.x = thisx->world.pos.x - thisx->prevPos.x; thisx->velocity.z = thisx->world.pos.z - thisx->prevPos.z; thisx->world.pos.y += 2.0f * Math_SinS(this->work[GND_VARIANCE_TIMER] * 1500); Math_ApproachS(&thisx->shape.rot.y, thisx->yawTowardsPlayer, 5, 0xBB8); if ((this->work[GND_VARIANCE_TIMER] & 1) == 0) { Vec3f pos; Vec3f vel = { 0.0f, 0.0f, 0.0f }; Vec3f accel = { 0.0f, 0.0f, 0.0f }; for (i = 0; i < 3; i++) { pos.x = Rand_CenteredFloat(20.0f) + this->spearTip.x; pos.y = Rand_CenteredFloat(20.0f) + this->spearTip.y; pos.z = Rand_CenteredFloat(20.0f) + this->spearTip.z; accel.y = -0.08f; EffectSsFhgFlash_SpawnLightBall(play, &pos, &vel, &accel, (s16)(Rand_ZeroOne() * 80.0f) + 150, FHGFLASH_LIGHTBALL_GREEN); } } if (player->unk_A73 != 0) { BossGanondrof_SetupBlock(this, play); } Audio_PlayActorSound2(thisx, NA_SE_EN_FANTOM_FLOAT - SFX_FLAG); } void BossGanondrof_SetupThrow(BossGanondrof* this, PlayState* play) { EnfHG* horseTemp; s16 lightTime; this->fwork[GND_END_FRAME] = Animation_GetLastFrame(&gPhantomGanonThrowAnim); Animation_MorphToPlayOnce(&this->skelAnime, &gPhantomGanonThrowAnim, -5.0f); this->actionFunc = BossGanondrof_Throw; if ((Rand_ZeroOne() <= 0.1f) && (this->work[GND_THROW_COUNT] >= 10) && (this->flyMode == GND_FLY_NEUTRAL)) { this->work[GND_ACTION_STATE] = THROW_SLOW; this->work[GND_THROW_FRAME] = 1000; lightTime = 32; } else { this->work[GND_ACTION_STATE] = THROW_NORMAL; this->work[GND_THROW_FRAME] = 25; lightTime = 25; } horseTemp = (EnfHG*)this->actor.child; Actor_SpawnAsChild(&play->actorCtx, &this->actor, play, ACTOR_EN_FHG_FIRE, this->spearTip.x, this->spearTip.y, this->spearTip.z, lightTime, FHGFIRE_LIGHT_GREEN, 0, FHGFIRE_SPEAR_LIGHT); this->actor.child = &horseTemp->actor; this->work[GND_THROW_COUNT]++; Audio_PlayActorSound2(&this->actor, NA_SE_EN_FANTOM_STICK); } void BossGanondrof_Throw(BossGanondrof* this, PlayState* play) { SkelAnime_Update(&this->skelAnime); osSyncPrintf("this->fwork[GND_END_FRAME] = %d\n", (s16)this->fwork[GND_END_FRAME]); osSyncPrintf("this->work[GND_SHOT_FRAME] = %d\n", this->work[GND_THROW_FRAME]); if (Animation_OnFrame(&this->skelAnime, this->fwork[GND_END_FRAME])) { BossGanondrof_SetupNeutral(this, -6.0f); } if ((this->work[GND_ACTION_STATE] != THROW_NORMAL) && Animation_OnFrame(&this->skelAnime, 21.0f)) { this->fwork[GND_END_FRAME] = Animation_GetLastFrame(&gPhantomGanonThrowEndAnim); Animation_MorphToPlayOnce(&this->skelAnime, &gPhantomGanonThrowEndAnim, 0.0f); this->work[GND_THROW_FRAME] = 10; } if (Animation_OnFrame(&this->skelAnime, this->work[GND_THROW_FRAME])) { if (this->flyMode <= GND_FLY_NEUTRAL) { Audio_PlayActorSound2(&this->actor, NA_SE_EN_FANTOM_MASIC2); } else { Audio_PlayActorSound2(&this->actor, NA_SE_EN_FANTOM_MASIC1); } Audio_PlayActorSound2(&this->actor, NA_SE_EN_FANTOM_VOICE); } if (Animation_OnFrame(&this->skelAnime, this->work[GND_THROW_FRAME])) { EnfHG* horseTemp = (EnfHG*)this->actor.child; Actor_SpawnAsChild(&play->actorCtx, &this->actor, play, ACTOR_EN_FHG_FIRE, this->spearTip.x, this->spearTip.y, this->spearTip.z, this->work[GND_ACTION_STATE], 0, 0, FHGFIRE_ENERGY_BALL); this->actor.child = &horseTemp->actor; } 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.world.pos.y += 2.0f * Math_SinS(this->work[GND_VARIANCE_TIMER] * 1500); } void BossGanondrof_SetupReturn(BossGanondrof* this, PlayState* play) { static AnimationHeader* returnAnim[] = { &gPhantomGanonReturn1Anim, &gPhantomGanonReturn2Anim }; s16 rand = Rand_ZeroOne() * 1.99f; this->fwork[GND_END_FRAME] = Animation_GetLastFrame(returnAnim[rand]); Animation_MorphToPlayOnce(&this->skelAnime, returnAnim[rand], 0.0f); this->actionFunc = BossGanondrof_Return; } void BossGanondrof_Return(BossGanondrof* this, PlayState* play) { SkelAnime_Update(&this->skelAnime); if (Animation_OnFrame(&this->skelAnime, 5.0f)) { Audio_PlayActorSound2(&this->actor, NA_SE_EN_FANTOM_VOICE); osSyncPrintf("VOISE 2 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n"); osSyncPrintf("VOISE 2 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n"); } if (Animation_OnFrame(&this->skelAnime, this->fwork[GND_END_FRAME])) { BossGanondrof_SetupNeutral(this, 0.0f); } 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.world.pos.y += 2.0f * Math_SinS(this->work[GND_VARIANCE_TIMER] * 1500); if (this->returnSuccess) { this->returnSuccess = false; BossGanondrof_SetupReturn(this, play); this->timers[0] = 80; } } void BossGanondrof_SetupStunned(BossGanondrof* this, PlayState* play) { if (this->actionFunc != BossGanondrof_Stunned) { this->fwork[GND_END_FRAME] = Animation_GetLastFrame(&gPhantomGanonAirDamageAnim); Animation_MorphToLoop(&this->skelAnime, &gPhantomGanonAirDamageAnim, 0.0f); this->timers[0] = 50; this->shockTimer = 60; } else { this->fwork[GND_END_FRAME] = Animation_GetLastFrame(&gPhantomGanonGroundDamageAnim); Animation_MorphToLoop(&this->skelAnime, &gPhantomGanonGroundDamageAnim, 0.0f); } this->actionFunc = BossGanondrof_Stunned; this->work[GND_ACTION_STATE] = STUNNED_FALL; this->actor.velocity.x = 0.0f; this->actor.velocity.z = 0.0f; } void BossGanondrof_Stunned(BossGanondrof* this, PlayState* play) { osSyncPrintf("DAMAGE .................................\n"); SkelAnime_Update(&this->skelAnime); this->actor.gravity = -0.2f; if (this->actor.world.pos.y <= 5.0f) { if (this->work[GND_ACTION_STATE] == STUNNED_FALL) { this->fwork[GND_END_FRAME] = Animation_GetLastFrame(&gPhantomGanonStunnedAnim); Animation_MorphToLoop(&this->skelAnime, &gPhantomGanonStunnedAnim, -10.0f); this->work[GND_ACTION_STATE] = STUNNED_GROUND; } this->actor.velocity.y = 0.0f; this->actor.gravity = 0.0f; if (Animation_OnFrame(&this->skelAnime, this->fwork[GND_END_FRAME])) { Audio_PlayActorSound2(&this->actor, NA_SE_EN_FANTOM_DAMAGE2); } this->actor.flags |= ACTOR_FLAG_10; } osSyncPrintf("TIME0 %d ********************************************\n", this->timers[0]); if (this->timers[0] == 0) { BossGanondrof_SetupNeutral(this, -5.0f); this->timers[0] = 30; this->timers[2] = 30; this->flyMode = GND_FLY_NEUTRAL; this->actor.velocity.y = 0.0f; this->actor.gravity = 0.0f; } Actor_MoveForward(&this->actor); } void BossGanondrof_SetupBlock(BossGanondrof* this, PlayState* play) { this->fwork[GND_END_FRAME] = Animation_GetLastFrame(&gPhantomGanonBlockAnim); Animation_MorphToLoop(&this->skelAnime, &gPhantomGanonBlockAnim, -3.0f); this->actionFunc = BossGanondrof_Block; this->timers[0] = 10; Audio_PlayActorSound2(&this->actor, NA_SE_EN_FANTOM_STICK); } void BossGanondrof_Block(BossGanondrof* this, PlayState* play) { this->colliderBody.base.colType = COLTYPE_METAL; SkelAnime_Update(&this->skelAnime); 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.world.pos.y += 2.0f * Math_SinS(this->work[GND_VARIANCE_TIMER] * 1500); if (this->timers[0] == 0) { BossGanondrof_SetupNeutral(this, -5.0f); this->timers[0] = 10; this->flyMode = GND_FLY_NEUTRAL; } } void BossGanondrof_SetupCharge(BossGanondrof* this, PlayState* play) { this->fwork[GND_END_FRAME] = Animation_GetLastFrame(&gPhantomGanonChargeWindupAnim); Animation_MorphToLoop(&this->skelAnime, &gPhantomGanonChargeWindupAnim, -3.0f); this->actionFunc = BossGanondrof_Charge; this->timers[0] = 20; this->work[GND_ACTION_STATE] = CHARGE_WINDUP; } void BossGanondrof_Charge(BossGanondrof* this, PlayState* play) { Player* player = GET_PLAYER(play); Actor* playerx = &player->actor; Actor* thisx = &this->actor; f32 dxCenter = thisx->world.pos.x - GND_BOSSROOM_CENTER_X; f32 dzCenter = thisx->world.pos.z - GND_BOSSROOM_CENTER_Z; this->colliderBody.base.colType = COLTYPE_METAL; SkelAnime_Update(&this->skelAnime); switch (this->work[GND_ACTION_STATE]) { case CHARGE_WINDUP: if (this->timers[0] == 218) { Audio_PlayActorSound2(thisx, NA_SE_EN_FANTOM_STICK); } if (this->timers[0] == 19) { Audio_PlayActorSound2(thisx, NA_SE_EN_FANTOM_ATTACK); } thisx->world.pos.x += thisx->velocity.x; thisx->world.pos.z += thisx->velocity.z; Math_ApproachZeroF(&thisx->velocity.x, 1.0f, 0.5f); Math_ApproachZeroF(&thisx->velocity.z, 1.0f, 0.5f); if (this->timers[0] == 0) { this->work[GND_ACTION_STATE] = CHARGE_START; this->timers[0] = 10; thisx->speedXZ = 0.0f; this->fwork[GND_END_FRAME] = Animation_GetLastFrame(&gPhantomGanonChargeStartAnim); Animation_MorphToPlayOnce(&this->skelAnime, &gPhantomGanonChargeStartAnim, 0.0f); } Math_ApproachS(&thisx->shape.rot.y, thisx->yawTowardsPlayer, 5, 0x7D0); break; case CHARGE_START: if (Animation_OnFrame(&this->skelAnime, this->fwork[GND_END_FRAME])) { this->fwork[GND_END_FRAME] = Animation_GetLastFrame(&gPhantomGanonChargeAnim); Animation_MorphToLoop(&this->skelAnime, &gPhantomGanonChargeAnim, 0.0f); this->work[GND_ACTION_STATE] = CHARGE_ATTACK; } case CHARGE_ATTACK: if (this->timers[0] != 0) { Vec3f vecToLink; Math_ApproachS(&thisx->shape.rot.y, thisx->yawTowardsPlayer, 5, 0x7D0); vecToLink.x = playerx->world.pos.x - thisx->world.pos.x; vecToLink.y = playerx->world.pos.y + 40.0f - thisx->world.pos.y; vecToLink.z = playerx->world.pos.z - thisx->world.pos.z; thisx->world.rot.y = thisx->shape.rot.y; thisx->world.rot.x = Math_FAtan2F(vecToLink.y, sqrtf(SQ(vecToLink.x) + SQ(vecToLink.z))) * (0x8000 / M_PI); } func_8002D908(thisx); func_8002D7EC(thisx); Math_ApproachF(&thisx->speedXZ, 10.0f, 1.0f, 0.5f); if ((sqrtf(SQ(dxCenter) + SQ(dzCenter)) > 280.0f) || (thisx->xyzDistToPlayerSq < SQ(100.0f))) { this->work[GND_ACTION_STATE] = CHARGE_FINISH; this->timers[0] = 20; } break; case CHARGE_FINISH: thisx->gravity = 0.2f; Actor_MoveForward(thisx); osSyncPrintf("YP %f @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@\n", thisx->world.pos.y); if (thisx->world.pos.y < 5.0f) { thisx->world.pos.y = 5.0f; thisx->velocity.y = 0.0f; } if (sqrtf(SQ(dxCenter) + SQ(dzCenter)) > 280.0f) { Math_ApproachZeroF(&thisx->speedXZ, 1.0f, 2.0f); this->timers[0] = 0; } if (this->timers[0] == 0) { Math_ApproachZeroF(&thisx->speedXZ, 1.0f, 2.0f); Math_ApproachZeroF(&thisx->velocity.y, 1.0f, 2.0f); Math_ApproachS(&thisx->shape.rot.y, thisx->yawTowardsPlayer, 5, 0x7D0); if ((thisx->speedXZ <= 0.5f) && (fabsf(thisx->velocity.y) <= 0.1f)) { BossGanondrof_SetupNeutral(this, -10.0f); this->timers[0] = 30; this->flyMode = GND_FLY_NEUTRAL; } } break; } if (thisx->world.pos.y > (GND_BOSSROOM_CENTER_Y + 83.0f)) { thisx->world.pos.y += 2.0f * Math_SinS(this->work[GND_VARIANCE_TIMER] * 1500); } { s16 i; Vec3f pos; Vec3f vel = { 0.0f, 0.0f, 0.0f }; Vec3f accel = { 0.0f, 0.0f, 0.0f }; Vec3f baseOffset = { 0.0f, 50.0f, 0.0f }; Vec3f offset; baseOffset.y = 10.0f; for (i = 0; i < 10; i++) { Matrix_Push(); Matrix_RotateY((thisx->shape.rot.y / (f32)0x8000) * M_PI, MTXMODE_NEW); Matrix_RotateX((thisx->shape.rot.x / (f32)0x8000) * M_PI, MTXMODE_APPLY); Matrix_RotateZ((this->work[GND_PARTICLE_ANGLE] / (f32)0x8000) * M_PI, MTXMODE_APPLY); Matrix_MultVec3f(&baseOffset, &offset); Matrix_Pop(); pos.x = this->spearTip.x + offset.x; pos.y = this->spearTip.y + offset.y; pos.z = this->spearTip.z + offset.z; vel.x = (offset.x * 500.0f) / 1000.0f; vel.y = (offset.y * 500.0f) / 1000.0f; vel.z = (offset.z * 500.0f) / 1000.0f; accel.x = (offset.x * -50.0f) / 1000.0f; accel.y = (offset.y * -50.0f) / 1000.0f; accel.z = (offset.z * -50.0f) / 1000.0f; EffectSsFhgFlash_SpawnLightBall(play, &pos, &vel, &accel, 150, i % 7); this->work[GND_PARTICLE_ANGLE] += 0x1A5C; } } if (!(this->work[GND_VARIANCE_TIMER] & 7)) { EnfHG* horse = (EnfHG*)thisx->child; Actor_SpawnAsChild(&play->actorCtx, thisx, play, ACTOR_EN_FHG_FIRE, this->spearTip.x, this->spearTip.y, this->spearTip.z, 8, FHGFIRE_LIGHT_BLUE, 0, FHGFIRE_SPEAR_LIGHT); thisx->child = &horse->actor; } } void BossGanondrof_SetupDeath(BossGanondrof* this, PlayState* play) { Animation_PlayOnce(&this->skelAnime, &gPhantomGanonDeathBlowAnim); this->fwork[GND_END_FRAME] = Animation_GetLastFrame(&gPhantomGanonDeathBlowAnim); this->actionFunc = BossGanondrof_Death; Audio_QueueSeqCmd(0x1 << 28 | SEQ_PLAYER_BGM_MAIN << 24 | 0x100FF); Audio_PlayActorSound2(&this->actor, NA_SE_EN_FANTOM_DEAD); this->deathState = DEATH_START; this->actor.flags &= ~ACTOR_FLAG_0; this->work[GND_VARIANCE_TIMER] = 0; this->shockTimer = 50; } void BossGanondrof_Death(BossGanondrof* this, PlayState* play) { u8 holdCamera = false; u8 bodyDecayLevel = 0; f32 camX; f32 camZ; f32 pad; Player* player = GET_PLAYER(play); Camera* camera = Play_GetCamera(play, 0); osSyncPrintf("PYP %f\n", player->actor.floorHeight); SkelAnime_Update(&this->skelAnime); this->work[GND_DEATH_SFX_TIMER]++; if (((60 < this->work[GND_DEATH_SFX_TIMER]) && (this->work[GND_DEATH_SFX_TIMER] < 500)) || ((501 < this->work[GND_DEATH_SFX_TIMER]) && (this->work[GND_DEATH_SFX_TIMER] < 620))) { Audio_PlayActorSound2(&this->actor, NA_SE_EN_GOMA_LAST - SFX_FLAG); } switch (this->deathState) { case DEATH_START: func_80064520(play, &play->csCtx); func_8002DF54(play, &this->actor, 1); this->deathCamera = Play_CreateSubCamera(play); Play_ChangeCameraStatus(play, MAIN_CAM, CAM_STAT_WAIT); osSyncPrintf("7\n"); Play_ChangeCameraStatus(play, this->deathCamera, CAM_STAT_ACTIVE); osSyncPrintf("8\n"); this->deathState = DEATH_THROES; player->actor.speedXZ = 0.0f; this->timers[0] = 50; this->cameraEye = camera->eye; this->cameraAt = camera->at; this->cameraNextEye.x = this->targetPos.x; this->cameraNextEye.y = GND_BOSSROOM_CENTER_Y + 83.0f; this->cameraNextEye.z = (this->targetPos.z + 100.0f) + 50; this->cameraNextAt.x = this->targetPos.x; this->cameraNextAt.y = this->targetPos.y - 10.0f; this->cameraNextAt.z = this->targetPos.z; this->cameraEyeVel.x = fabsf(camera->eye.x - this->cameraNextEye.x); this->cameraEyeVel.y = fabsf(camera->eye.y - this->cameraNextEye.y); this->cameraEyeVel.z = fabsf(camera->eye.z - this->cameraNextEye.z); this->cameraAtVel.x = fabsf(camera->at.x - this->cameraNextAt.x); this->cameraAtVel.y = fabsf(camera->at.y - this->cameraNextAt.y); this->cameraAtVel.z = fabsf(camera->at.z - this->cameraNextAt.z); this->cameraAccel = 0.02f; this->cameraEyeMaxVel.x = this->cameraEyeMaxVel.y = this->cameraEyeMaxVel.z = 0.05f; this->work[GND_ACTION_STATE] = DEATH_SPASM; this->timers[0] = 150; this->cameraAtMaxVel.x = 0.2f; this->cameraAtMaxVel.y = 0.2f; this->cameraAtMaxVel.z = 0.2f; case DEATH_THROES: switch (this->work[GND_ACTION_STATE]) { case DEATH_SPASM: if (Animation_OnFrame(&this->skelAnime, this->fwork[GND_END_FRAME]) && !gSaveContext.n64ddFlag) { this->fwork[GND_END_FRAME] = Animation_GetLastFrame(&gPhantomGanonAirDamageAnim); Animation_Change(&this->skelAnime, &gPhantomGanonAirDamageAnim, 0.5f, 0.0f, this->fwork[GND_END_FRAME], ANIMMODE_ONCE_INTERP, 0.0f); this->work[GND_ACTION_STATE] = DEATH_LIMP; } else if (gSaveContext.n64ddFlag) { // Skip to death scream animation and move ganondrof to middle this->deathState = DEATH_SCREAM; this->timers[0] = 50; Animation_MorphToLoop(&this->skelAnime, &gPhantomGanonScreamAnim, -10.0f); this->actor.world.pos.x = GND_BOSSROOM_CENTER_X; this->actor.world.pos.y = GND_BOSSROOM_CENTER_Y + 83.0f; this->actor.world.pos.z = GND_BOSSROOM_CENTER_Z; this->actor.shape.rot.y = 0; this->work[GND_BODY_DECAY_INDEX] = 0; Audio_PlayActorSound2(&this->actor, NA_SE_EN_FANTOM_LAST); // Move Player out of the center of the room player->actor.world.pos.x = GND_BOSSROOM_CENTER_X - 200.0f; player->actor.world.pos.z = GND_BOSSROOM_CENTER_Z; } break; case DEATH_LIMP: if (Animation_OnFrame(&this->skelAnime, this->fwork[GND_END_FRAME])) { this->fwork[GND_END_FRAME] = Animation_GetLastFrame(&gPhantomGanonLimpAnim); Animation_MorphToLoop(&this->skelAnime, &gPhantomGanonLimpAnim, -20.0f); this->work[GND_ACTION_STATE] = DEATH_HUNCHED; } case DEATH_HUNCHED: bodyDecayLevel = 1; break; } if (gSaveContext.n64ddFlag) { break; } Math_ApproachS(&this->actor.shape.rot.y, this->work[GND_VARIANCE_TIMER] * -100, 5, 0xBB8); Math_ApproachF(&this->cameraNextEye.z, this->targetPos.z + 60.0f, 0.02f, 0.5f); Math_ApproachF(&this->actor.world.pos.y, GND_BOSSROOM_CENTER_Y + 133.0f, 0.05f, 100.0f); this->actor.world.pos.y += Math_SinS(this->work[GND_VARIANCE_TIMER] * 1500); this->cameraNextAt.x = this->targetPos.x; this->cameraNextAt.y = this->targetPos.y - 10.0f; this->cameraNextAt.z = this->targetPos.z; if (this->timers[0] == 0) { this->deathState = DEATH_WARP; this->timers[0] = 350; this->timers[1] = 50; this->fwork[GND_CAMERA_ZOOM] = 300.0f; this->cameraNextEye.y = GND_BOSSROOM_CENTER_Y + 233.0f; player->actor.world.pos.x = GND_BOSSROOM_CENTER_X - 200.0f; player->actor.world.pos.z = GND_BOSSROOM_CENTER_Z; holdCamera = true; bodyDecayLevel = 1; } break; case DEATH_WARP: if (this->timers[1] == 1) { EnfHG* horseTemp = (EnfHG*)this->actor.child; Actor_SpawnAsChild(&play->actorCtx, &this->actor, play, ACTOR_EN_FHG_FIRE, GND_BOSSROOM_CENTER_X, GND_BOSSROOM_CENTER_Y + 3.0f, GND_BOSSROOM_CENTER_Z, 0x4000, 0, 0, FHGFIRE_WARP_DEATH); this->actor.child = &horseTemp->actor; Message_StartTextbox(play, 0x108E, NULL); } this->actor.shape.rot.y -= 0xC8; this->actor.world.pos.y += Math_SinS(this->work[GND_VARIANCE_TIMER] * 1500); this->fwork[GND_CAMERA_ANGLE] += 0x78; camX = Math_SinS(this->fwork[GND_CAMERA_ANGLE]) * this->fwork[GND_CAMERA_ZOOM]; camZ = Math_CosS(this->fwork[GND_CAMERA_ANGLE]) * this->fwork[GND_CAMERA_ZOOM]; this->cameraEye.x = GND_BOSSROOM_CENTER_X + camX; this->cameraEye.y = this->cameraNextEye.y; this->cameraEye.z = GND_BOSSROOM_CENTER_Z + camZ; this->cameraAt.x = GND_BOSSROOM_CENTER_X; this->cameraAt.y = GND_BOSSROOM_CENTER_Y + 23.0f; this->cameraAt.z = GND_BOSSROOM_CENTER_Z; Math_ApproachF(&this->cameraNextEye.y, GND_BOSSROOM_CENTER_Y + 33.0f, 0.05f, 0.5f); Math_ApproachF(&this->fwork[GND_CAMERA_ZOOM], 170.0f, 0.05f, 1.0f); Math_ApproachF(&this->actor.world.pos.x, GND_BOSSROOM_CENTER_X, 0.05f, 1.5f); Math_ApproachF(&this->actor.world.pos.y, GND_BOSSROOM_CENTER_Y + 83.0f, 0.05f, 1.0f); Math_ApproachF(&this->actor.world.pos.z, GND_BOSSROOM_CENTER_Z, 0.05f, 1.5f); if (this->timers[0] == 0) { this->deathState = DEATH_SCREAM; this->timers[0] = 50; Animation_MorphToLoop(&this->skelAnime, &gPhantomGanonScreamAnim, -10.0f); this->actor.world.pos.x = GND_BOSSROOM_CENTER_X; this->actor.world.pos.y = GND_BOSSROOM_CENTER_Y + 83.0f; this->actor.world.pos.z = GND_BOSSROOM_CENTER_Z; this->actor.shape.rot.y = 0; this->work[GND_BODY_DECAY_INDEX] = 0; Audio_PlayActorSound2(&this->actor, NA_SE_EN_FANTOM_LAST); } holdCamera = true; bodyDecayLevel = 1; break; case DEATH_SCREAM: holdCamera = true; bodyDecayLevel = 2; this->actor.world.pos.y = GND_BOSSROOM_CENTER_Y + 83.0f; this->cameraEye.x = GND_BOSSROOM_CENTER_X; this->cameraEye.y = GND_BOSSROOM_CENTER_Y + 83.0f; this->cameraEye.z = GND_BOSSROOM_CENTER_Z + 50.0f; this->cameraAt.x = GND_BOSSROOM_CENTER_X; this->cameraAt.y = GND_BOSSROOM_CENTER_Y + 103.0f; this->cameraAt.z = GND_BOSSROOM_CENTER_Z; if (this->timers[0] == 0) { this->deathState = DEATH_DISINTEGRATE; Animation_MorphToPlayOnce(&this->skelAnime, &gPhantomGanonLastPoseAnim, -10.0f); this->work[GND_BODY_DECAY_INDEX] = 0; this->timers[0] = 40; } break; case DEATH_DISINTEGRATE: holdCamera = true; bodyDecayLevel = 3; Math_ApproachZeroF(&this->cameraEye.y, 0.05f, 1.0f); // approaches GND_BOSSROOM_CENTER_Y + 33.0f Math_ApproachF(&this->cameraEye.z, GND_BOSSROOM_CENTER_Z + 170.0f, 0.05f, 2.0f); Math_ApproachF(&this->cameraAt.y, GND_BOSSROOM_CENTER_Y + 53.0f, 0.05f, 1.0f); if (this->timers[0] == 0) { this->timers[0] = 250; this->deathState = DEATH_FINISH; } break; case DEATH_FINISH: holdCamera = true; bodyDecayLevel = 10; if (this->timers[0] == 150) { Audio_QueueSeqCmd(SEQ_PLAYER_BGM_MAIN << 24 | NA_BGM_BOSS_CLEAR); Actor_Spawn(&play->actorCtx, play, ACTOR_DOOR_WARP1, GND_BOSSROOM_CENTER_X, GND_BOSSROOM_CENTER_Y, GND_BOSSROOM_CENTER_Z, 0, 0, 0, WARP_DUNGEON_ADULT, true); } Math_ApproachZeroF(&this->cameraEye.y, 0.05f, 1.0f); // GND_BOSSROOM_CENTER_Y + 33.0f Math_ApproachF(&this->cameraEye.z, GND_BOSSROOM_CENTER_Z + 170.0f, 0.05f, 2.0f); Math_ApproachF(&this->cameraAt.y, GND_BOSSROOM_CENTER_Y + 53.0f, 0.05f, 1.0f); if (this->timers[0] == 0) { EnfHG* horse = (EnfHG*)this->actor.child; camera->eye = this->cameraEye; camera->eyeNext = this->cameraEye; camera->at = this->cameraAt; func_800C08AC(play, this->deathCamera, 0); this->deathCamera = 0; func_80064534(play, &play->csCtx); func_8002DF54(play, &this->actor, 7); Actor_Spawn(&play->actorCtx, play, ACTOR_ITEM_B_HEART, GND_BOSSROOM_CENTER_X, GND_BOSSROOM_CENTER_Y, GND_BOSSROOM_CENTER_Z + 200.0f, 0, 0, 0, 0, true); this->actor.child = &horse->actor; this->killActor = true; horse->killActor = true; Flags_SetClear(play, play->roomCtx.curRoom.num); Flags_SetSwitch(play, 0x22); } break; } if (bodyDecayLevel) { Vec3f pos; Vec3f vel = { 0.0f, 0.0f, 0.0f }; Vec3f accelKFire = { 0.0f, 0.0f, 0.0f }; Vec3f accelHahen = { 0.0f, -0.5f, 0.0f }; s16 limbDecayIndex; s16 i; vel.x = this->actor.world.pos.x - this->actor.prevPos.x; vel.z = this->actor.world.pos.z - this->actor.prevPos.z; if (bodyDecayLevel < 10) { if (this->work[GND_DEATH_ENV_TIMER] == 0) { if (play->envCtx.unk_BF == 0) { play->envCtx.unk_BF = 3; this->work[GND_DEATH_ENV_TIMER] = (s16)Rand_ZeroFloat(5.0f) + 4.0f; play->envCtx.unk_D6 = 0x28; } else { play->envCtx.unk_BF = 0; this->work[GND_DEATH_ENV_TIMER] = (s16)Rand_ZeroFloat(2.0f) + 2.0f; play->envCtx.unk_D6 = 0x14; } } else { this->work[GND_DEATH_ENV_TIMER]--; } for (i = 0; i <= 0; i++) { limbDecayIndex = this->work[GND_LIMB_DECAY_INDEX]; this->work[GND_LIMB_DECAY_INDEX]++; this->work[GND_LIMB_DECAY_INDEX] %= 25; pos.x = this->bodyPartsPos[limbDecayIndex].x + Rand_CenteredFloat(5.0f); pos.y = this->bodyPartsPos[limbDecayIndex].y + Rand_CenteredFloat(5.0f); pos.z = this->bodyPartsPos[limbDecayIndex].z + Rand_CenteredFloat(5.0f); accelKFire.y = 0.0f; if (bodyDecayLevel == 3) { accelKFire.y = -0.2f; accelKFire.x = (GND_BOSSROOM_CENTER_X - pos.x) * 0.002f; accelKFire.z = (GND_BOSSROOM_CENTER_Z - pos.z) * 0.002f; accelHahen.x = (GND_BOSSROOM_CENTER_X - pos.x) * 0.001f; accelHahen.y = -1.0f; accelHahen.z = (GND_BOSSROOM_CENTER_Z - pos.z) * 0.001f; } EffectSsKFire_Spawn(play, &pos, &vel, &accelKFire, (s16)Rand_ZeroFloat(20.0f) + 15, bodyDecayLevel); if ((Rand_ZeroOne() < 0.5f) || (bodyDecayLevel == 3)) { EffectSsHahen_Spawn(play, &pos, &vel, &accelHahen, 0, (s16)Rand_ZeroFloat(4.0f) + 7, HAHEN_OBJECT_DEFAULT, 10, NULL); } } } else { play->envCtx.unk_BF = 0; play->envCtx.unk_D6 = 0x14; } this->work[GND_BODY_DECAY_FLAG] = true; for (i = 0; i < 5; i++) { if (bodyDecayLevel == 1) { BossGanondrof_ClearPixels(sDecayMaskLow, this->work[GND_BODY_DECAY_INDEX]); } else if (bodyDecayLevel == 2) { BossGanondrof_ClearPixels(sDecayMaskHigh, this->work[GND_BODY_DECAY_INDEX]); } else { BossGanondrof_ClearPixels(sDecayMaskTotal, this->work[GND_BODY_DECAY_INDEX]); } if (this->work[GND_BODY_DECAY_INDEX] < 0xFF) { this->work[GND_BODY_DECAY_INDEX]++; } } } if (this->deathCamera != 0) { if (!holdCamera) { Math_ApproachF(&this->cameraEye.x, this->cameraNextEye.x, this->cameraEyeMaxVel.x, this->cameraEyeVel.x * this->cameraSpeedMod); Math_ApproachF(&this->cameraEye.y, this->cameraNextEye.y, this->cameraEyeMaxVel.y, this->cameraEyeVel.y * this->cameraSpeedMod); Math_ApproachF(&this->cameraEye.z, this->cameraNextEye.z, this->cameraEyeMaxVel.z, this->cameraEyeVel.z * this->cameraSpeedMod); Math_ApproachF(&this->cameraAt.x, this->cameraNextAt.x, this->cameraAtMaxVel.x, this->cameraAtVel.x * this->cameraSpeedMod); Math_ApproachF(&this->cameraAt.y, this->cameraNextAt.y, this->cameraAtMaxVel.y, this->cameraAtVel.y * this->cameraSpeedMod); Math_ApproachF(&this->cameraAt.z, this->cameraNextAt.z, this->cameraAtMaxVel.z, this->cameraAtVel.z * this->cameraSpeedMod); Math_ApproachF(&this->cameraSpeedMod, 1.0f, 1.0f, this->cameraAccel); } Play_CameraSetAtEye(play, this->deathCamera, &this->cameraAt, &this->cameraEye); } } void BossGanondrof_CollisionCheck(BossGanondrof* this, PlayState* play) { s32 acHit; EnfHG* horse = (EnfHG*)this->actor.child; ColliderInfo* hurtbox; if (this->work[GND_INVINC_TIMER] != 0) { this->work[GND_INVINC_TIMER]--; this->returnCount = 0; this->colliderBody.base.acFlags &= ~AC_HIT; } else { acHit = this->colliderBody.base.acFlags & AC_HIT; if ((acHit && ((s8)this->actor.colChkInfo.health > 0)) || (this->returnCount != 0)) { if (acHit) { this->colliderBody.base.acFlags &= ~AC_HIT; hurtbox = this->colliderBody.info.acHitInfo; } if (this->flyMode != GND_FLY_PAINTING) { if (acHit && (this->actionFunc != BossGanondrof_Stunned) && (hurtbox->toucher.dmgFlags & 0x0001F8A4)) { Audio_PlayActorSound2(&this->actor, NA_SE_PL_WALK_GROUND - SFX_FLAG); osSyncPrintf("hit != 0 \n"); } else if (this->actionFunc != BossGanondrof_Charge) { if (this->returnCount == 0) { u8 dmg; u8 canKill = false; s32 dmgFlags = hurtbox->toucher.dmgFlags; if (dmgFlags & 0x80) { return; } dmg = CollisionCheck_GetSwordDamage(dmgFlags, play); (dmg == 0) ? (dmg = 2) : (canKill = true); if (((s8)this->actor.colChkInfo.health > 2) || canKill) { this->actor.colChkInfo.health -= dmg; } if ((s8)this->actor.colChkInfo.health <= 0) { BossGanondrof_SetupDeath(this, play); Enemy_StartFinishingBlow(play, &this->actor); gSaveContext.sohStats.itemTimestamp[TIMESTAMP_DEFEAT_PHANTOM_GANON] = GAMEPLAYSTAT_TOTAL_TIME; return; } } BossGanondrof_SetupStunned(this, play); if (this->returnCount >= 2) { this->timers[0] = 120; } this->work[GND_INVINC_TIMER] = 10; horse->hitTimer = 20; Audio_PlayActorSound2(&this->actor, NA_SE_EN_FANTOM_DAMAGE); } else { Audio_PlayActorSound2(&this->actor, NA_SE_PL_WALK_GROUND - SFX_FLAG); } } else if (acHit && (hurtbox->toucher.dmgFlags & 0x0001F8A4)) { this->work[GND_INVINC_TIMER] = 10; this->actor.colChkInfo.health -= 2; horse->hitTimer = 20; Audio_PlayActorSound2(&this->actor, NA_SE_EN_FANTOM_DAMAGE); } this->returnCount = 0; } } } void BossGanondrof_Update(Actor* thisx, PlayState* play) { f32 cs; f32 sn; f32 legRotTargetY; f32 legRotTargetZ; f32 legSplitTarget; s32 pad2; s16 i; s32 pad; BossGanondrof* this = (BossGanondrof*)thisx; EnfHG* horse; osSyncPrintf("MOVE START %d\n", this->actor.params); this->actor.flags &= ~ACTOR_FLAG_10; this->colliderBody.base.colType = COLTYPE_HIT3; if (this->killActor) { Actor_Kill(&this->actor); return; } this->work[GND_VARIANCE_TIMER]++; horse = (EnfHG*)this->actor.child; osSyncPrintf("MOVE START EEEEEEEEEEEEEEEEEEEEEE%d\n", this->actor.params); this->actionFunc(this, play); for (i = 0; i < ARRAY_COUNT(this->timers); i++) { if (this->timers[i]) { this->timers[i]--; } } if (this->work[GND_UNKTIMER_1]) { this->work[GND_UNKTIMER_1]--; } if (this->work[GND_UNKTIMER_2]) { this->work[GND_UNKTIMER_2]--; } if (this->actionFunc != BossGanondrof_Death) { BossGanondrof_CollisionCheck(this, play); } osSyncPrintf("MOVE END\n"); BossGanondrof_SetColliderPos(&this->targetPos, &this->colliderBody); BossGanondrof_SetColliderPos(&this->spearTip, &this->colliderSpear); if ((this->flyMode == GND_FLY_PAINTING) && !horse->bossGndInPainting) { CollisionCheck_SetAC(play, &play->colChkCtx, &this->colliderBody.base); } if ((this->actionFunc == BossGanondrof_Stunned) && (this->timers[0] > 1)) { CollisionCheck_SetAC(play, &play->colChkCtx, &this->colliderBody.base); CollisionCheck_SetOC(play, &play->colChkCtx, &this->colliderBody.base); } else if (this->actionFunc == BossGanondrof_Block) { CollisionCheck_SetAC(play, &play->colChkCtx, &this->colliderBody.base); } else if (this->actionFunc == BossGanondrof_Charge) { CollisionCheck_SetAC(play, &play->colChkCtx, &this->colliderBody.base); CollisionCheck_SetAT(play, &play->colChkCtx, &this->colliderBody.base); CollisionCheck_SetAT(play, &play->colChkCtx, &this->colliderSpear.base); } this->actor.focus.pos = this->targetPos; sn = Math_SinS(-this->actor.shape.rot.y); cs = Math_CosS(-this->actor.shape.rot.y); legRotTargetY = ((sn * this->actor.velocity.z) + (cs * this->actor.velocity.x)) * 300.0f; legRotTargetZ = ((-sn * this->actor.velocity.x) + (cs * this->actor.velocity.z)) * 300.0f; Math_ApproachF(&this->legRotY, legRotTargetY, 1.0f, 600.0f); Math_ApproachF(&this->legRotZ, legRotTargetZ, 1.0f, 600.0f); if ((this->flyMode != GND_FLY_PAINTING) && (this->actionFunc != BossGanondrof_Stunned) && (this->deathState == NOT_DEAD)) { legSplitTarget = (Math_SinS(this->work[GND_VARIANCE_TIMER] * 0x8DC) * -500.0f) - 500.0f; } else { legSplitTarget = 0.0f; } Math_ApproachF(&this->legSplitY, legSplitTarget, 1.0f, 100.0f); if (this->shockTimer != 0) { s16 j; this->shockTimer--; osSyncPrintf("F 1\n"); for (j = 0; j < 7; j++) { osSyncPrintf("F 15\n"); EffectSsFhgFlash_SpawnShock(play, &this->actor, &this->actor.world.pos, 45, FHGFLASH_SHOCK_PG); } osSyncPrintf("F 2\n"); } if (this->actor.params == GND_REAL_BOSS) { Lights_PointNoGlowSetInfo(&this->lightInfo, this->spearTip.x, this->spearTip.y, this->spearTip.z, 255, 255, 255, 200); } } s32 BossGanondrof_OverrideLimbDraw(PlayState* play, s32 limbIndex, Gfx** dList, Vec3f* pos, Vec3s* rot, void* thisx) { BossGanondrof* this = (BossGanondrof*)thisx; switch (limbIndex) { case 15: if ((this->actionFunc == BossGanondrof_Intro) && this->work[GND_MASK_OFF]) { *dList = gPhantomGanonFaceDL; } rot->y += this->rideRotY[limbIndex]; rot->z += this->rideRotZ[limbIndex]; break; case 19: rot->y += this->legRotY + this->legSplitY; rot->z += this->legRotZ; break; case 20: rot->y += this->legRotY + this->legSplitY; rot->z += this->legRotZ; break; case 21: rot->y += this->legRotY + this->legSplitY; rot->z += this->legRotZ; break; case 23: rot->y += this->legRotY - this->legSplitY; rot->z += this->legRotZ; break; case 24: rot->y += this->legRotY - this->legSplitY; rot->z += this->legRotZ; break; case 25: rot->y += this->legRotY - this->legSplitY; rot->z += this->legRotZ; break; case 5: case 6: case 7: rot->y += this->armRotY; rot->z += this->armRotZ; break; case 8: case 9: case 10: rot->y += this->armRotY; rot->z += this->armRotZ; break; case 13: if (this->deathState != NOT_DEAD) { *dList = NULL; } default: rot->y += this->rideRotY[limbIndex]; rot->z += this->rideRotZ[limbIndex]; break; } return 0; } void BossGanondrof_PostLimbDraw(PlayState* play, s32 limbIndex, Gfx** dList, Vec3s* rot, void* thisx) { static Vec3f zeroVec = { 0.0f, 0.0f, 0.0f }; static Vec3f spearVec = { 0.0f, 0.0f, 6000.0f }; BossGanondrof* this = (BossGanondrof*)thisx; if (limbIndex == 14) { Matrix_MultVec3f(&zeroVec, &this->targetPos); } else if (limbIndex == 13) { Matrix_MultVec3f(&spearVec, &this->spearTip); } if (((this->flyMode != GND_FLY_PAINTING) || (this->actionFunc == BossGanondrof_Intro)) && (limbIndex <= 25)) { Matrix_MultVec3f(&zeroVec, &this->bodyPartsPos[limbIndex - 1]); } } Gfx* BossGanondrof_GetClearPixelDList(GraphicsContext* gfxCtx) { Gfx* dList = (Gfx*)Graph_Alloc(gfxCtx, sizeof(Gfx) * 4); Gfx* dListHead = dList; gDPPipeSync(dListHead++); gDPSetRenderMode(dListHead++, G_RM_FOG_SHADE_A, G_RM_AA_ZB_TEX_EDGE2); gSPClearGeometryMode(dListHead++, G_CULL_BACK); gSPEndDisplayList(dListHead++); return dList; } Gfx* BossGanondrof_GetNullDList(GraphicsContext* gfxCtx) { Gfx* dList = (Gfx*)Graph_Alloc(gfxCtx, sizeof(Gfx) * 1); Gfx* dListHead = dList; gSPEndDisplayList(dListHead++); return dList; } void BossGanondrof_Draw(Actor* thisx, PlayState* play) { s32 pad; BossGanondrof* this = (BossGanondrof*)thisx; EnfHG* horse; OPEN_DISPS(play->state.gfxCtx); if (this->work[GND_BODY_DECAY_FLAG]) { gSPInvalidateTexCache(POLY_OPA_DISP++, sDecayTex); } osSyncPrintf("MOVE P = %x\n", this->actor.update); osSyncPrintf("STOP TIMER = %d ==============\n", this->actor.freezeTimer); horse = (EnfHG*)this->actor.child; if (this->flyMode == GND_FLY_PAINTING) { Matrix_RotateY((horse->turnRot * 3.1416f) / (f32)0x8000, MTXMODE_APPLY); } osSyncPrintf("YP %f\n", this->actor.world.pos.y); Gfx_SetupDL_25Opa(play->state.gfxCtx); if (this->work[GND_INVINC_TIMER] & 4) { POLY_OPA_DISP = Gfx_SetFog(POLY_OPA_DISP, 255, 50, 0, 0, 900, 1099); } else { POLY_OPA_DISP = Gfx_SetFog(POLY_OPA_DISP, (u32)horse->warpColorFilterR, (u32)horse->warpColorFilterG, (u32)horse->warpColorFilterB, 0, (s32)horse->warpColorFilterUnk1 + 995, (s32)horse->warpColorFilterUnk2 + 1000); } osSyncPrintf("DRAW 11\n"); osSyncPrintf("EYE_COL %d\n", (s16)this->fwork[GND_EYE_BRIGHTNESS]); gDPSetEnvColor(POLY_OPA_DISP++, (s16)this->fwork[GND_EYE_BRIGHTNESS], (s16)this->fwork[GND_EYE_BRIGHTNESS], (s16)this->fwork[GND_EYE_BRIGHTNESS], (s16)this->fwork[GND_EYE_ALPHA]); if (this->work[GND_BODY_DECAY_FLAG]) { gSPSegment(POLY_OPA_DISP++, 0x08, BossGanondrof_GetClearPixelDList(play->state.gfxCtx)); } else { gSPSegment(POLY_OPA_DISP++, 0x08, BossGanondrof_GetNullDList(play->state.gfxCtx)); } SkelAnime_DrawOpa(play, this->skelAnime.skeleton, this->skelAnime.jointTable, BossGanondrof_OverrideLimbDraw, BossGanondrof_PostLimbDraw, this); osSyncPrintf("DRAW 22\n"); POLY_OPA_DISP = Play_SetFog(play, POLY_OPA_DISP); CLOSE_DISPS(play->state.gfxCtx); osSyncPrintf("DRAW END %d\n", this->actor.params); }