mm/src/code/z_demo.c

1726 lines
61 KiB
C

#include "prevent_bss_reordering.h"
#include "prevent_bss_reordering2.h"
#include "PR/ultratypes.h"
// Variables are put before most headers as a hacky way to bypass bss reordering
struct CutsceneCamera;
s16 sCutsceneQuakeIndex;
struct CutsceneCamera sCutsceneCameraInfo;
u16 sCueTypeList[10];
u8 D_801F4DDC;
static s16 sBssPad;
u8 gDisablePlayerCsActionStartPos;
s16 gDungeonBossWarpSceneId;
#include "z64quake.h"
#include "z64rumble.h"
#include "z64shrink_window.h"
#include "overlays/gamestates/ovl_daytelop/z_daytelop.h"
#include "overlays/actors/ovl_En_Elf/z_en_elf.h"
void CutsceneHandler_DoNothing(PlayState* play, CutsceneContext* csCtx);
void CutsceneHandler_StartManual(PlayState* play, CutsceneContext* csCtx);
void CutsceneHandler_StopManual(PlayState* play, CutsceneContext* csCtx);
void CutsceneHandler_StartScript(PlayState* play, CutsceneContext* csCtx);
void CutsceneHandler_RunScript(PlayState* play, CutsceneContext* csCtx);
void CutsceneHandler_StopScript(PlayState* play, CutsceneContext* csCtx);
void Cutscene_SetupScripted(PlayState* play, CutsceneContext* csCtx);
static s32 sPad = 0;
u16 sCurTextId = 0;
u16 sCurOcarinaAction = 0;
u8 gOpeningEntranceIndex = 0;
u8 sCutsceneStoredPlayerForm = 0;
void Cutscene_InitContext(PlayState* play, CutsceneContext* csCtx) {
s32 i;
csCtx->state = CS_STATE_IDLE;
csCtx->curFrame = 0;
csCtx->timer = 0.0f;
play->csCtx.scriptListCount = 0;
play->csCtx.scriptIndex = 0;
for (i = 0; i < ARRAY_COUNT(sCueTypeList); i++) {
sCueTypeList[i] = 0;
}
gDisablePlayerCsActionStartPos = false;
Audio_SetCutsceneFlag(false);
}
void Cutscene_StartManual(PlayState* play, CutsceneContext* csCtx) {
csCtx->state = CS_STATE_START;
csCtx->playerCue = NULL;
}
void Cutscene_StopManual(PlayState* play, CutsceneContext* csCtx) {
if (csCtx->state != CS_STATE_RUN_UNSTOPPABLE) {
csCtx->state = CS_STATE_STOP;
}
}
typedef void (*CutsceneHandler)(PlayState* play, CutsceneContext* csCtx);
CutsceneHandler sManualCutsceneHandlers[] = {
CutsceneHandler_DoNothing, // CS_STATE_IDLE
CutsceneHandler_StartManual, // CS_STATE_START
CutsceneHandler_DoNothing, // CS_STATE_RUN
CutsceneHandler_StopManual, // CS_STATE_STOP
CutsceneHandler_DoNothing, // CS_STATE_RUN_UNSTOPPABLE
};
void Cutscene_UpdateManual(PlayState* play, CutsceneContext* csCtx) {
if (gSaveContext.save.cutsceneIndex < 0xFFF0) {
sManualCutsceneHandlers[csCtx->state](play, csCtx);
}
}
CutsceneHandler sScriptedCutsceneHandlers[] = {
CutsceneHandler_DoNothing, // CS_STATE_IDLE
CutsceneHandler_StartScript, // CS_STATE_START
CutsceneHandler_RunScript, // CS_STATE_RUN
CutsceneHandler_StopScript, // CS_STATE_STOP
CutsceneHandler_RunScript, // CS_STATE_RUN_UNSTOPPABLE
};
void Cutscene_UpdateScripted(PlayState* play, CutsceneContext* csCtx) {
if ((gSaveContext.cutsceneTrigger != 0) && (play->transitionTrigger == TRANS_TRIGGER_START)) {
gSaveContext.cutsceneTrigger = 0;
}
if ((gSaveContext.cutsceneTrigger != 0) && (csCtx->state == CS_STATE_IDLE)) {
gSaveContext.save.cutsceneIndex = 0xFFFD;
gSaveContext.cutsceneTrigger = 1;
}
if (gSaveContext.save.cutsceneIndex >= 0xFFF0) {
Cutscene_SetupScripted(play, csCtx);
sScriptedCutsceneHandlers[csCtx->state](play, csCtx);
}
}
void CutsceneHandler_DoNothing(PlayState* play, CutsceneContext* csCtx) {
}
s32 Cutscene_StepTimer(PlayState* play, CutsceneContext* csCtx, f32 target) {
return Math_StepToF(&csCtx->timer, target, 0.1f);
}
void CutsceneHandler_StartManual(PlayState* play, CutsceneContext* csCtx) {
Interface_SetHudVisibility(HUD_VISIBILITY_NONE);
ShrinkWindow_Letterbox_SetSizeTarget(32);
if (Cutscene_StepTimer(play, csCtx, 1.0f)) {
Audio_SetCutsceneFlag(true);
csCtx->state++; // CS_STATE_RUN
}
}
void CutsceneHandler_StartScript(PlayState* play, CutsceneContext* csCtx) {
CutsceneHandler_RunScript(play, csCtx);
Interface_SetHudVisibility(HUD_VISIBILITY_NONE);
ShrinkWindow_Letterbox_SetSizeTarget(32);
if (Cutscene_StepTimer(play, csCtx, 1.0f)) {
Audio_SetCutsceneFlag(true);
csCtx->state++; // CS_STATE_RUN
}
}
/* Start of command handling section */
void CutsceneCmd_Misc(PlayState* play, CutsceneContext* csCtx, CsCmdMisc* cmd) {
static u16 D_801BB15C = 0xFFFF;
Player* player = GET_PLAYER(play);
f32 lerp;
u8 isFirstFrame = false;
SceneTableEntry* loadedScene;
if (!((csCtx->curFrame >= cmd->startFrame) &&
((csCtx->curFrame < cmd->endFrame) || (cmd->endFrame == cmd->startFrame)))) {
return;
}
lerp = Environment_LerpWeight(cmd->endFrame - 1, cmd->startFrame, csCtx->curFrame);
if (csCtx->curFrame == cmd->startFrame) {
isFirstFrame = true;
}
switch (cmd->type) {
case CS_MISC_RAIN:
if (isFirstFrame) {
Environment_PlayStormNatureAmbience(play);
play->envCtx.precipitation[PRECIP_RAIN_MAX] = 60;
}
break;
case CS_MISC_LIGHTNING:
if (isFirstFrame) {
Audio_SetAmbienceChannelIO(AMBIENCE_CHANNEL_LIGHTNING, CHANNEL_IO_PORT_0, 0);
Environment_AddLightningBolts(play, 3);
gLightningStrike.state = LIGHTNING_STRIKE_START;
}
break;
case CS_MISC_LIFT_FOG:
if (play->envCtx.adjLightSettings.zFar < 12800) {
play->envCtx.adjLightSettings.zFar += 35;
}
break;
case CS_MISC_CLOUDY_SKY:
if (isFirstFrame) {
play->envCtx.changeSkyboxState = CHANGE_SKYBOX_REQUESTED;
play->envCtx.skyboxConfig = SKYBOX_CONFIG_1;
play->envCtx.changeSkyboxNextConfig = SKYBOX_CONFIG_0;
play->envCtx.changeSkyboxTimer = 60;
play->envCtx.changeLightEnabled = true;
play->envCtx.lightConfig = 0;
play->envCtx.changeLightNextConfig = 1;
play->envCtx.changeDuration = 60;
play->envCtx.changeLightTimer = play->envCtx.changeDuration;
}
break;
case CS_MISC_STOP_CUTSCENE:
if (isFirstFrame && (csCtx->state != CS_STATE_RUN_UNSTOPPABLE)) {
csCtx->state = CS_STATE_STOP;
}
break;
case CS_MISC_SHOW_TITLE_CARD:
if (isFirstFrame) {
loadedScene = play->loadedScene;
if (loadedScene->titleTextId != 0) {
Message_DisplaySceneTitleCard(play, loadedScene->titleTextId);
}
}
break;
case CS_MISC_EARTHQUAKE_MEDIUM:
Audio_PlaySfx_2(NA_SE_EV_EARTHQUAKE_LAST - SFX_FLAG);
if (isFirstFrame) {
sCutsceneQuakeIndex = Quake_Request(GET_ACTIVE_CAM(play), QUAKE_TYPE_6);
Quake_SetSpeed(sCutsceneQuakeIndex, 22000);
Quake_SetPerturbations(sCutsceneQuakeIndex, 6, 4, 0, 0);
Quake_SetDuration(sCutsceneQuakeIndex, 800);
}
break;
case CS_MISC_EARTHQUAKE_STOP:
if (isFirstFrame) {
Quake_Init();
}
break;
case CS_MISC_VISMONO_BLACK_AND_WHITE:
gPlayVisMonoColor.r = 255;
gPlayVisMonoColor.g = 255;
gPlayVisMonoColor.b = 255;
gPlayVisMonoColor.a = 255 * lerp;
break;
case CS_MISC_VISMONO_SEPIA:
gPlayVisMonoColor.r = 255;
gPlayVisMonoColor.g = 180;
gPlayVisMonoColor.b = 100;
gPlayVisMonoColor.a = 255 * lerp;
break;
case CS_MISC_HIDE_ROOM:
play->roomCtx.curRoom.segment = NULL;
break;
case CS_MISC_RED_PULSATING_LIGHTS:
if (play->state.frames & 8) {
if (play->envCtx.adjLightSettings.ambientColor[0] < 40) {
play->envCtx.adjLightSettings.ambientColor[0] += 2;
play->envCtx.adjLightSettings.light1Color[1] -= 3;
play->envCtx.adjLightSettings.light1Color[2] -= 3;
}
} else {
if (play->envCtx.adjLightSettings.ambientColor[0] > 2) {
play->envCtx.adjLightSettings.ambientColor[0] -= 2;
play->envCtx.adjLightSettings.light1Color[1] += 3;
play->envCtx.adjLightSettings.light1Color[2] += 3;
}
}
break;
case CS_MISC_HALT_ALL_ACTORS:
play->haltAllActors = true;
break;
case CS_MISC_RESUME_ALL_ACTORS:
play->haltAllActors = false;
break;
case CS_MISC_SANDSTORM_FILL:
if (isFirstFrame) {
play->envCtx.sandstormState = SANDSTORM_FILL;
}
Audio_PlaySfx_2(NA_SE_EV_SAND_STORM - SFX_FLAG);
break;
case CS_MISC_SUNSSONG_START:
gSaveContext.sunsSongState = SUNSSONG_START;
break;
case CS_MISC_FREEZE_TIME:
if (!gSaveContext.save.isNight) {
gSaveContext.save.time = CURRENT_TIME - (u16)R_TIME_SPEED;
} else {
gSaveContext.save.time = CURRENT_TIME - (u16)(2 * R_TIME_SPEED);
}
break;
case CS_MISC_LONG_SCARECROW_SONG:
AudioOcarina_PlayLongScarecrowSong();
csCtx->curFrame = cmd->startFrame - 1; // the cutscene runs forever
break;
case CS_MISC_SET_CSFLAG_3:
CutsceneFlags_Set(play, 3);
break;
case CS_MISC_SET_CSFLAG_4:
CutsceneFlags_Set(play, 4);
break;
case CS_MISC_PLAYER_FORM_DEKU:
gSaveContext.save.playerForm = PLAYER_FORM_DEKU;
break;
case CS_MISC_ENABLE_PLAYER_REFLECTION:
player->stateFlags2 |= PLAYER_STATE2_4000000;
break;
case CS_MISC_DISABLE_PLAYER_REFLECTION:
player->stateFlags2 &= ~PLAYER_STATE2_4000000;
break;
case CS_MISC_PLAYER_FORM_HUMAN:
sCutsceneStoredPlayerForm = GET_PLAYER_FORM;
gSaveContext.save.playerForm = PLAYER_FORM_HUMAN;
gSaveContext.save.equippedMask = PLAYER_MASK_NONE;
break;
case CS_MISC_EARTHQUAKE_STRONG:
Audio_PlaySfx_2(NA_SE_EV_EARTHQUAKE_LAST2 - SFX_FLAG);
if (isFirstFrame) {
sCutsceneQuakeIndex = Quake_Request(GET_ACTIVE_CAM(play), QUAKE_TYPE_6);
Quake_SetSpeed(sCutsceneQuakeIndex, 30000);
Quake_SetPerturbations(sCutsceneQuakeIndex, 20, 10, 0, 0);
Quake_SetDuration(sCutsceneQuakeIndex, 800);
}
break;
case CS_MISC_DEST_MOON_CRASH_FIRE_WALL:
if (isFirstFrame) {
play->nextEntrance = ENTRANCE(CUTSCENE, 0);
gSaveContext.nextCutsceneIndex = 0xFFF8;
play->transitionTrigger = TRANS_TRIGGER_START;
play->transitionType = TRANS_TYPE_FADE_WHITE;
}
break;
case CS_MISC_MOON_CRASH_SKYBOX:
if (isFirstFrame) {
play->envCtx.skyboxConfig = SKYBOX_CONFIG_13;
}
break;
case CS_MISC_PLAYER_FORM_RESTORED:
gSaveContext.save.playerForm = sCutsceneStoredPlayerForm;
break;
case CS_MISC_DISABLE_PLAYER_CSACTION_START_POS:
gDisablePlayerCsActionStartPos = true;
break;
case CS_MISC_ENABLE_PLAYER_CSACTION_START_POS:
gDisablePlayerCsActionStartPos = false;
break;
case CS_MISC_SAVE_ENTER_CLOCK_TOWN:
if (isFirstFrame) {
Sram_SaveSpecialEnterClockTown(play);
}
break;
case CS_MISC_RESET_SAVE_FROM_MOON_CRASH:
if (isFirstFrame) {
Sram_ResetSaveFromMoonCrash(&play->sramCtx);
}
break;
case CS_MISC_TIME_ADVANCE:
if (csCtx->curFrame != D_801BB15C) {
D_801BB15C = csCtx->curFrame;
if (R_TIME_SPEED != 0) {
gSaveContext.save.time = CURRENT_TIME + (u16)R_TIME_SPEED;
gSaveContext.save.time = CURRENT_TIME + (u16)((void)0, gSaveContext.save.timeSpeedOffset);
}
}
break;
case CS_MISC_EARTHQUAKE_WEAK:
Audio_PlaySfx_2(NA_SE_EV_EARTHQUAKE_LAST - SFX_FLAG);
if (isFirstFrame) {
sCutsceneQuakeIndex = Quake_Request(GET_ACTIVE_CAM(play), QUAKE_TYPE_6);
Quake_SetSpeed(sCutsceneQuakeIndex, 22000);
Quake_SetPerturbations(sCutsceneQuakeIndex, 2, 1, 0, 0);
Quake_SetDuration(sCutsceneQuakeIndex, 800);
}
break;
case CS_MISC_DAWN_OF_A_NEW_DAY:
gSaveContext.save.day = 9; // 9 % 5 is day number 4, see `CURRENT_DAY`
STOP_GAMESTATE(&play->state);
SET_NEXT_GAMESTATE(&play->state, DayTelop_Init, sizeof(DayTelopState));
Sram_SaveSpecialNewDay(play);
break;
case CS_MISC_PLAYER_FORM_ZORA:
gSaveContext.save.playerForm = PLAYER_FORM_ZORA;
break;
case CS_MISC_FINALE:
csCtx->curFrame = cmd->startFrame - 1; // the cutscene runs forever
break;
default:
break;
}
}
void CutsceneCmd_SetLightSetting(PlayState* play, CutsceneContext* csCtx, CsCmdLightSetting* cmd) {
if (csCtx->curFrame == cmd->startFrame) {
if (cmd->settingPlusOne != 32) {
play->envCtx.lightSettingOverride = cmd->settingPlusOne - 1;
play->envCtx.lightBlend = 1.0f;
} else {
play->envCtx.lightSettingOverride = LIGHT_SETTING_OVERRIDE_NONE;
}
}
}
void CutsceneCmd_StartSequence(PlayState* play, CutsceneContext* csCtx, CsCmdStartSeq* cmd) {
if (csCtx->curFrame == cmd->startFrame) {
Audio_PlaySequenceInCutscene(cmd->seqIdPlusOne - 1);
}
}
void CutsceneCmd_StopSequence(PlayState* play, CutsceneContext* csCtx, CsCmdStopSeq* cmd) {
if ((csCtx->curFrame >= cmd->startFrame) && (csCtx->curFrame <= cmd->endFrame)) {
Audio_StopSequenceInCutscene(cmd->seqIdPlusOne - 1);
}
}
void CutsceneCmd_FadeOutSequence(PlayState* play, CutsceneContext* csCtx, CsCmdFadeOutSeq* cmd) {
if ((csCtx->curFrame == cmd->startFrame) && (csCtx->curFrame < cmd->endFrame)) {
u8 fadeOutDuration = cmd->endFrame - cmd->startFrame;
if (cmd->seqPlayer == CS_FADE_OUT_FANFARE) {
SEQCMD_STOP_SEQUENCE(SEQ_PLAYER_FANFARE, fadeOutDuration);
} else { // CS_FADE_OUT_BGM_MAIN
SEQCMD_STOP_SEQUENCE(SEQ_PLAYER_BGM_MAIN, fadeOutDuration);
}
}
}
void CutsceneCmd_StartAmbience(PlayState* play, CutsceneContext* csCtx, CsCmdStartAmbience* cmd) {
if (csCtx->curFrame == cmd->startFrame) {
Audio_PlayAmbience(play->sceneSequences.ambienceId);
}
}
void Cutscene_SetSfxReverbIndexTo2(PlayState* play, CutsceneContext* csCtx, CsCmdSfxReverbIndexTo2* cmd) {
if (csCtx->curFrame == cmd->startFrame) {
Audio_SetSfxReverbIndexExceptOcarinaBank(2);
}
}
void Cutscene_SetSfxReverbIndexTo1(PlayState* play, CutsceneContext* csCtx, CsCmdSfxReverbIndexTo1* cmd) {
if (csCtx->curFrame == cmd->startFrame) {
Audio_SetSfxReverbIndexExceptOcarinaBank(1);
}
}
void CutsceneCmd_ModifySequence(PlayState* play, CutsceneContext* csCtx, CsCmdModifySeq* cmd) {
static u16 sSeqId;
u8 dayMinusOne;
if (csCtx->curFrame == cmd->startFrame) {
dayMinusOne = gSaveContext.save.day - 1;
if (dayMinusOne >= 3) {
dayMinusOne = 0;
}
switch (cmd->type) {
case CS_MOD_SEQ_0:
func_801A246C(SEQ_PLAYER_BGM_MAIN, 1);
break;
case CS_MOD_SEQ_1:
func_801A246C(SEQ_PLAYER_BGM_MAIN, 0);
break;
case CS_MOD_SEQ_2:
func_801A246C(SEQ_PLAYER_BGM_MAIN, 2);
break;
case CS_MOD_AMBIENCE_0:
func_801A246C(SEQ_PLAYER_AMBIENCE, 1);
break;
case CS_MOD_AMBIENCE_1:
func_801A246C(SEQ_PLAYER_AMBIENCE, 0);
break;
case CS_MOD_AMBIENCE_2:
func_801A246C(SEQ_PLAYER_AMBIENCE, 2);
break;
case CS_MOD_SEQ_STORE:
sSeqId = AudioSeq_GetActiveSeqId(SEQ_PLAYER_BGM_MAIN);
break;
case CS_MOD_SEQ_RESTORE:
if (sSeqId != NA_BGM_DISABLED) {
Audio_PlaySceneSequence(sSeqId, dayMinusOne);
}
break;
default:
break;
}
}
}
void CutsceneCmd_FadeOutAmbience(PlayState* play, CutsceneContext* csCtx, CsCmdFadeOutAmbience* cmd) {
if ((csCtx->curFrame == cmd->startFrame) && (csCtx->curFrame < cmd->endFrame)) {
u8 fadeOutDuration = cmd->endFrame - cmd->startFrame;
SEQCMD_STOP_SEQUENCE(SEQ_PLAYER_AMBIENCE, fadeOutDuration);
}
}
void CutsceneCmd_RumbleController(PlayState* play, CutsceneContext* csCtx, CsCmdRumble* cmd) {
switch (cmd->type) {
case CS_RUMBLE_ONCE:
if (csCtx->curFrame == cmd->startFrame) {
Rumble_Request(0.0f, cmd->intensity, cmd->decayTimer, cmd->decayStep);
}
break;
case CS_RUMBLE_PULSE:
if ((csCtx->curFrame >= cmd->startFrame) && (csCtx->curFrame <= cmd->endFrame)) {
if ((csCtx->curFrame == cmd->startFrame) || (play->state.frames % 64 == 0)) {
Rumble_Request(0.0f, cmd->intensity, cmd->decayTimer, cmd->decayStep);
}
}
break;
default:
break;
}
}
void CutsceneCmd_TransitionGeneral(PlayState* play, CutsceneContext* csCtx, CsCmdTransitionGeneral* cmd) {
if ((csCtx->curFrame >= cmd->startFrame) && (cmd->endFrame >= csCtx->curFrame)) {
f32 alpha;
play->envCtx.fillScreen = true;
alpha = Environment_LerpWeight(cmd->endFrame, cmd->startFrame, csCtx->curFrame);
if (((cmd->type == CS_TRANS_GENERAL_FILL_IN)) || (cmd->type == CS_TRANS_GENERAL_FILL_OUT)) {
play->envCtx.screenFillColor[0] = cmd->color.r;
play->envCtx.screenFillColor[1] = cmd->color.g;
play->envCtx.screenFillColor[2] = cmd->color.b;
if (cmd->type == CS_TRANS_GENERAL_FILL_OUT) {
play->envCtx.screenFillColor[3] = (1.0f - alpha) * 255.0f;
} else {
play->envCtx.screenFillColor[3] = 255.0f * alpha;
}
}
}
}
void CutsceneCmd_SetTime(PlayState* play, CutsceneContext* csCtx, CsCmdTime* cmd) {
u16 hourAsMinutes;
u16 minutes;
if (csCtx->curFrame == cmd->startFrame) {
hourAsMinutes = CLOCK_TIME_ALT_F(cmd->hour, 0);
minutes = CLOCK_TIME_ALT_F(0, cmd->minute + 1);
gSaveContext.save.time = hourAsMinutes + minutes;
gSaveContext.skyboxTime = hourAsMinutes + minutes;
}
}
void CutsceneCmd_DestinationDefault(PlayState* play, CutsceneContext* csCtx, CsCmdDestination* cmd) {
csCtx->state = CS_STATE_RUN_UNSTOPPABLE;
Play_DisableMotionBlur();
Audio_SetCutsceneFlag(false);
gSaveContext.cutsceneTransitionControl = 1;
// `hudVisibilityForceButtonAlphasByStatus` has a secondary purpose, which is to signal to the title
// screen actor that it should display immediately. This occurs when a title screen cutscene that
// is not the main clock town scene is skipped.
if ((gSaveContext.gameMode != GAMEMODE_NORMAL) && (csCtx->curFrame != cmd->startFrame)) {
gSaveContext.hudVisibilityForceButtonAlphasByStatus = true;
}
gSaveContext.save.cutsceneIndex = 0;
if (cmd->type == CS_DESTINATION_DEFAULT) {
play->nextEntrance = play->csCtx.scriptList[play->csCtx.scriptIndex].nextEntrance;
gSaveContext.nextCutsceneIndex = 0;
play->transitionTrigger = TRANS_TRIGGER_START;
if (gSaveContext.gameMode != GAMEMODE_TITLE_SCREEN) {
Scene_SetExitFade(play);
} else {
gOpeningEntranceIndex++;
if (gOpeningEntranceIndex >= 2) {
gOpeningEntranceIndex = 0;
}
play->transitionType = TRANS_TYPE_FADE_BLACK_FAST;
}
if ((play->nextEntrance & 0xF) > 0) {
gSaveContext.nextCutsceneIndex = (play->nextEntrance & 0xF) + 0xFFEF;
}
play->nextEntrance &= ~0xF;
}
}
void CutsceneCmd_Destination(PlayState* play, CutsceneContext* csCtx, CsCmdDestination* cmd) {
if (cmd->type == CS_DESTINATION_DEFAULT) {
if (csCtx->curFrame == cmd->startFrame) {
CutsceneCmd_DestinationDefault(play, csCtx, cmd);
}
} else if (cmd->type == CS_DESTINATION_BOSS_WARP) {
if (csCtx->curFrame == cmd->startFrame) {
Play_DisableMotionBlur();
switch (gDungeonBossWarpSceneId) {
case SCENE_MITURIN_BS:
if (CHECK_WEEKEVENTREG(WEEKEVENTREG_CLEARED_WOODFALL_TEMPLE)) {
play->nextEntrance = ENTRANCE(WOODFALL_TEMPLE, 1);
play->transitionTrigger = TRANS_TRIGGER_START;
play->transitionType = TRANS_TYPE_FADE_WHITE;
} else {
play->nextEntrance = ENTRANCE(WOODFALL, 0);
gSaveContext.nextCutsceneIndex = 0xFFF0;
play->transitionTrigger = TRANS_TRIGGER_START;
play->transitionType = TRANS_TYPE_FADE_WHITE;
}
break;
case SCENE_HAKUGIN_BS:
if (CHECK_WEEKEVENTREG(WEEKEVENTREG_CLEARED_SNOWHEAD_TEMPLE)) {
play->nextEntrance = ENTRANCE(MOUNTAIN_VILLAGE_SPRING, 7);
play->transitionTrigger = TRANS_TRIGGER_START;
play->transitionType = TRANS_TYPE_FADE_WHITE;
} else {
play->nextEntrance = ENTRANCE(MOUNTAIN_VILLAGE_SPRING, 0);
gSaveContext.nextCutsceneIndex = 0xFFF0;
play->transitionTrigger = TRANS_TRIGGER_START;
play->transitionType = TRANS_TYPE_FADE_WHITE;
}
break;
case SCENE_SEA_BS:
SET_WEEKEVENTREG(WEEKEVENTREG_CLEARED_GREAT_BAY_TEMPLE);
play->nextEntrance = ENTRANCE(ZORA_CAPE, 8);
gSaveContext.nextCutsceneIndex = 0xFFF0;
play->transitionTrigger = TRANS_TRIGGER_START;
play->transitionType = TRANS_TYPE_FADE_WHITE;
break;
case SCENE_INISIE_BS:
SET_WEEKEVENTREG(WEEKEVENTREG_CLEARED_STONE_TOWER_TEMPLE);
play->nextEntrance = ENTRANCE(IKANA_CANYON, 0);
gSaveContext.nextCutsceneIndex = 0xFFF1;
play->transitionTrigger = TRANS_TRIGGER_START;
play->transitionType = TRANS_TYPE_FADE_WHITE;
break;
default:
break;
}
}
}
}
// Chooses between a cutscene or a rotating mask depending on whether the player has the corresponding mask
void CutsceneCmd_ChooseCreditsScenes(PlayState* play, CutsceneContext* csCtx, CsCmdChooseCreditsScene* cmd) {
if ((csCtx->curFrame >= cmd->startFrame) && (func_801A3950(SEQ_PLAYER_BGM_MAIN, true) != 0xFF)) {
switch (cmd->type) {
case CS_CREDITS_DESTINATION:
CutsceneCmd_DestinationDefault(play, csCtx, (CsCmdDestination*)cmd);
break;
case CS_CREDITS_MASK_KAMARO:
if (INV_CONTENT(ITEM_MASK_KAMARO) == ITEM_MASK_KAMARO) {
play->nextEntrance = ENTRANCE(MILK_BAR, 0);
gSaveContext.nextCutsceneIndex = 0xFFF0;
} else {
play->nextEntrance = ENTRANCE(CUTSCENE, 0);
gSaveContext.nextCutsceneIndex = 0xFFF9;
}
play->transitionTrigger = TRANS_TRIGGER_START;
break;
case CS_CREDITS_MASK_GREAT_FAIRY:
if (INV_CONTENT(ITEM_MASK_GREAT_FAIRY) == ITEM_MASK_GREAT_FAIRY) {
play->nextEntrance = ENTRANCE(FAIRY_FOUNTAIN, 0);
gSaveContext.nextCutsceneIndex = 0xFFF0;
} else {
play->nextEntrance = ENTRANCE(CUTSCENE, 1);
gSaveContext.nextCutsceneIndex = 0xFFF9;
}
play->transitionTrigger = TRANS_TRIGGER_START;
break;
case CS_CREDITS_MASK_ROMANI:
if (INV_CONTENT(ITEM_MASK_ROMANI) == ITEM_MASK_ROMANI) {
play->nextEntrance = ENTRANCE(ROMANI_RANCH, 0);
gSaveContext.nextCutsceneIndex = 0xFFF1;
} else {
play->nextEntrance = ENTRANCE(CUTSCENE, 2);
gSaveContext.nextCutsceneIndex = 0xFFF9;
}
play->transitionTrigger = TRANS_TRIGGER_START;
break;
case CS_CREDITS_MASK_BLAST:
if (INV_CONTENT(ITEM_MASK_BLAST) == ITEM_MASK_BLAST) {
play->nextEntrance = ENTRANCE(WEST_CLOCK_TOWN, 0);
gSaveContext.nextCutsceneIndex = 0xFFF0;
} else {
play->nextEntrance = ENTRANCE(CUTSCENE, 3);
gSaveContext.nextCutsceneIndex = 0xFFF9;
}
play->transitionTrigger = TRANS_TRIGGER_START;
break;
case CS_CREDITS_MASK_CIRCUS_LEADER:
if (INV_CONTENT(ITEM_MASK_CIRCUS_LEADER) == ITEM_MASK_CIRCUS_LEADER) {
play->nextEntrance = ENTRANCE(MILK_BAR, 0);
gSaveContext.nextCutsceneIndex = 0xFFF1;
} else {
play->nextEntrance = ENTRANCE(CUTSCENE, 5);
gSaveContext.nextCutsceneIndex = 0xFFF9;
}
play->transitionTrigger = TRANS_TRIGGER_START;
break;
case CS_CREDITS_MASK_BREMEN:
if (INV_CONTENT(ITEM_MASK_BREMEN) == ITEM_MASK_BREMEN) {
play->nextEntrance = ENTRANCE(MILK_BAR, 0);
gSaveContext.nextCutsceneIndex = 0xFFF3;
} else {
play->nextEntrance = ENTRANCE(CUTSCENE, 6);
gSaveContext.nextCutsceneIndex = 0xFFF9;
}
play->transitionTrigger = TRANS_TRIGGER_START;
break;
case CS_CREDITS_IKANA:
play->nextEntrance = ENTRANCE(IKANA_CANYON, 0);
gSaveContext.nextCutsceneIndex = 0xFFF3;
play->transitionTrigger = TRANS_TRIGGER_START;
break;
case CS_CREDITS_MASK_COUPLE:
if (INV_CONTENT(ITEM_MASK_COUPLE) == ITEM_MASK_COUPLE) {
play->nextEntrance = ENTRANCE(TERMINA_FIELD, 0);
gSaveContext.nextCutsceneIndex = 0xFFF8;
} else {
play->nextEntrance = ENTRANCE(CUTSCENE, 7);
gSaveContext.nextCutsceneIndex = 0xFFF9;
}
play->transitionTrigger = TRANS_TRIGGER_START;
break;
case CS_CREDITS_MASK_BUNNY:
if (INV_CONTENT(ITEM_MASK_BUNNY) == ITEM_MASK_BUNNY) {
play->nextEntrance = ENTRANCE(CUCCO_SHACK, 0);
gSaveContext.nextCutsceneIndex = 0xFFF0;
} else {
play->nextEntrance = ENTRANCE(CUTSCENE, 4);
gSaveContext.nextCutsceneIndex = 0xFFF9;
}
play->transitionTrigger = TRANS_TRIGGER_START;
break;
case CS_CREDITS_MASK_POSTMAN:
if (INV_CONTENT(ITEM_MASK_POSTMAN) == ITEM_MASK_POSTMAN) {
play->nextEntrance = ENTRANCE(TERMINA_FIELD, 1);
gSaveContext.nextCutsceneIndex = 0xFFF8;
} else {
play->nextEntrance = ENTRANCE(CUTSCENE, 8);
gSaveContext.nextCutsceneIndex = 0xFFF9;
}
play->transitionTrigger = TRANS_TRIGGER_START;
break;
default:
break;
}
}
}
void CutsceneCmd_MotionBlur(PlayState* play, CutsceneContext* csCtx, CsCmdMotionBlur* cmd) {
if ((csCtx->curFrame >= cmd->startFrame) && (cmd->endFrame >= csCtx->curFrame)) {
if ((csCtx->curFrame == cmd->startFrame) && (cmd->type == CS_MOTION_BLUR_ENABLE)) {
Play_EnableMotionBlur(180);
}
if (cmd->type == CS_MOTION_BLUR_DISABLE) {
f32 lerp = Environment_LerpWeight(cmd->endFrame, cmd->startFrame, csCtx->curFrame);
if (lerp >= 0.9f) {
Play_DisableMotionBlur();
} else {
Play_SetMotionBlurAlpha((1.0f - lerp) * 180.0f);
}
}
}
}
void CutsceneCmd_GiveTatlToPlayer(PlayState* play, CutsceneContext* csCtx, CsCmdGiveTatl* cmd) {
Player* player = GET_PLAYER(play);
if (csCtx->curFrame == cmd->startFrame) {
if (cmd->giveTatl == true) {
gSaveContext.save.hasTatl = true;
if (player->tatlActor != NULL) {
return;
}
player->tatlActor =
Actor_Spawn(&play->actorCtx, play, ACTOR_EN_ELF, player->actor.world.pos.x, player->actor.world.pos.y,
player->actor.world.pos.z, 0, 0, 0, FAIRY_PARAMS(FAIRY_TYPE_0, false, 0));
}
}
}
void CutsceneCmd_Transition(PlayState* play, CutsceneContext* csCtx, CsCmdTransition* cmd) {
if ((csCtx->curFrame >= cmd->startFrame) && (cmd->endFrame >= csCtx->curFrame)) {
f32 lerp;
play->envCtx.fillScreen = true;
lerp = Environment_LerpWeight(cmd->endFrame, cmd->startFrame, csCtx->curFrame);
switch (cmd->type) {
case CS_TRANS_GRAY_FILL_IN:
case CS_TRANS_GRAY_FILL_OUT:
play->envCtx.screenFillColor[0] = 160;
play->envCtx.screenFillColor[1] = 160;
play->envCtx.screenFillColor[2] = 160;
if (cmd->type == CS_TRANS_GRAY_FILL_IN) {
play->envCtx.screenFillColor[3] = 255.0f * lerp;
if (lerp == 0.0f) {
Audio_PlaySfx_2(NA_SE_EV_S_STONE_FLASH);
}
} else {
play->envCtx.screenFillColor[3] = (1.0f - lerp) * 255.0f;
}
break;
case CS_TRANS_BLUE_FILL_IN:
case CS_TRANS_BLUE_FILL_OUT:
play->envCtx.screenFillColor[0] = 0;
play->envCtx.screenFillColor[1] = 0;
play->envCtx.screenFillColor[2] = 255;
if (cmd->type == CS_TRANS_BLUE_FILL_IN) {
play->envCtx.screenFillColor[3] = 255.0f * lerp;
} else {
play->envCtx.screenFillColor[3] = (1.0f - lerp) * 255.0f;
}
break;
case CS_TRANS_RED_FILL_OUT:
case CS_TRANS_RED_FILL_IN:
play->envCtx.screenFillColor[0] = 255;
play->envCtx.screenFillColor[1] = 0;
play->envCtx.screenFillColor[2] = 0;
if (cmd->type == CS_TRANS_RED_FILL_OUT) {
play->envCtx.screenFillColor[3] = (1.0f - lerp) * 255.0f;
} else {
play->envCtx.screenFillColor[3] = 255.0f * lerp;
}
break;
case CS_TRANS_GREEN_FILL_OUT:
case CS_TRANS_GREEN_FILL_IN:
play->envCtx.screenFillColor[0] = 0;
play->envCtx.screenFillColor[1] = 255;
play->envCtx.screenFillColor[2] = 0;
if (cmd->type == CS_TRANS_GREEN_FILL_OUT) {
play->envCtx.screenFillColor[3] = (1.0f - lerp) * 255.0f;
} else {
play->envCtx.screenFillColor[3] = 255.0f * lerp;
}
break;
case CS_TRANS_TRIGGER_INSTANCE:
gSaveContext.cutsceneTransitionControl = 1;
break;
case CS_TRANS_BLACK_FILL_OUT:
case CS_TRANS_BLACK_FILL_IN:
play->envCtx.screenFillColor[0] = 0;
play->envCtx.screenFillColor[1] = 0;
play->envCtx.screenFillColor[2] = 0;
if (cmd->type == CS_TRANS_BLACK_FILL_OUT) {
play->envCtx.screenFillColor[3] = (1.0f - lerp) * 255.0f;
} else {
play->envCtx.screenFillColor[3] = 255.0f * lerp;
}
break;
case CS_TRANS_GRAY_TO_BLACK:
play->envCtx.screenFillColor[0] = (1.0f - lerp) * 160.0f;
play->envCtx.screenFillColor[1] = play->envCtx.screenFillColor[0];
play->envCtx.screenFillColor[2] = play->envCtx.screenFillColor[0];
play->envCtx.screenFillColor[3] = 255;
break;
case CS_TRANS_BLACK_TO_GRAY:
play->envCtx.screenFillColor[0] = 160.0f * lerp;
play->envCtx.screenFillColor[1] = play->envCtx.screenFillColor[0];
play->envCtx.screenFillColor[2] = play->envCtx.screenFillColor[0];
play->envCtx.screenFillColor[3] = 255;
break;
default:
break;
}
}
}
s32 CutsceneCmd_UpdateCamSpline(PlayState* play, u8* script) {
s32 cmdBytes = 0;
bcopy(script, &cmdBytes, sizeof(cmdBytes));
script += sizeof(cmdBytes);
if (!Play_IsDebugCamEnabled()) {
CutsceneCamera_UpdateSplines(script, &sCutsceneCameraInfo);
}
return cmdBytes + sizeof(cmdBytes);
}
/**
* Counts how many masks the player has
* The count doesn't include transformation masks
*/
s32 Cutscene_CountNormalMasks(void) {
s32 count = 0;
if (INV_CONTENT(ITEM_MASK_TRUTH) == ITEM_MASK_TRUTH) {
count++;
}
if (INV_CONTENT(ITEM_MASK_KAFEIS_MASK) == ITEM_MASK_KAFEIS_MASK) {
count++;
}
if (INV_CONTENT(ITEM_MASK_ALL_NIGHT) == ITEM_MASK_ALL_NIGHT) {
count++;
}
if (INV_CONTENT(ITEM_MASK_BUNNY) == ITEM_MASK_BUNNY) {
count++;
}
if (INV_CONTENT(ITEM_MASK_KEATON) == ITEM_MASK_KEATON) {
count++;
}
if (INV_CONTENT(ITEM_MASK_GARO) == ITEM_MASK_GARO) {
count++;
}
if (INV_CONTENT(ITEM_MASK_ROMANI) == ITEM_MASK_ROMANI) {
count++;
}
if (INV_CONTENT(ITEM_MASK_CIRCUS_LEADER) == ITEM_MASK_CIRCUS_LEADER) {
count++;
}
if (INV_CONTENT(ITEM_MASK_POSTMAN) == ITEM_MASK_POSTMAN) {
count++;
}
if (INV_CONTENT(ITEM_MASK_COUPLE) == ITEM_MASK_COUPLE) {
count++;
}
if (INV_CONTENT(ITEM_MASK_GREAT_FAIRY) == ITEM_MASK_GREAT_FAIRY) {
count++;
}
if (INV_CONTENT(ITEM_MASK_GIBDO) == ITEM_MASK_GIBDO) {
count++;
}
if (INV_CONTENT(ITEM_MASK_DON_GERO) == ITEM_MASK_DON_GERO) {
count++;
}
if (INV_CONTENT(ITEM_MASK_KAMARO) == ITEM_MASK_KAMARO) {
count++;
}
if (INV_CONTENT(ITEM_MASK_CAPTAIN) == ITEM_MASK_CAPTAIN) {
count++;
}
if (INV_CONTENT(ITEM_MASK_STONE) == ITEM_MASK_STONE) {
count++;
}
if (INV_CONTENT(ITEM_MASK_BREMEN) == ITEM_MASK_BREMEN) {
count++;
}
if (INV_CONTENT(ITEM_MASK_BLAST) == ITEM_MASK_BLAST) {
count++;
}
if (INV_CONTENT(ITEM_MASK_SCENTS) == ITEM_MASK_SCENTS) {
count++;
}
if (INV_CONTENT(ITEM_MASK_GIANT) == ITEM_MASK_GIANT) {
count++;
}
return count;
}
void CutsceneCmd_Text(PlayState* play, CutsceneContext* csCtx, CsCmdText* cmd) {
static s32 sCutsceneTextboxType = CS_TEXT_TYPE_DEFAULT;
u8 talkState;
s32 pad;
u16 endFrame;
if ((csCtx->curFrame <= cmd->startFrame) || (csCtx->curFrame > cmd->endFrame)) {
return;
}
if (cmd->type != CS_TEXT_OCARINA_ACTION) {
if (sCurTextId != cmd->textId) {
if (sCutsceneTextboxType == CS_TEXT_TYPE_3) {
csCtx->curFrame--;
}
sCutsceneTextboxType = CS_TEXT_TYPE_1;
sCurTextId = cmd->textId;
if (cmd->type == CS_TEXT_TYPE_BOSSES_REMAINS) {
if (CHECK_QUEST_ITEM(QUEST_REMAINS_ODOLWA) && CHECK_QUEST_ITEM(QUEST_REMAINS_GOHT) &&
CHECK_QUEST_ITEM(QUEST_REMAINS_GYORG) && CHECK_QUEST_ITEM(QUEST_REMAINS_TWINMOLD)) {
if (cmd->altTextId1 != 0xFFFF) {
Message_StartTextbox(play, cmd->altTextId1, NULL);
}
} else {
Message_StartTextbox(play, cmd->textId, NULL);
}
} else if (cmd->type == CS_TEXT_TYPE_ALL_NORMAL_MASKS) {
if (Cutscene_CountNormalMasks() == 20) {
if (cmd->altTextId1 != 0xFFFF) {
Message_StartTextbox(play, cmd->altTextId1, NULL);
}
} else {
Message_StartTextbox(play, cmd->textId, NULL);
}
} else {
Message_StartTextbox(play, cmd->textId, NULL);
}
//! FAKE: return;
goto end;
}
} else {
if (sCurOcarinaAction != cmd->textId) {
sCutsceneTextboxType = CS_TEXT_OCARINA_ACTION;
sCurOcarinaAction = cmd->textId;
Message_DisplayOcarinaStaff(play, cmd->textId);
return;
}
}
if (csCtx->curFrame >= cmd->endFrame) {
// The Textbox command can change the current cutscene frame, mainly to prevent advancing the cutscene when
// a textbox that is expected to be closed by the user is still open.
endFrame = csCtx->curFrame;
talkState = Message_GetState(&play->msgCtx);
if ((talkState != TEXT_STATE_CLOSING) && (talkState != TEXT_STATE_NONE) &&
(talkState != TEXT_STATE_SONG_DEMO_DONE) && (talkState != TEXT_STATE_8)) {
csCtx->curFrame--;
if ((talkState == TEXT_STATE_CHOICE) && Message_ShouldAdvance(play)) {
if (play->msgCtx.choiceIndex == 0) {
if (cmd->textId == 0x33BD) {
// Gorman Track: do you understand?
Audio_PlaySfx_MessageCancel();
}
if (cmd->altTextId1 != 0xFFFF) {
Message_ContinueTextbox(play, cmd->altTextId1);
if (cmd->type == CS_TEXT_TYPE_3) {
sCutsceneTextboxType = CS_TEXT_TYPE_3;
if (cmd->altTextId2 != 0xFFFF) {
csCtx->curFrame++;
}
}
} else {
Message_CloseTextbox(play);
csCtx->curFrame++;
}
} else {
if (cmd->textId == 0x33BD) {
// Gorman Track: do you understand?
Audio_PlaySfx_MessageDecide();
}
if (cmd->altTextId2 != 0xFFFF) {
Message_ContinueTextbox(play, cmd->altTextId2);
if (cmd->type == CS_TEXT_TYPE_3) {
sCutsceneTextboxType = CS_TEXT_TYPE_3;
if (cmd->altTextId1 != 0xFFFF) {
csCtx->curFrame++;
}
}
} else {
Message_CloseTextbox(play);
csCtx->curFrame++;
}
}
}
if ((talkState == TEXT_STATE_EVENT) && Message_ShouldAdvance(play)) {
Message_DisplayOcarinaStaff(play, cmd->textId);
}
}
if ((talkState == TEXT_STATE_CLOSING) && (sCutsceneTextboxType == CS_TEXT_TYPE_3)) {
csCtx->curFrame--;
sCurTextId++;
}
if (endFrame == csCtx->curFrame) {
Interface_SetHudVisibility(HUD_VISIBILITY_NONE);
sCurTextId = 0;
sCurOcarinaAction = 0;
CutsceneCamera_Reset();
} else {
CutsceneCamera_SetState(CS_CAM_STATE_UPDATE_SPLINE);
}
}
end:;
}
void Cutscene_SetActorCue(CutsceneContext* csCtx, u8** script, s16 cueChannel) {
s32 i;
s32 numCues;
bcopy(*script, &numCues, sizeof(numCues));
*script += sizeof(numCues);
for (i = 0; i < numCues; i++) {
CsCmdActorCue* cue = (CsCmdActorCue*)(*script);
if ((csCtx->curFrame >= cue->startFrame) && (csCtx->curFrame < cue->endFrame)) {
csCtx->actorCues[cueChannel] = cue;
}
*script += sizeof(CsCmdActorCue);
}
}
/**
* Loops over the cutscene data itself (`script`), applying the effects of each command instantaneously (for most
* commands).
*
* The cutscene data is an irregularly-structured array of words, which is made up of
* - A beginning, which contains the number of command lists of this cutscene and the ending frame (see
* `CS_BEGIN_CUTSCENE`).
* - Any number of cutscene command lists (which should be the number specified in the beginning), each one of which
* contains commands of the corresponding category.
* - A end marker (see `CS_END`).
*
* This function iterates over each command list until either it has processed the number of command lists stated on
* `CS_BEGIN_CUTSCENE` or until it finds a end marker.
*
* A command list starts with a pair of words containing the command category and a count of how many commands this list
* has (N). This is followed by N commands of the said category. (The exception is Camera, which has the length of the
* list in bytes instead of the number of commands).
*
* For most command lists categories (all but the actorCues and Camera commands):
* - For each command list found, read the number of commands and loop over them, passing each one to the corresponding
* function which handles the command, applying its effects and checking the frame range stored in the command.
*
* This function is invoked once per frame that a cutscene is active.
*
* TODO: consider changing the type of `script` to `uintptr_t` when this function matches.
*/
void Cutscene_ProcessScript(PlayState* play, CutsceneContext* csCtx, u8* script) {
s32 i;
s32 j;
s32 pad;
s32 totalEntries;
u32 cmdType;
s32 cmdEntries;
s32 pad2;
s32 csFrameCount;
void* cmd;
// Read the command list count and the ending frame for this cutscene
bcopy(script, &totalEntries, sizeof(totalEntries));
script += sizeof(totalEntries);
bcopy(script, &csFrameCount, sizeof(csFrameCount));
script += sizeof(csFrameCount);
if ((csCtx->curFrame > (u16)csFrameCount) && (play->transitionTrigger != TRANS_TRIGGER_START) &&
(csCtx->state != CS_STATE_RUN_UNSTOPPABLE)) {
csCtx->state = CS_STATE_STOP;
return;
}
// Loop over every command list
for (i = 0; i < totalEntries; i++) {
// Read the command type of the current command list.
bcopy(script, &cmdType, sizeof(cmdType));
script += sizeof(cmdType);
if (cmdType == CS_CAM_STOP) {
break;
}
if (((cmdType >= CS_CMD_ACTOR_CUE_100) && (cmdType <= CS_CMD_ACTOR_CUE_149)) ||
(cmdType == CS_CMD_ACTOR_CUE_201) ||
((cmdType >= CS_CMD_ACTOR_CUE_450) && (cmdType <= CS_CMD_ACTOR_CUE_599))) {
for (j = 0; j < ARRAY_COUNT(sCueTypeList); j = (s16)(j + 1)) {
if (sCueTypeList[j] == (u16)cmdType) {
Cutscene_SetActorCue(csCtx, &script, j);
cmdType = CS_CMD_ACTOR_CUE_POST_PROCESS;
break;
}
if (sCueTypeList[j] == 0) {
sCueTypeList[j] = cmdType;
Cutscene_SetActorCue(csCtx, &script, j);
cmdType = CS_CMD_ACTOR_CUE_POST_PROCESS;
break;
}
}
}
switch (cmdType) {
case CS_CMD_MISC:
bcopy(script, &cmdEntries, sizeof(cmdEntries));
script += sizeof(cmdEntries);
for (j = 0; j < cmdEntries; j++) {
CutsceneCmd_Misc(play, csCtx, (CsCmdMisc*)script);
script += sizeof(CsCmdMisc);
}
break;
case CS_CMD_LIGHT_SETTING:
bcopy(script, &cmdEntries, sizeof(cmdEntries));
script += sizeof(cmdEntries);
for (j = 0; j < cmdEntries; j++) {
CutsceneCmd_SetLightSetting(play, csCtx, (CsCmdLightSetting*)script);
script += sizeof(CsCmdLightSetting);
}
break;
case CS_CMD_START_SEQ:
bcopy(script, &cmdEntries, sizeof(cmdEntries));
script += sizeof(cmdEntries);
for (j = 0; j < cmdEntries; j++) {
CutsceneCmd_StartSequence(play, csCtx, (CsCmdStartSeq*)script);
script += sizeof(CsCmdStartSeq);
}
break;
case CS_CMD_STOP_SEQ:
bcopy(script, &cmdEntries, sizeof(cmdEntries));
script += sizeof(cmdEntries);
for (j = 0; j < cmdEntries; j++) {
CutsceneCmd_StopSequence(play, csCtx, (CsCmdStopSeq*)script);
script += sizeof(CsCmdStartSeq);
}
break;
case CS_CMD_FADE_OUT_SEQ:
bcopy(script, &cmdEntries, sizeof(cmdEntries));
script += sizeof(cmdEntries);
for (j = 0; j < cmdEntries; j++) {
CutsceneCmd_FadeOutSequence(play, csCtx, (CsCmdFadeOutSeq*)script);
script += sizeof(CsCmdFadeOutSeq);
}
break;
case CS_CMD_START_AMBIENCE:
bcopy(script, &cmdEntries, sizeof(cmdEntries));
script += sizeof(cmdEntries);
for (j = 0; j < cmdEntries; j++) {
CutsceneCmd_StartAmbience(play, csCtx, (CsCmdStartAmbience*)script);
script += sizeof(CsCmdStartAmbience);
}
break;
case CS_CMD_FADE_OUT_AMBIENCE:
bcopy(script, &cmdEntries, sizeof(cmdEntries));
script += sizeof(cmdEntries);
for (j = 0; j < cmdEntries; j++) {
CutsceneCmd_FadeOutAmbience(play, csCtx, (CsCmdFadeOutAmbience*)script);
script += sizeof(CsCmdFadeOutAmbience);
}
break;
case CS_CMD_SFX_REVERB_INDEX_2:
bcopy(script, &cmdEntries, sizeof(cmdEntries));
script += sizeof(cmdEntries);
for (j = 0; j < cmdEntries; j++) {
Cutscene_SetSfxReverbIndexTo2(play, csCtx, (CsCmdSfxReverbIndexTo2*)script);
script += sizeof(CsCmdSfxReverbIndexTo2);
}
break;
case CS_CMD_SFX_REVERB_INDEX_1:
bcopy(script, &cmdEntries, sizeof(cmdEntries));
script += sizeof(cmdEntries);
for (j = 0; j < cmdEntries; j++) {
Cutscene_SetSfxReverbIndexTo1(play, csCtx, (CsCmdSfxReverbIndexTo1*)script);
script += sizeof(CsCmdSfxReverbIndexTo1);
}
break;
case CS_CMD_MODIFY_SEQ:
bcopy(script, &cmdEntries, sizeof(cmdEntries));
script += sizeof(cmdEntries);
for (j = 0; j < cmdEntries; j++) {
CutsceneCmd_ModifySequence(play, csCtx, (CsCmdModifySeq*)script);
script += sizeof(CsCmdModifySeq);
}
break;
case CS_CMD_RUMBLE:
bcopy(script, &cmdEntries, sizeof(cmdEntries));
script += sizeof(cmdEntries);
for (j = 0; j < cmdEntries; j++) {
CutsceneCmd_RumbleController(play, csCtx, (CsCmdRumble*)script);
script += sizeof(CsCmdRumble);
}
break;
case CS_CMD_TRANSITION_GENERAL:
bcopy(script, &cmdEntries, sizeof(cmdEntries));
script += sizeof(cmdEntries);
for (j = 0; j < cmdEntries; j++) {
CutsceneCmd_TransitionGeneral(play, csCtx, (CsCmdTransitionGeneral*)script);
script += sizeof(CsCmdTransitionGeneral);
}
break;
case CS_CMD_TIME:
bcopy(script, &cmdEntries, sizeof(cmdEntries));
script += sizeof(cmdEntries);
for (j = 0; j < cmdEntries; j++) {
CutsceneCmd_SetTime(play, csCtx, (CsCmdTime*)script);
script += sizeof(CsCmdTime);
}
break;
case CS_CMD_PLAYER_CUE:
bcopy(script, &cmdEntries, sizeof(cmdEntries));
script += sizeof(cmdEntries);
for (j = 0; j < cmdEntries; j++) {
cmd = script;
if ((((CsCmdActorCue*)cmd)->startFrame <= csCtx->curFrame) &&
(csCtx->curFrame < ((CsCmdActorCue*)cmd)->endFrame)) {
csCtx->playerCue = (CsCmdActorCue*)cmd;
}
script += sizeof(CsCmdActorCue);
}
break;
case CS_CMD_CAMERA_SPLINE:
script += CutsceneCmd_UpdateCamSpline(play, script);
break;
case CS_CMD_DESTINATION:
bcopy(script, &cmdEntries, sizeof(cmdEntries));
script += sizeof(cmdEntries);
for (j = 0; j < cmdEntries; j++) {
CutsceneCmd_Destination(play, csCtx, (CsCmdDestination*)script);
script += sizeof(CsCmdDestination);
}
break;
case CS_CMD_CHOOSE_CREDITS_SCENES:
bcopy(script, &cmdEntries, sizeof(cmdEntries));
script += sizeof(cmdEntries);
for (j = 0; j < cmdEntries; j++) {
CutsceneCmd_ChooseCreditsScenes(play, csCtx, (CsCmdChooseCreditsScene*)script);
script += sizeof(CsCmdChooseCreditsScene);
}
break;
case CS_CMD_TEXT:
bcopy(script, &cmdEntries, sizeof(cmdEntries));
script += sizeof(cmdEntries);
for (j = 0; j < cmdEntries; j++) {
cmd = script;
if (((CsCmdText*)cmd)->textId != 0xFFFF) {
CutsceneCmd_Text(play, csCtx, (CsCmdText*)cmd);
}
script += sizeof(CsCmdText);
}
break;
case CS_CMD_TRANSITION:
bcopy(script, &cmdEntries, sizeof(cmdEntries));
script += sizeof(cmdEntries);
for (j = 0; j < cmdEntries; j++) {
CutsceneCmd_Transition(play, csCtx, (CsCmdTransition*)script);
script += sizeof(CsCmdTransition);
}
break;
case CS_CMD_MOTION_BLUR:
bcopy(script, &cmdEntries, sizeof(cmdEntries));
script += sizeof(cmdEntries);
for (j = 0; j < cmdEntries; j++) {
CutsceneCmd_MotionBlur(play, csCtx, (CsCmdMotionBlur*)script);
script += sizeof(CsCmdMotionBlur);
}
break;
case CS_CMD_GIVE_TATL:
bcopy(script, &cmdEntries, sizeof(cmdEntries));
script += sizeof(cmdEntries);
for (j = 0; j < cmdEntries; j++) {
CutsceneCmd_GiveTatlToPlayer(play, csCtx, (CsCmdGiveTatl*)script);
script += sizeof(CsCmdGiveTatl);
}
break;
case CS_CMD_ACTOR_CUE_POST_PROCESS:
break;
default:
bcopy(script, &cmdEntries, sizeof(cmdEntries));
script += sizeof(cmdEntries);
for (j = 0; j < cmdEntries; j++) {
script += sizeof(CsCmdUnimplemented);
}
break;
}
}
}
/* End of command handling section */
void CutsceneHandler_RunScript(PlayState* play, CutsceneContext* csCtx) {
if (gSaveContext.save.cutsceneIndex >= 0xFFF0) {
csCtx->curFrame++;
Cutscene_ProcessScript(play, csCtx, (u8*)play->csCtx.script);
}
}
void CutsceneHandler_StopManual(PlayState* play, CutsceneContext* csCtx) {
if (Cutscene_StepTimer(play, csCtx, 0.0f)) {
Audio_SetCutsceneFlag(false);
csCtx->state = CS_STATE_IDLE;
}
}
void CutsceneHandler_StopScript(PlayState* play, CutsceneContext* csCtx) {
if (Cutscene_StepTimer(play, csCtx, 0.0f)) {
s16 i;
csCtx->playerCue = NULL;
for (i = 0; i < ARRAY_COUNT(csCtx->actorCues); i++) {
csCtx->actorCues[i] = NULL;
}
gSaveContext.save.cutsceneIndex = 0;
gSaveContext.gameMode = GAMEMODE_NORMAL;
CutsceneManager_Stop(CS_ID_GLOBAL_END);
Audio_SetCutsceneFlag(false);
csCtx->state = CS_STATE_IDLE;
}
}
void Cutscene_SetupScripted(PlayState* play, CutsceneContext* csCtx) {
if ((gSaveContext.cutsceneTrigger != 0) && (csCtx->state == CS_STATE_IDLE) && !Player_InCsMode(play)) {
gSaveContext.save.cutsceneIndex = 0xFFFD;
}
if ((gSaveContext.save.cutsceneIndex >= 0xFFF0) && (csCtx->state == CS_STATE_IDLE)) {
s16 i;
sCurTextId = 0;
sCurOcarinaAction = 0;
csCtx->playerCue = NULL;
for (i = 0; i < ARRAY_COUNT(csCtx->actorCues); i++) {
csCtx->actorCues[i] = NULL;
}
csCtx->state++; // CS_STATE_START
if (csCtx->state == CS_STATE_START) {
Audio_SetCutsceneFlag(true);
csCtx->curFrame = 0xFFFF;
csCtx->subCamId = CutsceneManager_GetCurrentSubCamId(CS_ID_GLOBAL_END);
CutsceneCamera_Init(Play_GetCamera(play, csCtx->subCamId), &sCutsceneCameraInfo);
// OoT Remnant
csCtx->camEyeSplinePointsAppliedFrame = CS_CAM_DATA_NOT_APPLIED;
if (gSaveContext.cutsceneTrigger == 0) {
Interface_SetHudVisibility(HUD_VISIBILITY_NONE);
ShrinkWindow_Letterbox_SetSizeTarget(32);
ShrinkWindow_Letterbox_SetSize(32);
csCtx->state++; // CS_STATE_RUN
}
CutsceneHandler_RunScript(play, csCtx);
}
gSaveContext.cutsceneTrigger = 0;
}
}
void Cutscene_HandleEntranceTriggers(PlayState* play) {
s32 pad;
s16 csId;
SceneTableEntry* scene;
s32 scriptIndex;
if (((gSaveContext.gameMode == GAMEMODE_NORMAL) || (gSaveContext.gameMode == GAMEMODE_TITLE_SCREEN)) &&
(gSaveContext.respawnFlag <= 0)) {
// Try to find an actor cutscene that's triggered by the current spawn
csId = CutsceneManager_FindEntranceCsId();
if (csId != CS_ID_NONE) {
scriptIndex = CutsceneManager_GetCutsceneScriptIndex(csId);
if (scriptIndex != CS_SCRIPT_ID_NONE) {
// A scripted cutscene is triggered by a spawn
if ((play->csCtx.scriptList[scriptIndex].spawnFlags != CS_SPAWN_FLAG_NONE) &&
(gSaveContext.respawnFlag == 0)) {
if (play->csCtx.scriptList[scriptIndex].spawnFlags == CS_SPAWN_FLAG_ALWAYS) {
// Entrance cutscenes that always run
CutsceneManager_Start(csId, NULL);
gSaveContext.showTitleCard = false;
} else if (!CHECK_CS_SPAWN_FLAG_WEEKEVENTREG(play->csCtx.scriptList[scriptIndex].spawnFlags)) {
// Entrance cutscenes that only run once
SET_CS_SPAWN_FLAG_WEEKEVENTREG(play->csCtx.scriptList[scriptIndex].spawnFlags);
CutsceneManager_Start(csId, NULL);
// The title card will be used by the cs misc command if necessary.
gSaveContext.showTitleCard = false;
}
}
} else {
// A non-scripted cutscene is triggered by a spawn
CutsceneManager_StartWithPlayerCs(csId, NULL);
}
}
}
if ((gSaveContext.respawnFlag == 0) || (gSaveContext.respawnFlag == -2)) {
scene = play->loadedScene;
if ((scene->titleTextId != 0) && gSaveContext.showTitleCard) {
if ((Entrance_GetTransitionFlags(((void)0, gSaveContext.save.entrance) +
((void)0, gSaveContext.sceneLayer)) &
0x4000) != 0) {
Message_DisplaySceneTitleCard(play, scene->titleTextId);
}
}
gSaveContext.showTitleCard = true;
}
}
void func_800EDDB0(PlayState* play) {
}
void func_800EDDBC(UNK_TYPE arg0, UNK_TYPE arg1) {
}
void Cutscene_StartScripted(PlayState* play, u8 scriptIndex) {
if (!R_USE_DEBUG_CUTSCENE) {
play->csCtx.scriptIndex = scriptIndex;
play->csCtx.script = Lib_SegmentedToVirtual(play->csCtx.scriptList[scriptIndex].script);
}
gSaveContext.cutsceneTrigger = 1;
}
/* Start of actor utilities section */
/**
* Interpolates the actor's position based on the corresponding actor action's position
* and the current cutscene frame
*/
void Cutscene_ActorTranslate(Actor* actor, PlayState* play, s32 cueChannel) {
Vec3f startPos;
Vec3f endPos;
CsCmdActorCue* cue = play->csCtx.actorCues[cueChannel];
f32 lerp;
startPos.x = cue->startPos.x;
startPos.y = cue->startPos.y;
startPos.z = cue->startPos.z;
endPos.x = cue->endPos.x;
endPos.y = cue->endPos.y;
endPos.z = cue->endPos.z;
lerp = Environment_LerpWeight(cue->endFrame, cue->startFrame, play->csCtx.curFrame);
VEC3F_LERPIMPDST(&actor->world.pos, &startPos, &endPos, lerp);
}
/**
* Interpolates the actor's position based on the corresponding actor action's position
* and the current cutscene frame, and sets the actor's yaw using the actor action yaw
*/
void Cutscene_ActorTranslateAndYaw(Actor* actor, PlayState* play, s32 cueChannel) {
Cutscene_ActorTranslate(actor, play, cueChannel);
actor->world.rot.y = play->csCtx.actorCues[cueChannel]->rot.y;
actor->shape.rot.y = actor->world.rot.y;
}
/**
* Interpolates the actor's position and yaw based on the corresponding actor action's
* position and the current cutscene frame
*/
void Cutscene_ActorTranslateAndYawSmooth(Actor* actor, PlayState* play, s32 cueChannel) {
Vec3f startPos;
Vec3f endPos;
CsCmdActorCue* cue;
f32 lerp;
startPos.x = play->csCtx.actorCues[cueChannel]->startPos.x;
startPos.y = play->csCtx.actorCues[cueChannel]->startPos.y;
startPos.z = play->csCtx.actorCues[cueChannel]->startPos.z;
endPos.x = play->csCtx.actorCues[cueChannel]->endPos.x;
endPos.y = play->csCtx.actorCues[cueChannel]->endPos.y;
endPos.z = play->csCtx.actorCues[cueChannel]->endPos.z;
cue = play->csCtx.actorCues[cueChannel];
lerp = Environment_LerpWeight(cue->endFrame, cue->startFrame, play->csCtx.curFrame);
VEC3F_LERPIMPDST(&actor->world.pos, &startPos, &endPos, lerp);
Math_SmoothStepToS(&actor->world.rot.y, Math_Vec3f_Yaw(&startPos, &endPos), 10, 0x3E8, 1);
actor->shape.rot.y = actor->world.rot.y;
}
/**
* Interpolates the actor's XZ position and yaw based on the corresponding actor action's
* position and the current cutscene frame
*/
void Cutscene_ActorTranslateXZAndYawSmooth(Actor* actor, PlayState* play, s32 cueChannel) {
Vec3f startPos;
Vec3f endPos;
CsCmdActorCue* cue;
f32 lerp;
startPos.x = play->csCtx.actorCues[cueChannel]->startPos.x;
startPos.z = play->csCtx.actorCues[cueChannel]->startPos.z;
endPos.x = play->csCtx.actorCues[cueChannel]->endPos.x;
endPos.z = play->csCtx.actorCues[cueChannel]->endPos.z;
cue = play->csCtx.actorCues[cueChannel];
lerp = Environment_LerpWeight(cue->endFrame, cue->startFrame, play->csCtx.curFrame);
actor->world.pos.x = startPos.x + (endPos.x - startPos.x) * lerp;
actor->world.pos.z = startPos.z + (endPos.z - startPos.z) * lerp;
Math_SmoothStepToS(&actor->world.rot.y, Math_Vec3f_Yaw(&startPos, &endPos), 10, 0x3E8, 1);
actor->shape.rot.y = actor->world.rot.y;
}
s32 Cutscene_GetSceneLayer(PlayState* play) {
s32 sceneLayer = 0;
if (gSaveContext.sceneLayer > 0) {
sceneLayer = gSaveContext.sceneLayer;
}
return sceneLayer;
}
/**
* @param play See `PlayState`
* @param cueType See `cmdType`
* @return cue channel
*/
s32 Cutscene_GetCueChannel(PlayState* play, u16 cueType) {
s32 i;
s32 cueChannel = -1;
for (i = 0; i < ARRAY_COUNT(sCueTypeList); i++) {
if (cueType == sCueTypeList[i]) {
cueChannel = i;
}
}
return cueChannel;
}
/**
* @param play See `PlayState`
* @param cueType See `cmdType`
* @return is cuetype in a channel
*/
s32 Cutscene_IsCueInChannel(PlayState* play, u16 cueType) {
if (play->csCtx.state != CS_STATE_IDLE) {
s32 cueChannel = Cutscene_GetCueChannel(play, cueType);
if (cueChannel != -1) {
return play->csCtx.actorCues[cueChannel] != NULL;
}
}
return false;
}
u8 Cutscene_IsPlaying(PlayState* play) {
return (gSaveContext.cutsceneTrigger != 0) || (play->csCtx.state != CS_STATE_IDLE);
}
/* End of actor utilities section */