[Time Saver Enhancement] Nighttime GS Always Spawn (#2611)

* Nighttime GS Always Spawn

* add hook onSceneSpawnActors

* implement onSceneSpawnActors hook to spawn GS in various day scenes

* handle the kak tree skull during the day

* Update mods.cpp

* Add the &

* Fixes

* Update soh/soh/Enhancements/mods.cpp

Co-authored-by: Adam Bird <Archez@users.noreply.github.com>

---------

Co-authored-by: Adam Bird <archez39@me.com>
Co-authored-by: Adam Bird <Archez@users.noreply.github.com>
This commit is contained in:
Patrick12115 2023-04-02 21:11:08 -04:00 committed by GitHub
parent 94ad837c02
commit 2af952b180
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 79 additions and 1 deletions

View File

@ -148,6 +148,7 @@ public:
DEFINE_HOOK(OnSaleEnd, void(GetItemEntry itemEntry)); DEFINE_HOOK(OnSaleEnd, void(GetItemEntry itemEntry));
DEFINE_HOOK(OnTransitionEnd, void(int16_t sceneNum)); DEFINE_HOOK(OnTransitionEnd, void(int16_t sceneNum));
DEFINE_HOOK(OnSceneInit, void(int16_t sceneNum)); DEFINE_HOOK(OnSceneInit, void(int16_t sceneNum));
DEFINE_HOOK(OnSceneSpawnActors, void());
DEFINE_HOOK(OnPlayerUpdate, void()); DEFINE_HOOK(OnPlayerUpdate, void());
DEFINE_HOOK(OnOcarinaSongAction, void()); DEFINE_HOOK(OnOcarinaSongAction, void());

View File

@ -30,6 +30,10 @@ void GameInteractor_ExecuteOnSceneInitHooks(int16_t sceneNum) {
GameInteractor::Instance->ExecuteHooks<GameInteractor::OnSceneInit>(sceneNum); GameInteractor::Instance->ExecuteHooks<GameInteractor::OnSceneInit>(sceneNum);
} }
void GameInteractor_ExecuteOnSceneSpawnActors() {
GameInteractor::Instance->ExecuteHooks<GameInteractor::OnSceneSpawnActors>();
}
void GameInteractor_ExecuteOnPlayerUpdate() { void GameInteractor_ExecuteOnPlayerUpdate() {
GameInteractor::Instance->ExecuteHooks<GameInteractor::OnPlayerUpdate>(); GameInteractor::Instance->ExecuteHooks<GameInteractor::OnPlayerUpdate>();
} }

View File

@ -8,6 +8,7 @@ extern "C" void GameInteractor_ExecuteOnItemReceiveHooks(GetItemEntry itemEntry)
extern "C" void GameInteractor_ExecuteOnSaleEndHooks(GetItemEntry itemEntry); extern "C" void GameInteractor_ExecuteOnSaleEndHooks(GetItemEntry itemEntry);
extern "C" void GameInteractor_ExecuteOnTransitionEndHooks(int16_t sceneNum); extern "C" void GameInteractor_ExecuteOnTransitionEndHooks(int16_t sceneNum);
extern "C" void GameInteractor_ExecuteOnSceneInit(int16_t sceneNum); extern "C" void GameInteractor_ExecuteOnSceneInit(int16_t sceneNum);
extern "C" void GameInteractor_ExecuteOnSceneSpawnActors();
extern "C" void GameInteractor_ExecuteOnPlayerUpdate(); extern "C" void GameInteractor_ExecuteOnPlayerUpdate();
extern "C" void GameInteractor_ExecuteOnOcarinaSongAction(); extern "C" void GameInteractor_ExecuteOnOcarinaSongAction();
extern "C" void GameInteractor_ExecuteOnActorUpdate(void* actor); extern "C" void GameInteractor_ExecuteOnActorUpdate(void* actor);

View File

