Check Tracker Update (#2668)

* Initial commit. What works so far:

Data file loads, saves, deletes, and is created properly.
 - Need to run `HasItemBeenCollected` when creating in case of mid-seed regen.
Organized checks into maps set by area.
Areas show in tracker.

 - Checks pulled from checkObjectsByArea are not currently listed.
 - Areas are being assumed completed and hidden at the start.

* Checks now populate. Still not counting area totals properly.

* Don't track RC_LINKS_POCKET in the data file, and instead manually add it in every time a save is load. rcObjects doesn't contain Link's Pocket location (duh).

* Moved ItemReceive hook to randomizer_check_tracker.cpp.

Skipped items are properly sorted and formatted.

Saved items contribute to an area's gotten/skipped checks on load.

Changed skipped items default main color to gray.

General code cleanup.

* Skipped checks now contribute to area totals for area completion logic.

* Mid-menu rework transfer.

* Started check lookup on item receive.
Tried to enable scum checking on save.

* Fixed scum check on save.

* More code cleanup, which revealed some issues with loading and saving. Loading now works 100%, but entries in checkTrackerData are disappearing in code somewhere, causing issues with later checks.

Scummed color now displays properly.

Collected but unsaved displays properly.

Unchecked, Skipped, Seen and Scummed checks all have arrows for skipping/unskipping.

Counts a little messed up, probably double adding skips.

* Fixed item counts being off across resets. Restarts were fine.

* Beginning of rework for individual check updates. Adds `Actor lastCheck` to PlayState to allow assigning of last actor to give an item. This allows precise tracking of GS, freestanding PoH, etc. Works for chests too. Currently does not work for shops, unknown for normal NPC gives. Doesn't track last gives for non-checks, like drops from grass, pots, crates, etc.

* Actor-based checks fully fleshed out, but can't help with sale-based checks. Implemented `pendingSaleCheck` to back out of an RC check cycle if `gSaveContext->pendingSale` is not NONE when `OnItemReceive` is called, processed through `OnSaleEnd`. This should be the final change necessary to streamline the checks.

* Temporary hook to message box close to tie in trackers for non-gs/chest actors that aren't added to `GetCheckFromActor`. This triggers a 2-frame countdown to check with `HasItemBeenCollected` since half of the methods in there aren't updated until after a text box closes.

Added scene tracking to get last scene so that checks in scenes that span several overworld areas could be tied to their areas properly.

Modified tracker data creation to apply Link's Pocket and Song from Impa conditionally based on randomizer settings instead of just being assumed to be done. May need modification for checking click and drag spoiler loading.

Removed the scene equivalence check from CheckChecks so that an entire area is checked each time to avoid issues with, e.g., leaving a shop before a pending sale finishes.

* Changed check data creation to use RSKs from gRandomizer to account for settings loaded from drag and drop spoilers.

* Beginning of tracker code organization.

Changed check color defaults.

* Reverted moving tracker colors to header.

Some individual check fixes.

* Missed something from previous merge?

* Removed last remnant of removed performance mode.

* Better handle checking when skullsanity is off.

* Song checks sceneIDs don't match the scenes they're actually in for some reason. Removed that check.

* SaleEnd checks don't need the GetItemEntry parameter. Also set a flag that should prevent the check tracker from running during vanilla saves should that not be resolved by the time it's merged.

* Attempting to have only the relevant checks added to the tracker data, as well as showing vanilla checks for non-rando saves. Not working yet (everything is displayed in vanilla).

* Added vanilla check tracker population via `vanillaCheck` bool in `RandomizerCheckObject`. This is also added to `IsVisibleInImGui` to handle file loading and saving to eliminate checks that aren't part of vanilla or the rando seed.

Implemented deleted tracker data file recreation.

Added some extra item name text checks. Songs are still... tricky, so they stay blank.

Utilize local copies of the randomizer check objects that are applicable for performance purposes.

Fixed vanilla item-giving cutscene crashes when triggered mid-transition.

* Apparently not all toolchains can handle macro instantiation without all parameters, like VS can.

* Fixed scummed detail color not showing properly on tracker.

* Fixed dungeon area totals and GS checks not functioning properly.

* Major revamp of checking code. Doesn't rely on `GetCheckFromActor` at all anymore, but instead simply sets a flag to evaluate a number of checks in an area every frame until the check that was gotten is identified. With the new setup, it's much less hardware intensive, and at 60fps 6 checks per frame shows no noticeable effect on framerate. I can envision needing to add a collectedCount in order to account for times when two checks are gotten one right after the other.

Changed area scroll to happen from `OnTransitionEnd`, and allow for scrolling in bazaar (was disabled previously because it would jump back and forth between Kak and Market while you were in one).

Also setup area check order updating by area instead of all at the same time.

* Fixed shooting gallery not reporting proper area.

Prevent checks from actually happening if area is RCAREA_INVALID, now that all areas are reporting something if they have checks.

Fixed `GetCheckArea()` not updating checks for standard scenes.

Lots of vanilla tracking updates, mainly manual checks for gems, medallions and songs, since the often don't have proper scenes in the data.

Prevent vanilla from triggering check loops if junk items are collected.

Fixed sorting based on saved vs collected.

Prevent item name display for vanilla runs.

Change coloring of checks so the check name reflects status in vanilla runs.

* Fixed "Recheck Area" button erroneously adding to an area's check totals?

* Fix DMC vanilla checks.

* Fix check tracker data recreation on data file loss.

Removed redundant file exists check from SaveTrackerDataHook, as it doesn't matter if it exists or not at that point.

Limited check loops to 3 to avoid infinite check loops on non-check/junk item pickup.

Added more checks to disable tracker operations if a save is not running.

Changed check ordering to put boss reward and heart container checks at the end of whatever RCSHOW group they belong to.

* Fixed IsRunning calculation.

Further improving vanilla checking.

Starting framework for checking medallions, stones, and songs on data file recreation.

Fixed medallion check collection.

Added GIFT_FROM_SAGES to check list for vanilla file for tracking light medallion.

Added check loop limitation to prevent infinite checking after picking up junk item from the ground.

* Finished vanilla file recreation and recheck (songs and dungeon rewards all check properly now). This includes deku shield with KF shop item 3.

Finished Gift from Raoru light medallion tracking for vanilla.

Commented all CheckByScene functionality for now.

* Fixed new save data file creation.

Disabled entrance area calculation for now, as it only tracks the previous entrance for some reason.

Fixed area detection for ToT checks.

* GetCheckArea() now utilizes EntranceData almost exclusively, via either `gSaveContext.entranceIndex` or the entrance tracker's `currentGrottoId` in the case of grottos.

This also means that EVERYTHING CAN AUTOSCROLL NOW. Entrance shuffles are now much easier to track.

Autoscroll is now also triggered on save, just in case someone isn't autosaving and has a lot of checks that get converted to saved.

* Fixed missing GS check in LW.

* Added area scroll on toggling Show Hidden Items. May configure differently later.

Fixed area detection for entrances in Gerudo Fortress/Valley and Collossus. Haunted wasteland doesn't autoscroll due to a bug in `OnTransitionEnd` hook, but checks still evaluate properly there.

Rely on scene-based area detection if scene is a main overworld or dungeon scene.

* Fixed grotto detection when shuffle is off.

* Small code cleanup.

Fixed Colossus hand chest checks.

* Missing lus bump from merge conflict resolution

* Fixed Colossus Grotto making `GetCheckArea` return Wasteland.

* Improved OnItemReceive processing for non-token GS checks, since they can sometimes take long enough for the checking process to time out before the item is registered as received.

* First attempt at thread safety for tracker data file writing. Seems to work, but might need more testing.

* Fixed Recheck Area not unskipping items that register as saved.

Improved delayed saving with autosaving, as the autosave triggered quite often before the tracker data save, making things not properly register as saved.

* merge cleanup

* Converted check tracker data to the sectional saves, adding `CheckTrackerData` to `SaveContext`.

Implemented section ID returning and fullSave boolean passing to section functions from my PR temporarily for it to work properly.

Moved `RandomizerCheckTrackerData` enum to `randomizerTypes.h` to accommodate that.

Changed `Randomizer_SaveInit` to a `SaveManager::InitFunc` to allow for other randomizer-dependent sections to be initialized after that. Required a little refactoring to maintain intro cutscene skip when starting a randomizer file.

* Revert section index return and randomizer init changes. Will need to wait on yet more changes to main.

* Fixed check tracker initialization (shouldn't be tied to window initialization), restored saving/loading functionality.

* Removed `CheckTracker::Init` and put SaveManager calls in `CheckTrackerWindow::InitElement`. Also a bit of cleanup from transition back to save file use.

* Fixed tracker displaying check categories while file not loaded.

* Fix Darunia's Joy check not marking in vanilla.

* Fix autosave not triggering change from collected to saved in tracker data.

* Changed default colors for scummed and collected display.

* Merge upgrade code cleanup.

* Add `OnTransitionEnd` calls to sandstorm transitions both to and from Wasteland. Also improved `gSaveContext.lastScene` assignment for both transition types. Allowed a bit of cleanup in `GetCheckArea`.

* Added `StateButton`, a button like `ArrowButton` but that allows text instead of arrow icons. Apparently the changes from ArrowButton to StateButton happened in a previous commit...

* Changed section name to `trackerDataCheck` to force SoH to load it after the randomizer section, as it required some randoSettings to be loaded first, and nothing else I tried to make the randomizer section load before it worked. This hacks a solution to checks not displaying on fast file load to a specific slot.

F*** you, SaveManager, and f*** you too, JSON.

* Forgot to change the section string for loading with the name change.

* Fix check ordering for checks that trigger the autosave.

* Adds option to remove right-side shop items (slots 1-4) from the tracker list. Enabled by default.

* Fix default state of Hide Shop Right Checks checkbox.

* Fixes grotto and great fairy scrolling and checks.

Fixes array overflow from `checkTrackerData` which was creating the issue trying to load the base and randomizer sections first, among other things.

That also fixed the massive file loads that were being exhibited in debug mode.

* Fix shooting galleries being set as collected again when being played a second time after getting the checks.

* Fix Bazaar autoscroll.

* Add Saria's Song to `GetCheckFromActor` and removed some limitations from the messageCloseCheck function to make that check track properly.

* Fix Song from Impa check.

Implemented prevention for multiple "collections" of great fairies, just in case getting the health refill would trigger it with the previous setup.

* Fix ice traps on GS tokens not triggering OnItemReceive.

* Complete fix for ice trap collection from GS.

Add autoscroll when clicking "Expand All".

* Add `OnShopSlotChange` with cursorSlot and basePrice parameters.

* Fixed include in en_ossan for shop slot hook.

Added registration for `OnShopSlotChange` in the tracker, storing the price in a new `price` field in `CheckTrackerData`.

Added "seen" functionality to shop checks. Displays the model item name upon first entering a shop, adding the price and switching to trickName (if it's an ice trap) upon navigating to the slot in buy mode, triggered by an invisible "identified" status that mirrors "seen" in every other way.

Added tooltips to most options for check tracker color picking to describe what each status actually means.

* `std::format` pls

* So apparently std::format just decided to break with the latest merge from develop, but fmt::format exists and works?

* Removed the last vestiges of `locationsSkipped`. Other general code and formatting cleanups.

Moved `IsGameRunning` to `OTRGlobals` so the item tracker could also access it.

Used preceding to "fix" item and bottle display in the item tracker on startup.

* Some more code cleanup.

Removed "Recheck Area" button and relevant code.

Backported changes to Anchor branch applicable for single-player, including making a checkAreas vector and structuring the frame by frame checks around that. Also includes fix for Silver Gauntlets and Mirror Shield check collection crash associated with those changes.

Fixed Kakariko Bazaar "seen" updates.

Fixed tracker window not showing on initial load like it should.

* Forgot 1 formatting fix.

* Removed conditions for showing Song from Impa (isn't junk under certain conditions, so should show all the time).

* Fix vanilla checks, add Zelda's Letter and Malon's Egg to manual check collection.

Fix autoscroll while in child stealth section.

* Fix crash in Happy Mask Shop in OnSlotChange (referenced non-existent shop id in a tracker-specific enum).

* General code cleanup.

* Missed one reversion.

* One more.

* Fix column alignment in `randomizer_check_objects`.

* Fix file encoding on `randomizer_check_tracker`. Again.

* Fix indentation for `actualItemtrackerItemMap`. Also removed unnecessary parts of the map.

* Rename `HasEqItem` to `HasEquipment`.

* Slightly better indentation for `actualItemTrackerItemMap`.

* Add magic bean salesman to vanilla check tracking, and genericized deku shield to trigger KF shop item 3 wherever you get the shield.

Renamed `vanillaCheck` to `vanillaHundoCheck` to (supposedly) clarify the meaning of the usage.

* One more rename to `vanillaCompletion` to avoid possible confusion with 100% speedrun conditions.

* give me a break XD

Co-authored-by: briaguya <70942617+briaguya-ai@users.noreply.github.com>

* Changes suggested by briaguya (rename `RandomizerCheckShow` to `Status`, unused code, newline formatting)

* Remove unused `itemNames` table.

* Remove `IsGameRunning` in favor of `GameInteractor::IsSaveLoaded`.

* Restore anti-spoiler functionality for dungeons with dungeon maps.

* Review cleanup.

* Fix prices not showing for Kak bazaar items.

---------

Co-authored-by: briaguya <70942617+briaguya-ai@users.noreply.github.com>
This commit is contained in:
Malkierian 2023-10-04 08:03:36 -07:00 committed by GitHub
parent b1f0c964d2
commit 70a83f647f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
22 changed files with 1177 additions and 588 deletions

View File

@ -92,7 +92,6 @@ typedef struct {
/* */ u32 count[COUNT_MAX];
/* */ u32 entrancesDiscovered[SAVEFILE_ENTRANCES_DISCOVERED_IDX_COUNT];
/* */ u32 scenesDiscovered[SAVEFILE_SCENES_DISCOVERED_IDX_COUNT];
/* */ u8 locationsSkipped[RC_MAX];
/* */ bool rtaTiming;
/* */ uint64_t fileCreatedAt;
} SohStats;
@ -292,6 +291,7 @@ typedef struct {
/* */ SohStats sohStats;
/* */ u8 temporaryWeapon;
/* */ FaroresWindData backupFW;
/* */ RandomizerCheckTrackerData checkTrackerData[RC_MAX];
// #endregion
// #region SOH [Randomizer]
// Upstream TODO: Move these to their own struct or name to more obviously specific to Randomizer

View File

@ -159,6 +159,7 @@ public:
DEFINE_HOOK(OnSceneSpawnActors, void());
DEFINE_HOOK(OnPlayerUpdate, void());
DEFINE_HOOK(OnOcarinaSongAction, void());
DEFINE_HOOK(OnShopSlotChange, void(uint8_t cursorIndex, int16_t price));
DEFINE_HOOK(OnActorInit, void(void* actor));
DEFINE_HOOK(OnActorUpdate, void(void* actor));
DEFINE_HOOK(OnActorKill, void(void* actor));

View File

@ -58,6 +58,10 @@ void GameInteractor_ExecuteOnOcarinaSongAction() {
GameInteractor::Instance->ExecuteHooks<GameInteractor::OnOcarinaSongAction>();
}
void GameInteractor_ExecuteOnShopSlotChangeHooks(uint8_t cursorIndex, int16_t price) {
GameInteractor::Instance->ExecuteHooks<GameInteractor::OnShopSlotChange>(cursorIndex, price);
}
void GameInteractor_ExecuteOnActorInit(void* actor) {
GameInteractor::Instance->ExecuteHooks<GameInteractor::OnActorInit>(actor);
}

View File

@ -24,6 +24,7 @@ void GameInteractor_ExecuteOnActorKill(void* actor);
void GameInteractor_ExecuteOnEnemyDefeat(void* actor);
void GameInteractor_ExecuteOnPlayerBonk();
void GameInteractor_ExecuteOnOcarinaSongAction();
void GameInteractor_ExecuteOnShopSlotChangeHooks(uint8_t cursorIndex, int16_t price);
void GameInteractor_ExecuteOnPlayDestroy();
void GameInteractor_ExecuteOnPlayDrawEnd();

View File

@ -334,9 +334,6 @@ void LoadStatsVersion1() {
SaveManager::Instance->LoadArray("entrancesDiscovered", ARRAY_COUNT(gSaveContext.sohStats.entrancesDiscovered), [](size_t i) {
SaveManager::Instance->LoadData("", gSaveContext.sohStats.entrancesDiscovered[i]);
});
SaveManager::Instance->LoadArray("locationsSkipped", ARRAY_COUNT(gSaveContext.sohStats.locationsSkipped), [](size_t i) {
SaveManager::Instance->LoadData("", gSaveContext.sohStats.locationsSkipped[i]);
});
}
void SaveStats(SaveContext* saveContext, int sectionID, bool fullSave) {
@ -378,9 +375,6 @@ void SaveStats(SaveContext* saveContext, int sectionID, bool fullSave) {
SaveManager::Instance->SaveArray("entrancesDiscovered", ARRAY_COUNT(saveContext->sohStats.entrancesDiscovered), [&](size_t i) {
SaveManager::Instance->SaveData("", saveContext->sohStats.entrancesDiscovered[i]);
});
SaveManager::Instance->SaveArray("locationsSkipped", ARRAY_COUNT(saveContext->sohStats.locationsSkipped), [&](size_t i) {
SaveManager::Instance->SaveData("", saveContext->sohStats.locationsSkipped[i]);
});
}
void GameplayStatsRow(const char* label, const std::string& value, ImVec4 color = COLOR_WHITE) {
@ -688,9 +682,6 @@ void InitStats(bool isDebug) {
for (int entrancesIdx = 0; entrancesIdx < ARRAY_COUNT(gSaveContext.sohStats.entrancesDiscovered); entrancesIdx++) {
gSaveContext.sohStats.entrancesDiscovered[entrancesIdx] = 0;
}
for (int rc = 0; rc < ARRAY_COUNT(gSaveContext.sohStats.locationsSkipped); rc++) {
gSaveContext.sohStats.locationsSkipped[rc] = 0;
}
strncpy(gSaveContext.sohStats.buildVersion, (const char*) gBuildVersion, sizeof(gSaveContext.sohStats.buildVersion) - 1);
gSaveContext.sohStats.buildVersion[sizeof(gSaveContext.sohStats.buildVersion) - 1] = 0;

View File

@ -6,10 +6,9 @@
#include <macros.h>
#include <objects/gameplay_keep/gameplay_keep.h>
#include <functions.h>
#include <libultraship/bridge.h>
#include <libultraship/libultraship.h>
#include <textures/icon_item_static/icon_item_static.h>
#include <textures/icon_item_24_static/icon_item_24_static.h>
#include <thread>
#include "3drando/rando_main.hpp"
#include "3drando/random.hpp"
#include "../../UIWidgets.hpp"
@ -21,14 +20,16 @@
#include <stdexcept>
#include "randomizer_check_objects.h"
#include "randomizer_tricks.h"
#include "randomizer_check_tracker.h"
#include <sstream>
#include <tuple>
#include <functional>
#include "draw.h"
#include "rando_hash.h"
#include "soh/Enhancements/game-interactor/GameInteractor.h"
#include <boost_custom/container_hash/hash_32.hpp>
#include <libultraship/libultraship.h>
#include "randomizer_settings_window.h"
#include "savefile.h"
extern "C" uint32_t ResourceMgr_IsGameMasterQuest();
extern "C" uint32_t ResourceMgr_IsSceneMasterQuest(s16 sceneNum);
@ -48,6 +49,8 @@ std::set<RandomizerCheck> excludedLocations;
std::set<RandomizerTrick> enabledTricks;
std::set<RandomizerTrick> enabledGlitches;
std::set<std::map<RandomizerCheck, RandomizerCheckTrackerData>> checkTrackerStates;
u8 generated;
char* seedString;
@ -133,18 +136,6 @@ Randomizer::Randomizer() {
item.GetName().french,
};
}
for (auto area : rcAreaNames) {
SpoilerfileAreaNameToEnum[area.second] = area.first;
}
SpoilerfileAreaNameToEnum["Inside Ganon's Castle"] = RCAREA_GANONS_CASTLE;
SpoilerfileAreaNameToEnum["the Lost Woods"] = RCAREA_LOST_WOODS;
SpoilerfileAreaNameToEnum["the Market"] = RCAREA_MARKET;
SpoilerfileAreaNameToEnum["the Graveyard"] = RCAREA_GRAVEYARD;
SpoilerfileAreaNameToEnum["Haunted Wasteland"] = RCAREA_WASTELAND;
SpoilerfileAreaNameToEnum["outside Ganon's Castle"] = RCAREA_HYRULE_CASTLE;
for (auto [type, name] : hintTypeNames) {
SpoilerfileHintTypeNameToEnum[name] = type;
}
}
Sprite* Randomizer::GetSeedTexture(uint8_t index) {
@ -2601,6 +2592,11 @@ RandomizerCheckObject Randomizer::GetCheckObjectFromActor(s16 actorId, s16 scene
if((actorParams & 0xF) < 10) specialRc = RC_MARKET_TREASURE_CHEST_GAME_ITEM_5;
}
break;
case SCENE_SACRED_FOREST_MEADOW:
if (actorId == ACTOR_EN_SA) {
specialRc = RC_SONG_FROM_SARIA;
}
break;
case SCENE_TEMPLE_OF_TIME_EXTERIOR_DAY:
case SCENE_TEMPLE_OF_TIME_EXTERIOR_NIGHT:
case SCENE_TEMPLE_OF_TIME_EXTERIOR_RUINS:
@ -4106,9 +4102,8 @@ void RandomizerSettingsWindow::DrawElement() {
ImGui::PopItemWidth();
switch (CVarGetInteger("gRandomizeShuffleKeyRings", RO_KEYRINGS_OFF)) {
case RO_KEYRINGS_COUNT:
maxKeyringCount =
(CVarGetInteger("gRandomizeGerudoFortress", RO_GF_NORMAL) == RO_GF_NORMAL &&
CVarGetInteger("gRandomizeGerudoKeys", RO_GERUDO_KEYS_VANILLA) != RO_GERUDO_KEYS_VANILLA) ? 9 : 8;
maxKeyringCount = (CVarGetInteger("gRandomizeGerudoFortress", RO_GF_NORMAL) == RO_GF_NORMAL &&
CVarGetInteger("gRandomizeGerudoKeys", RO_GERUDO_KEYS_VANILLA) != RO_GERUDO_KEYS_VANILLA) ? 9 : 8;
UIWidgets::PaddedEnhancementSliderInt("Key Ring Count: %d",
"##RandomizeShuffleKeyRingsRandomCount",
"gRandomizeShuffleKeyRingsRandomCount", 1,
@ -4117,7 +4112,7 @@ void RandomizerSettingsWindow::DrawElement() {
case RO_KEYRINGS_SELECTION:
disableGFKeyring =
CVarGetInteger("gRandomizeGerudoFortress", RO_GF_NORMAL) != RO_GF_NORMAL || CVarGetInteger("gRandomizeGerudoKeys", RO_GERUDO_KEYS_VANILLA) == RO_GERUDO_KEYS_VANILLA;
UIWidgets::EnhancementCheckbox( "Gerudo Fortress##RandomizeShuffleKeyRings", "gRandomizeShuffleKeyRingsGerudoFortress",
UIWidgets::EnhancementCheckbox("Gerudo Fortress##RandomizeShuffleKeyRings", "gRandomizeShuffleKeyRingsGerudoFortress",
disableGFKeyring, "Disabled because the currently selected Gerudo Fortress Carpenters\n setting and/or Gerudo Fortress Keys setting is incompatible with \nhaving a Gerudo Fortress keyring.");
UIWidgets::EnhancementCheckbox("Forest Temple##RandomizeShuffleKeyRings", "gRandomizeShuffleKeyRingsForestTemple");
UIWidgets::EnhancementCheckbox("Fire Temple##RandomizeShuffleKeyRings", "gRandomizeShuffleKeyRingsFireTemple");
@ -4397,15 +4392,13 @@ void RandomizerSettingsWindow::DrawElement() {
UIWidgets::PaddedSeparator();
// Complete mask quest
UIWidgets::EnhancementCheckbox(Settings::CompleteMaskQuest.GetName().c_str(),
"gRandomizeCompleteMaskQuest");
UIWidgets::EnhancementCheckbox(Settings::CompleteMaskQuest.GetName().c_str(), "gRandomizeCompleteMaskQuest");
UIWidgets::InsertHelpHoverText("Once the happy mask shop is opened, all masks will be available to be borrowed.");
UIWidgets::PaddedSeparator();
// Skip Scarecrow Song
UIWidgets::EnhancementCheckbox(Settings::FreeScarecrow.GetName().c_str(),
"gRandomizeSkipScarecrowsSong");
UIWidgets::EnhancementCheckbox(Settings::FreeScarecrow.GetName().c_str(), "gRandomizeSkipScarecrowsSong");
UIWidgets::InsertHelpHoverText(
"Start with the ability to summon Pierre the scarecrow. Pulling out an ocarina in the usual locations will automatically summon him."
);
@ -5220,12 +5213,13 @@ void RandomizerSettingsWindow::DrawElement() {
UIWidgets::EnhancementCheckbox(Settings::StartingSongOfTime.GetName().c_str(), "gRandomizeStartingSongOfTime");
UIWidgets::EnhancementCheckbox(Settings::StartingSongOfStorms.GetName().c_str(), "gRandomizeStartingSongOfStorms");
UIWidgets::PaddedSeparator();
ImGui::Text("Warp Songs");
UIWidgets::PaddedSeparator();
UIWidgets::EnhancementCheckbox(Settings::StartingMinuetOfForest.GetName().c_str(), "gRandomizeStartingMinuetOfForest");
UIWidgets::EnhancementCheckbox(Settings::StartingMinuetOfForest.GetName().c_str(), "gRandomizeStartingMinuetOfForest");
UIWidgets::EnhancementCheckbox(Settings::StartingBoleroOfFire.GetName().c_str(), "gRandomizeStartingBoleroOfFire");
UIWidgets::EnhancementCheckbox(Settings::StartingSerenadeOfWater.GetName().c_str(), "gRandomizeStartingSerenadeOfWater");
UIWidgets::EnhancementCheckbox(Settings::StartingRequiemOfSpirit.GetName().c_str(), "gRandomizeStartingRequiemOfSpirit");
UIWidgets::EnhancementCheckbox(Settings::StartingRequiemOfSpirit.GetName().c_str(), "gRandomizeStartingRequiemOfSpirit");
UIWidgets::EnhancementCheckbox(Settings::StartingNocturneOfShadow.GetName().c_str(), "gRandomizeStartingNocturneOfShadow");
UIWidgets::EnhancementCheckbox(Settings::StartingPreludeOfLight.GetName().c_str(), "gRandomizeStartingPreludeOfLight");
UIWidgets::PaddedSeparator();

View File

@ -38,7 +38,6 @@ class Randomizer {
void ParseItemLocationsFile(const char* spoilerFileName, bool silent);
void ParseEntranceDataFile(const char* spoilerFileName, bool silent);
bool IsItemVanilla(RandomizerGet randoGet);
GetItemEntry GetItemEntryFromRGData(RandomizerGetData rgData, GetItemID ogItemId, bool checkObtainability = true);
int16_t GetVanillaMerchantPrice(RandomizerCheck check);
public:
@ -78,6 +77,7 @@ class Randomizer {
RandomizerInf GetRandomizerInfFromCheck(RandomizerCheck rc);
RandomizerGetData GetRandomizerGetDataFromActor(s16 actorId, s16 sceneNum, s16 actorParams);
RandomizerGetData GetRandomizerGetDataFromKnownCheck(RandomizerCheck randomizerCheck);
GetItemEntry GetItemEntryFromRGData(RandomizerGetData rgData, GetItemID ogItemId, bool checkObtainability = true);
std::string GetChildAltarText() const;
std::string GetAdultAltarText() const;
std::string GetGanonText() const;

View File

@ -20,6 +20,16 @@ typedef struct {
uint8_t id;
} Sprite;
// Check tracker check visibility categories
typedef enum {
RCSHOW_UNCHECKED,
RCSHOW_SEEN,
RCSHOW_IDENTIFIED,
RCSHOW_SCUMMED,
RCSHOW_COLLECTED,
RCSHOW_SAVED,
} RandomizerCheckStatus;
typedef enum {
HINT_TYPE_TRIAL,
HINT_TYPE_ALWAYS,
@ -1461,6 +1471,13 @@ typedef enum {
RSK_MAX
} RandomizerSettingKey;
typedef struct {
RandomizerCheckStatus status;
uint16_t skipped;
int16_t price;
uint16_t hintItem;
} RandomizerCheckTrackerData;
//Generic Settings (any binary option can use this)
// off/on
typedef enum {

View File

@ -891,6 +891,9 @@ std::map<SceneID, RandomizerCheckArea> RandomizerCheckObjects::GetAllRCAreaBySce
for (int id = (int)SCENE_MARKET_ENTRANCE_DAY; id <= (int)SCENE_MARKET_RUINS; id++) {
rcAreaBySceneID[(SceneID)id] = RCAREA_MARKET;
}
rcAreaBySceneID[SCENE_TEMPLE_OF_TIME] = RCAREA_MARKET;
rcAreaBySceneID[SCENE_CASTLE_COURTYARD_GUARDS_DAY] = RCAREA_HYRULE_CASTLE;
rcAreaBySceneID[SCENE_CASTLE_COURTYARD_GUARDS_NIGHT] = RCAREA_HYRULE_CASTLE;
}
return rcAreaBySceneID;
}

File diff suppressed because it is too large Load Diff

View File

@ -1,4 +1,7 @@
#pragma once
#include <nlohmann/json.hpp>
#include "randomizerTypes.h"
#include "randomizer_check_objects.h"
#include <libultraship/libultraship.h>
@ -26,17 +29,6 @@ class CheckTrackerWindow : public LUS::GuiWindow {
void UpdateElement() override {};
};
// Check tracker check visibility categories
typedef enum {
RCSHOW_UNCHECKED,
RCSHOW_SKIPPED,
RCSHOW_SEEN,
RCSHOW_HINTED,
RCSHOW_CHECKED,
RCSHOW_SCUMMED,
RCSHOW_SAVED,
} RandomizerCheckShow;
//Converts an index into a Little Endian bitmask, as follows:
//00: 0000000100000000
//01: 0000001000000000
@ -51,6 +43,18 @@ typedef enum {
//repeat...
#define INDEX_TO_16BIT_LITTLE_ENDIAN_BITMASK(idx) (0x8000 >> (7 - (idx % 8) + ((idx % 16) / 8) * 8))
void DefaultCheckData(RandomizerCheck rc);
void Teardown();
void UpdateAllOrdering();
bool IsVisibleInCheckTracker(RandomizerCheckObject rcObj);
void InitTrackerData(bool isDebug);
void SetLastItemGetRC(RandomizerCheck rc);
RandomizerCheckArea GetCheckArea();
void CheckTrackerDialogClosed();
void ToggleShopRightChecks();
void UpdateCheck(uint32_t, RandomizerCheckTrackerData);
} // namespace CheckTracker
void to_json(nlohmann::json & j, const RandomizerCheckTrackerData& rctd);
void from_json(const nlohmann::json& j, RandomizerCheckTrackerData& rctd);

View File

@ -319,7 +319,7 @@ const EntranceData entranceData[] = {
{ 0x03AC, 0x0130, SINGLE_SCENE_INFO(0x5E), "Haunted Wasteland", "GF", ENTRANCE_GROUP_HAUNTED_WASTELAND, ENTRANCE_GROUP_GERUDO_VALLEY, ENTRANCE_TYPE_OVERWORLD, "hw,gerudo fortress"},
{ 0x0123, 0x0365, SINGLE_SCENE_INFO(0x5E), "Haunted Wasteland", "Desert Colossus", ENTRANCE_GROUP_HAUNTED_WASTELAND, ENTRANCE_GROUP_HAUNTED_WASTELAND, ENTRANCE_TYPE_OVERWORLD, "dc,hw"},
{ 0x0365, 0x0123, SINGLE_SCENE_INFO(0x5C), "Desert Colossus", "Haunted Wasteland", ENTRANCE_GROUP_HAUNTED_WASTELAND, ENTRANCE_GROUP_HAUNTED_WASTELAND, ENTRANCE_TYPE_OVERWORLD, "dc,hw"},
{ 0x0588, 0x057C, SINGLE_SCENE_INFO(0x5C), "Colossus", "Colossus Great Fairy Fountain", ENTRANCE_GROUP_HAUNTED_WASTELAND, ENTRANCE_GROUP_HAUNTED_WASTELAND, ENTRANCE_TYPE_INTERIOR, "dc", 1},
{ 0x0588, 0x057C, SINGLE_SCENE_INFO(0x5C), "Desert Colossus", "Colossus Great Fairy Fountain", ENTRANCE_GROUP_HAUNTED_WASTELAND, ENTRANCE_GROUP_HAUNTED_WASTELAND, ENTRANCE_TYPE_INTERIOR, "dc", 1},
{ 0x0700, 0x0800, SINGLE_SCENE_INFO(0x5C), "Desert Colossus", "Colossus Grotto", ENTRANCE_GROUP_HAUNTED_WASTELAND, ENTRANCE_GROUP_HAUNTED_WASTELAND, ENTRANCE_TYPE_GROTTO, "dc,scrubs", 1},
{ 0x0082, 0x01E1, SINGLE_SCENE_INFO(0x5C), "Desert Colossus", "Spirit Temple", ENTRANCE_GROUP_HAUNTED_WASTELAND, ENTRANCE_GROUP_HAUNTED_WASTELAND, ENTRANCE_TYPE_DUNGEON, "dc", 1},
{ 0x057C, 0x0588, {{ 0x3D, 0x02 }}, "Colossus Great Fairy Fountain", "Colossus", ENTRANCE_GROUP_HAUNTED_WASTELAND, ENTRANCE_GROUP_HAUNTED_WASTELAND, ENTRANCE_TYPE_INTERIOR, "dc"},
@ -537,6 +537,14 @@ void SortEntranceListByArea(EntranceOverride* entranceList, u8 byDest) {
}
}
s16 GetLastEntranceOverride() {
return lastEntranceIndex;
}
s16 GetCurrentGrottoId() {
return currentGrottoId;
}
void SetCurrentGrottoIDForTracker(s16 entranceIndex) {
currentGrottoId = entranceIndex;
}

View File

@ -3,6 +3,7 @@
#include <string>
#include <vector>
#include <cstdint>
#include <libultraship/libultraship.h>
typedef enum {
@ -80,6 +81,9 @@ void SetCurrentGrottoIDForTracker(int16_t entranceIndex);
void SetLastEntranceOverrideForTracker(int16_t entranceIndex);
void ClearEntranceTrackingData();
void InitEntranceTrackingData();
s16 GetLastEntranceOverride();
s16 GetCurrentGrottoId();
const EntranceData* GetEntranceData(s16);
class EntranceTrackerWindow : public LUS::GuiWindow {
public:

View File

@ -611,7 +611,7 @@ void DrawItem(ItemTrackerItem item) {
break;
}
if (hasItem && item.id != actualItemId && actualItemTrackerItemMap.find(actualItemId) != actualItemTrackerItemMap.end()) {
if (GameInteractor::IsSaveLoaded() && (hasItem && item.id != actualItemId && actualItemTrackerItemMap.find(actualItemId) != actualItemTrackerItemMap.end())) {
item = actualItemTrackerItemMap[actualItemId];
}
@ -634,7 +634,7 @@ void DrawBottle(ItemTrackerItem item) {
uint32_t actualItemId = gSaveContext.inventory.items[SLOT(item.id) + item.data];
bool hasItem = actualItemId != ITEM_NONE;
if (hasItem && item.id != actualItemId && actualItemTrackerItemMap.find(actualItemId) != actualItemTrackerItemMap.end()) {
if (GameInteractor::IsSaveLoaded() && (hasItem && item.id != actualItemId && actualItemTrackerItemMap.find(actualItemId) != actualItemTrackerItemMap.end())) {
item = actualItemTrackerItemMap[actualItemId];
}

View File

@ -2198,3 +2198,7 @@ extern "C" void EntranceTracker_SetLastEntranceOverride(s16 entranceIndex) {
extern "C" void Gfx_RegisterBlendedTexture(const char* name, u8* mask, u8* replacement) {
gfx_register_blended_texture(name, mask, replacement);
}
extern "C" void CheckTracker_OnMessageClose() {
CheckTracker::CheckTrackerDialogClosed();
}

View File

@ -154,6 +154,7 @@ void EntranceTracker_SetCurrentGrottoID(s16 entranceIndex);
void EntranceTracker_SetLastEntranceOverride(s16 entranceIndex);
void Gfx_RegisterBlendedTexture(const char* name, u8* mask, u8* replacement);
void SaveManager_ThreadPoolWait();
void CheckTracker_OnMessageClose();
int32_t GetGIID(uint32_t itemID);
#endif

View File

@ -1590,9 +1590,6 @@ void SaveManager::LoadBaseVersion3() {
SaveManager::Instance->LoadArray("entrancesDiscovered", ARRAY_COUNT(gSaveContext.sohStats.entrancesDiscovered), [](size_t i) {
SaveManager::Instance->LoadData("", gSaveContext.sohStats.entrancesDiscovered[i]);
});
SaveManager::Instance->LoadArray("locationsSkipped", ARRAY_COUNT(gSaveContext.sohStats.locationsSkipped), [](size_t i) {
SaveManager::Instance->LoadData("", gSaveContext.sohStats.locationsSkipped[i]);
});
});
SaveManager::Instance->LoadArray("sceneFlags", ARRAY_COUNT(gSaveContext.sceneFlags), [](size_t i) {
SaveManager::Instance->LoadStruct("", [&i]() {

View File

@ -690,4 +690,48 @@ namespace UIWidgets {
}
ImGui::PopID();
}
bool StateButtonEx(const char* str_id, const char* label, ImVec2 size, ImGuiButtonFlags flags) {
ImGuiContext& g = *GImGui;
ImGuiWindow* window = ImGui::GetCurrentWindow();
if (window->SkipItems)
return false;
const ImGuiStyle& style = g.Style;
const ImVec2 label_size = ImGui::CalcTextSize(label, NULL, true);
const ImGuiID id = window->GetID(str_id);
const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + size);
const float default_size = ImGui::GetFrameHeight();
ImGui::ItemSize(size, (size.y >= default_size) ? g.Style.FramePadding.y : -1.0f);
if (!ImGui::ItemAdd(bb, id))
return false;
if (g.LastItemData.InFlags & ImGuiItemFlags_ButtonRepeat)
flags |= ImGuiButtonFlags_Repeat;
bool hovered, held;
bool pressed = ImGui::ButtonBehavior(bb, id, &hovered, &held, flags);
// Render
const ImU32 bg_col = ImGui::GetColorU32((held && hovered) ? ImGuiCol_ButtonActive
: hovered ? ImGuiCol_ButtonHovered
: ImGuiCol_Button);
//const ImU32 text_col = ImGui::GetColorU32(ImGuiCol_Text);
ImGui::RenderNavHighlight(bb, id);
ImGui::RenderFrame(bb.Min, bb.Max, bg_col, true, g.Style.FrameRounding);
ImGui::RenderTextClipped(bb.Min + style.FramePadding, bb.Max - style.FramePadding, label, NULL, &label_size, {0.55f, 0.45f}, &bb);
/*ImGui::RenderArrow(window->DrawList,
bb.Min +
ImVec2(ImMax(0.0f, (size.x - g.FontSize) * 0.5f), ImMax(0.0f, (size.y - g.FontSize) * 0.5f)),
text_col, dir);*/
IMGUI_TEST_ENGINE_ITEM_INFO(id, str_id, g.LastItemData.StatusFlags);
return pressed;
}
bool StateButton(const char* str_id, const char* label) {
float sz = ImGui::GetFrameHeight();
return StateButtonEx(str_id, label, ImVec2(sz, sz), ImGuiButtonFlags_None);
}
}

View File

@ -95,6 +95,7 @@ namespace UIWidgets {
void DrawFlagArray32(const std::string& name, uint32_t& flags);
void DrawFlagArray16(const std::string& name, uint16_t& flags);
void DrawFlagArray8(const std::string& name, uint8_t& flags);
bool StateButton(const char* str_id, const char* label);
}
#endif /* UIWidgets_hpp */

View File

@ -3384,6 +3384,7 @@ void Message_Update(PlayState* play) {
}
sLastPlayedSong = 0xFF;
osSyncPrintf("OCARINA_MODE=%d chk_ocarina_no=%d\n", play->msgCtx.ocarinaMode, msgCtx->unk_E3F2);
CheckTracker_OnMessageClose();
break;
case MSGMODE_PAUSED:
break;

View File

@ -967,6 +967,7 @@ void Play_Update(PlayState* play) {
R_UPDATE_RATE = 3;
}
// Transition end for standard transitions
GameInteractor_ExecuteOnTransitionEndHooks(play->sceneNum);
}
play->sceneLoadFlag = 0;
@ -1075,6 +1076,9 @@ void Play_Update(PlayState* play) {
R_UPDATE_RATE = 3;
play->sceneLoadFlag = 0;
play->transitionMode = 0;
// Transition end for sandstorm effect (delayed until effect is finished)
GameInteractor_ExecuteOnTransitionEndHooks(play->sceneNum);
}
} else {
if (play->envCtx.sandstormEnvA == 255) {
@ -1109,6 +1113,9 @@ void Play_Update(PlayState* play) {
R_UPDATE_RATE = 3;
play->sceneLoadFlag = 0;
play->transitionMode = 0;
// Transition end for sandstorm effect (delayed until effect is finished)
GameInteractor_ExecuteOnTransitionEndHooks(play->sceneNum);
}
}
break;

View File

@ -15,6 +15,7 @@
#include "objects/object_masterkokirihead/object_masterkokirihead.h"
#include "soh/Enhancements/randomizer/randomizer_entrance.h"
#include "soh/Enhancements/cosmetics/cosmeticsTypes.h"
#include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h"
#include <assert.h>
#define FLAGS (ACTOR_FLAG_TARGETABLE | ACTOR_FLAG_FRIENDLY | ACTOR_FLAG_UPDATE_WHILE_CULLED)
@ -1005,6 +1006,7 @@ void EnOssan_State_FacingShopkeeper(EnOssan* this, PlayState* play, Player* play
Interface_SetDoAction(play, DO_ACTION_DECIDE);
this->stickLeftPrompt.isEnabled = false;
func_80078884(NA_SE_SY_CURSOR);
GameInteractor_ExecuteOnShopSlotChangeHooks(this->cursorIndex, this->shelfSlots[this->cursorIndex]->basePrice);
}
} else if ((this->stickAccumX > 0) || (dpad && CHECK_BTN_ALL(input->press.button, dRight))) {
nextIndex = EnOssan_SetCursorIndexFromNeutral(this, 0);
@ -1014,6 +1016,7 @@ void EnOssan_State_FacingShopkeeper(EnOssan* this, PlayState* play, Player* play
Interface_SetDoAction(play, DO_ACTION_DECIDE);
this->stickRightPrompt.isEnabled = false;
func_80078884(NA_SE_SY_CURSOR);
GameInteractor_ExecuteOnShopSlotChangeHooks(this->cursorIndex, this->shelfSlots[this->cursorIndex]->basePrice);
}
}
}
@ -1277,6 +1280,7 @@ void EnOssan_State_BrowseLeftShelf(EnOssan* this, PlayState* play, Player* playe
}
EnOssan_CursorUpDown(this, play);
if (this->cursorIndex != prevIndex) {
GameInteractor_ExecuteOnShopSlotChangeHooks(this->cursorIndex, this->shelfSlots[this->cursorIndex]->basePrice);
Message_ContinueTextbox(play, this->shelfSlots[this->cursorIndex]->actor.textId);
func_80078884(NA_SE_SY_CURSOR);
}
@ -1346,6 +1350,7 @@ void EnOssan_State_BrowseRightShelf(EnOssan* this, PlayState* play, Player* play
}
EnOssan_CursorUpDown(this, play);
if (this->cursorIndex != prevIndex) {
GameInteractor_ExecuteOnShopSlotChangeHooks(this->cursorIndex, this->shelfSlots[this->cursorIndex]->basePrice);
Message_ContinueTextbox(play, this->shelfSlots[this->cursorIndex]->actor.textId);
func_80078884(NA_SE_SY_CURSOR);
}