mirror of
https://github.com/HarbourMasters/Shipwright.git
synced 2025-02-11 21:00:12 -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,6 +149,8 @@ public:
|
|||||||
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(OnPlayerUpdate, void());
|
DEFINE_HOOK(OnPlayerUpdate, void());
|
||||||
|
DEFINE_HOOK(OnOcarinaSongAction, void());
|
||||||
|
|
||||||
DEFINE_HOOK(OnActorUpdate, void(void* actor));
|
DEFINE_HOOK(OnActorUpdate, void(void* actor));
|
||||||
DEFINE_HOOK(OnPlayerBonk, void());
|
DEFINE_HOOK(OnPlayerBonk, void());
|
||||||
|
|
||||||
|
@ -34,6 +34,10 @@ void GameInteractor_ExecuteOnPlayerUpdate() {
|
|||||||
GameInteractor::Instance->ExecuteHooks<GameInteractor::OnPlayerUpdate>();
|
GameInteractor::Instance->ExecuteHooks<GameInteractor::OnPlayerUpdate>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GameInteractor_ExecuteOnOcarinaSongAction() {
|
||||||
|
GameInteractor::Instance->ExecuteHooks<GameInteractor::OnOcarinaSongAction>();
|
||||||
|
}
|
||||||
|
|
||||||
void GameInteractor_ExecuteOnActorUpdate(void* actor) {
|
void GameInteractor_ExecuteOnActorUpdate(void* actor) {
|
||||||
GameInteractor::Instance->ExecuteHooks<GameInteractor::OnActorUpdate>(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_ExecuteOnTransitionEndHooks(int16_t sceneNum);
|
||||||
extern "C" void GameInteractor_ExecuteOnSceneInit(int16_t sceneNum);
|
extern "C" void GameInteractor_ExecuteOnSceneInit(int16_t sceneNum);
|
||||||
extern "C" void GameInteractor_ExecuteOnPlayerUpdate();
|
extern "C" void GameInteractor_ExecuteOnPlayerUpdate();
|
||||||
|
extern "C" void GameInteractor_ExecuteOnOcarinaSongAction();
|
||||||
extern "C" void GameInteractor_ExecuteOnActorUpdate(void* actor);
|
extern "C" void GameInteractor_ExecuteOnActorUpdate(void* actor);
|
||||||
extern "C" void GameInteractor_ExecuteOnPlayerBonk();
|
extern "C" void GameInteractor_ExecuteOnPlayerBonk();
|
||||||
|
extern "C" void GameInteractor_ExecuteOnOcarinaSongAction();
|
||||||
|
|
||||||
// MARK: - Save Files
|
// MARK: - Save Files
|
||||||
extern "C" void GameInteractor_ExecuteOnSaveFile(int32_t fileNum);
|
extern "C" void GameInteractor_ExecuteOnSaveFile(int32_t fileNum);
|
||||||
|
@ -6,14 +6,28 @@
|
|||||||
extern "C" {
|
extern "C" {
|
||||||
#include <z64.h>
|
#include <z64.h>
|
||||||
#include "macros.h"
|
#include "macros.h"
|
||||||
|
#include "functions.h"
|
||||||
#include "variables.h"
|
#include "variables.h"
|
||||||
#include "functions.h"
|
#include "functions.h"
|
||||||
extern SaveContext gSaveContext;
|
extern SaveContext gSaveContext;
|
||||||
extern PlayState* gPlayState;
|
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 performDelayedSave = false;
|
||||||
bool performSave = 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() {
|
void RegisterInfiniteMoney() {
|
||||||
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnGameFrameUpdate>([]() {
|
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnGameFrameUpdate>([]() {
|
||||||
if (CVarGetInteger("gInfiniteMoney", 0) != 0) {
|
if (CVarGetInteger("gInfiniteMoney", 0) != 0) {
|
||||||
@ -155,11 +169,7 @@ void RegisterSwitchAge() {
|
|||||||
playerPos = GET_PLAYER(gPlayState)->actor.world.pos;
|
playerPos = GET_PLAYER(gPlayState)->actor.world.pos;
|
||||||
playerYaw = GET_PLAYER(gPlayState)->actor.shape.rot.y;
|
playerYaw = GET_PLAYER(gPlayState)->actor.shape.rot.y;
|
||||||
|
|
||||||
gPlayState->nextEntranceIndex = gSaveContext.entranceIndex;
|
ReloadSceneTogglingLinkAge();
|
||||||
gPlayState->sceneLoadFlag = 0x14;
|
|
||||||
gPlayState->fadeTransition = 11;
|
|
||||||
gSaveContext.nextTransitionType = 11;
|
|
||||||
gPlayState->linkAgeOnLoad ^= 1;
|
|
||||||
|
|
||||||
warped = true;
|
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) {
|
void AutoSave(GetItemEntry itemEntry) {
|
||||||
u8 item = itemEntry.itemId;
|
u8 item = itemEntry.itemId;
|
||||||
// Don't autosave immediately after buying items from shops to prevent getting them for free!
|
// Don't autosave immediately after buying items from shops to prevent getting them for free!
|
||||||
@ -386,6 +448,7 @@ void InitMods() {
|
|||||||
RegisterUnrestrictedItems();
|
RegisterUnrestrictedItems();
|
||||||
RegisterFreezeTime();
|
RegisterFreezeTime();
|
||||||
RegisterSwitchAge();
|
RegisterSwitchAge();
|
||||||
|
RegisterOcarinaTimeTravel();
|
||||||
RegisterAutoSave();
|
RegisterAutoSave();
|
||||||
RegisterRupeeDash();
|
RegisterRupeeDash();
|
||||||
RegisterHyperBosses();
|
RegisterHyperBosses();
|
||||||
|
@ -356,6 +356,15 @@ namespace GameMenuBar {
|
|||||||
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("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::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();
|
ImGui::EndMenu();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2557,6 +2557,7 @@ void Message_DrawMain(PlayState* play, Gfx** p) {
|
|||||||
osSyncPrintf(VT_RST);
|
osSyncPrintf(VT_RST);
|
||||||
osSyncPrintf("→ OCARINA_MODE=%d\n", play->msgCtx.ocarinaMode);
|
osSyncPrintf("→ OCARINA_MODE=%d\n", play->msgCtx.ocarinaMode);
|
||||||
}
|
}
|
||||||
|
GameInteractor_ExecuteOnOcarinaSongAction();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case MSGMODE_DISPLAY_SONG_PLAYED:
|
case MSGMODE_DISPLAY_SONG_PLAYED:
|
||||||
|
Loading…
Reference in New Issue
Block a user