mirror of https://github.com/zeldaret/mm.git
1726 lines
61 KiB
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 */
|