mirror of
https://github.com/HarbourMasters/Shipwright
synced 2026-10-13 07:25:54 -04:00
Set time of day when skipping sun song cutscene (#7235)
Simulate cutscenes to match various time of day quirks
This commit is contained in:
@@ -0,0 +1,284 @@
|
||||
#include "CutsceneTime.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <ship/Context.h>
|
||||
#include <ship/resource/ResourceManager.h>
|
||||
|
||||
#include "soh/ResourceManagerHelpers.h"
|
||||
#include "soh/resource/type/Scene.h"
|
||||
#include "soh/resource/type/scenecommand/SetAlternateHeaders.h"
|
||||
#include "soh/resource/type/scenecommand/SetCutscenes.h"
|
||||
#include "soh/resource/type/scenecommand/SetRoomList.h"
|
||||
#include "soh/resource/type/scenecommand/SetTimeSettings.h"
|
||||
|
||||
extern "C" {
|
||||
#include "z64.h"
|
||||
#include "macros.h"
|
||||
#include "variables.h"
|
||||
}
|
||||
|
||||
// Misc cutscene command types
|
||||
enum {
|
||||
CS_MISC_STOP_CUTSCENE = 12,
|
||||
CS_MISC_STOP_STORM_AND_ADVANCE_TO_DAY = 18,
|
||||
CS_MISC_TIME_ADVANCE_TO_NIGHT = 25,
|
||||
CS_MISC_SUNSSONG_START = 33,
|
||||
CS_MISC_FREEZE_TIME = 34,
|
||||
};
|
||||
|
||||
// Textbox entries with this id show no text
|
||||
static constexpr uint16_t CS_TEXT_ID_NONE = 0xFFFF;
|
||||
// Room time settings use this for "not set"
|
||||
static constexpr uint8_t TIME_SETTING_UNSET = 0xFF;
|
||||
static constexpr uint16_t SUNS_SONG_TIME_SPEED = 400;
|
||||
|
||||
// Command entry sizes, in 4 byte words
|
||||
static constexpr int32_t CAMERA_POINT_WORDS = sizeof(CutsceneCameraPoint) / sizeof(CutsceneData);
|
||||
// CS_CMD_09 and set time entries are the same size as textboxes
|
||||
static constexpr int32_t TEXTBOX_WORDS = sizeof(CsCmdTextbox) / sizeof(CutsceneData);
|
||||
static constexpr int32_t ACTOR_CUE_WORDS = sizeof(CsCmdActorCue) / sizeof(CutsceneData);
|
||||
|
||||
static SOH::Scene* LoadScene(const std::string& path) {
|
||||
return (SOH::Scene*)Ship::Context::GetRawInstance()->GetResourceManager()->LoadResource(path).get();
|
||||
}
|
||||
|
||||
template <typename T> static T* FindCommand(SOH::Scene* scene, SOH::SceneCommandID id) {
|
||||
for (auto& cmd : scene->commands) {
|
||||
if (cmd->cmdId == id) {
|
||||
return (T*)cmd.get();
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Same header pick as Scene_CommandAlternateHeaderList
|
||||
static SOH::Scene* LayerHeader(SOH::Scene* scene, int32_t sceneLayer) {
|
||||
auto headers = FindCommand<SOH::SetAlternateHeaders>(scene, SOH::SceneCommandID::SetAlternateHeaders);
|
||||
if (headers == nullptr || sceneLayer - 1 >= (int32_t)headers->headers.size()) {
|
||||
return nullptr;
|
||||
}
|
||||
return headers->headers[sceneLayer - 1].get();
|
||||
}
|
||||
|
||||
// Same path as OTRPlay_SpawnScene
|
||||
static std::string ScenePath(int32_t sceneNum) {
|
||||
std::string version = "shared";
|
||||
if ((sceneNum >= SCENE_DEKU_TREE && sceneNum <= SCENE_ICE_CAVERN) || sceneNum == SCENE_GERUDO_TRAINING_GROUND ||
|
||||
sceneNum == SCENE_INSIDE_GANONS_CASTLE) {
|
||||
version = ResourceMgr_IsGameMasterQuest() ? "mq" : "nonmq";
|
||||
}
|
||||
const char* file = gSceneTable[sceneNum].sceneFile.fileName;
|
||||
return "scenes/" + version + "/" + file + "/" + file;
|
||||
}
|
||||
|
||||
static bool IsNight(uint16_t time) {
|
||||
return time > CLOCK_TIME(18, 0) || time < CLOCK_TIME(6, 30);
|
||||
}
|
||||
|
||||
// Sun's Song speeds time up to the next of these
|
||||
static bool IsSunsSongDay(uint16_t time) {
|
||||
return time >= CLOCK_TIME(6, 30) && time <= CLOCK_TIME(18, 0) + 1;
|
||||
}
|
||||
|
||||
struct CutsceneTimeScript {
|
||||
std::vector<CsCmdBase> misc;
|
||||
std::vector<CsCmdBase> textboxes;
|
||||
std::vector<CsCmdDayTime> setTimes;
|
||||
int32_t lastFrame = 0;
|
||||
};
|
||||
|
||||
// Walks the command list the same way as Cutscene_ProcessCommands, keeping only what touches the clock
|
||||
static CutsceneTimeScript ReadScript(const uint32_t* data) {
|
||||
CutsceneTimeScript script;
|
||||
int32_t totalEntries = data[0];
|
||||
script.lastFrame = data[1];
|
||||
const uint32_t* ptr = data + 2;
|
||||
|
||||
for (int32_t i = 0; i < totalEntries; i++) {
|
||||
int32_t cmdType = *ptr++;
|
||||
if (cmdType == CS_CMD_STOP) {
|
||||
break;
|
||||
}
|
||||
|
||||
switch (cmdType) {
|
||||
case CS_CMD_CAM_EYE:
|
||||
case CS_CMD_CAM_EYE_REL_TO_PLAYER:
|
||||
case CS_CMD_CAM_AT:
|
||||
case CS_CMD_CAM_AT_REL_TO_PLAYER: {
|
||||
ptr += 2;
|
||||
bool more = true;
|
||||
while (more) {
|
||||
more = ((CutsceneCameraPoint*)ptr)->continueFlag != CS_CMD_STOP;
|
||||
ptr += CAMERA_POINT_WORDS;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case CS_CMD_07:
|
||||
case CS_CMD_08:
|
||||
ptr += 6;
|
||||
break;
|
||||
case CS_CMD_TERMINATOR:
|
||||
script.lastFrame = ((CsCmdBase*)(ptr + 1))->startFrame;
|
||||
ptr += 3;
|
||||
break;
|
||||
case CS_CMD_SCENE_TRANS_FX:
|
||||
ptr += 3;
|
||||
break;
|
||||
case CS_CMD_TEXTBOX:
|
||||
case CS_CMD_09:
|
||||
case CS_CMD_SETTIME: {
|
||||
int32_t entries = *ptr++;
|
||||
for (int32_t j = 0; j < entries; j++, ptr += TEXTBOX_WORDS) {
|
||||
if (cmdType == CS_CMD_TEXTBOX && ((CsCmdBase*)ptr)->base != CS_TEXT_ID_NONE) {
|
||||
script.textboxes.push_back(*(CsCmdBase*)ptr);
|
||||
} else if (cmdType == CS_CMD_SETTIME) {
|
||||
script.setTimes.push_back(*(CsCmdDayTime*)ptr);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
int32_t entries = *ptr++;
|
||||
for (int32_t j = 0; j < entries; j++, ptr += ACTOR_CUE_WORDS) {
|
||||
if (cmdType == CS_CMD_MISC) {
|
||||
CsCmdBase* cmd = (CsCmdBase*)ptr;
|
||||
script.misc.push_back(*cmd);
|
||||
if (cmd->base == CS_MISC_STOP_CUTSCENE) {
|
||||
script.lastFrame = std::min(script.lastFrame, (int32_t)cmd->startFrame);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return script;
|
||||
}
|
||||
|
||||
// Runs the cutscene frame by frame, starting from the given time and time speed
|
||||
static uint16_t RunScript(const uint32_t* data, uint16_t dayTime, uint16_t timeSpeed, int32_t fadeInFrames,
|
||||
const CutsceneTimeActorUpdate& actorUpdate) {
|
||||
uint16_t roomTimeSpeed = timeSpeed;
|
||||
CutsceneTimeScript script = ReadScript(data);
|
||||
bool night = IsNight(dayTime);
|
||||
bool sunsSongStarted = false;
|
||||
bool sunsSongSpeeding = false;
|
||||
bool sunsSongToDusk = false;
|
||||
uint16_t sunsSongPrevSpeed = 0;
|
||||
|
||||
// csCtx->frames starts at 0xFFFF and ticks before commands run, so commands start on frame 0.
|
||||
// Frame 0 is counted as paused by the fade in check.
|
||||
for (int32_t frame = 0; frame <= script.lastFrame; frame++) {
|
||||
if (actorUpdate) {
|
||||
actorUpdate(dayTime, timeSpeed);
|
||||
}
|
||||
|
||||
// func_80064824, only the cases that touch the clock
|
||||
for (auto& cmd : script.misc) {
|
||||
if (frame < cmd.startFrame || (frame >= cmd.endFrame && cmd.endFrame != cmd.startFrame)) {
|
||||
continue;
|
||||
}
|
||||
switch (cmd.base) {
|
||||
case CS_MISC_STOP_STORM_AND_ADVANCE_TO_DAY:
|
||||
if (dayTime < CLOCK_TIME(7, 0)) {
|
||||
dayTime += 30;
|
||||
}
|
||||
break;
|
||||
case CS_MISC_TIME_ADVANCE_TO_NIGHT:
|
||||
dayTime += 30;
|
||||
if (dayTime > CLOCK_TIME(19, 0) - 1) {
|
||||
dayTime = CLOCK_TIME(19, 0) - 1;
|
||||
}
|
||||
break;
|
||||
case CS_MISC_SUNSSONG_START:
|
||||
sunsSongStarted = true;
|
||||
break;
|
||||
case CS_MISC_FREEZE_TIME:
|
||||
dayTime -= night ? timeSpeed * 2 : timeSpeed;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// func_80065134
|
||||
for (auto& cmd : script.setTimes) {
|
||||
if (frame == cmd.startFrame) {
|
||||
s16 hours = (s16)((cmd.hour * 60.0f) / (360.0f / 0x4000));
|
||||
s16 minutes = (s16)((cmd.minute + 1) / (360.0f / 0x4000));
|
||||
dayTime = hours + minutes;
|
||||
}
|
||||
}
|
||||
|
||||
// Sun's Song handling in Interface_Update
|
||||
if (sunsSongStarted && roomTimeSpeed != 0) {
|
||||
if (!sunsSongSpeeding) {
|
||||
sunsSongToDusk = IsSunsSongDay(dayTime);
|
||||
sunsSongSpeeding = true;
|
||||
sunsSongPrevSpeed = timeSpeed;
|
||||
timeSpeed = SUNS_SONG_TIME_SPEED;
|
||||
} else if (sunsSongToDusk ? dayTime > CLOCK_TIME(18, 0) + 1 : IsSunsSongDay(dayTime)) {
|
||||
sunsSongStarted = sunsSongSpeeding = false;
|
||||
timeSpeed = sunsSongPrevSpeed;
|
||||
}
|
||||
}
|
||||
|
||||
// Environment_Update pauses time during the fade in and while a textbox is up.
|
||||
// A textbox is up after its start frame, and the cutscene stalls on its end frame until it closes.
|
||||
bool paused = frame <= fadeInFrames;
|
||||
for (auto& cmd : script.textboxes) {
|
||||
paused |= frame > cmd.startFrame && frame < cmd.endFrame;
|
||||
}
|
||||
if (!paused) {
|
||||
dayTime += night && timeSpeed < SUNS_SONG_TIME_SPEED ? timeSpeed * 2 : timeSpeed;
|
||||
}
|
||||
night = IsNight(dayTime);
|
||||
}
|
||||
|
||||
return dayTime;
|
||||
}
|
||||
|
||||
uint16_t CutsceneTime_Simulate(int32_t sceneNum, uint16_t cutsceneIndex, uint16_t dayTime, int32_t fadeInFrames,
|
||||
const CutsceneTimeActorUpdate& actorUpdate) {
|
||||
int32_t sceneLayer = SCENE_LAYER_CUTSCENE_FIRST + (cutsceneIndex & 0xF);
|
||||
SOH::Scene* scene = LoadScene(ScenePath(sceneNum));
|
||||
SOH::Scene* layer = scene != nullptr ? LayerHeader(scene, sceneLayer) : nullptr;
|
||||
auto cutscene =
|
||||
layer != nullptr ? FindCommand<SOH::SetCutscenes>(layer, SOH::SceneCommandID::SetCutscenes) : nullptr;
|
||||
auto rooms = FindCommand<SOH::SetRoomList>(scene, SOH::SceneCommandID::SetRoomList);
|
||||
if (cutscene == nullptr || rooms == nullptr || rooms->fileNames.empty()) {
|
||||
return dayTime;
|
||||
}
|
||||
|
||||
// Room time settings, as in Scene_CommandTimeSettings. Assumes the cutscene starts in the first room.
|
||||
uint16_t timeSpeed = 0;
|
||||
SOH::Scene* room = LoadScene(rooms->fileNames[0]);
|
||||
SOH::Scene* roomLayer = room != nullptr ? LayerHeader(room, sceneLayer) : nullptr;
|
||||
auto timeSettings = roomLayer != nullptr
|
||||
? FindCommand<SOH::SetTimeSettings>(roomLayer, SOH::SceneCommandID::SetTimeSettings)
|
||||
: nullptr;
|
||||
if (timeSettings != nullptr) {
|
||||
auto& settings = timeSettings->settings;
|
||||
if (settings.hour != TIME_SETTING_UNSET && settings.minute != TIME_SETTING_UNSET) {
|
||||
dayTime = (uint16_t)(((settings.hour + (settings.minute / 60.0f)) * 60.0f) / ((f32)(24 * 60) / 0x10000));
|
||||
}
|
||||
timeSpeed = settings.timeIncrement != TIME_SETTING_UNSET ? settings.timeIncrement : 0;
|
||||
}
|
||||
|
||||
return RunScript(cutscene->cutscene->commands.data(), dayTime, timeSpeed, fadeInFrames, actorUpdate);
|
||||
}
|
||||
|
||||
uint16_t CutsceneTime_SimulateScript(const void* cutscene, uint16_t dayTime, uint16_t timeSpeed, int32_t fadeInFrames,
|
||||
const CutsceneTimeActorUpdate& actorUpdate) {
|
||||
// Same lookup as Cutscene_ProcessCommands
|
||||
char* data = (char*)cutscene;
|
||||
if (ResourceMgr_OTRSigCheck(data)) {
|
||||
data = (char*)ResourceMgr_LoadCSByName(data);
|
||||
}
|
||||
if (data == nullptr) {
|
||||
return dayTime;
|
||||
}
|
||||
return RunScript((const uint32_t*)data, dayTime, timeSpeed, fadeInFrames, actorUpdate);
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <functional>
|
||||
|
||||
// Stands in for actors that change the time during a cutscene. Runs each frame before the cutscene commands.
|
||||
using CutsceneTimeActorUpdate = std::function<void(uint16_t& dayTime, uint16_t& timeSpeed)>;
|
||||
|
||||
// Simulate cutscene's time of day, returning dayTime at end.
|
||||
// fadeInFrames is how many frames time stays paused while the scene fades in.
|
||||
uint16_t CutsceneTime_Simulate(int32_t sceneNum, uint16_t cutsceneIndex, uint16_t dayTime, int32_t fadeInFrames,
|
||||
const CutsceneTimeActorUpdate& actorUpdate = nullptr);
|
||||
|
||||
// Same for a cutscene an actor starts in the current scene, which keeps the scene's time speed.
|
||||
// cutscene is the csCtx.segment the actor would have set.
|
||||
uint16_t CutsceneTime_SimulateScript(const void* cutscene, uint16_t dayTime, uint16_t timeSpeed,
|
||||
int32_t fadeInFrames = 0, const CutsceneTimeActorUpdate& actorUpdate = nullptr);
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
#include "soh/Enhancements/game-interactor/GameInteractor.h"
|
||||
#include "soh/Enhancements/randomizer/randomizer_entrance.h"
|
||||
#include "soh/Enhancements/TimeSavers/SkipCutscene/CutsceneTime.h"
|
||||
#include "soh/ShipInit.hpp"
|
||||
#include <cassert>
|
||||
|
||||
@@ -9,8 +10,11 @@ extern "C" {
|
||||
#include "z64.h"
|
||||
#include "z64save.h"
|
||||
#include "z64scene.h"
|
||||
#include "scenes/overworld/spot06/spot06_scene.h"
|
||||
#include "scenes/overworld/spot16/spot16_scene.h"
|
||||
extern PlayState* gPlayState;
|
||||
extern SaveContext gSaveContext;
|
||||
extern u16 gTimeSpeed;
|
||||
|
||||
u8 Randomizer_GetSettingValue(RandomizerSettingKey randoSettingKey);
|
||||
}
|
||||
@@ -42,6 +46,11 @@ static s16 GetEntranceIndex(s32 owlType) {
|
||||
}
|
||||
|
||||
static void SkipOwlTravel(s32 owlType) {
|
||||
// Time keeps running during the flight
|
||||
const char* cutscene = owlType == 7 ? gLakeHyliaOwlCs : gDMTOwlCs;
|
||||
gSaveContext.dayTime = gSaveContext.skyboxTime =
|
||||
CutsceneTime_SimulateScript(cutscene, gSaveContext.dayTime, gTimeSpeed);
|
||||
|
||||
gPlayState->nextEntranceIndex = GetEntranceIndex(owlType);
|
||||
gPlayState->transitionTrigger = TRANS_TRIGGER_START;
|
||||
gPlayState->transitionType = TRANS_TYPE_FADE_BLACK;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include <libultraship/bridge/consolevariablebridge.h>
|
||||
|
||||
#include "soh/Enhancements/game-interactor/GameInteractor.h"
|
||||
#include "soh/Enhancements/TimeSavers/SkipCutscene/CutsceneTime.h"
|
||||
#include "soh/ShipInit.hpp"
|
||||
|
||||
extern "C" {
|
||||
@@ -48,6 +49,18 @@ void SkipZeldaFleeingCastle_OnActorInit(void* actorPtr) {
|
||||
}
|
||||
}
|
||||
|
||||
// BgSpot00Hanebasi_Update swaps the room's time speed for one that reaches dawn by the end, then stops time at dawn.
|
||||
// Walking in late at night picks a speed too low to reach morning, and time can end up barely moving at all.
|
||||
static void HanebasiTime(uint16_t& dayTime, uint16_t& timeSpeed) {
|
||||
if (timeSpeed == 50) {
|
||||
int32_t dawn = dayTime >= 0xD557 ? 0x1D556 : 0xD556;
|
||||
timeSpeed = static_cast<s16>((dawn - dayTime) * (1.0f / 350.0f));
|
||||
}
|
||||
if (dayTime >= 0x2AAC && dayTime < 0x3000) {
|
||||
timeSpeed = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void RegisterSkipZeldaFleeingCastle() {
|
||||
COND_ID_HOOK(OnActorInit, ACTOR_ITEM_OCARINA,
|
||||
CVarGetInteger(CVAR_ENHANCEMENT("TimeSavers.SkipCutscene.Story"), IS_RANDO),
|
||||
@@ -55,7 +68,8 @@ void RegisterSkipZeldaFleeingCastle() {
|
||||
COND_VB_SHOULD(VB_PLAY_TRANSITION_CS, CVarGetInteger(CVAR_ENHANCEMENT("TimeSavers.SkipCutscene.Story"), IS_RANDO), {
|
||||
if (gSaveContext.entranceIndex == ENTR_HYRULE_FIELD_PAST_BRIDGE_SPAWN && gSaveContext.cutsceneIndex == 0xFFF1) {
|
||||
// Normally set in the cutscene
|
||||
gSaveContext.dayTime = gSaveContext.skyboxTime = 0x4AAA;
|
||||
gSaveContext.dayTime = gSaveContext.skyboxTime =
|
||||
CutsceneTime_Simulate(SCENE_HYRULE_FIELD, 0xFFF1, gSaveContext.dayTime, 7, HanebasiTime);
|
||||
|
||||
gSaveContext.cutsceneIndex = 0;
|
||||
*should = false;
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#include <libultraship/bridge/consolevariablebridge.h>
|
||||
#include "soh/Enhancements/game-interactor/GameInteractor.h"
|
||||
#include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h"
|
||||
#include "soh/Enhancements/TimeSavers/SkipCutscene/CutsceneTime.h"
|
||||
#include "soh/Enhancements/enhancementTypes.h"
|
||||
#include "soh/Enhancements/randomizer/SeedContext.h"
|
||||
#include "soh/Enhancements/randomizer/randomizer_entrance.h"
|
||||
@@ -35,8 +36,10 @@ extern "C" {
|
||||
#include "src/overlays/actors/ovl_Obj_Lightswitch/z_obj_lightswitch.h"
|
||||
#include "src/overlays/actors/ovl_Bg_Jya_Bombchuiwa/z_bg_jya_bombchuiwa.h"
|
||||
#include <overlays/actors/ovl_En_Ik/z_en_ik.h>
|
||||
#include "scenes/overworld/spot06/spot06_scene.h"
|
||||
extern SaveContext gSaveContext;
|
||||
extern PlayState* gPlayState;
|
||||
extern u16 gTimeSpeed;
|
||||
extern int32_t D_8011D3AC;
|
||||
|
||||
extern void BgSpot03Taki_HandleWaterfallState(BgSpot03Taki* bgSpot03Taki, PlayState* play);
|
||||
@@ -222,6 +225,7 @@ void TimeSaverOnVanillaBehaviorHandler(GIVanillaBehavior id, bool* should, va_li
|
||||
case VB_PLAY_ENTRANCE_CS: {
|
||||
s32 entranceFlag = va_arg(args, s32);
|
||||
s32 entranceIndex = va_arg(args, s32);
|
||||
void* cutscene = va_arg(args, void*);
|
||||
|
||||
// Epona LLR fence jump cutscenes not skipped to allow the player and epona to load in the world correctly
|
||||
// Nabooru fight cutscene is handled by boss intro skip instead (which deals with other flags needing to be
|
||||
@@ -229,6 +233,12 @@ void TimeSaverOnVanillaBehaviorHandler(GIVanillaBehavior id, bool* should, va_li
|
||||
if (CVarGetInteger(CVAR_ENHANCEMENT("TimeSavers.SkipCutscene.Entrances"), IS_RANDO) &&
|
||||
(entranceFlag != EVENTCHKINF_EPONA_OBTAINED) && entranceIndex != ENTR_SPIRIT_TEMPLE_BOSS_ENTRANCE) {
|
||||
*should = false;
|
||||
// Time keeps running during the cutscene in fields.
|
||||
// Left alone elsewhere, as the scene has already snapped skyboxTime.
|
||||
uint16_t dayTime = CutsceneTime_SimulateScript(cutscene, gSaveContext.dayTime, gTimeSpeed);
|
||||
if (dayTime != gSaveContext.dayTime) {
|
||||
gSaveContext.dayTime = gSaveContext.skyboxTime = dayTime;
|
||||
}
|
||||
|
||||
// Check for dispulsion of Ganon's Tower barrier
|
||||
switch (entranceIndex) {
|
||||
@@ -633,7 +643,10 @@ void TimeSaverOnVanillaBehaviorHandler(GIVanillaBehavior id, bool* should, va_li
|
||||
} else {
|
||||
gPlayState->nextEntranceIndex = ENTR_HYRULE_FIELD_17;
|
||||
}
|
||||
gSaveContext.dayTime = gSaveContext.skyboxTime = 0x8000;
|
||||
// Normally set in Impa's escort cutscene, which keeps the time and lets it run.
|
||||
// Its fade in is a slow circle, 50 frames.
|
||||
gSaveContext.dayTime = gSaveContext.skyboxTime =
|
||||
CutsceneTime_Simulate(SCENE_HYRULE_FIELD, 0xFFF8, gSaveContext.dayTime, 50);
|
||||
gPlayState->transitionType = TRANS_TYPE_FADE_WHITE;
|
||||
gPlayState->transitionTrigger = TRANS_TRIGGER_START;
|
||||
gSaveContext.nextTransitionType = 2;
|
||||
@@ -680,9 +693,8 @@ void TimeSaverOnVanillaBehaviorHandler(GIVanillaBehavior id, bool* should, va_li
|
||||
if (CVarGetInteger(CVAR_ENHANCEMENT("TimeSavers.SkipCutscene.LearnSong"), IS_RANDO) || IS_RANDO) {
|
||||
*should = false;
|
||||
Flags_SetEventChkInf(EVENTCHKINF_LEARNED_SUNS_SONG);
|
||||
// SoH [Randomizer] TODO: Increment time X amount (find out X)
|
||||
// When time is 0, it's changed to 0x46A7
|
||||
// When it's 0x8000, it's changed to 0xC090
|
||||
gSaveContext.dayTime = gSaveContext.skyboxTime =
|
||||
CutsceneTime_Simulate(SCENE_GRAVEYARD, 0xFFF1, gSaveContext.dayTime, 11);
|
||||
}
|
||||
break;
|
||||
case VB_PLAY_ROYAL_FAMILY_TOMB_CS: {
|
||||
@@ -733,6 +745,9 @@ void TimeSaverOnVanillaBehaviorHandler(GIVanillaBehavior id, bool* should, va_li
|
||||
case VB_PLAY_FIRE_ARROW_CS: {
|
||||
if (CVarGetInteger(CVAR_ENHANCEMENT("TimeSavers.SkipMiscInteractions"), IS_RANDO)) {
|
||||
*should = false;
|
||||
// Cutscene sets the time to 7:00
|
||||
gSaveContext.dayTime = gSaveContext.skyboxTime =
|
||||
CutsceneTime_SimulateScript(gLakeHyliaFireArrowsCS, gSaveContext.dayTime, gTimeSpeed);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -2160,9 +2160,11 @@ typedef enum {
|
||||
// ##### In `z_demo.c`:
|
||||
// - `int32_t` (entranceCutscene->flag) (promoted from `uint8_t` by va_arg)
|
||||
// - `int32_t` (entranceCutscene->entrance) (promoted from `uint16_t` by va_arg)
|
||||
// - `void*` (entranceCutscene->segAddr)
|
||||
// ##### In `z_bg_breakwall.c` and `z_bg_toki_swd.c`:
|
||||
// - `int32_t` (EVENTCHKINF)
|
||||
// - `int32_t` (gSaveContext.entranceIndex)
|
||||
// - `void*` (cutscene)
|
||||
VB_PLAY_ENTRANCE_CS,
|
||||
|
||||
// #### `result`
|
||||
|
||||
@@ -2203,7 +2203,8 @@ void Cutscene_HandleEntranceTriggers(PlayState* play) {
|
||||
(gSaveContext.cutsceneIndex < 0xFFF0) && ((u8)gSaveContext.linkAge == requiredAge) &&
|
||||
(gSaveContext.respawnFlag <= 0)) {
|
||||
Flags_SetEventChkInf(entranceCutscene->flag);
|
||||
if (GameInteractor_Should(VB_PLAY_ENTRANCE_CS, true, entranceCutscene->flag, entranceCutscene->entrance)) {
|
||||
if (GameInteractor_Should(VB_PLAY_ENTRANCE_CS, true, entranceCutscene->flag, entranceCutscene->entrance,
|
||||
entranceCutscene->segAddr)) {
|
||||
Cutscene_SetSegment(play, entranceCutscene->segAddr);
|
||||
gSaveContext.cutsceneTrigger = 2;
|
||||
gSaveContext.showTitleCard = false;
|
||||
|
||||
@@ -254,7 +254,7 @@ void BgBreakwall_Wait(BgBreakwall* this, PlayState* play) {
|
||||
if ((wallType == BWALL_DC_ENTRANCE) && (!Flags_GetEventChkInf(EVENTCHKINF_ENTERED_DODONGOS_CAVERN))) {
|
||||
Flags_SetEventChkInf(EVENTCHKINF_ENTERED_DODONGOS_CAVERN);
|
||||
if (GameInteractor_Should(VB_PLAY_ENTRANCE_CS, true, EVENTCHKINF_ENTERED_DODONGOS_CAVERN,
|
||||
gSaveContext.entranceIndex)) {
|
||||
gSaveContext.entranceIndex, gDcOpeningCs)) {
|
||||
Cutscene_SetSegment(play, gDcOpeningCs);
|
||||
gSaveContext.cutsceneTrigger = 1;
|
||||
Player_SetCsActionWithHaltedActors(play, NULL, 0x31);
|
||||
|
||||
@@ -118,7 +118,7 @@ void func_808BAF40(BgTokiSwd* this, PlayState* play) {
|
||||
Actor_IsFacingAndNearPlayer(&this->actor, 800.0f, 0x7530) && !Play_InCsMode(play)) {
|
||||
Flags_SetEventChkInf(EVENTCHKINF_ENTERED_MASTER_SWORD_CHAMBER);
|
||||
if (GameInteractor_Should(VB_PLAY_ENTRANCE_CS, true, EVENTCHKINF_ENTERED_MASTER_SWORD_CHAMBER,
|
||||
gSaveContext.entranceIndex)) {
|
||||
gSaveContext.entranceIndex, D_808BBD90)) {
|
||||
play->csCtx.segment = D_808BBD90;
|
||||
gSaveContext.cutsceneTrigger = 1;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user