#include "z_demo_effect.h" #include "vt.h" #include "objects/gameplay_keep/gameplay_keep.h" #include "objects/object_efc_crystal_light/object_efc_crystal_light.h" #include "objects/object_efc_fire_ball/object_efc_fire_ball.h" #include "objects/object_efc_lgt_shower/object_efc_lgt_shower.h" #include "objects/object_god_lgt/object_god_lgt.h" #include "objects/object_light_ring/object_light_ring.h" #include "objects/object_triforce_spot/object_triforce_spot.h" #include "objects/object_efc_tw/object_efc_tw.h" #include "objects/object_gi_jewel/object_gi_jewel.h" #define FLAGS (ACTOR_FLAG_4 | ACTOR_FLAG_5) void DemoEffect_Init(Actor* thisx, GlobalContext* globalCtx); void DemoEffect_Destroy(Actor* thisx, GlobalContext* globalCtx); void DemoEffect_Update(Actor* thisx, GlobalContext* globalCtx); void DemoEffect_DrawCrystalLight(Actor* thisx, GlobalContext* globalCtx); void DemoEffect_DrawFireBall(Actor* thisx, GlobalContext* globalCtx); void DemoEffect_DrawBlueOrb(Actor* thisx, GlobalContext* globalCtx); void DemoEffect_DrawLgtShower(Actor* thisx, GlobalContext* globalCtx); void DemoEffect_DrawGodLgt(Actor* thisx, GlobalContext* globalCtx); void DemoEffect_DrawLightRing(Actor* thisx, GlobalContext* globalCtx); void DemoEffect_DrawTriforceSpot(Actor* thisx, GlobalContext* globalCtx); void DemoEffect_DrawGetItem(Actor* thisx, GlobalContext* globalCtx); void DemoEffect_DrawLightEffect(Actor* thisx, GlobalContext* globalCtx); void DemoEffect_DrawTimeWarp(Actor* thisx, GlobalContext* globalCtx); void DemoEffect_DrawJewel(Actor* thisx, GlobalContext* globalCtx); void DemoEffect_Wait(DemoEffect* this, GlobalContext* globalCtx); void DemoEffect_InitTimeWarp(DemoEffect* this, GlobalContext* globalCtx); void DemoEffect_InitTimeWarpTimeblock(DemoEffect* this, GlobalContext* globalCtx); void DemoEffect_InitCreationFireball(DemoEffect* this, GlobalContext* globalCtx); void DemoEffect_InitJewel(GlobalContext* globalCtx, DemoEffect* this); void DemoEffect_InitJewelColor(DemoEffect* this); void DemoEffect_UpdateCrystalLight(DemoEffect* this, GlobalContext* globalCtx); void DemoEffect_UpdatePositionToParent(DemoEffect* this, GlobalContext* globalCtx); void DemoEffect_UpdateBlueOrbGrow(DemoEffect* this, GlobalContext* globalCtx); void DemoEffect_UpdateBlueOrbShrink(DemoEffect* this, GlobalContext* globalCtx); void DemoEffect_UpdateLgtShower(DemoEffect* this, GlobalContext* globalCtx); void DemoEffect_UpdateGodLgtDin(DemoEffect* this, GlobalContext* globalCtx); void DemoEffect_UpdateGodLgtNayru(DemoEffect* this, GlobalContext* globalCtx); void DemoEffect_UpdateGodLgtFarore(DemoEffect* this, GlobalContext* globalCtx); void DemoEffect_UpdateLightRingExpanding(DemoEffect* this, GlobalContext* globalCtx); void DemoEffect_UpdateTriforceSpot(DemoEffect* this, GlobalContext* globalCtx); void DemoEffect_UpdateGetItem(DemoEffect* this, GlobalContext* globalCtx); void DemoEffect_UpdateLightRingShrinking(DemoEffect* this, GlobalContext* globalCtx); void DemoEffect_UpdateLightRingTriforce(DemoEffect* this, GlobalContext* globalCtx); void DemoEffect_UpdateLightEffect(DemoEffect* this, GlobalContext* globalCtx); void DemoEffect_UpdateJewelChild(DemoEffect* this, GlobalContext* globalCtx); void DemoEffect_UpdateJewelAdult(DemoEffect* this, GlobalContext* globalCtx); void DemoEffect_UpdateDust(DemoEffect* this, GlobalContext* globalCtx); void DemoEffect_UpdateCreationFireball(DemoEffect* this, GlobalContext* globalCtx); void DemoEffect_UpdateTimeWarpReturnFromChamberOfSages(DemoEffect* this, GlobalContext* globalCtx); void DemoEffect_UpdateTimeWarpPullMasterSword(DemoEffect* this, GlobalContext* globalCtx); void DemoEffect_UpdateTimeWarpTimeblock(DemoEffect* this, GlobalContext* globalCtx); s32 DemoEffect_CheckCsAction(DemoEffect* this, GlobalContext* globalCtx, s32 csActionCompareId); void DemoEffect_InitPositionFromCsAction(DemoEffect* this, GlobalContext* globalCtx, s32 csActionIndex); void DemoEffect_MoveToCsEndpoint(DemoEffect* this, GlobalContext* globalCtx, s32 csActionId, s32 shouldUpdateFacing); void DemoEffect_MoveGetItem(DemoEffect* this, GlobalContext* globalCtx, s32 csActionId, f32 speed); const ActorInit Demo_Effect_InitVars = { ACTOR_DEMO_EFFECT, ACTORCAT_BG, FLAGS, OBJECT_GAMEPLAY_KEEP, sizeof(DemoEffect), (ActorFunc)DemoEffect_Init, (ActorFunc)DemoEffect_Destroy, (ActorFunc)DemoEffect_Update, NULL, NULL, }; // This variable assures only one jewel will play SFX static s16 sSfxJewelId[] = { 0 }; // The object used by the effectType static s16 sEffectTypeObjects[] = { /* 0x00 */ OBJECT_EFC_CRYSTAL_LIGHT, /* 0x01 */ OBJECT_EFC_FIRE_BALL, /* 0x02 */ OBJECT_GAMEPLAY_KEEP, /* 0x03 */ OBJECT_EFC_LGT_SHOWER, /* 0x04 */ OBJECT_GOD_LGT, /* 0x05 */ OBJECT_GOD_LGT, /* 0x06 */ OBJECT_GOD_LGT, /* 0x07 */ OBJECT_LIGHT_RING, /* 0x08 */ OBJECT_TRIFORCE_SPOT, /* 0x09 */ OBJECT_GI_MEDAL, /* 0x0A */ OBJECT_GI_MEDAL, /* 0x0B */ OBJECT_GI_MEDAL, /* 0x0C */ OBJECT_GI_MEDAL, /* 0x0D */ OBJECT_GI_MEDAL, /* 0x0E */ OBJECT_GI_MEDAL, /* 0x0F */ OBJECT_EFC_TW, /* 0x10 */ OBJECT_LIGHT_RING, /* 0x11 */ OBJECT_LIGHT_RING, /* 0x12 */ OBJECT_GAMEPLAY_KEEP, /* 0x13 */ OBJECT_GI_JEWEL, /* 0x14 */ OBJECT_GI_JEWEL, /* 0x15 */ OBJECT_GI_JEWEL, /* 0x16 */ OBJECT_GI_JEWEL, /* 0x17 */ OBJECT_GI_M_ARROW, /* 0x18 */ OBJECT_EFC_TW, /* 0x19 */ OBJECT_EFC_TW, }; static u8 sTimewarpVertexSizeIndices[] = { 1, 1, 2, 0, 1, 1, 2, 0, 1, 2, 0, 2, 1, 0, 1, 0, 2, 0, 2, 2, 0 }; static Color_RGB8 sJewelSparkleColors[5][2] = { { { 255, 255, 255 }, { 100, 255, 0 } }, { { 255, 255, 255 }, { 200, 0, 150 } }, { { 255, 255, 255 }, { 0, 100, 255 } }, { { 0, 0, 0 }, { 0, 0, 0 } }, { { 223, 0, 0 }, { 0, 0, 0 } }, }; /** * Sets up the update function. */ void DemoEffect_SetupUpdate(DemoEffect* this, DemoEffectFunc updateFunc) { this->updateFunc = updateFunc; } /** * Gives a number on the range of 0.0f - 1.0f representing current cutscene action completion percentage. */ f32 DemoEffect_InterpolateCsFrames(GlobalContext* globalCtx, s32 csActionId) { f32 interpolated = Environment_LerpWeight(globalCtx->csCtx.npcActions[csActionId]->endFrame, globalCtx->csCtx.npcActions[csActionId]->startFrame, globalCtx->csCtx.frames); if (interpolated > 1.0f) { interpolated = 1.0f; } return interpolated; } /** * Initializes information for Jewel/Spritual Stone actors. */ void DemoEffect_InitJewel(GlobalContext* globalCtx, DemoEffect* this) { this->initDrawFunc = DemoEffect_DrawJewel; if (!LINK_IS_ADULT) { this->initUpdateFunc = DemoEffect_UpdateJewelChild; } else { this->initUpdateFunc = DemoEffect_UpdateJewelAdult; } if (globalCtx->sceneNum == SCENE_TOKINOMA) { Actor_SetScale(&this->actor, 0.35f); } else { Actor_SetScale(&this->actor, 0.10f); } this->csActionId = 1; this->actor.shape.rot.x = 16384; DemoEffect_InitJewelColor(this); this->jewel.alpha = 0; this->jewelCsRotation.x = this->jewelCsRotation.y = this->jewelCsRotation.z = 0; sSfxJewelId[0] = 0; } /** * Initializes information for Get Item actors. * These are the Medal and Light Arrow actors. */ void DemoEffect_InitGetItem(DemoEffect* this) { this->getItem.isPositionInit = 0; this->getItem.isLoaded = 0; this->initDrawFunc = DemoEffect_DrawGetItem; this->initUpdateFunc = DemoEffect_UpdateGetItem; Actor_SetScale(&this->actor, 0.25f); this->csActionId = 6; } /** * Main Actor Init function */ void DemoEffect_Init(Actor* thisx, GlobalContext* globalCtx2) { GlobalContext* globalCtx = globalCtx2; DemoEffect* this = (DemoEffect*)thisx; s32 effectType; s32 lightEffect; s32 objectIndex; DemoEffect* crystalLight; DemoEffect* lightRing; effectType = (this->actor.params & 0x00FF); lightEffect = ((this->actor.params & 0xF000) >> 12); osSyncPrintf(VT_FGCOL(CYAN) " no = %d\n" VT_RST, effectType); objectIndex = sEffectTypeObjects[effectType] == OBJECT_GAMEPLAY_KEEP ? 0 : Object_GetIndex(&globalCtx->objectCtx, sEffectTypeObjects[effectType]); osSyncPrintf(VT_FGCOL(CYAN) " bank_ID = %d\n" VT_RST, objectIndex); if (objectIndex < 0) { ASSERT(objectIndex < 0); } else { this->initObjectBankIndex = objectIndex; } this->effectFlags = 0; Actor_SetScale(&this->actor, 0.2f); switch (effectType) { case DEMO_EFFECT_CRYSTAL_LIGHT: this->initDrawFunc = DemoEffect_DrawCrystalLight; this->initUpdateFunc = DemoEffect_UpdateCrystalLight; break; case DEMO_EFFECT_FIRE_BALL: this->initDrawFunc = DemoEffect_DrawFireBall; this->initUpdateFunc = DemoEffect_UpdatePositionToParent; Actor_SetScale(&this->actor, 0.1f); break; case DEMO_EFFECT_BLUE_ORB: this->initDrawFunc = DemoEffect_DrawBlueOrb; this->initUpdateFunc = DemoEffect_UpdateBlueOrbGrow; this->blueOrb.alpha = 255; this->blueOrb.scale = 5; this->blueOrb.rotation = 0; Actor_SetScale(&this->actor, 0.05f); this->primXluColor[0] = 188; this->primXluColor[1] = 255; this->primXluColor[2] = 255; this->envXluColor[1] = 100; this->envXluColor[2] = 255; this->envXluColor[0] = 0; break; case DEMO_EFFECT_LIGHT: this->initDrawFunc = DemoEffect_DrawLightEffect; this->initUpdateFunc = DemoEffect_UpdateLightEffect; this->light.alpha = 255; this->light.scaleFlag = 0; this->light.flicker = 0; this->light.rotation = 0; switch (lightEffect) { case DEMO_EFFECT_LIGHT_RED: this->primXluColor[0] = 255; this->primXluColor[1] = 255; this->primXluColor[2] = 255; this->envXluColor[1] = 50; this->envXluColor[0] = 255; this->envXluColor[2] = 0; break; case DEMO_EFFECT_LIGHT_BLUE: this->primXluColor[0] = 255; this->primXluColor[1] = 255; this->primXluColor[2] = 255; this->envXluColor[1] = 150; this->envXluColor[0] = 0; this->envXluColor[2] = 255; break; case DEMO_EFFECT_LIGHT_GREEN: this->primXluColor[0] = 255; this->primXluColor[1] = 255; this->primXluColor[2] = 255; this->envXluColor[1] = 200; this->envXluColor[0] = 0; this->envXluColor[2] = 0; break; case DEMO_EFFECT_LIGHT_ORANGE: this->primXluColor[0] = 255; this->primXluColor[1] = 255; this->primXluColor[2] = 255; this->envXluColor[1] = 150; this->envXluColor[0] = 255; this->envXluColor[2] = 0; break; case DEMO_EFFECT_LIGHT_YELLOW: this->primXluColor[0] = 255; this->primXluColor[1] = 255; this->primXluColor[2] = 255; this->envXluColor[0] = 200; this->envXluColor[1] = 255; this->envXluColor[2] = 0; break; case DEMO_EFFECT_LIGHT_PURPLE: this->primXluColor[0] = 255; this->primXluColor[1] = 255; this->primXluColor[2] = 255; // clang-format off this->envXluColor[0] = 200; this->envXluColor[1] = 50; this->envXluColor[2] = 255; // Sameline prevents reordering // clang-format on break; case DEMO_EFFECT_LIGHT_GREEN2: this->primXluColor[0] = 255; this->primXluColor[1] = 255; this->primXluColor[2] = 255; this->envXluColor[1] = 200; this->envXluColor[0] = 0; this->envXluColor[2] = 0; break; } this->csActionId = 7; Actor_SetScale(thisx, 0.0f); break; case DEMO_EFFECT_LGT_SHOWER: this->lgtShower.alpha = 255; this->initDrawFunc = DemoEffect_DrawLgtShower; this->initUpdateFunc = DemoEffect_UpdateLgtShower; break; case DEMO_EFFECT_GOD_LGT_DIN: Actor_SetScale(&this->actor, 0.1f); this->initDrawFunc = DemoEffect_DrawGodLgt; this->primXluColor[1] = 170; this->primXluColor[0] = 255; this->primXluColor[2] = 255; this->envXluColor[0] = 255; this->envXluColor[2] = 255; this->envXluColor[1] = 0; this->godLgt.type = GOD_LGT_DIN; this->godLgt.rotation = 0; this->initUpdateFunc = DemoEffect_UpdateGodLgtDin; this->csActionId = 0; break; case DEMO_EFFECT_GOD_LGT_NAYRU: if (gSaveContext.entranceIndex == 0x013D) { Actor_SetScale(&this->actor, 1.0f); } else { Actor_SetScale(&this->actor, 0.1f); } this->initDrawFunc = DemoEffect_DrawGodLgt; this->primXluColor[0] = 170; this->primXluColor[1] = 255; this->primXluColor[2] = 255; this->envXluColor[1] = 40; this->envXluColor[2] = 255; this->envXluColor[0] = 0; this->godLgt.type = GOD_LGT_NAYRU; this->godLgt.lightRingSpawnDelay = 4; this->godLgt.rotation = 0; this->godLgt.lightRingSpawnTimer = 0; this->initUpdateFunc = DemoEffect_UpdateGodLgtNayru; this->csActionId = 1; break; case DEMO_EFFECT_GOD_LGT_FARORE: if (gSaveContext.entranceIndex == 0x00EE) { Actor_SetScale(&this->actor, 2.4f); } else { Actor_SetScale(&this->actor, 0.1f); } this->initDrawFunc = DemoEffect_DrawGodLgt; this->primXluColor[0] = 170; this->primXluColor[2] = 170; this->primXluColor[1] = 255; this->envXluColor[1] = 200; this->envXluColor[0] = 0; this->envXluColor[2] = 0; this->godLgt.type = GOD_LGT_FARORE; this->godLgt.rotation = 0; this->initUpdateFunc = DemoEffect_UpdateGodLgtFarore; this->csActionId = 2; break; case DEMO_EFFECT_LIGHTRING_EXPANDING: this->initDrawFunc = DemoEffect_DrawLightRing; this->initUpdateFunc = DemoEffect_UpdateLightRingExpanding; this->lightRing.timer = 20; this->lightRing.timerIncrement = 4; this->lightRing.alpha = 255; break; case DEMO_EFFECT_LIGHTRING_TRIFORCE: this->initDrawFunc = DemoEffect_DrawLightRing; this->initUpdateFunc = DemoEffect_UpdateLightRingTriforce; this->lightRing.timer = 20; this->lightRing.timerIncrement = 4; this->lightRing.alpha = 0; this->csActionId = 4; break; case DEMO_EFFECT_LIGHTRING_SHRINKING: this->initDrawFunc = DemoEffect_DrawLightRing; this->initUpdateFunc = DemoEffect_UpdateLightRingShrinking; this->lightRing.timer = 351; this->lightRing.timerIncrement = 2; this->lightRing.alpha = 0; break; case DEMO_EFFECT_TRIFORCE_SPOT: this->initDrawFunc = DemoEffect_DrawTriforceSpot; this->initUpdateFunc = DemoEffect_UpdateTriforceSpot; this->triforceSpot.crystalLightOpacity = 0; this->triforceSpot.lightColumnOpacity = 0; this->triforceSpot.triforceSpotOpacity = 0; this->triforceSpot.rotation = 0; this->primXluColor[0] = 0; this->csActionId = 3; Actor_SetScale(&this->actor, 0.020f); crystalLight = (DemoEffect*)Actor_SpawnAsChild( &globalCtx->actorCtx, &this->actor, globalCtx, ACTOR_DEMO_EFFECT, this->actor.world.pos.x, this->actor.world.pos.y, this->actor.world.pos.z, 0, 0, 0, DEMO_EFFECT_CRYSTAL_LIGHT); if (crystalLight != NULL) { Actor_SetScale(&crystalLight->actor, 0.6f); } lightRing = (DemoEffect*)Actor_SpawnAsChild( &globalCtx->actorCtx, &crystalLight->actor, globalCtx, ACTOR_DEMO_EFFECT, this->actor.world.pos.x, this->actor.world.pos.y, this->actor.world.pos.z, 0, 0, 0, DEMO_EFFECT_LIGHTRING_TRIFORCE); if (lightRing != NULL) { Actor_SetScale(&lightRing->actor, 0.4f); } break; case DEMO_EFFECT_MEDAL_FIRE: DemoEffect_InitGetItem(this); this->getItem.drawId = GID_MEDALLION_FIRE; break; case DEMO_EFFECT_MEDAL_WATER: DemoEffect_InitGetItem(this); this->getItem.drawId = GID_MEDALLION_WATER; break; case DEMO_EFFECT_MEDAL_FOREST: DemoEffect_InitGetItem(this); this->getItem.drawId = GID_MEDALLION_FOREST; break; case DEMO_EFFECT_MEDAL_SPIRIT: DemoEffect_InitGetItem(this); this->getItem.drawId = GID_MEDALLION_SPIRIT; break; case DEMO_EFFECT_MEDAL_SHADOW: DemoEffect_InitGetItem(this); this->getItem.drawId = GID_MEDALLION_SHADOW; break; case DEMO_EFFECT_MEDAL_LIGHT: DemoEffect_InitGetItem(this); this->getItem.drawId = GID_MEDALLION_LIGHT; break; case DEMO_EFFECT_LIGHTARROW: DemoEffect_InitGetItem(this); this->getItem.drawId = GID_ARROW_LIGHT; break; case DEMO_EFFECT_TIMEWARP_TIMEBLOCK_LARGE: case DEMO_EFFECT_TIMEWARP_TIMEBLOCK_SMALL: this->actor.flags |= ACTOR_FLAG_25; case DEMO_EFFECT_TIMEWARP_MASTERSWORD: this->initDrawFunc = DemoEffect_DrawTimeWarp; this->initUpdateFunc = DemoEffect_InitTimeWarp; this->envXluColor[0] = 0; this->envXluColor[1] = 100; this->envXluColor[2] = 255; SkelCurve_Clear(&this->skelCurve); this->timeWarp.shrinkTimer = 0; break; case DEMO_EFFECT_JEWEL_KOKIRI: this->jewelDisplayList = gGiKokiriEmeraldGemDL; this->jewelHolderDisplayList = gGiKokiriEmeraldSettingDL; this->jewel.type = DEMO_EFFECT_JEWEL_KOKIRI; this->jewel.isPositionInit = 0; DemoEffect_InitJewel(globalCtx, this); break; case DEMO_EFFECT_JEWEL_GORON: this->jewelDisplayList = gGiGoronRubyGemDL; this->jewelHolderDisplayList = gGiGoronRubySettingDL; this->jewel.type = DEMO_EFFECT_JEWEL_GORON; this->jewel.isPositionInit = 0; DemoEffect_InitJewel(globalCtx, this); break; case DEMO_EFFECT_JEWEL_ZORA: this->jewelDisplayList = gGiZoraSapphireGemDL; this->jewelHolderDisplayList = gGiZoraSapphireSettingDL; this->jewel.type = DEMO_EFFECT_JEWEL_ZORA; this->jewel.isPositionInit = 0; DemoEffect_InitJewel(globalCtx, this); Actor_ChangeCategory(globalCtx, &globalCtx->actorCtx, &this->actor, ACTOR_EN_DOOR); if ((globalCtx->sceneNum == SCENE_BDAN) && (gSaveContext.infTable[20] & 0x20)) { Actor_Kill(&this->actor); return; } break; case DEMO_EFFECT_DUST: this->initDrawFunc = NULL; this->initUpdateFunc = DemoEffect_UpdateDust; this->dust.timer = 0; this->csActionId = 2; break; default: ASSERT(0); break; } ActorShape_Init(&thisx->shape, 0.0f, NULL, 0.0f); DemoEffect_SetupUpdate(this, DemoEffect_Wait); } /** * Main Actor Destroy function */ void DemoEffect_Destroy(Actor* thisx, GlobalContext* globalCtx) { DemoEffect* this = (DemoEffect*)thisx; s32 effectType = (this->actor.params & 0x00FF); if (effectType == DEMO_EFFECT_TIMEWARP_MASTERSWORD || effectType == DEMO_EFFECT_TIMEWARP_TIMEBLOCK_LARGE || effectType == DEMO_EFFECT_TIMEWARP_TIMEBLOCK_SMALL) { SkelCurve_Destroy(globalCtx, &this->skelCurve); } } /** * This update function waits until the associate object is loaded. * Once the object is loaded, it will copy over the initUpdateFunc/initDrawFunc funcs to be active. * They are copied to actor.draw and updateFunc. * initUpdateFunc/initDrawFunc are set during initialization and are NOT executed. */ void DemoEffect_Wait(DemoEffect* this, GlobalContext* globalCtx) { if (Object_IsLoaded(&globalCtx->objectCtx, this->initObjectBankIndex)) { this->actor.objBankIndex = this->initObjectBankIndex; this->actor.draw = this->initDrawFunc; this->updateFunc = this->initUpdateFunc; osSyncPrintf(VT_FGCOL(CYAN) " 転送終了 move_wait " VT_RST); } } /** * Copies the current Actor's position to the parent Actor's position. */ void DemoEffect_UpdatePositionToParent(DemoEffect* this, GlobalContext* globalCtx) { if (this->actor.parent != NULL) { // Struct copy affects regalloc this->actor.world.pos.x = this->actor.parent->world.pos.x; this->actor.world.pos.y = this->actor.parent->world.pos.y; this->actor.world.pos.z = this->actor.parent->world.pos.z; } } /** * Update function for the Crystal Light actor. * The Crystal Light actor is the three beams of light under the Triforce that converge on it. * The Crystal Light's position is set to the parent Actor (Triforce) each frame. * If the Crystal Light has no parent Actor, then it will raise into the sky. */ void DemoEffect_UpdateCrystalLight(DemoEffect* this, GlobalContext* globalCtx) { DemoEffect_UpdatePositionToParent(this, globalCtx); this->actor.world.pos.y += 14.0f; } /** * Spawns sparkle effects for Medals */ void DemoEffect_MedalSparkle(DemoEffect* this, GlobalContext* globalCtx, s32 isSmallSpawner) { Vec3f velocity; Vec3f accel; Vec3f pos; Color_RGBA8 primColor; Color_RGBA8 envColor; if (isSmallSpawner != 1 || (globalCtx->gameplayFrames & 1) == 0) { primColor.r = 255; primColor.g = 255; primColor.b = 255; envColor.r = 255; envColor.g = 255; envColor.b = 100; primColor.a = 0; velocity.y = 0.0f; accel.x = 0.0f; accel.y = -0.1f; accel.z = 0.0f; if (isSmallSpawner) { velocity.x = Rand_ZeroOne() - 0.5f; velocity.z = Rand_ZeroOne() - 0.5f; } else { velocity.x = (Rand_ZeroOne() - 0.5f) * 2.0f; velocity.z = (Rand_ZeroOne() - 0.5f) * 2.0f; } pos.x = Rand_CenteredFloat(10.0f) + this->actor.world.pos.x; pos.y = Rand_CenteredFloat(10.0f) + this->actor.world.pos.y; pos.z = Rand_CenteredFloat(10.0f) + this->actor.world.pos.z; EffectSsKiraKira_SpawnDispersed(globalCtx, &pos, &velocity, &accel, &primColor, &envColor, 1000, 16); } } /** * Update function for the GetItem Actors. * Medals and Light Arrows. * It spawns Medal Sparkle Effects and scales/moves the Actor based on the current Cutscene Action */ void DemoEffect_UpdateGetItem(DemoEffect* this, GlobalContext* globalCtx) { Actor* thisx = &this->actor; if (globalCtx->csCtx.state != CS_STATE_IDLE && globalCtx->csCtx.npcActions[this->csActionId] != NULL) { if (this->getItem.isPositionInit) { DemoEffect_MoveGetItem(this, globalCtx, this->csActionId, 0.1f); } else { DemoEffect_InitPositionFromCsAction(this, globalCtx, this->csActionId); this->getItem.isPositionInit = 1; } if (this->getItem.drawId != GID_ARROW_LIGHT) { this->actor.shape.rot.x = 0xE0C0; } else { this->actor.shape.rot.y += 0x0400; } Actor_SetScale(thisx, 0.20f); if (gSaveContext.entranceIndex == 0x0053 || (gSaveContext.n64ddFlag && gSaveContext.entranceIndex == 0x05F4)) { switch (globalCtx->csCtx.npcActions[this->csActionId]->action) { case 2: DemoEffect_MedalSparkle(this, globalCtx, 0); break; case 3: DemoEffect_MedalSparkle(this, globalCtx, 1); break; } } switch (globalCtx->csCtx.npcActions[this->csActionId]->action) { case 2: if (gSaveContext.entranceIndex == 0x0053 || (gSaveContext.n64ddFlag && gSaveContext.entranceIndex == 0x05F4)) { Audio_PlayActorSound2(thisx, NA_SE_EV_MEDAL_APPEAR_L - SFX_FLAG); } else { func_800788CC(NA_SE_EV_MEDAL_APPEAR_S - SFX_FLAG); } if (this->getItem.drawId != GID_ARROW_LIGHT) { this->actor.shape.rot.y += 0x3E80; } this->getItem.rotation = 0x3E80; break; case 3: this->getItem.rotation -= (s16)((this->getItem.rotation - 0x03E8) * 0.10f); if (this->getItem.drawId != GID_ARROW_LIGHT) { this->actor.shape.rot.y += this->getItem.rotation; } if (gSaveContext.entranceIndex == 0x0053 || (gSaveContext.n64ddFlag && gSaveContext.entranceIndex == 0x05F4)) { Audio_PlayActorSound2(thisx, NA_SE_EV_MEDAL_APPEAR_L - SFX_FLAG); } else { func_800788CC(NA_SE_EV_MEDAL_APPEAR_S - SFX_FLAG); } break; case 4: Audio_PlayActorSound2(thisx, NA_SE_EV_MEDAL_APPEAR_S - SFX_FLAG); break; } } } /** * Initializes Timewarp Actors. * This is an Update Function that is only ran for one frame. * Timewarp actors are spawned when Link... * 1) Pulls the Master Sword * 2) Returns from the Chamber of Sages for the first time * 3) Timeblock is cleared with the Song of Time (Large and Small have different versions of Timewarp) */ void DemoEffect_InitTimeWarp(DemoEffect* this, GlobalContext* globalCtx) { s32 effectType = (this->actor.params & 0x00FF); if (!SkelCurve_Init(globalCtx, &this->skelCurve, &gTimeWarpSkel, &gTimeWarpAnim)) { ASSERT(!SkelCurve_Init(globalCtx, &this->skelCurve, &gTimeWarpSkel, &gTimeWarpAnim)); } if (effectType == DEMO_EFFECT_TIMEWARP_TIMEBLOCK_LARGE || effectType == DEMO_EFFECT_TIMEWARP_TIMEBLOCK_SMALL) { SkelCurve_SetAnim(&this->skelCurve, &gTimeWarpAnim, 1.0f, 59.0f, 1.0f, 1.7f); SkelCurve_Update(globalCtx, &this->skelCurve); this->updateFunc = DemoEffect_InitTimeWarpTimeblock; if (effectType == DEMO_EFFECT_TIMEWARP_TIMEBLOCK_LARGE) { Actor_SetScale(&this->actor, 0.14f); } else { Actor_SetScale(&this->actor, 84 * 0.001f); } } else if (gSaveContext.sceneSetupIndex == 5 || gSaveContext.sceneSetupIndex == 4 || (gSaveContext.entranceIndex == 0x0324 && !((gSaveContext.eventChkInf[12] & 0x200)))) { SkelCurve_SetAnim(&this->skelCurve, &gTimeWarpAnim, 1.0f, 59.0f, 59.0f, 0.0f); SkelCurve_Update(globalCtx, &this->skelCurve); this->updateFunc = DemoEffect_UpdateTimeWarpReturnFromChamberOfSages; osSyncPrintf(VT_FGCOL(CYAN) " 縮むバージョン \n" VT_RST); } else { SkelCurve_SetAnim(&this->skelCurve, &gTimeWarpAnim, 1.0f, 59.0f, 1.0f, 1.0f); SkelCurve_Update(globalCtx, &this->skelCurve); this->updateFunc = DemoEffect_UpdateTimeWarpPullMasterSword; osSyncPrintf(VT_FGCOL(CYAN) " 通常 バージョン \n" VT_RST); } } /** * Update function for the Timewarp Actor that is used when Link pulls the Mastersword * It changes the Background Music and updates its SkelCurve animation. */ void DemoEffect_UpdateTimeWarpPullMasterSword(DemoEffect* this, GlobalContext* globalCtx) { if (Flags_GetEnv(globalCtx, 1)) { if (!(this->effectFlags & 0x2)) { func_800F3F3C(0); this->effectFlags |= 0x2; } if (SkelCurve_Update(globalCtx, &this->skelCurve)) { SkelCurve_SetAnim(&this->skelCurve, &gTimeWarpAnim, 1.0f, 60.0f, 59.0f, 0.0f); } } } /** * Shrinks the Timewarp object vertices. * Used by the Chamber of Sages return timewarp and Timeblock clear timewarp. */ void DemoEffect_TimewarpShrink(f32 size) { Vtx* vertices; s32 i; u8 sizes[3]; // This function uses the data in obj_efc_tw offset 0x0060 to 0x01B0 vertices = ResourceMgr_LoadVtxByName(SEGMENTED_TO_VIRTUAL(gTimeWarpVtx)); sizes[0] = 0; sizes[1] = (s32)(202.0f * size); sizes[2] = (s32)(255.0f * size); for (i = 0; i < 21; i++) { if (sTimewarpVertexSizeIndices[i] != 0) { vertices[i].v.cn[3] = sizes[sTimewarpVertexSizeIndices[i]]; } } } /** * Update function for the Timewarp Actor that is used when Link returns from the Chamber of Sages for the first time. * It shrinks the timewarp vertices and scales the Actor. */ void DemoEffect_UpdateTimeWarpReturnFromChamberOfSages(DemoEffect* this, GlobalContext* globalCtx) { f32 shrinkProgress; this->timeWarp.shrinkTimer++; if (this->timeWarp.shrinkTimer > 250) { if (gSaveContext.entranceIndex == 0x0324) { gSaveContext.eventChkInf[12] |= 0x200; } Actor_Kill(&this->actor); return; } if (this->timeWarp.shrinkTimer > 100) { shrinkProgress = (250 - this->timeWarp.shrinkTimer) * (1.0f / 750.0f); this->actor.scale.x = shrinkProgress; this->actor.scale.z = shrinkProgress; DemoEffect_TimewarpShrink(shrinkProgress * 5.0f); } func_8002F948(&this->actor, NA_SE_EV_TIMETRIP_LIGHT - SFX_FLAG); } /** * Update function for the Timewarp Actor that is used when a Timeblock is cleared. * It shrinks the timewarp vertices and scales the Actor. */ void DemoEffect_UpdateTimeWarpTimeblock(DemoEffect* this, GlobalContext* globalCtx) { f32 shrinkProgress; f32 scale; this->timeWarp.shrinkTimer++; if (this->timeWarp.shrinkTimer <= 100) { shrinkProgress = (100 - this->timeWarp.shrinkTimer) * 0.010f; scale = shrinkProgress * 0.14f; if ((this->actor.params & 0x00FF) == DEMO_EFFECT_TIMEWARP_TIMEBLOCK_SMALL) { scale *= 0.6f; } this->actor.scale.x = scale; this->actor.scale.z = scale; DemoEffect_TimewarpShrink(shrinkProgress); func_8002F948(&this->actor, NA_SE_EV_TIMETRIP_LIGHT - SFX_FLAG); return; } DemoEffect_TimewarpShrink(1.0f); Actor_Kill(&this->actor); } /** * Initializes information for the Timewarp Actor used for the Timeblock clear effect. * This is an Update Func that is only ran for one frame. */ void DemoEffect_InitTimeWarpTimeblock(DemoEffect* this, GlobalContext* globalCtx) { func_8002F948(&this->actor, NA_SE_EV_TIMETRIP_LIGHT - SFX_FLAG); if (SkelCurve_Update(globalCtx, &this->skelCurve)) { SkelCurve_SetAnim(&this->skelCurve, &gTimeWarpAnim, 1.0f, 60.0f, 59.0f, 0.0f); this->updateFunc = DemoEffect_UpdateTimeWarpTimeblock; this->timeWarp.shrinkTimer = 0; } } /** * Update function for the Triforce Actor. * It rotates and updates the alpha of the Triforce and child actors. */ void DemoEffect_UpdateTriforceSpot(DemoEffect* this, GlobalContext* globalCtx) { this->triforceSpot.rotation += 0x03E8; if (globalCtx->csCtx.state != CS_STATE_IDLE && globalCtx->csCtx.npcActions[this->csActionId] != NULL) { DemoEffect_MoveToCsEndpoint(this, globalCtx, this->csActionId, 0); if (globalCtx->csCtx.npcActions[this->csActionId]->action == 2) { if (this->primXluColor[0] < 140) { this->primXluColor[0]++; } if (this->primXluColor[0] < 30) { this->triforceSpot.triforceSpotOpacity = ((s32)this->primXluColor[0]) * 8.5f; } else { this->triforceSpot.triforceSpotOpacity = 255; if (this->primXluColor[0] < 60) { this->triforceSpot.lightColumnOpacity = (((s32)this->primXluColor[0]) - 30) * 8.5f; } else { if (this->primXluColor[0] <= 140) { this->triforceSpot.lightColumnOpacity = 255; this->triforceSpot.crystalLightOpacity = (((s32)this->primXluColor[0]) - 60) * 3.1875f; } } } } if (gSaveContext.entranceIndex == 0x00A0 && gSaveContext.sceneSetupIndex == 6 && globalCtx->csCtx.frames == 143) { Audio_PlayActorSound2(&this->actor, NA_SE_IT_DM_RING_EXPLOSION); } } } /** * Update function for the LightRing actor that shrinks. * This is used in the creation cutscene when Din leaves a fireball that explodes into Death Mountain. */ void DemoEffect_UpdateLightRingShrinking(DemoEffect* this, GlobalContext* globalCtx) { if (this->lightRing.timer < this->lightRing.timerIncrement) { Actor_Kill(&this->actor); this->lightRing.timer = 0; } else { this->lightRing.timer -= this->lightRing.timerIncrement; } if (this->lightRing.timer <= 255) { if (this->lightRing.timer >= 225) { this->lightRing.alpha = (-this->lightRing.timer * 8) + 2048; } else { this->lightRing.alpha = 255; } } if (this->lightRing.timer == 255) { func_800F3F3C(5); } } /** * Update function for the Lightring Actor that expands. * These are spawned by Nayru. * These are also used by Din in the creation cutscene when she leaves a fireball that explodes into Death Mountain. */ void DemoEffect_UpdateLightRingExpanding(DemoEffect* this, GlobalContext* globalCtx) { DemoEffect_UpdatePositionToParent(this, globalCtx); this->lightRing.timer += this->lightRing.timerIncrement; if (this->lightRing.timer >= 225) { this->lightRing.alpha = (-this->lightRing.timer * 8) + 2048; } if (this->lightRing.timer > 255) { this->lightRing.timer = 255; Actor_Kill(&this->actor); this->lightRing.timer = 0; } } /** * Update function for the Lightring Actor that expands. This is a special version for the Triforce Actor. * This version spawns a blue orb when the cutscene action state is set to 2. * Once the Blue Orb Actor is spawned the Update Function is changed to the regular Light Ring Expanding Update Func. */ void DemoEffect_UpdateLightRingTriforce(DemoEffect* this, GlobalContext* globalCtx) { DemoEffect* blueOrb; DemoEffect_UpdatePositionToParent(this, globalCtx); if (globalCtx->csCtx.state != CS_STATE_IDLE) { if (globalCtx->csCtx.npcActions[this->csActionId] != NULL && globalCtx->csCtx.npcActions[this->csActionId]->action == 2) { blueOrb = (DemoEffect*)Actor_Spawn(&globalCtx->actorCtx, globalCtx, ACTOR_DEMO_EFFECT, this->actor.world.pos.x, this->actor.world.pos.y, this->actor.world.pos.z, 0, 0, 0, DEMO_EFFECT_BLUE_ORB); if (blueOrb != NULL) { Actor_SetScale(&blueOrb->actor, 0.0f); } this->updateFunc = DemoEffect_UpdateLightRingExpanding; this->lightRing.alpha = 255; } } } /** * Update function for the FireBall Actor. * This is a special version that is used in the creation cutscene. * It moves based on gravity and decrements a timer until zero. Once the timer is zero it will spawn other Actors: * A Blue Orb Actor, and a Light Ring Expanding Actor, and a Light Ring Shrinking Actor. */ void DemoEffect_UpdateCreationFireball(DemoEffect* this, GlobalContext* globalCtx) { DemoEffect* effect; Actor_MoveForward(&this->actor); this->actor.speedXZ = this->actor.speedXZ + (this->actor.gravity * 0.5f); if (this->fireBall.timer != 0) { this->fireBall.timer--; return; } effect = (DemoEffect*)Actor_Spawn(&globalCtx->actorCtx, globalCtx, ACTOR_DEMO_EFFECT, this->actor.world.pos.x, this->actor.world.pos.y, this->actor.world.pos.z, 0, 0, 0, DEMO_EFFECT_BLUE_ORB); if (effect != NULL) { Actor_SetScale(&effect->actor, 0.0f); } effect = (DemoEffect*)Actor_Spawn(&globalCtx->actorCtx, globalCtx, ACTOR_DEMO_EFFECT, this->actor.world.pos.x, this->actor.world.pos.y, this->actor.world.pos.z, 0, 0, 0, DEMO_EFFECT_LIGHTRING_EXPANDING); if (effect != NULL) { Actor_SetScale(&effect->actor, 0.1f); } effect = (DemoEffect*)Actor_Spawn(&globalCtx->actorCtx, globalCtx, ACTOR_DEMO_EFFECT, this->actor.world.pos.x, this->actor.world.pos.y, this->actor.world.pos.z, 0, 0, 0, DEMO_EFFECT_LIGHTRING_SHRINKING); if (effect != NULL) { Actor_SetScale(&effect->actor, 0.2f); } func_800788CC(NA_SE_IT_DM_RING_EXPLOSION); Actor_Kill(&this->actor); } /** * Initialization function for the FireBall Actor. * This is a special version that is used in the creation cutscene. * It is an Update Function only executed for one frame. The Update Function is then changed to UpdateCreationFireball. */ void DemoEffect_InitCreationFireball(DemoEffect* this, GlobalContext* globalCtx) { Actor* parent = this->actor.parent; this->actor.world.rot.y = parent->shape.rot.y; this->fireBall.timer = 50; this->actor.speedXZ = 1.5f; this->actor.minVelocityY = -1.5f; this->actor.gravity = -0.03f; this->updateFunc = DemoEffect_UpdateCreationFireball; } /** * Update action for the Blue Orb Actor. * This Update Function is run while the Blue Orb is Shrinking. * The Blue Orb Actor is the blue light sparkle that is in Din's creation cutscene. * It's spawned in the middle of the expanding Light Ring. * The Blue Orb Actor shrinks after it grows to max size. */ void DemoEffect_UpdateBlueOrbShrink(DemoEffect* this, GlobalContext* globalCtx) { this->blueOrb.alpha = this->blueOrb.scale * 16; this->blueOrb.scale--; Actor_SetScale(&this->actor, this->actor.scale.x * 0.9f); if (this->blueOrb.scale == 0) { Actor_Kill(&this->actor); } } /** * Update action for the Blue Orb Actor. * This Update Function is run while the Blue Orb is Growing. * The Blue Orb Actor is the blue light sparkle that is in Din's creation cutscene. * It's spawned in the middle of the expanding Light Ring. * When the scale timer value reaches 0 the Blue Orb's Update Function changes to UpdateBlueOrbShrink. */ void DemoEffect_UpdateBlueOrbGrow(DemoEffect* this, GlobalContext* globalCtx) { if (this->actor.parent != NULL) { // s32 cast necessary to match codegen. Without the explicit cast to u32 the compiler generates complex cast of // u8 to float Actor_SetScale(&this->actor, (((5.0f - (s32)this->blueOrb.scale) * 0.01f) * 10.0f) * this->actor.parent->scale.x); } else { Actor_SetScale(&this->actor, (5.0f - (s32)this->blueOrb.scale) * 0.01f); } if (this->blueOrb.scale != 0) { this->blueOrb.scale--; } else { this->blueOrb.scale = 15; this->updateFunc = DemoEffect_UpdateBlueOrbShrink; } } /** * Update action for the Light Effect Actor. * The Light Effect has various use cases. * This function updates the position and scale of the actor based on the current cutscene command. */ void DemoEffect_UpdateLightEffect(DemoEffect* this, GlobalContext* globalCtx) { u16 action; s32 isLargeSize; isLargeSize = ((this->actor.params & 0x0F00) >> 8); if (globalCtx->csCtx.state != CS_STATE_IDLE && globalCtx->csCtx.npcActions[this->csActionId] != NULL) { DemoEffect_MoveToCsEndpoint(this, globalCtx, this->csActionId, 0); switch (globalCtx->csCtx.npcActions[this->csActionId]->action) { case 2: if (this->light.rotation < 240) { if (!isLargeSize) { if (this->actor.scale.x < 0.23f) { this->actor.scale.x += 0.001f; Actor_SetScale(&this->actor, this->actor.scale.x); } } else { if (this->actor.scale.x < 2.03f) { this->actor.scale.x += 0.05f; Actor_SetScale(&this->actor, this->actor.scale.x); } } } this->light.rotation += 6; this->light.scaleFlag += 1; break; case 3: Math_SmoothStepToF(&this->actor.scale.x, 0.0f, 0.1f, 0.1f, 0.005f); Actor_SetScale(&this->actor, this->actor.scale.x); break; default: break; } if (globalCtx->sceneNum == SCENE_SPOT04 && gSaveContext.sceneSetupIndex == 6 && globalCtx->csCtx.frames == 197) { Audio_PlayActorSound2(&this->actor, NA_SE_EV_WHITE_OUT); } if (globalCtx->sceneNum == SCENE_SPOT16 && gSaveContext.sceneSetupIndex == 5) { if (!DemoEffect_CheckCsAction(this, globalCtx, 1)) { Audio_PlayActorSound2(&this->actor, NA_SE_EV_LIGHT_GATHER - SFX_FLAG); } if (globalCtx->csCtx.frames == 640) { Audio_PlayActorSound2(&this->actor, NA_SE_EV_WHITE_OUT); } } if (globalCtx->sceneNum == SCENE_SPOT08 && gSaveContext.sceneSetupIndex == 4) { if (!DemoEffect_CheckCsAction(this, globalCtx, 1)) { Audio_PlayActorSound2(&this->actor, NA_SE_EV_LIGHT_GATHER - SFX_FLAG); } if (globalCtx->csCtx.frames == 648) { Audio_PlayActorSound2(&this->actor, NA_SE_EV_WHITE_OUT); } } if (globalCtx->sceneNum == SCENE_TOKINOMA && gSaveContext.sceneSetupIndex == 14) { if (globalCtx->csCtx.npcActions[this->csActionId]->action == 2) { Audio_PlayActorSound2(&this->actor, NA_SE_EV_LIGHT_GATHER - SFX_FLAG); } } if (globalCtx->sceneNum == SCENE_DAIYOUSEI_IZUMI || globalCtx->sceneNum == SCENE_YOUSEI_IZUMI_YOKO) { if (globalCtx->csCtx.npcActions[this->csActionId]->action == 2) { Audio_PlayActorSound2(&this->actor, NA_SE_EV_LIGHT_GATHER - SFX_FLAG); } } } } /** * Update action for the Lgt Shower Actor. * The Lgt Shower Actor is the green light effect spawned by Farore in the Kokiri Forst creation cutscene. * This function updates the scale and alpha of the Actor. */ void DemoEffect_UpdateLgtShower(DemoEffect* this, GlobalContext* globalCtx) { if (this->lgtShower.alpha > 3) { this->lgtShower.alpha -= 3; this->actor.scale.x *= 1.05f; this->actor.scale.y *= 1.05f; this->actor.scale.z *= 1.05f; } else { Actor_Kill(&this->actor); } } /** * Update action for the God Lgt Din Actor. * This is the Goddess Din. * This function moves God Lgt Din based on the current cutscene command. * This function also spawns a Fireball Actor and sets its update function to the special InitCreationFireball. * The spawned Fireball Actor is also scaled to be smaller than regular by this function. */ void DemoEffect_UpdateGodLgtDin(DemoEffect* this, GlobalContext* globalCtx) { DemoEffect* fireBall; if (globalCtx->csCtx.state != CS_STATE_IDLE && globalCtx->csCtx.npcActions[this->csActionId] != NULL) { DemoEffect_MoveToCsEndpoint(this, globalCtx, this->csActionId, 1); if (globalCtx->csCtx.npcActions[this->csActionId]->action == 3) { fireBall = (DemoEffect*)Actor_SpawnAsChild(&globalCtx->actorCtx, &this->actor, globalCtx, ACTOR_DEMO_EFFECT, this->actor.world.pos.x, this->actor.world.pos.y, this->actor.world.pos.z, 0, 0, 0, DEMO_EFFECT_FIRE_BALL); if (fireBall != NULL) { fireBall->initUpdateFunc = DemoEffect_InitCreationFireball; Actor_SetScale(&fireBall->actor, 0.020f); } } if (gSaveContext.entranceIndex == 0x00A0) { switch (gSaveContext.sceneSetupIndex) { case 4: if (globalCtx->csCtx.frames == 288) { Audio_PlayActorSound2(&this->actor, NA_SE_IT_DM_FLYING_GOD_PASS); } if (globalCtx->csCtx.frames == 635) { Audio_PlayActorSound2(&this->actor, NA_SE_IT_DM_FLYING_GOD_PASS); } break; case 6: if (globalCtx->csCtx.frames == 55) { Audio_PlayActorSound2(&this->actor, NA_SE_IT_DM_FLYING_GOD_DASH); } break; case 11: if (globalCtx->csCtx.frames == 350) { Audio_PlayActorSound2(&this->actor, NA_SE_IT_DM_FLYING_GOD_DASH); } break; } } } } /** * Update action for the God Lgt Nayru Actor. * This is the Goddess Nayru. * This function moves God Lgt Nayure based on the current cutscene command. * This function also spawns expanding light rings around Nayru in the creation cutscene */ void DemoEffect_UpdateGodLgtNayru(DemoEffect* this, GlobalContext* globalCtx) { DemoEffect* lightRing; if (globalCtx->csCtx.state != CS_STATE_IDLE && globalCtx->csCtx.npcActions[this->csActionId] != NULL) { DemoEffect_MoveToCsEndpoint(this, globalCtx, this->csActionId, 1); if (globalCtx->csCtx.npcActions[this->csActionId]->action == 3) { if (this->godLgt.lightRingSpawnTimer != 0) { this->godLgt.lightRingSpawnTimer--; } else { this->godLgt.lightRingSpawnTimer = this->godLgt.lightRingSpawnDelay; lightRing = (DemoEffect*)Actor_Spawn( &globalCtx->actorCtx, globalCtx, ACTOR_DEMO_EFFECT, this->actor.world.pos.x, this->actor.world.pos.y, this->actor.world.pos.z, this->actor.world.rot.x + 0x4000, this->actor.world.rot.y, this->actor.world.rot.z, DEMO_EFFECT_LIGHTRING_EXPANDING); if (lightRing != NULL) { Actor_SetScale(&lightRing->actor, 1.0f); } } } if (gSaveContext.entranceIndex == 0x00A0) { switch (gSaveContext.sceneSetupIndex) { case 4: if (globalCtx->csCtx.frames == 298) { Audio_PlayActorSound2(&this->actor, NA_SE_IT_DM_FLYING_GOD_PASS); } break; case 6: if (globalCtx->csCtx.frames == 105) { Audio_PlayActorSound2(&this->actor, NA_SE_IT_DM_FLYING_GOD_DASH); } break; case 11: if (globalCtx->csCtx.frames == 360) { Audio_PlayActorSound2(&this->actor, NA_SE_IT_DM_FLYING_GOD_DASH); } break; } } if (gSaveContext.entranceIndex == 0x013D && gSaveContext.sceneSetupIndex == 4) { if (globalCtx->csCtx.frames == 72) { Audio_PlayActorSound2(&this->actor, NA_SE_IT_DM_FLYING_GOD_DASH); } if (globalCtx->csCtx.frames == 80) { func_800F3F3C(4); } } } } /** * Update action for the God Lgt Farore Actor. * This is the Goddess Farore. * This function moves God Lgt Farore based on the current cutscene command. * This function also spawns an Lgt Shower Actor during the Kokiri creation cutscene. */ void DemoEffect_UpdateGodLgtFarore(DemoEffect* this, GlobalContext* globalCtx) { DemoEffect* lgtShower; if (globalCtx->csCtx.state != CS_STATE_IDLE && globalCtx->csCtx.npcActions[this->csActionId] != NULL) { DemoEffect_MoveToCsEndpoint(this, globalCtx, this->csActionId, 1); if (globalCtx->csCtx.npcActions[this->csActionId]->action == 3) { lgtShower = (DemoEffect*)Actor_SpawnAsChild( &globalCtx->actorCtx, &this->actor, globalCtx, ACTOR_DEMO_EFFECT, this->actor.world.pos.x, this->actor.world.pos.y - 150.0f, this->actor.world.pos.z, 0, 0, 0, DEMO_EFFECT_LGT_SHOWER); if (lgtShower != NULL) { lgtShower->actor.scale.x = 0.23f; lgtShower->actor.scale.y = 0.15f; lgtShower->actor.scale.z = 0.23f; } Audio_PlayActorSound2(&this->actor, NA_SE_IT_DM_FLYING_GOD_DASH); func_800F3F3C(3); } if (gSaveContext.entranceIndex == 0x00A0) { switch (gSaveContext.sceneSetupIndex) { case 4: if (globalCtx->csCtx.frames == 315) { Audio_PlayActorSound2(&this->actor, NA_SE_IT_DM_FLYING_GOD_PASS); } break; case 6: if (globalCtx->csCtx.frames == 80) { Audio_PlayActorSound2(&this->actor, NA_SE_IT_DM_FLYING_GOD_DASH); } break; case 11: if (globalCtx->csCtx.frames == 370) { Audio_PlayActorSound2(&this->actor, NA_SE_IT_DM_FLYING_GOD_DASH); } break; } } } } /** * Moves this actor towards the target position with a given speed. */ void DemoEffect_MoveTowardTarget(Vec3f targetPos, DemoEffect* this, f32 speed) { this->actor.world.pos.x += (targetPos.x - this->actor.world.pos.x) * speed; this->actor.world.pos.y += (targetPos.y - this->actor.world.pos.y) * speed; this->actor.world.pos.z += (targetPos.z - this->actor.world.pos.z) * speed; } /** * Initializes Jewel colors. */ void DemoEffect_InitJewelColor(DemoEffect* this) { u8 jewelType = this->jewel.type; switch (jewelType) { case DEMO_EFFECT_JEWEL_KOKIRI: this->primXluColor[2] = 160; this->primXluColor[0] = 255; this->primXluColor[1] = 255; this->envXluColor[0] = 0; this->envXluColor[1] = 255; this->envXluColor[2] = 0; this->primOpaColor[2] = 170; this->primOpaColor[0] = 255; this->primOpaColor[1] = 255; this->envOpaColor[1] = 120; this->envOpaColor[0] = 150; this->envOpaColor[2] = 0; break; case DEMO_EFFECT_JEWEL_GORON: this->primXluColor[1] = 170; this->primXluColor[0] = 255; this->primXluColor[2] = 255; this->envXluColor[2] = 100; this->envXluColor[0] = 255; this->envXluColor[1] = 0; this->primOpaColor[2] = 170; this->primOpaColor[0] = 255; this->primOpaColor[1] = 255; this->envOpaColor[1] = 120; this->envOpaColor[0] = 150; this->envOpaColor[2] = 0; break; case DEMO_EFFECT_JEWEL_ZORA: this->primXluColor[0] = 50; this->primXluColor[1] = 255; this->primXluColor[2] = 255; this->envXluColor[2] = 150; this->envXluColor[0] = 50; this->envXluColor[1] = 0; this->primOpaColor[2] = 170; this->primOpaColor[0] = 255; this->primOpaColor[1] = 255; this->envOpaColor[1] = 120; this->envOpaColor[0] = 150; this->envOpaColor[2] = 0; break; } } /** * Sets the Jewel color based on the alpha variable. * This function if a value of less than 1.0f is supplied will drain the color from the Jewels. * This effect can be seen in prerelease screenshots. */ void DemoEffect_SetJewelColor(DemoEffect* this, f32 alpha) { DemoEffect_InitJewelColor(this); this->primXluColor[0] = (((s32)this->primXluColor[0]) * alpha) + (255.0f * (1.0f - alpha)); this->primXluColor[1] = (((s32)this->primXluColor[1]) * alpha) + (255.0f * (1.0f - alpha)); this->primXluColor[2] = (((s32)this->primXluColor[2]) * alpha) + (255.0f * (1.0f - alpha)); this->primOpaColor[0] = (((s32)this->primOpaColor[0]) * alpha) + (255.0f * (1.0f - alpha)); this->primOpaColor[1] = (((s32)this->primOpaColor[1]) * alpha) + (255.0f * (1.0f - alpha)); this->primOpaColor[2] = (((s32)this->primOpaColor[2]) * alpha) + (255.0f * (1.0f - alpha)); this->envXluColor[0] = ((s32)this->envXluColor[0]) * alpha; this->envXluColor[1] = ((s32)this->envXluColor[1]) * alpha; this->envXluColor[2] = ((s32)this->envXluColor[2]) * alpha; this->envOpaColor[0] = ((s32)this->envOpaColor[0]) * alpha; this->envOpaColor[1] = ((s32)this->envOpaColor[1]) * alpha; this->envOpaColor[2] = ((s32)this->envOpaColor[2]) * alpha; } /** * Moves the Jewel Actor during the activation of the Door of Time cutscene. * This is used once the Jewel Actor is done orbiting Link and split up to move into the pedastal slots. */ void DemoEffect_MoveJewelSplit(PosRot* world, DemoEffect* this) { switch (this->jewel.type) { case DEMO_EFFECT_JEWEL_KOKIRI: world->pos.x -= 40.0f; break; case DEMO_EFFECT_JEWEL_GORON: break; case DEMO_EFFECT_JEWEL_ZORA: world->pos.x += 40.0f; break; } } /** * Moves the Jewel Actor spherically from startPos to endPos. * This is used by the Jewel Actor during the Door of Time activation cutscene. * This is run when the Jewels merge from Link and begin orbiting him. */ void DemoEffect_MoveJewelSpherical(f32 degrees, f32 frameDivisor, Vec3f startPos, Vec3f endPos, f32 radius, Vec3s rotation, DemoEffect* this) { s32 pad; s32 pad2; f32 distance; f32 xPos; f32 ySpherical; f32 xzSpherical; distance = frameDivisor * sqrtf(SQ(endPos.x - startPos.x) + SQ(endPos.y - startPos.y) + SQ(endPos.z - startPos.z)); this->actor.world.pos.x = radius * cosf(degrees * (M_PI / 180.0f)); this->actor.world.pos.y = distance; this->actor.world.pos.z = radius * sinf(degrees * (M_PI / 180.0f)); xPos = this->actor.world.pos.x; ySpherical = (this->actor.world.pos.y * cosf(rotation.x * (M_PI / 0x8000))) - (sinf(rotation.x * (M_PI / 0x8000)) * this->actor.world.pos.z); xzSpherical = (this->actor.world.pos.z * cosf(rotation.x * (M_PI / 0x8000))) + (sinf(rotation.x * (M_PI / 0x8000)) * this->actor.world.pos.y); this->actor.world.pos.x = (xPos * cosf(rotation.y * (M_PI / 0x8000))) - (sinf(rotation.y * (M_PI / 0x8000)) * xzSpherical); this->actor.world.pos.y = ySpherical; this->actor.world.pos.z = (xzSpherical * cosf(rotation.y * (M_PI / 0x8000))) + (sinf(rotation.y * (M_PI / 0x8000)) * xPos); this->actor.world.pos.x += startPos.x; this->actor.world.pos.y += startPos.y; this->actor.world.pos.z += startPos.z; } /** * Moves the Jewel Actor spherically from startPos to endPos. * This is used by the Jewel Actor during the Door of Time activation cutscene. * This is run when the Jewels merge from Link and begin orbiting him. */ void DemoEffect_MoveJewelActivateDoorOfTime(DemoEffect* this, GlobalContext* globalCtx) { Vec3f startPos; Vec3f endPos; f32 frameDivisor; f32 degrees; f32 radius; s32 csActionId; csActionId = this->csActionId; startPos.x = globalCtx->csCtx.npcActions[csActionId]->startPos.x; startPos.y = globalCtx->csCtx.npcActions[csActionId]->startPos.y; startPos.z = globalCtx->csCtx.npcActions[csActionId]->startPos.z; endPos.x = globalCtx->csCtx.npcActions[csActionId]->endPos.x; endPos.y = globalCtx->csCtx.npcActions[csActionId]->endPos.y; endPos.z = globalCtx->csCtx.npcActions[csActionId]->endPos.z; frameDivisor = DemoEffect_InterpolateCsFrames(globalCtx, csActionId); switch (this->jewel.type) { case DEMO_EFFECT_JEWEL_KOKIRI: degrees = 0.0f; break; case DEMO_EFFECT_JEWEL_GORON: degrees = 120.0f; break; case DEMO_EFFECT_JEWEL_ZORA: degrees = 240.0f; break; } radius = 50.0f * frameDivisor; if (radius > 30.0f) { radius = 30.0f; } if (startPos.x != endPos.x || startPos.y != endPos.y || startPos.z != endPos.z) { this->jewelCsRotation.x = Math_Atan2F(endPos.z - startPos.z, -(endPos.x - startPos.x)) * (0x8000 / M_PI); this->jewelCsRotation.y = Math_Vec3f_Yaw(&startPos, &endPos); } this->jewelCsRotation.z += 0x0400; degrees += this->jewelCsRotation.z * (360.0f / 65536.0f); DemoEffect_MoveJewelSpherical(degrees, frameDivisor, startPos, endPos, radius, this->jewelCsRotation, this); } /** * Spawns Sparkle Effects for the Jewel Actor. */ void DemoEffect_JewelSparkle(DemoEffect* this, GlobalContext* globalCtx, s32 spawnerCount) { Vec3f velocity; Vec3f accel; Color_RGBA8 primColor; Color_RGBA8 envColor; Color_RGB8* sparkleColors; s32 i; velocity.y = 0.0f; accel.x = 0.0f; accel.y = -0.1f; accel.z = 0.0f; sparkleColors = sJewelSparkleColors[this->jewel.type - DEMO_EFFECT_JEWEL_KOKIRI]; primColor.r = sparkleColors[0].r; primColor.g = sparkleColors[0].g; primColor.b = sparkleColors[0].b; envColor.r = sparkleColors[1].r; envColor.g = sparkleColors[1].g; envColor.b = sparkleColors[1].b; primColor.a = 0; for (i = 0; i < spawnerCount; i++) { velocity.x = (Rand_ZeroOne() - 0.5f) * 1.5f; velocity.z = (Rand_ZeroOne() - 0.5f) * 1.5f; EffectSsKiraKira_SpawnDispersed(globalCtx, &this->actor.world.pos, &velocity, &accel, &primColor, &envColor, 3000, 16); } } /** * Plays Jewel sound effects. * The sSfxJewelId global variable is used to ensure only one Jewel Actor is playing SFX when all are spawned. */ void DemoEffect_PlayJewelSfx(DemoEffect* this, GlobalContext* globalCtx) { if (!DemoEffect_CheckCsAction(this, globalCtx, 1)) { if (this->actor.params == sSfxJewelId[0]) { func_8002F974(&this->actor, NA_SE_EV_SPIRIT_STONE - SFX_FLAG); } else if (sSfxJewelId[0] == 0) { sSfxJewelId[0] = this->actor.params; func_8002F974(&this->actor, NA_SE_EV_SPIRIT_STONE - SFX_FLAG); } } } /** * Update Function for the Jewel Actor that is run when Link is an adult. * This rotates the Jewel and updates a timer that is used to scroll Jewel textures. * There is a call SetJewelColor that does nothing since 1.0f is passed. * If a value of less than 1.0f were passed to SetJewelColor, then it would appear to drain the Jewel's color. * This can be seen in preprelease screenshots. */ void DemoEffect_UpdateJewelAdult(DemoEffect* this, GlobalContext* globalCtx) { this->jewel.timer++; this->actor.shape.rot.y += 0x0400; DemoEffect_PlayJewelSfx(this, globalCtx); if (gSaveContext.n64ddFlag) { switch (this->jewel.type) { case DEMO_EFFECT_JEWEL_KOKIRI: if (CHECK_QUEST_ITEM(QUEST_KOKIRI_EMERALD)) { DemoEffect_SetJewelColor(this, 1.0f); } else { DemoEffect_SetJewelColor(this, 0.0f); } break; case DEMO_EFFECT_JEWEL_GORON: if (CHECK_QUEST_ITEM(QUEST_GORON_RUBY)) { DemoEffect_SetJewelColor(this, 1.0f); } else { DemoEffect_SetJewelColor(this, 0.0f); } break; case DEMO_EFFECT_JEWEL_ZORA: if (CHECK_QUEST_ITEM(QUEST_ZORA_SAPPHIRE)) { DemoEffect_SetJewelColor(this, 1.0f); } else { DemoEffect_SetJewelColor(this, 0.0f); } break; } } else { DemoEffect_SetJewelColor(this, 1.0f); } } /** * Update Function for the Jewel Actor that is run when Link is a child. * This rotates the Jewel and updates a timer that is used to scroll Jewel textures. * This also updates the Jewel's position based on different cutscenes. */ void DemoEffect_UpdateJewelChild(DemoEffect* this, GlobalContext* globalCtx) { s32 hasCmdAction; Actor* thisx = &this->actor; this->jewel.timer++; if (globalCtx->csCtx.state && globalCtx->csCtx.npcActions[this->csActionId]) { switch (globalCtx->csCtx.npcActions[this->csActionId]->action) { case 3: if (gSaveContext.eventChkInf[4] & 0x800) { gSaveContext.eventChkInf[4] |= 0x800; } DemoEffect_MoveJewelActivateDoorOfTime(this, globalCtx); if ((globalCtx->gameplayFrames & 1) == 0) { DemoEffect_JewelSparkle(this, globalCtx, 1); } break; case 4: if (this->jewel.isPositionInit) { DemoEffect_MoveToCsEndpoint(this, globalCtx, this->csActionId, 0); DemoEffect_MoveJewelSplit(&thisx->world, this); if ((globalCtx->gameplayFrames & 1) == 0) { DemoEffect_JewelSparkle(this, globalCtx, 1); } } else { DemoEffect_InitPositionFromCsAction(this, globalCtx, this->csActionId); DemoEffect_MoveJewelSplit(&thisx->world, this); this->jewel.isPositionInit = 1; } break; case 6: Actor_Kill(thisx); return; default: DemoEffect_MoveToCsEndpoint(this, globalCtx, this->csActionId, 0); if (gSaveContext.entranceIndex == 0x0053 || (gSaveContext.n64ddFlag && gSaveContext.entranceIndex == 0x05F4)) { DemoEffect_MoveJewelSplit(&thisx->world, this); } break; } } if (gSaveContext.entranceIndex == 0x0053 || (gSaveContext.n64ddFlag && gSaveContext.entranceIndex == 0x05F4)) { if (!(gSaveContext.eventChkInf[4] & 0x800)) { hasCmdAction = globalCtx->csCtx.state && globalCtx->csCtx.npcActions[this->csActionId]; if (!hasCmdAction) { this->effectFlags |= 0x1; return; } } } thisx->shape.rot.y += 0x0400; DemoEffect_PlayJewelSfx(this, globalCtx); this->effectFlags &= ~1; if (gSaveContext.n64ddFlag) { switch (this->jewel.type) { case DEMO_EFFECT_JEWEL_KOKIRI: if (CHECK_QUEST_ITEM(QUEST_KOKIRI_EMERALD)) { DemoEffect_SetJewelColor(this, 1.0f); } else { DemoEffect_SetJewelColor(this, 0.0f); } break; case DEMO_EFFECT_JEWEL_GORON: if (CHECK_QUEST_ITEM(QUEST_GORON_RUBY)) { DemoEffect_SetJewelColor(this, 1.0f); } else { DemoEffect_SetJewelColor(this, 0.0f); } break; case DEMO_EFFECT_JEWEL_ZORA: if (CHECK_QUEST_ITEM(QUEST_ZORA_SAPPHIRE)) { DemoEffect_SetJewelColor(this, 1.0f); } else { DemoEffect_SetJewelColor(this, 0.0f); } break; } } else { // Contrary to it's adult conterpart Authenthic doesn't use DemoEffect_SetJewelColor(this, 1.0f); here } } /** * Update Function for the Dust Actor. * This is the dust that is spawned in the Temple of Time during the Light Arrows cutscene. * This spawns the dust particles and increments a timer */ void DemoEffect_UpdateDust(DemoEffect* this, GlobalContext* globalCtx) { Vec3f pos; Vec3f velocity; Vec3f accel; if (globalCtx->csCtx.state != CS_STATE_IDLE && globalCtx->csCtx.npcActions[this->csActionId] != NULL && globalCtx->csCtx.npcActions[this->csActionId]->action == 2) { pos = this->actor.world.pos; pos.y += 600.0f; pos.x += Rand_CenteredFloat(300.0f); pos.z += 200.0f + Rand_CenteredFloat(300.0f); velocity.z = 0.0f; velocity.x = 0.0f; velocity.y = -20.0f; accel.z = 0.0f; accel.x = 0.0f; accel.y = 0.2f; func_8002873C(globalCtx, &pos, &velocity, &accel, 300, 0, 30); this->dust.timer++; } } /** * This is the main Actor Update Function. */ void DemoEffect_Update(Actor* thisx, GlobalContext* globalCtx) { DemoEffect* this = (DemoEffect*)thisx; this->updateFunc(this, globalCtx); } /** * Check if the current cutscene action matches the passed in cutscene action ID. */ s32 DemoEffect_CheckCsAction(DemoEffect* this, GlobalContext* globalCtx, s32 csActionCompareId) { if (globalCtx->csCtx.state != CS_STATE_IDLE && globalCtx->csCtx.npcActions[this->csActionId] != NULL && globalCtx->csCtx.npcActions[this->csActionId]->action == csActionCompareId) { return 1; } return 0; } /** * Draw function for the Jewel Actor. */ void DemoEffect_DrawJewel(Actor* thisx, GlobalContext* globalCtx2) { DemoEffect* this = (DemoEffect*)thisx; GlobalContext* globalCtx = globalCtx2; u32 frames = this->jewel.timer; OPEN_DISPS(globalCtx->state.gfxCtx); if (!DemoEffect_CheckCsAction(this, globalCtx, 1)) { if (!(this->effectFlags & 0x1)) { switch (this->jewel.type) { case DEMO_EFFECT_JEWEL_KOKIRI: gSPSegment(POLY_XLU_DISP++, 9, Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, (frames * 4) % 256, (256 - ((frames * 2) % 256)) - 1, 64, 64, 1, (frames * 2) % 256, (256 - (frames % 256)) - 1, 16, 16)); break; case DEMO_EFFECT_JEWEL_GORON: gSPSegment(POLY_XLU_DISP++, 9, Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, (frames * 4) % 128, (256 - ((frames * 2) % 256)) - 1, 32, 64, 1, (frames * 2) % 256, (256 - (frames % 256)) - 1, 16, 8)); break; case DEMO_EFFECT_JEWEL_ZORA: gSPSegment(POLY_XLU_DISP++, 9, Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, (frames * 4) % 256, (256 - ((frames * 2) % 256)) - 1, 32, 32, 1, (frames * 2) % 256, (256 - (frames % 256)) - 1, 16, 16)); break; } if (!frames) {} gSPSegment(POLY_OPA_DISP++, 8, Gfx_TexScroll(globalCtx->state.gfxCtx, (u8)frames, (u8)frames, 16, 16)); gSPMatrix(POLY_XLU_DISP++, MATRIX_NEWMTX(globalCtx->state.gfxCtx), G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); gSPMatrix(POLY_OPA_DISP++, MATRIX_NEWMTX(globalCtx->state.gfxCtx), G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); func_80093D84(globalCtx->state.gfxCtx); func_8002ED80(&this->actor, globalCtx, 0); gDPSetPrimColor(POLY_XLU_DISP++, 0, 128, this->primXluColor[0], this->primXluColor[1], this->primXluColor[2], 255); gDPSetEnvColor(POLY_XLU_DISP++, this->envXluColor[0], this->envXluColor[1], this->envXluColor[2], 255); gSPDisplayList(POLY_XLU_DISP++, this->jewelDisplayList); func_80093D18(globalCtx->state.gfxCtx); func_8002EBCC(&this->actor, globalCtx, 0); gDPSetPrimColor(POLY_OPA_DISP++, 0, 128, this->primOpaColor[0], this->primOpaColor[1], this->primOpaColor[2], 255); gDPSetEnvColor(POLY_OPA_DISP++, this->envOpaColor[0], this->envOpaColor[1], this->envOpaColor[2], 255); gSPDisplayList(POLY_OPA_DISP++, this->jewelHolderDisplayList); } } CLOSE_DISPS(globalCtx->state.gfxCtx); } /** * Draw function for the Crystal Light Actor. */ void DemoEffect_DrawCrystalLight(Actor* thisx, GlobalContext* globalCtx) { DemoEffect* this = (DemoEffect*)thisx; DemoEffect* parent = (DemoEffect*)this->actor.parent; u32 frames = globalCtx->gameplayFrames & 0xFFFF; OPEN_DISPS(globalCtx->state.gfxCtx); if (parent != NULL) { gDPSetPrimColor(POLY_XLU_DISP++, 128, 128, 255, 255, 170, parent->triforceSpot.crystalLightOpacity); } else { gDPSetPrimColor(POLY_XLU_DISP++, 128, 128, 255, 255, 170, 255); } func_80093D84(globalCtx->state.gfxCtx); gSPSegment(POLY_XLU_DISP++, 8, Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, (frames * 2) % 512, 512 - (frames % 512) - 1, 128, 128, 1, 512 - ((frames * 2) % 512) - 1, 0, 64, 64)); Matrix_Push(); Matrix_RotateY(0.0f, MTXMODE_APPLY); Matrix_RotateX((11.0 * M_PI) / 180.0, MTXMODE_APPLY); Matrix_Translate(0.0f, 150.0f, 0.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++, gCrystalLightDL); Matrix_Pop(); Matrix_Push(); Matrix_RotateY((2.0f * M_PI) / 3.0f, MTXMODE_APPLY); Matrix_RotateX((11.0 * M_PI) / 180.0, MTXMODE_APPLY); Matrix_Translate(0.0f, 150.0f, 0.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++, gCrystalLightDL); Matrix_Pop(); Matrix_Push(); Matrix_RotateY((4.0f * M_PI) / 3.0f, MTXMODE_APPLY); Matrix_RotateX((11.0 * M_PI) / 180.0, MTXMODE_APPLY); Matrix_Translate(0.0f, 150.0f, 0.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++, gCrystalLightDL); Matrix_Pop(); CLOSE_DISPS(globalCtx->state.gfxCtx); } /** * Draw function for the Fire Ball Actor. */ void DemoEffect_DrawFireBall(Actor* thisx, GlobalContext* globalCtx) { DemoEffect* this = (DemoEffect*)thisx; u32 frames = globalCtx->gameplayFrames; OPEN_DISPS(globalCtx->state.gfxCtx); gDPSetPrimColor(POLY_XLU_DISP++, 64, 64, 255, 200, 0, 255); gDPSetEnvColor(POLY_XLU_DISP++, 255, 0, 0, 255); func_80093D84(globalCtx->state.gfxCtx); gSPMatrix(POLY_XLU_DISP++, MATRIX_NEWMTX(globalCtx->state.gfxCtx), G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); gSPMatrix(POLY_XLU_DISP++, globalCtx->billboardMtx, G_MTX_NOPUSH | G_MTX_MUL | G_MTX_MODELVIEW); gSPSegment( POLY_XLU_DISP++, 8, Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, 0, 0, 32, 32, 1, 0, 128 - ((frames * 20) % 128) - 1, 32, 32)); gSPDisplayList(POLY_XLU_DISP++, gCreationFireBallDL); CLOSE_DISPS(globalCtx->state.gfxCtx); } /** * Draw function for the God Lgt Actors. * This draws either Din, Nayru, or Farore based on the colors set in the DemoEffect struct. */ void DemoEffect_DrawGodLgt(Actor* thisx, GlobalContext* globalCtx) { DemoEffect* this = (DemoEffect*)thisx; s32 pad; u32 frames = globalCtx->gameplayFrames; OPEN_DISPS(globalCtx->state.gfxCtx); if (!DemoEffect_CheckCsAction(this, globalCtx, 2)) { if (gSaveContext.entranceIndex == 0x00A0) { if (gSaveContext.sceneSetupIndex == 4) { if (globalCtx->csCtx.frames <= 680) { func_80078914(&this->actor.projectedPos, NA_SE_EV_GOD_FLYING - SFX_FLAG); } } else { func_80078914(&this->actor.projectedPos, NA_SE_EV_GOD_FLYING - SFX_FLAG); } } else { func_80078914(&this->actor.projectedPos, NA_SE_EV_GOD_FLYING - SFX_FLAG); } gSPSegment(POLY_XLU_DISP++, 8, Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, (frames * 4) % 512, 0, 128, 64, 1, (frames * 2) % 256, 512 - ((frames * 70) % 512) - 1, 64, 32)); gSPSegment(POLY_XLU_DISP++, 9, Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, 0, 0, 16, 96, 1, (frames * 10) % 256, 256 - ((frames * 30) % 512) - 1, 8, 32)); gDPSetPrimColor(POLY_XLU_DISP++, 128, 128, this->primXluColor[0], this->primXluColor[1], this->primXluColor[2], 255); gDPSetEnvColor(POLY_XLU_DISP++, this->envXluColor[0], this->envXluColor[1], this->envXluColor[2], 255); func_80093D84(globalCtx->state.gfxCtx); Matrix_Push(); gSPMatrix(POLY_XLU_DISP++, MATRIX_NEWMTX(globalCtx->state.gfxCtx), G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); gSPDisplayList(POLY_XLU_DISP++, gGoldenGoddessAuraDL); func_80093D18(globalCtx->state.gfxCtx); func_8002EBCC(&this->actor, globalCtx, 0); Matrix_Pop(); this->godLgt.rotation++; if (this->godLgt.rotation > 120) { this->godLgt.rotation = 0; } Matrix_RotateZ((((s32)this->godLgt.rotation) * 3.0f) * (M_PI / 180.0f), MTXMODE_APPLY); Matrix_RotateX(M_PI / 2.0f, MTXMODE_APPLY); Matrix_Translate(0.0f, -140.0f, 0.0f, MTXMODE_APPLY); Matrix_Scale(0.03f, 0.03f, 0.03f, MTXMODE_APPLY); gSPMatrix(POLY_OPA_DISP++, MATRIX_NEWMTX(globalCtx->state.gfxCtx), G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); gSPDisplayList(POLY_OPA_DISP++, gGoldenGoddessBodyDL); } CLOSE_DISPS(globalCtx->state.gfxCtx); } /** * Draw function for the Light Effect Actor. */ void DemoEffect_DrawLightEffect(Actor* thisx, GlobalContext* globalCtx) { DemoEffect* this = (DemoEffect*)thisx; u8* alpha; Gfx* disp; OPEN_DISPS(globalCtx->state.gfxCtx); if (!DemoEffect_CheckCsAction(this, globalCtx, 1)) { if (this->light.flicker == 0) { this->light.flicker = 1; } else { disp = (uintptr_t)gEffFlash1DL; alpha = &this->light.alpha; func_80093D84(globalCtx->state.gfxCtx); gDPSetPrimColor(POLY_XLU_DISP++, 0, 128, this->primXluColor[0], this->primXluColor[1], this->primXluColor[2], *alpha); gDPSetEnvColor(POLY_XLU_DISP++, this->envXluColor[0], this->envXluColor[1], this->envXluColor[2], 255); Matrix_Scale(((this->light.scaleFlag & 1) * 0.05f) + 1.0f, ((this->light.scaleFlag & 1) * 0.05f) + 1.0f, ((this->light.scaleFlag & 1) * 0.05f) + 1.0f, MTXMODE_APPLY); Matrix_Push(); Matrix_Mult(&globalCtx->billboardMtxF, MTXMODE_APPLY); Matrix_RotateZ(this->light.rotation * (M_PI / 180.0f), MTXMODE_APPLY); gSPMatrix(POLY_XLU_DISP++, MATRIX_NEWMTX(globalCtx->state.gfxCtx), G_MTX_MODELVIEW | G_MTX_LOAD | G_MTX_NOPUSH); gSPDisplayList(POLY_XLU_DISP++, disp); Matrix_Pop(); Matrix_Mult(&globalCtx->billboardMtxF, MTXMODE_APPLY); Matrix_RotateZ(-(f32)this->light.rotation * (M_PI / 180.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++, disp); } } CLOSE_DISPS(globalCtx->state.gfxCtx); } /** * Draw function for the Blue Orb Actor. */ void DemoEffect_DrawBlueOrb(Actor* thisx, GlobalContext* globalCtx) { DemoEffect* this = (DemoEffect*)thisx; s32 pad2; OPEN_DISPS(globalCtx->state.gfxCtx); gDPSetPrimColor(POLY_XLU_DISP++, 128, 128, 188, 255, 255, this->blueOrb.alpha); gDPSetEnvColor(POLY_XLU_DISP++, 0, 100, 255, 255); func_80093D84(globalCtx->state.gfxCtx); Matrix_Mult(&globalCtx->billboardMtxF, MTXMODE_APPLY); Matrix_RotateZ(this->blueOrb.rotation * (M_PI / 0x8000), MTXMODE_APPLY); gSPMatrix(POLY_XLU_DISP++, MATRIX_NEWMTX(globalCtx->state.gfxCtx), G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); this->blueOrb.rotation += 0x01F4; gSPDisplayList(POLY_XLU_DISP++, gEffFlash1DL); CLOSE_DISPS(globalCtx->state.gfxCtx); } /** * Draw function for the Lgt Shower Actor. */ void DemoEffect_DrawLgtShower(Actor* thisx, GlobalContext* globalCtx) { DemoEffect* this = (DemoEffect*)thisx; s32 pad; u32 frames = globalCtx->gameplayFrames; OPEN_DISPS(globalCtx->state.gfxCtx); gDPSetPrimColor(POLY_XLU_DISP++, 64, 64, 255, 255, 160, this->lgtShower.alpha); gDPSetEnvColor(POLY_XLU_DISP++, 50, 200, 0, 255); func_80093D84(globalCtx->state.gfxCtx); gSPMatrix(POLY_XLU_DISP++, MATRIX_NEWMTX(globalCtx->state.gfxCtx), G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); gSPSegment(POLY_XLU_DISP++, 8, Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, (frames * 5) % 1024, 0, 256, 64, 1, (frames * 10) % 128, 512 - ((frames * 50) % 512), 32, 16)); gSPDisplayList(POLY_XLU_DISP++, gEnliveningLightDL); CLOSE_DISPS(globalCtx->state.gfxCtx); } /** * Draw function for the Light Ring Actor. */ void DemoEffect_DrawLightRing(Actor* thisx, GlobalContext* globalCtx2) { DemoEffect* this = (DemoEffect*)thisx; GlobalContext* globalCtx = globalCtx2; u32 frames = this->lightRing.timer; OPEN_DISPS(globalCtx->state.gfxCtx); func_80093D84(globalCtx->state.gfxCtx); gDPSetPrimColor(POLY_XLU_DISP++, 128, 128, 170, 255, 255, this->lightRing.alpha); gDPSetEnvColor(POLY_XLU_DISP++, 0, 100, 255, 255); gSPMatrix(POLY_XLU_DISP++, MATRIX_NEWMTX(globalCtx->state.gfxCtx), G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); gSPSegment(POLY_XLU_DISP++, 8, Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, (frames * 5) % 64, 512 - ((frames * 2) % 512) - 1, 16, 128, 1, 0, 0, 8, 1024)); gSPDisplayList(POLY_XLU_DISP++, gGoldenGoddessLightRingDL); CLOSE_DISPS(globalCtx->state.gfxCtx); } /** * Draw function for the Triforce Spot Actor. */ void DemoEffect_DrawTriforceSpot(Actor* thisx, GlobalContext* globalCtx) { DemoEffect* this = (DemoEffect*)thisx; s32 pad; Vtx* vertices = ResourceMgr_LoadVtxByName(SEGMENTED_TO_VIRTUAL(gTriforceVtx)); u32 frames = globalCtx->gameplayFrames; OPEN_DISPS(globalCtx->state.gfxCtx); if (gSaveContext.entranceIndex != 0x0400 || globalCtx->csCtx.frames < 885) { func_80093D84(globalCtx->state.gfxCtx); if (this->triforceSpot.lightColumnOpacity > 0) { Audio_PlayActorSound2(&this->actor, NA_SE_EV_AURORA - SFX_FLAG); Matrix_Push(); Matrix_Scale(1.0f, 2.4f, 1.0f, MTXMODE_APPLY); gSPMatrix(POLY_XLU_DISP++, MATRIX_NEWMTX(globalCtx->state.gfxCtx), G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); gSPSegment(POLY_XLU_DISP++, 9, Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, 0, 256 - ((frames * 4) % 256) - 1, 64, 64, 1, 0, 256 - ((frames * 2) % 256) - 1, 64, 32)); vertices[86].n.a = vertices[87].n.a = vertices[88].n.a = vertices[89].n.a = vertices[92].n.a = vertices[93].n.a = vertices[94].n.a = vertices[95].n.a = (s8)this->triforceSpot.lightColumnOpacity; gDPSetPrimColor(POLY_XLU_DISP++, 128, 128, 180, 255, 255, this->triforceSpot.lightColumnOpacity); gDPSetEnvColor(POLY_XLU_DISP++, 0, 255, 150, 255); gSPDisplayList(POLY_XLU_DISP++, gTriforceLightColumnDL); Matrix_Pop(); } if (this->triforceSpot.triforceSpotOpacity != 0) { Audio_PlayActorSound2(&this->actor, NA_SE_EV_TRIFORCE - SFX_FLAG); gSPMatrix(POLY_XLU_DISP++, MATRIX_NEWMTX(globalCtx->state.gfxCtx), G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); if (this->triforceSpot.triforceSpotOpacity < 250) { func_8002ED80(&this->actor, globalCtx, 0); func_80093D84(globalCtx->state.gfxCtx); gDPSetRenderMode(POLY_XLU_DISP++, G_RM_PASS, G_RM_AA_ZB_XLU_SURF2); Matrix_RotateY(this->triforceSpot.rotation * (M_PI / 0x8000), MTXMODE_APPLY); gSPMatrix(POLY_XLU_DISP++, MATRIX_NEWMTX(globalCtx->state.gfxCtx), G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); gSPSegment(POLY_XLU_DISP++, 8, Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, 0, 0, 32, 16, 1, 0, 0, 16, 8)); gDPSetPrimColor(POLY_XLU_DISP++, 128, 128, 255, 255, 160, this->triforceSpot.triforceSpotOpacity); gDPSetEnvColor(POLY_XLU_DISP++, 170, 140, 0, 255); gSPDisplayList(POLY_XLU_DISP++, gTriforceDL); } else { func_8002EBCC(&this->actor, globalCtx, 0); func_80093D18(globalCtx->state.gfxCtx); gDPSetRenderMode(POLY_OPA_DISP++, G_RM_PASS, G_RM_AA_ZB_OPA_SURF2); Matrix_RotateY(this->triforceSpot.rotation * (M_PI / 0x8000), MTXMODE_APPLY); gSPMatrix(POLY_OPA_DISP++, MATRIX_NEWMTX(globalCtx->state.gfxCtx), G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); gSPSegment(POLY_OPA_DISP++, 8, Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, 0, 0, 32, 16, 1, 0, 0, 16, 8)); gDPSetPrimColor(POLY_OPA_DISP++, 128, 128, 255, 255, 160, 255); gDPSetEnvColor(POLY_OPA_DISP++, 170, 140, 0, 255); gSPDisplayList(POLY_OPA_DISP++, gTriforceDL); } } } CLOSE_DISPS(globalCtx->state.gfxCtx); } /** * Draw function for the Get Item Actors. * This is either Medals or Light Arrows based on the drawId. */ void DemoEffect_DrawGetItem(Actor* thisx, GlobalContext* globalCtx) { DemoEffect* this = (DemoEffect*)thisx; if (!DemoEffect_CheckCsAction(this, globalCtx, 1) && !DemoEffect_CheckCsAction(this, globalCtx, 4)) { if (!this->getItem.isLoaded) { this->getItem.isLoaded = 1; return; } func_8002EBCC(thisx, globalCtx, 0); func_8002ED80(thisx, globalCtx, 0); GetItem_Draw(globalCtx, this->getItem.drawId); } } /** * Callback for the SkelCurve system to draw the animated limbs. */ s32 DemoEffect_DrawTimewarpLimbs(GlobalContext* globalCtx, SkelAnimeCurve* skelCuve, s32 limbIndex, void* thisx) { s32 pad; DemoEffect* this = (DemoEffect*)thisx; u32 frames = globalCtx->gameplayFrames; OPEN_DISPS(globalCtx->state.gfxCtx); func_80093D84(globalCtx->state.gfxCtx); gDPSetPrimColor(POLY_XLU_DISP++, 0, 128, 170, 255, 255, 255); gDPSetEnvColor(POLY_XLU_DISP++, this->envXluColor[0], this->envXluColor[1], this->envXluColor[2], 255); gSPSegment(POLY_XLU_DISP++, 8, Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, (frames * 6) % 1024, 256 - ((frames * 16) % 256) - 1, 256, 64, 1, (frames * 4) % 512, 128 - ((frames * 12) % 128) - 1, 128, 32)); CLOSE_DISPS(globalCtx->state.gfxCtx); if (limbIndex == 0) { LimbTransform* transform = &skelCuve->transforms[0]; transform->scale.y = 1024; transform->scale.z = transform->scale.x = 1024; } return 1; } /** * Draw function for the Time Warp Actors. */ void DemoEffect_DrawTimeWarp(Actor* thisx, GlobalContext* globalCtx) { DemoEffect* this = (DemoEffect*)thisx; GraphicsContext* gfxCtx = globalCtx->state.gfxCtx; u8 effectType = (this->actor.params & 0x00FF); if (effectType == DEMO_EFFECT_TIMEWARP_TIMEBLOCK_LARGE || effectType == DEMO_EFFECT_TIMEWARP_TIMEBLOCK_SMALL || Flags_GetEnv(globalCtx, 1) || gSaveContext.sceneSetupIndex >= 4 || gSaveContext.entranceIndex == 0x0324) { OPEN_DISPS(gfxCtx); POLY_XLU_DISP = Gfx_CallSetupDL(POLY_XLU_DISP, 25); Matrix_Scale(2.0f, 2.0f, 2.0f, MTXMODE_APPLY); SkelCurve_Draw(thisx, globalCtx, &this->skelCurve, DemoEffect_DrawTimewarpLimbs, NULL, 1, this); CLOSE_DISPS(gfxCtx); } } /** * Faces/rotates the Actor towards the current cutscene action end point. */ void DemoEffect_FaceToCsEndpoint(DemoEffect* this, Vec3f startPos, Vec3f endPos) { s32 pad; f32 x = endPos.x - startPos.x; f32 z = endPos.z - startPos.z; f32 xzDistance = sqrtf(SQ(x) + SQ(z)); this->actor.shape.rot.y = Math_FAtan2F(x, z) * (32768.0f / M_PI); this->actor.shape.rot.x = Math_FAtan2F(-(endPos.y - startPos.y), xzDistance) * (32768.0f / M_PI); } /** * Moves the Actor towards the current cutscene action end point. * Will only update the Actor's facing/rotation if the shouldUpdateFacing argument is true. * The speed is based on the current progress in the cutscene action. */ void DemoEffect_MoveToCsEndpoint(DemoEffect* this, GlobalContext* globalCtx, s32 csActionId, s32 shouldUpdateFacing) { Vec3f startPos; Vec3f endPos; f32 speed; startPos.x = globalCtx->csCtx.npcActions[csActionId]->startPos.x; startPos.y = globalCtx->csCtx.npcActions[csActionId]->startPos.y; startPos.z = globalCtx->csCtx.npcActions[csActionId]->startPos.z; endPos.x = globalCtx->csCtx.npcActions[csActionId]->endPos.x; endPos.y = globalCtx->csCtx.npcActions[csActionId]->endPos.y; endPos.z = globalCtx->csCtx.npcActions[csActionId]->endPos.z; speed = DemoEffect_InterpolateCsFrames(globalCtx, csActionId); this->actor.world.pos.x = ((endPos.x - startPos.x) * speed) + startPos.x; this->actor.world.pos.y = ((endPos.y - startPos.y) * speed) + startPos.y; this->actor.world.pos.z = ((endPos.z - startPos.z) * speed) + startPos.z; if (shouldUpdateFacing) { DemoEffect_FaceToCsEndpoint(this, startPos, endPos); } } /** * Moves a GetItem actor towards the current cutscene action's endpoint. */ void DemoEffect_MoveGetItem(DemoEffect* this, GlobalContext* globalCtx, s32 csActionId, f32 speed) { Vec3f endPos; endPos.x = globalCtx->csCtx.npcActions[csActionId]->endPos.x; endPos.y = globalCtx->csCtx.npcActions[csActionId]->endPos.y; endPos.z = globalCtx->csCtx.npcActions[csActionId]->endPos.z; DemoEffect_MoveTowardTarget(endPos, this, speed); } /** * Initializes the Actor's position to the current cutscene action's start point. */ void DemoEffect_InitPositionFromCsAction(DemoEffect* this, GlobalContext* globalCtx, s32 csActionIndex) { f32 x = globalCtx->csCtx.npcActions[csActionIndex]->startPos.x; f32 y = globalCtx->csCtx.npcActions[csActionIndex]->startPos.y; f32 z = globalCtx->csCtx.npcActions[csActionIndex]->startPos.z; this->actor.world.pos.x = x; this->actor.world.pos.y = y; this->actor.world.pos.z = z; }