mirror of
https://github.com/HarbourMasters/Shipwright.git
synced 2025-01-30 23:10:14 -05:00
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:
parent
fb8dacbc69
commit
77cc91d0de
@ -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));
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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();
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
|
@ -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:
|
||||
|
Loading…
Reference in New Issue
Block a user