@ -14,6 +14,8 @@ extern PlayState* gPlayState;
extern void Play_PerformSave(PlayState* play); extern void Play_PerformSave(PlayState* play);
extern s32 Health_ChangeBy(PlayState* play, s16 healthChange); extern s32 Health_ChangeBy(PlayState* play, s16 healthChange);
extern void Rupees_ChangeBy(s16 rupeeChange); extern void Rupees_ChangeBy(s16 rupeeChange);
extern Actor* Actor_Spawn(ActorContext* actorCtx, PlayState* play, s16 actorId, f32 posX, f32 posY, f32 posZ,
s16 rotX, s16 rotY, s16 rotZ, s16 params, s16 canRandomize);
extern void Inventory_ChangeEquipment(s16 equipment, u16 value); extern void Inventory_ChangeEquipment(s16 equipment, u16 value);
} }
bool performDelayedSave = false; bool performDelayedSave = false;
@ -350,6 +352,62 @@ void RegisterRupeeDash() {
}); });
} }
struct DayTimeGoldSkulltulas {
uint16_t scene;
uint16_t room;
bool forChild;
std::vector<ActorEntry> actorEntries;
};
using DayTimeGoldSkulltulasList = std::vector<DayTimeGoldSkulltulas>;
void RegisterDaytimeGoldSkultullas() {
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnSceneSpawnActors>([]() {
if (!CVarGetInteger("gNightGSAlwaysSpawn", 0)) {
return;
}
// Gold Skulltulas that are not part of the scene actor list during the day
// Actor values copied from the night time scene actor list
static const DayTimeGoldSkulltulasList dayTimeGoldSkulltulas = {
// Graveyard
{ SCENE_SPOT02, 1, true, { { ACTOR_EN_SW, { 156, 315, 795 }, { 16384, -32768, 0 }, -20096 } } },
// ZF
{ SCENE_SPOT08, 0, true, { { ACTOR_EN_SW, { -1891, 187, 1911 }, { 16384, 18022, 0 }, -19964 } } },
// GF
{ SCENE_SPOT12, 0, false, { { ACTOR_EN_SW, { 1598, 999, -2008 }, { 16384, -16384, 0 }, -19198 } } },
{ SCENE_SPOT12, 1, false, { { ACTOR_EN_SW, { 3377, 1734, -4935 }, { 16384, 0, 0 }, -19199 } } },
// Kak
{ SCENE_SPOT01, 0, false, { { ACTOR_EN_SW, { -18, 540, 1800 }, { 0, -32768, 0 }, -20160 } } },
{ SCENE_SPOT01,
0,
true,
{ { ACTOR_EN_SW, { -465, 377, -888 }, { 0, 28217, 0 }, -20222 },
{ ACTOR_EN_SW, { 5, 686, -171 }, { 0, -32768, 0 }, -20220 },
{ ACTOR_EN_SW, { 324, 270, 905 }, { 16384, 0, 0 }, -20216 },
{ ACTOR_EN_SW, { -602, 120, 1120 }, { 16384, 0, 0 }, -20208 } } },
// LLR
{ SCENE_SPOT20,
0,
true,
{ { ACTOR_EN_SW, { -2344, 180, 672 }, { 16384, 22938, 0 }, -29695 },
{ ACTOR_EN_SW, { 808, 48, 326 }, { 16384, 0, 0 }, -29694 },
{ ACTOR_EN_SW, { 997, 286, -2698 }, { 16384, -16384, 0 }, -29692 } } },
};
for (const auto& dayTimeGS : dayTimeGoldSkulltulas) {
if (IS_DAY && dayTimeGS.forChild == LINK_IS_CHILD && dayTimeGS.scene == gPlayState->sceneNum &&
dayTimeGS.room == gPlayState->roomCtx.curRoom.num) {
for (const auto& actorEntry : dayTimeGS.actorEntries) {
Actor_Spawn(&gPlayState->actorCtx, gPlayState, actorEntry.id, actorEntry.pos.x, actorEntry.pos.y,
actorEntry.pos.z, actorEntry.rot.x, actorEntry.rot.y, actorEntry.rot.z,
actorEntry.params, false);
}
}
}
});
}
void RegisterHyperBosses() { void RegisterHyperBosses() {
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnActorUpdate>([](void* refActor) { GameInteractor::Instance->RegisterGameHook<GameInteractor::OnActorUpdate>([](void* refActor) {
// Run the update function a second time to make bosses move and act twice as fast. // Run the update function a second time to make bosses move and act twice as fast.
@ -450,6 +508,7 @@ void InitMods() {
RegisterSwitchAge(); RegisterSwitchAge();
RegisterOcarinaTimeTravel(); RegisterOcarinaTimeTravel();
RegisterAutoSave(); RegisterAutoSave();
RegisterDaytimeGoldSkultullas();
RegisterRupeeDash(); RegisterRupeeDash();
RegisterHyperBosses(); RegisterHyperBosses();
RegisterBonkDamage(); RegisterBonkDamage();

View File

@ -181,6 +181,7 @@ const std::vector<const char*> enhancementsCvars = {
"gStaticExplosionRadius", "gStaticExplosionRadius",
"gNoInputForCredits", "gNoInputForCredits",
"gFastFarores", "gFastFarores",
"gNightGSAlwaysSpawn",
}; };
const std::vector<const char*> randomizerCvars = { const std::vector<const char*> randomizerCvars = {

View File

@ -354,6 +354,8 @@ namespace GameMenuBar {
"to the guard next to the gate."); "to the guard next to the gate.");
UIWidgets::PaddedEnhancementCheckbox("Faster Farore's Wind", "gFastFarores", true, false); UIWidgets::PaddedEnhancementCheckbox("Faster Farore's Wind", "gFastFarores", true, false);
UIWidgets::Tooltip("Greatly decreases cast time of Farore's Wind magic spell."); UIWidgets::Tooltip("Greatly decreases cast time of Farore's Wind magic spell.");
UIWidgets::PaddedEnhancementCheckbox("Nighttime GS Always Spawn", "gNightGSAlwaysSpawn", true, false);
UIWidgets::Tooltip("Nighttime Skulltulas will spawn during both day and night.");
UIWidgets::PaddedEnhancementCheckbox("Dampe Appears All Night", "gDampeAllNight", true, false); UIWidgets::PaddedEnhancementCheckbox("Dampe Appears All Night", "gDampeAllNight", true, false);
UIWidgets::Tooltip("Makes Dampe appear anytime during it's night, not just his usual working hours."); UIWidgets::Tooltip("Makes Dampe appear anytime during it's night, not just his usual working hours.");
UIWidgets::PaddedEnhancementCheckbox("Time Travel with the Song of Time", "gTimeTravel", true, false); UIWidgets::PaddedEnhancementCheckbox("Time Travel with the Song of Time", "gTimeTravel", true, false);

View File

@ -2516,6 +2516,7 @@ void Actor_UpdateAll(PlayState* play, ActorContext* actorCtx) {
Actor_SpawnEntry(&play->actorCtx, actorEntry++, play); Actor_SpawnEntry(&play->actorCtx, actorEntry++, play);
} }
play->numSetupActors = 0; play->numSetupActors = 0;
GameInteractor_ExecuteOnSceneSpawnActors();
} }
if (actorCtx->unk_02 != 0) { if (actorCtx->unk_02 != 0) {

View File

@ -550,7 +550,7 @@ void func_80B0D590(EnSw* this, PlayState* play) {
this->collider.elements[0].info.ocElemFlags = 1; this->collider.elements[0].info.ocElemFlags = 1;
} }
Math_ApproachF(&this->actor.scale.x, !IS_DAY ? 0.02f : 0.0f, 0.2f, 0.01f); Math_ApproachF(&this->actor.scale.x, !IS_DAY || CVarGetInteger("gNightGSAlwaysSpawn", 0) ? 0.02f : 0.0f, 0.2f, 0.01f);
Actor_SetScale(&this->actor, this->actor.scale.x); Actor_SetScale(&this->actor, this->actor.scale.x);
} }

View File

@ -173,6 +173,15 @@ void EnWood02_Init(Actor* thisx, PlayState* play2) {
f32 floorY; f32 floorY;
s16 extraRot; s16 extraRot;
// The tree in Kakariko's day scene does not have the same params to spawn the GS
// as the night scene, For the always spawn GS enhancement we apply the needed
// params to have the GS drop when bonking
if ((this->actor.params & 0xFF) == WOOD_TREE_CONICAL_MEDIUM && IS_DAY &&
play->sceneNum == SCENE_SPOT01 && CVarGetInteger("gNightGSAlwaysSpawn", 0)) {
this->actor.params = 0x2001;
this->actor.home.rot.z = 0x71;
}
spawnType = WOOD_SPAWN_NORMAL; spawnType = WOOD_SPAWN_NORMAL;
actorScale = 1.0f; actorScale = 1.0f;
this->unk_14C = (this->actor.params >> 8) & 0xFF; this->unk_14C = (this->actor.params >> 8) & 0xFF;