ADD: Time Saver --> Time Travel with the Song of Time (#2575)

* ADD: Time Saver --> Time Travel with the Song of Time

Co-Authored-By: aMannus <4244591+aMannus@users.noreply.github.com>
Co-Authored-By: David Chavez <david@dcvz.io>

* TWEAK: one liner for the gPlayState check

* ADD: Time Saver --> Time Travel with the Song of Time

Co-Authored-By: aMannus <4244591+aMannus@users.noreply.github.com>
Co-Authored-By: David Chavez <david@dcvz.io>

* TWEAK: one liner for the gPlayState check

* Timetravel stuff

* tweazk oopsies

* Fixes timeline

* tweak frog dist

* ADD: Time Saver --> Time Travel with the Song of Time

Co-Authored-By: aMannus <4244591+aMannus@users.noreply.github.com>
Co-Authored-By: David Chavez <david@dcvz.io>

* TWEAK: one liner for the gPlayState check

* Timetravel stuff

* tweazk oopsies

* Fixes timeline

* tweak frog dist

* oppsie²

* del dupe

* del dupe

* tweak tooltip

---------

Co-authored-by: aMannus <4244591+aMannus@users.noreply.github.com>
Co-authored-by: David Chavez <david@dcvz.io>
This commit is contained in:
PurpleHato 2023-04-02 18:34:02 +02:00 committed by GitHub
parent fb8dacbc69
commit 77cc91d0de
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 87 additions and 6 deletions

View File

@ -149,9 +149,11 @@ public:
DEFINE_HOOK(OnTransitionEnd, void(int16_t sceneNum));
DEFINE_HOOK(OnSceneInit, void(int16_t sceneNum));
DEFINE_HOOK(OnPlayerUpdate, void());
DEFINE_HOOK(OnOcarinaSongAction, void());
DEFINE_HOOK(OnActorUpdate, void(void* actor));
DEFINE_HOOK(OnPlayerBonk, void());
DEFINE_HOOK(OnSaveFile, void(int32_t fileNum));
DEFINE_HOOK(OnLoadFile, void(int32_t fileNum));
DEFINE_HOOK(OnDeleteFile, void(int32_t fileNum));

View File

@ -34,6 +34,10 @@ void GameInteractor_ExecuteOnPlayerUpdate() {
GameInteractor::Instance->ExecuteHooks<GameInteractor::OnPlayerUpdate>();
}
void GameInteractor_ExecuteOnOcarinaSongAction() {
GameInteractor::Instance->ExecuteHooks<GameInteractor::OnOcarinaSongAction>();
}
void GameInteractor_ExecuteOnActorUpdate(void* actor) {
GameInteractor::Instance->ExecuteHooks<GameInteractor::OnActorUpdate>(actor);
}

View File

@ -9,8 +9,10 @@ extern "C" void GameInteractor_ExecuteOnSaleEndHooks(GetItemEntry itemEntry);
extern "C" void GameInteractor_ExecuteOnTransitionEndHooks(int16_t sceneNum);
extern "C" void GameInteractor_ExecuteOnSceneInit(int16_t sceneNum);
extern "C" void GameInteractor_ExecuteOnPlayerUpdate();
extern "C" void GameInteractor_ExecuteOnOcarinaSongAction();
extern "C" void GameInteractor_ExecuteOnActorUpdate(void* actor);
extern "C" void GameInteractor_ExecuteOnPlayerBonk();
extern "C" void GameInteractor_ExecuteOnOcarinaSongAction();
// MARK: - Save Files
extern "C" void GameInteractor_ExecuteOnSaveFile(int32_t fileNum);

View File

@ -6,14 +6,28 @@
extern "C" {
#include <z64.h>
#include "macros.h"
#include "functions.h"
#include "variables.h"
#include "functions.h"
extern SaveContext gSaveContext;
extern PlayState* gPlayState;
extern void Play_PerformSave(PlayState* play);
extern s32 Health_ChangeBy(PlayState* play, s16 healthChange);
extern void Rupees_ChangeBy(s16 rupeeChange);
extern void Inventory_ChangeEquipment(s16 equipment, u16 value);
}
bool performDelayedSave = false;
bool performSave = false;
// TODO: When there's more uses of something like this, create a new GI::RawAction?
void ReloadSceneTogglingLinkAge() {
gPlayState->nextEntranceIndex = gSaveContext.entranceIndex;
gPlayState->sceneLoadFlag = 0x14;
gPlayState->fadeTransition = 11;
gSaveContext.nextTransitionType = 11;
gPlayState->linkAgeOnLoad ^= 1; // toggle linkAgeOnLoad
}
void RegisterInfiniteMoney() {
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnGameFrameUpdate>([]() {
if (CVarGetInteger("gInfiniteMoney", 0) != 0) {
@ -155,11 +169,7 @@ void RegisterSwitchAge() {
playerPos = GET_PLAYER(gPlayState)->actor.world.pos;
playerYaw = GET_PLAYER(gPlayState)->actor.shape.rot.y;
gPlayState->nextEntranceIndex = gSaveContext.entranceIndex;
gPlayState->sceneLoadFlag = 0x14;
gPlayState->fadeTransition = 11;
gSaveContext.nextTransitionType = 11;
gPlayState->linkAgeOnLoad ^= 1;
ReloadSceneTogglingLinkAge();
warped = true;
}
@ -172,6 +182,58 @@ void RegisterSwitchAge() {
});
}
/// Switches Link's age and respawns him at the last entrance he entered.
void RegisterOcarinaTimeTravel() {
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnGameFrameUpdate>([]() {
if (!gPlayState) return;
// For the gTimeTravel: Don't give child Link a Kokiri Sword if we don't have one
if (LINK_AGE_IN_YEARS == 5 && CVarGetInteger("gTimeTravel", 0)) {
uint32_t kokiriSwordBitMask = 1 << 0;
if (!(gSaveContext.inventory.equipment & kokiriSwordBitMask)) {
Player* player = GET_PLAYER(gPlayState);
player->currentSwordItemId = ITEM_NONE;
gSaveContext.equips.buttonItems[0] = ITEM_NONE;
Inventory_ChangeEquipment(EQUIP_SWORD, PLAYER_SWORD_NONE);
}
}
// Switches Link's age and respawns him at the last entrance he entered.
if (CVarGetInteger("gTimeTravel", 0) && CVarGetInteger("gSwitchTimeline", 0)) {
CVarSetInteger("gSwitchTimeline", 0);
ReloadSceneTogglingLinkAge();
}
});
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnOcarinaSongAction>([]() {
if (!gPlayState) {
return;
}
Actor* player = &GET_PLAYER(gPlayState)->actor;
Actor* nearbyTimeBlockEmpty = Actor_FindNearby(gPlayState, player, ACTOR_OBJ_WARP2BLOCK, ACTORCAT_ITEMACTION, 300.0f);
Actor* nearbyTimeBlock = Actor_FindNearby(gPlayState, player, ACTOR_OBJ_TIMEBLOCK, ACTORCAT_ITEMACTION, 300.0f);
Actor* nearbyOcarinaSpot = Actor_FindNearby(gPlayState, player, ACTOR_EN_OKARINA_TAG, ACTORCAT_PROP, 120.0f);
Actor* nearbyDoorOfTime = Actor_FindNearby(gPlayState, player, ACTOR_DOOR_TOKI, ACTORCAT_BG, 500.0f);
Actor* nearbyFrogs = Actor_FindNearby(gPlayState, player, ACTOR_EN_FR, ACTORCAT_NPC, 300.0f);
uint8_t hasMasterSword = (gBitFlags[ITEM_SWORD_MASTER - ITEM_SWORD_KOKIRI] << gEquipShifts[EQUIP_SWORD]) & gSaveContext.inventory.equipment;
uint8_t hasOcarinaOfTime = (INV_CONTENT(ITEM_OCARINA_TIME) == ITEM_OCARINA_TIME);
// If TimeTravel + Player have the Ocarina of Time + Have Master Sword + is in proper range
// TODO: Once Swordless Adult is fixed: Remove the Master Sword check
if (CVarGetInteger("gTimeTravel", 0) && hasOcarinaOfTime && hasMasterSword &&
gPlayState->msgCtx.lastPlayedSong == OCARINA_SONG_TIME && !nearbyTimeBlockEmpty && !nearbyTimeBlock &&
!nearbyOcarinaSpot && !nearbyFrogs) {
if (gSaveContext.n64ddFlag) {
CVarSetInteger("gSwitchTimeline", 1);
} else if (!gSaveContext.n64ddFlag && !nearbyDoorOfTime) {
// This check is made for when Link is learning the Song Of Time in a vanilla save file that load a
// Temple of Time scene where the only object present is the Door of Time
CVarSetInteger("gSwitchTimeline", 1);
}
}
});
}
void AutoSave(GetItemEntry itemEntry) {
u8 item = itemEntry.itemId;
// Don't autosave immediately after buying items from shops to prevent getting them for free!
@ -386,6 +448,7 @@ void InitMods() {
RegisterUnrestrictedItems();
RegisterFreezeTime();
RegisterSwitchAge();
RegisterOcarinaTimeTravel();
RegisterAutoSave();
RegisterRupeeDash();
RegisterHyperBosses();

View File

@ -356,6 +356,15 @@ namespace GameMenuBar {
UIWidgets::Tooltip("Greatly decreases cast time of Farore's Wind magic spell.");
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::PaddedEnhancementCheckbox("Time Travel with the Song of Time", "gTimeTravel", true, false);
UIWidgets::Tooltip("Allows Link to freely change age by playing the Song of Time.\n"
"Time Blocks can still be used properly.\n\n"
"Requirements:\n"
"- Obtained the Ocarina of Time\n"
"- Obtained the Song of Time\n"
"- Obtained the Master Sword\n"
"- Not within range of Time Block\n"
"- Not within range of Ocarina playing spots");
ImGui::EndMenu();
}

View File

@ -2557,6 +2557,7 @@ void Message_DrawMain(PlayState* play, Gfx** p) {
osSyncPrintf(VT_RST);
osSyncPrintf("→ OCARINA_MODE=%d\n", play->msgCtx.ocarinaMode);
}
GameInteractor_ExecuteOnOcarinaSongAction();
}
break;
case MSGMODE_DISPLAY_SONG_PLAYED: