Merge branch 'develop' into ItemName

This commit is contained in:
Pepper0ni 2025-04-02 12:40:13 +01:00
commit f7c358dff8
25 changed files with 453 additions and 68 deletions

View file

@ -10,6 +10,8 @@ set(CVAR_PREFIX_TRACKER "gTrackers")
set(CVAR_PREFIX_DEVELOPER_TOOLS "gDeveloperTools")
set(CVAR_PREFIX_GENERAL "gGeneral")
set(CVAR_PREFIX_REMOTE "gRemote")
set(CVAR_PREFIX_GAMEPLAY_STATS "gGameplayStats")
set(CVAR_PREFIX_TIME_DISPLAY "gTimeDisplay")
add_compile_definitions(
CVAR_PREFIX_RANDOMIZER_ENHANCEMENT="${CVAR_PREFIX_RANDOMIZER_ENHANCEMENT}"
CVAR_PREFIX_RANDOMIZER_SETTING="${CVAR_PREFIX_RANDOMIZER_SETTING}"
@ -23,4 +25,6 @@ add_compile_definitions(
CVAR_PREFIX_DEVELOPER_TOOLS="${CVAR_PREFIX_DEVELOPER_TOOLS}"
CVAR_PREFIX_GENERAL="${CVAR_PREFIX_GENERAL}"
CVAR_PREFIX_REMOTE="${CVAR_PREFIX_REMOTE}"
CVAR_PREFIX_GAMEPLAY_STATS="${CVAR_PREFIX_GAMEPLAY_STATS}"
CVAR_PREFIX_TIME_DISPLAY="${CVAR_PREFIX_TIME_DISPLAY}"
)

View file

@ -37,10 +37,10 @@ const static std::vector<std::pair<std::string, const char*>> digitList = {
};
const std::vector<TimeObject> timeDisplayList = {
{ DISPLAY_IN_GAME_TIMER, "Display Gameplay Timer", CVAR_ENHANCEMENT("TimeDisplay.Timers.InGameTimer") },
{ DISPLAY_TIME_OF_DAY, "Display Time of Day", CVAR_ENHANCEMENT("TimeDisplay.Timers.TimeofDay") },
{ DISPLAY_CONDITIONAL_TIMER, "Display Conditional Timer", CVAR_ENHANCEMENT("TimeDisplay.Timers.HotWater") },
{ DISPLAY_NAVI_TIMER, "Display Navi Timer", CVAR_ENHANCEMENT("TimeDisplay.Timers.NaviTimer") }
{ DISPLAY_IN_GAME_TIMER, "Display Gameplay Timer", CVAR_TIME_DISPLAY("Timers.InGameTimer") },
{ DISPLAY_TIME_OF_DAY, "Display Time of Day", CVAR_TIME_DISPLAY("Timers.TimeofDay") },
{ DISPLAY_CONDITIONAL_TIMER, "Display Conditional Timer", CVAR_TIME_DISPLAY("Timers.HotWater") },
{ DISPLAY_NAVI_TIMER, "Display Navi Timer", CVAR_TIME_DISPLAY("Timers.NaviTimer") }
};
static std::vector<TimeObject> activeTimers;
@ -227,11 +227,11 @@ void TimeDisplayWindow::Draw() {
}
void TimeDisplayInitSettings() {
fontScale = CVarGetFloat(CVAR_ENHANCEMENT("TimeDisplay.FontScale"), 1.0f);
fontScale = CVarGetFloat(CVAR_TIME_DISPLAY("FontScale"), 1.0f);
if (fontScale < 1.0f) {
fontScale = 1.0f;
}
if (CVarGetInteger(CVAR_ENHANCEMENT("TimeDisplay.ShowWindowBG"), 0)) {
if (CVarGetInteger(CVAR_TIME_DISPLAY("ShowWindowBG"), 0)) {
windowBG = ImVec4(0, 0, 0, 0);
} else {
windowBG = ImVec4(0, 0, 0, 0.5f);

View file

@ -1164,6 +1164,9 @@ const std::vector<FlagTable> flagTables = {
{ RAND_INF_COLOSSUS_GREAT_FAIRY_REWARD, "RAND_INF_COLOSSUS_GREAT_FAIRY_REWARD" },
{ RAND_INF_OGC_GREAT_FAIRY_REWARD, "RAND_INF_OGC_GREAT_FAIRY_REWARD" },
{ RAND_INF_ZELDAS_LETTER, "RAND_INF_ZELDAS_LETTER" },
{ RAND_INF_WEIRD_EGG, "RAND_INF_WEIRD_EGG" },
{ RAND_INF_KF_SOUTH_GRASS_WEST_RUPEE, "RAND_INF_KF_SOUTH_GRASS_WEST_RUPEE" },
{ RAND_INF_KF_NORTH_GRASS_WEST_RUPEE, "RAND_INF_KF_NORTH_GRASS_WEST_RUPEE" },
{ RAND_INF_KF_NORTH_GRASS_EAST_RUPEE, "RAND_INF_KF_NORTH_GRASS_EAST_RUPEE" },

View file

@ -12,6 +12,10 @@ std::chrono::duration<double, std::milli> GetPerformanceTimer(TimerID timer){
return totalTimes[timer];
}
void ResetPerformanceTimer(TimerID timer) {
totalTimes[timer] = {};
}
void ResetPerformanceTimers(){
totalTimes = {};
}

View file

@ -25,12 +25,14 @@ typedef enum {
PT_TOD_ACCESS,
PT_ENTRANCE_LOGIC,
PT_LOCATION_LOGIC,
PT_RECALCULATE_AVAILABLE_CHECKS,
PT_MAX
} TimerID;
void StartPerformanceTimer(TimerID timer);
void StopPerformanceTimer(TimerID timer);
std::chrono::duration<double, std::milli> GetPerformanceTimer(TimerID timer);
void ResetPerformanceTimer(TimerID timer);
void ResetPerformanceTimers();
static std::array<std::chrono::duration<double, std::milli>, PT_MAX> totalTimes = {};
static std::array<std::chrono::high_resolution_clock::time_point, PT_MAX> timeStarted = {};

View file

@ -391,7 +391,7 @@ void GameplayStatsRow(const char* label, const std::string& value, ImVec4 color
}
bool compareTimestampInfoByTime(const TimestampInfo& a, const TimestampInfo& b) {
return CVarGetInteger(CVAR_ENHANCEMENT("GameplayStats.ReverseTimestamps"), 0) ? a.time > b.time : a.time < b.time;
return CVarGetInteger(CVAR_GAMEPLAY_STATS("ReverseTimestamps"), 0) ? a.time > b.time : a.time < b.time;
}
const char* ResolveSceneID(int sceneID, int roomID){
@ -452,13 +452,13 @@ void DrawGameplayStatsHeader() {
} else {
GameplayStatsRow("Total Game Time:", formatTimestampGameplayStat(GAMEPLAYSTAT_TOTAL_TIME), gSaveContext.ship.stats.gameComplete ? COLOR_GREEN : COLOR_WHITE);
}
if (CVarGetInteger(CVAR_ENHANCEMENT("GameplayStats.ShowAdditionalTimers"), 0)) { // !Only display total game time
if (CVarGetInteger(CVAR_GAMEPLAY_STATS("ShowAdditionalTimers"), 0)) { // !Only display total game time
GameplayStatsRow("Gameplay Time:", formatTimestampGameplayStat(gSaveContext.ship.stats.playTimer / 2), COLOR_GREY);
GameplayStatsRow("Pause Menu Time:", formatTimestampGameplayStat(gSaveContext.ship.stats.pauseTimer / 3), COLOR_GREY);
GameplayStatsRow("Time in scene:", formatTimestampGameplayStat(gSaveContext.ship.stats.sceneTimer / 2), COLOR_LIGHT_BLUE);
GameplayStatsRow("Time in room:", formatTimestampGameplayStat(gSaveContext.ship.stats.roomTimer / 2), COLOR_LIGHT_BLUE);
}
if (gPlayState != NULL && CVarGetInteger(CVAR_ENHANCEMENT("GameplayStats.ShowDebugInfo"), 0)) { // && display debug info
if (gPlayState != NULL && CVarGetInteger(CVAR_GAMEPLAY_STATS("ShowDebugInfo"), 0)) { // && display debug info
GameplayStatsRow("play->sceneNum:", formatHexGameplayStat(gPlayState->sceneNum), COLOR_YELLOW);
GameplayStatsRow("gSaveContext.entranceIndex:", formatHexGameplayStat(gSaveContext.entranceIndex), COLOR_YELLOW);
GameplayStatsRow("gSaveContext.cutsceneIndex:", formatHexOnlyGameplayStat(gSaveContext.cutsceneIndex), COLOR_YELLOW);
@ -576,13 +576,13 @@ void DrawGameplayStatsBreakdownTab() {
for (int i = 0; i < gSaveContext.ship.stats.tsIdx; i++) {
std::string sceneName = ResolveSceneID(gSaveContext.ship.stats.sceneTimestamps[i].scene, gSaveContext.ship.stats.sceneTimestamps[i].room);
std::string name;
if (CVarGetInteger(CVAR_ENHANCEMENT("GameplayStats.RoomBreakdown"), 0) && gSaveContext.ship.stats.sceneTimestamps[i].scene != SCENE_GROTTOS) {
if (CVarGetInteger(CVAR_GAMEPLAY_STATS("RoomBreakdown"), 0) && gSaveContext.ship.stats.sceneTimestamps[i].scene != SCENE_GROTTOS) {
name = fmt::format("{:s} Room {:d}", sceneName, gSaveContext.ship.stats.sceneTimestamps[i].room);
} else {
name = sceneName;
}
strcpy(sceneTimestampDisplay[i].name, name.c_str());
sceneTimestampDisplay[i].time = CVarGetInteger(CVAR_ENHANCEMENT("GameplayStats.RoomBreakdown"), 0) ?
sceneTimestampDisplay[i].time = CVarGetInteger(CVAR_GAMEPLAY_STATS("RoomBreakdown"), 0) ?
gSaveContext.ship.stats.sceneTimestamps[i].roomTime : gSaveContext.ship.stats.sceneTimestamps[i].sceneTime;
sceneTimestampDisplay[i].color = COLOR_GREY;
sceneTimestampDisplay[i].isRoom = gSaveContext.ship.stats.sceneTimestamps[i].isRoom;
@ -593,13 +593,13 @@ void DrawGameplayStatsBreakdownTab() {
ImGui::TableSetupColumn("stat", ImGuiTableColumnFlags_WidthStretch);
for (int i = 0; i < gSaveContext.ship.stats.tsIdx; i++) {
TimestampInfo tsInfo = sceneTimestampDisplay[i];
bool canShow = !tsInfo.isRoom || CVarGetInteger(CVAR_ENHANCEMENT("GameplayStats.RoomBreakdown"), 0);
bool canShow = !tsInfo.isRoom || CVarGetInteger(CVAR_GAMEPLAY_STATS("RoomBreakdown"), 0);
if (tsInfo.time > 0 && strnlen(tsInfo.name, 40) > 1 && canShow) {
GameplayStatsRow(tsInfo.name, formatTimestampGameplayStat(tsInfo.time), tsInfo.color);
}
}
std::string toPass;
if (CVarGetInteger(CVAR_ENHANCEMENT("GameplayStats.RoomBreakdown"), 0) && gSaveContext.ship.stats.sceneNum != SCENE_GROTTOS) {
if (CVarGetInteger(CVAR_GAMEPLAY_STATS("RoomBreakdown"), 0) && gSaveContext.ship.stats.sceneNum != SCENE_GROTTOS) {
toPass = fmt::format("{:s} Room {:d}", ResolveSceneID(gSaveContext.ship.stats.sceneNum, gSaveContext.ship.stats.roomNum), gSaveContext.ship.stats.roomNum);
} else {
toPass = ResolveSceneID(gSaveContext.ship.stats.sceneNum, gSaveContext.ship.stats.roomNum);
@ -610,27 +610,27 @@ void DrawGameplayStatsBreakdownTab() {
}
void DrawGameplayStatsOptionsTab() {
UIWidgets::CVarCheckbox("Show in-game total timer", CVAR_ENHANCEMENT("GameplayStats.ShowIngameTimer"),
UIWidgets::CVarCheckbox("Show in-game total timer", CVAR_GAMEPLAY_STATS("ShowIngameTimer"),
UIWidgets::CheckboxOptions()
.Tooltip("Keep track of the timer as an in-game HUD element. The position of the "
"timer can be changed in the Cosmetics Editor.")
.Color(THEME_COLOR));
UIWidgets::CVarCheckbox("Show latest timestamps on top", CVAR_ENHANCEMENT("GameplayStats.ReverseTimestamps"),
UIWidgets::CVarCheckbox("Show latest timestamps on top", CVAR_GAMEPLAY_STATS("ReverseTimestamps"),
UIWidgets::CheckboxOptions().Color(THEME_COLOR));
UIWidgets::CVarCheckbox("Room Breakdown", CVAR_ENHANCEMENT("GameplayStats.RoomBreakdown"),
UIWidgets::CVarCheckbox("Room Breakdown", CVAR_GAMEPLAY_STATS("RoomBreakdown"),
UIWidgets::CheckboxOptions()
.Tooltip("Allows a more in-depth perspective of time spent in a certain map.")
.Color(THEME_COLOR));
UIWidgets::CVarCheckbox("RTA Timing on new files", CVAR_ENHANCEMENT("GameplayStats.RTATiming"),
UIWidgets::CVarCheckbox("RTA Timing on new files", CVAR_GAMEPLAY_STATS("RTATiming"),
UIWidgets::CheckboxOptions()
.Tooltip("Timestamps are relative to starting timestamp rather than in game time, "
"usually necessary for races/speedruns.\n\n"
"Starting timestamp is on first non-C-up input after intro cutscene.\n\n"
"NOTE: THIS NEEDS TO BE SET BEFORE CREATING A FILE TO TAKE EFFECT")
.Color(THEME_COLOR));
UIWidgets::CVarCheckbox("Show additional detail timers", CVAR_ENHANCEMENT("GameplayStats.ShowAdditionalTimers"),
UIWidgets::CVarCheckbox("Show additional detail timers", CVAR_GAMEPLAY_STATS("ShowAdditionalTimers"),
UIWidgets::CheckboxOptions().Color(THEME_COLOR));
UIWidgets::CVarCheckbox("Show Debug Info", CVAR_ENHANCEMENT("GameplayStats.ShowDebugInfo"),
UIWidgets::CVarCheckbox("Show Debug Info", CVAR_GAMEPLAY_STATS("ShowDebugInfo"),
UIWidgets::CheckboxOptions().Color(THEME_COLOR));
}
@ -671,7 +671,7 @@ void InitStats(bool isDebug) {
for (int dungeon = 0; dungeon < ARRAY_COUNT(gSaveContext.ship.stats.dungeonKeys); dungeon++) {
gSaveContext.ship.stats.dungeonKeys[dungeon] = isDebug ? 8 : 0;
}
gSaveContext.ship.stats.rtaTiming = CVarGetInteger(CVAR_ENHANCEMENT("GameplayStats.RTATiming"), 0);
gSaveContext.ship.stats.rtaTiming = CVarGetInteger(CVAR_GAMEPLAY_STATS("RTATiming"), 0);
gSaveContext.ship.stats.fileCreatedAt = 0;
gSaveContext.ship.stats.playTimer = 0;
gSaveContext.ship.stats.pauseTimer = 0;

View file

@ -27,7 +27,7 @@ extern "C" {
: gSaveContext.ship.stats.itemTimestamp[TIMESTAMP_TRIFORCE_COMPLETED])) \
:\
(gSaveContext.ship.stats.playTimer / 2 + gSaveContext.ship.stats.pauseTimer / 3))
#define CURRENT_MODE_TIMER (CVarGetInteger(CVAR_ENHANCEMENT("GameplayStats.RoomBreakdown"), 0) ?\
#define CURRENT_MODE_TIMER (CVarGetInteger(CVAR_GAMEPLAY_STATS("RoomBreakdown"), 0) ?\
gSaveContext.ship.stats.roomTimer :\
gSaveContext.ship.stats.sceneTimer)

View file

@ -402,7 +402,13 @@ bool AddCheckToLogic(LocationAccess& locPair, GetAccessibleLocationsStruct& gals
Rando::ItemLocation* location = ctx->GetItemLocation(loc);
RandomizerGet locItem = location->GetPlacedRandomizerGet();
if (!location->IsAddedToPool() && locPair.ConditionsMet(parentRegion)) {
if (!location->IsAddedToPool() && locPair.ConditionsMet(parentRegion, gals.calculatingAvailableChecks)) {
if (gals.calculatingAvailableChecks) {
gals.accessibleLocations.push_back(loc);
StopPerformanceTimer(PT_LOCATION_LOGIC);
return false;
}
location->AddToPool();
if (locItem == RG_NONE) {
@ -498,19 +504,23 @@ void ProcessRegion(Region* region, GetAccessibleLocationsStruct& gals, Randomize
}
// Return any of the targetLocations that are accessible in logic
std::vector<RandomizerCheck> ReachabilitySearch(const std::vector<RandomizerCheck>& targetLocations, RandomizerGet ignore /* = RG_NONE*/) {
std::vector<RandomizerCheck> ReachabilitySearch(const std::vector<RandomizerCheck>& targetLocations, RandomizerGet ignore /* = RG_NONE*/, bool calculatingAvailableChecks /* = false */) {
auto ctx = Rando::Context::GetInstance();
GetAccessibleLocationsStruct gals(0);
ResetLogic(ctx, gals, true);
gals.calculatingAvailableChecks = calculatingAvailableChecks;
ResetLogic(ctx, gals, !calculatingAvailableChecks);
do {
gals.InitLoop();
for (size_t i = 0; i < gals.regionPool.size(); i++) {
ProcessRegion(RegionTable(gals.regionPool[i]), gals, ignore);
}
} while (gals.logicUpdated);
erase_if(gals.accessibleLocations, [&targetLocations, ctx](RandomizerCheck loc){
erase_if(gals.accessibleLocations, [&targetLocations, ctx, calculatingAvailableChecks](RandomizerCheck loc) {
if (ctx->GetItemLocation(loc)->GetPlacedRandomizerGet() != RG_NONE && !calculatingAvailableChecks) {
return false;
}
for (RandomizerCheck allowedLocation : targetLocations) {
if (loc == allowedLocation || ctx->GetItemLocation(loc)->GetPlacedRandomizerGet() != RG_NONE) {
if (loc == allowedLocation) {
return false;
}
}

View file

@ -34,6 +34,8 @@ struct GetAccessibleLocationsStruct {
std::vector<RandomizerCheck> itemSphere;
std::list<Rando::Entrance*> entranceSphere;
bool calculatingAvailableChecks = false;
GetAccessibleLocationsStruct(int _maxGsCount){
regionPool = {RR_ROOT};
gsCount = 0;
@ -62,7 +64,7 @@ std::vector<RandomizerCheck> GetEmptyLocations(std::vector<RandomizerCheck> allo
void ProcessRegion(Region* region, GetAccessibleLocationsStruct& gals, RandomizerGet ignore = RG_NONE,
bool stopOnBeatable = false, bool addToPlaythrough = false);
std::vector<RandomizerCheck> ReachabilitySearch(const std::vector<RandomizerCheck>& allowedLocations, RandomizerGet ignore=RG_NONE);
std::vector<RandomizerCheck> ReachabilitySearch(const std::vector<RandomizerCheck>& allowedLocations, RandomizerGet ignore=RG_NONE, bool calculatingAvailableChecks=false);
void GeneratePlaythrough();

View file

@ -236,6 +236,14 @@ void RandomizerOnFlagSetHandler(int16_t flagType, int16_t flag) {
Flags_UnsetRandomizerInf(RAND_INF_CHILD_TRADES_HAS_CHICKEN);
}
if (flagType == FLAG_EVENT_CHECK_INF && flag == EVENTCHKINF_OBTAINED_ZELDAS_LETTER) {
Flags_SetRandomizerInf(RAND_INF_ZELDAS_LETTER);
}
if (flagType == FLAG_EVENT_CHECK_INF && flag == EVENTCHKINF_OBTAINED_POCKET_EGG) {
Flags_SetRandomizerInf(RAND_INF_WEIRD_EGG);
}
RandomizerCheck rc = GetRandomizerCheckFromFlag(flagType, flag);
if (rc == RC_UNKNOWN_CHECK) return;
@ -351,6 +359,7 @@ void RandomizerOnItemReceiveHandler(GetItemEntry receivedItemEntry) {
loc->SetCheckStatus(RCSHOW_COLLECTED);
CheckTracker::SpoilAreaFromCheck(randomizerQueuedCheck);
CheckTracker::RecalculateAllAreaTotals();
CheckTracker::RecalculateAvailableChecks();
SaveManager::Instance->SaveSection(gSaveContext.fileNum, SECTION_ID_TRACKER_DATA, true);
randomizerQueuedCheck = RC_UNKNOWN_CHECK;
randomizerQueuedItemEntry = GET_ITEM_NONE;

View file

@ -228,5 +228,14 @@ void ItemLocation::ResetVariables() {
areas = {};
status = RCSHOW_UNCHECKED;
isSkipped = false;
isAvailable = false;
}
bool ItemLocation::IsAvailable() const {
return isAvailable;
}
void ItemLocation::SetAvailable(bool isAvailable_) {
isAvailable = isAvailable_;
}
}

View file

@ -56,6 +56,8 @@ class ItemLocation {
bool IsFoolishCandidate() const;
void SetBarrenCandidate();
void ResetVariables();
bool IsAvailable() const;
void SetAvailable(bool isAvailable_);
private:
RandomizerCheck rc;
@ -76,5 +78,6 @@ class ItemLocation {
bool barrenCandidate = false;
RandomizerCheckStatus status = RCSHOW_UNCHECKED;
bool isSkipped = false;
bool isAvailable = false;
};
} // namespace Rando

View file

@ -11,6 +11,11 @@
#include <fstream>
extern "C" {
extern SaveContext gSaveContext;
extern PlayState* gPlayState;
}
//generic grotto event list
std::vector<EventAccess> grottoEvents;
@ -27,7 +32,7 @@ bool LocationAccess::CheckConditionAtAgeTime(bool& age, bool& time) const {
return GetConditionsMet();
}
bool LocationAccess::ConditionsMet(Region* parentRegion) const {
bool LocationAccess::ConditionsMet(Region* parentRegion, bool calculatingAvailableChecks) const {
//WARNING enterance validation can run this after resetting the access for sphere 0 validation
//When refactoring ToD access, either fix the above or do not assume that we
//have any access at all just because this is being run
@ -41,8 +46,8 @@ bool LocationAccess::ConditionsMet(Region* parentRegion) const {
) {
conditionsMet = true;
}
return conditionsMet && CanBuy();
return conditionsMet && (calculatingAvailableChecks || CanBuy()); // TODO: run CanBuy when price is known due to settings
}
bool LocationAccess::CanBuy() const {
@ -224,8 +229,73 @@ bool MQSpiritSharedBrokenWallRoom(const RandomizerRegion region, ConditionFn con
return areaTable[region].MQSpiritShared(condition, true, anyAge);
}
bool BeanPlanted(const RandomizerRegion region) {
// swchFlag found using the Actor Viewer to get the Obj_Bean parameters & 0x3F
// not tested with multiple OTRs, but can be automated similarly to GetDungeonSmallKeyDoors
SceneID sceneID;
uint8_t swchFlag;
switch (region) {
case RR_ZORAS_RIVER:
sceneID = SceneID::SCENE_ZORAS_RIVER;
swchFlag = 3;
break;
case RR_THE_GRAVEYARD:
sceneID = SceneID::SCENE_GRAVEYARD;
swchFlag = 3;
break;
case RR_KOKIRI_FOREST:
sceneID = SceneID::SCENE_KOKIRI_FOREST;
swchFlag = 9;
break;
case RR_THE_LOST_WOODS:
sceneID = SceneID::SCENE_LOST_WOODS;
swchFlag = 4;
break;
case RR_LW_BEYOND_MIDO:
sceneID = SceneID::SCENE_LOST_WOODS;
swchFlag = 18;
break;
case RR_DEATH_MOUNTAIN_TRAIL:
sceneID = SceneID::SCENE_DEATH_MOUNTAIN_TRAIL;
swchFlag = 6;
break;
case RR_LAKE_HYLIA:
sceneID = SceneID::SCENE_LAKE_HYLIA;
swchFlag = 1;
break;
case RR_GERUDO_VALLEY:
sceneID = SceneID::SCENE_GERUDO_VALLEY;
swchFlag = 3;
break;
case RR_DMC_CENTRAL_LOCAL:
sceneID = SceneID::SCENE_DEATH_MOUNTAIN_CRATER;
swchFlag = 3;
break;
case RR_DESERT_COLOSSUS:
sceneID = SceneID::SCENE_DESERT_COLOSSUS;
swchFlag = 24;
break;
default:
sceneID = SCENE_ID_MAX;
swchFlag = 0;
break;
}
// Get the swch value for the scene
uint32_t swch;
if (gPlayState != nullptr && gPlayState->sceneNum == sceneID) {
swch = gPlayState->actorCtx.flags.swch;
} else if (sceneID != SCENE_ID_MAX) {
swch = gSaveContext.sceneFlags[sceneID].swch;
} else {
swch = 0;
}
return swch >> swchFlag & 1;
}
bool CanPlantBean(const RandomizerRegion region) {
return areaTable[region].CanPlantBeanCheck();
return areaTable[region].CanPlantBeanCheck() || BeanPlanted(region);
}
bool BothAges(const RandomizerRegion region) {

View file

@ -75,7 +75,7 @@ class LocationAccess {
bool CheckConditionAtAgeTime(bool& age, bool& time) const;
bool ConditionsMet(Region* parentRegion) const;
bool ConditionsMet(Region* parentRegion, bool calculatingAvailableChecks) const;
RandomizerCheck GetLocation() const {
return location;

View file

@ -13,6 +13,11 @@
#include "macros.h"
#include "variables.h"
#include <spdlog/spdlog.h>
#include "StringHelper.h"
#include "soh/resource/type/Scene.h"
#include "soh/resource/type/scenecommand/SetTransitionActorList.h"
#include "src/overlays/actors/ovl_En_Door/z_en_door.h"
#include "src/overlays/actors/ovl_Door_Shutter/z_door_shutter.h"
namespace Rando {
@ -87,7 +92,7 @@ namespace Rando {
case RG_BOMB_BAG:
return CurrentUpgrade(UPG_BOMB_BAG);
case RG_MAGIC_SINGLE:
return GetSaveContext()->magicLevel >= 1;
return GetSaveContext()->magicLevel >= 1 || GetSaveContext()->isMagicAcquired;
// Songs
case RG_ZELDAS_LULLABY:
case RG_EPONAS_SONG:
@ -217,6 +222,7 @@ namespace Rando {
case RG_GOLDEN_SCALE:
return CurrentUpgrade(UPG_SCALE) >= 2;
case RG_POCKET_EGG:
return CheckRandoInf(RAND_INF_ADULT_TRADES_HAS_POCKET_EGG);
case RG_COJIRO:
case RG_ODD_MUSHROOM:
case RG_ODD_POTION:
@ -226,7 +232,7 @@ namespace Rando {
case RG_EYEBALL_FROG:
case RG_EYEDROPS:
case RG_CLAIM_CHECK:
return CheckRandoInf(itemName - RG_POCKET_EGG + RAND_INF_ADULT_TRADES_HAS_POCKET_EGG);
return CheckRandoInf(itemName - RG_COJIRO + RAND_INF_ADULT_TRADES_HAS_COJIRO);
case RG_BOTTLE_WITH_BIG_POE:
case RG_BOTTLE_WITH_BLUE_FIRE:
case RG_BOTTLE_WITH_BLUE_POTION:
@ -1509,6 +1515,8 @@ namespace Rando {
mSaveContext->isDoubleDefenseAcquired = state;
break;
case RG_POCKET_EGG:
SetRandoInf(RAND_INF_ADULT_TRADES_HAS_POCKET_EGG, state);
break;
case RG_COJIRO:
case RG_ODD_MUSHROOM:
case RG_ODD_POTION:
@ -1518,7 +1526,7 @@ namespace Rando {
case RG_EYEBALL_FROG:
case RG_EYEDROPS:
case RG_CLAIM_CHECK:
SetRandoInf(randoGet - RG_POCKET_EGG + RAND_INF_ADULT_TRADES_HAS_POCKET_EGG, state);
SetRandoInf(randoGet - RG_COJIRO + RAND_INF_ADULT_TRADES_HAS_COJIRO, state);
break;
case RG_PROGRESSIVE_HOOKSHOT:
{
@ -2096,8 +2104,97 @@ namespace Rando {
}
}
// Get the swch bit positions for the dungeon
const std::vector<uint8_t>& GetDungeonSmallKeyDoors(SceneID sceneId) {
static const std::vector<uint8_t> emptyVector;
auto dungeonInfo = Rando::Context::GetInstance()->GetDungeons()->GetDungeonFromScene(sceneId);
if (dungeonInfo == nullptr) {
return emptyVector;
}
bool masterQuest = dungeonInfo->IsMQ();
// Create a unique key for the dungeon and master quest
uint8_t key = sceneId | (masterQuest << 7);
static std::unordered_map<uint8_t, std::vector<uint8_t>> dungeonSmallKeyDoors;
auto foundEntry = dungeonSmallKeyDoors.find(key);
if (foundEntry != dungeonSmallKeyDoors.end()) {
return foundEntry->second;
}
dungeonSmallKeyDoors[key] = {};
// Get the scene path
SceneTableEntry* sceneTableEntry = &gSceneTable[sceneId];
std::string scenePath = StringHelper::Sprintf("scenes/%s/%s/%s", masterQuest ? "mq" : "nonmq",
sceneTableEntry->sceneFile.fileName, sceneTableEntry->sceneFile.fileName);
// Load the scene
std::shared_ptr<SOH::Scene> scene = std::dynamic_pointer_cast<SOH::Scene>(
Ship::Context::GetInstance()->GetResourceManager()->LoadResource(scenePath));
if (scene == nullptr) {
return emptyVector;
}
// Find the SetTransitionActorList command
std::shared_ptr<SOH::SetTransitionActorList> transitionActorListCommand = nullptr;
for (auto& command : scene->commands) {
if (command->cmdId == SOH::SceneCommandID::SetTransitionActorList) {
transitionActorListCommand = std::dynamic_pointer_cast<SOH::SetTransitionActorList>(command);
break;
}
}
if (transitionActorListCommand == nullptr) {
return emptyVector;
}
// Find the bit position for the small key doors
for (auto& transitionActor : transitionActorListCommand->transitionActorList) {
if (transitionActor.id == ACTOR_EN_DOOR) {
uint8_t doorType = (transitionActor.params >> 7) & 7;
if (doorType == DOOR_LOCKED) {
dungeonSmallKeyDoors[key].emplace_back(transitionActor.params & 0x3F);
}
} else if (transitionActor.id == ACTOR_DOOR_SHUTTER) {
uint8_t doorType = (transitionActor.params >> 7) & 15;
if (doorType == SHUTTER_BACK_LOCKED || doorType == SHUTTER_BOSS || doorType == SHUTTER_KEY_LOCKED) {
dungeonSmallKeyDoors[key].emplace_back(transitionActor.params & 0x3F);
}
}
}
return dungeonSmallKeyDoors[key];
}
int8_t GetUsedSmallKeyCount(SceneID sceneId) {
const auto& smallKeyDoors = GetDungeonSmallKeyDoors(sceneId);
// Get the swch value for the scene
uint32_t swch;
if (gPlayState != nullptr && gPlayState->sceneNum == sceneId) {
swch = gPlayState->actorCtx.flags.swch;
} else {
swch = gSaveContext.sceneFlags[sceneId].swch;
}
// Count the number of small keys doors unlocked
int8_t unlockedSmallKeyDoors = 0;
for (auto& smallKeyDoor : smallKeyDoors) {
unlockedSmallKeyDoors += swch >> smallKeyDoor & 1;
}
// RANDOTODO: Account for MQ Water trick that causes the basement lock to unlock when the player clears the stalfos pit.
return unlockedSmallKeyDoors;
}
uint8_t Logic::GetSmallKeyCount(uint32_t dungeonIndex) {
return mSaveContext->inventory.dungeonKeys[dungeonIndex];
int8_t dungeonKeys = mSaveContext->inventory.dungeonKeys[dungeonIndex];
if (dungeonKeys == -1) {
// never got keys, so can't have used keys
return 0;
}
return dungeonKeys + GetUsedSmallKeyCount(SceneID(dungeonIndex));
}
void Logic::SetSmallKeyCount(uint32_t dungeonIndex, uint8_t count) {
@ -2172,7 +2269,8 @@ namespace Rando {
IsKeysanity = ctx->GetOption(RSK_KEYSANITY).Is(RO_DUNGEON_ITEM_LOC_ANYWHERE) ||
ctx->GetOption(RSK_KEYSANITY).Is(RO_DUNGEON_ITEM_LOC_ANYWHERE) ||
ctx->GetOption(RSK_KEYSANITY).Is(RO_DUNGEON_ITEM_LOC_ANYWHERE);
AmmoCanDrop = /*AmmoDrops.IsNot(AMMODROPS_NONE) TODO: AmmoDrop setting*/ true;
//AmmoCanDrop = /*AmmoDrops.IsNot(AMMODROPS_NONE)*/ false; TODO: AmmoDrop setting
//Child item logic
SkullMask = false;

View file

@ -90,7 +90,7 @@ class Logic {
bool FairyPot = false;
bool FreeFairies = false;
bool FairyPond = false;
bool AmmoCanDrop = false;
bool AmmoCanDrop = true;
uint8_t PieceOfHeart = 0;
uint8_t HeartContainer = 0;

View file

@ -9,7 +9,10 @@
#include "soh/SohGui/UIWidgets.hpp"
#include "soh/SohGui/SohGui.hpp"
#include "dungeon.h"
#include "entrance.h"
#include "location_access.h"
#include "3drando/fill.hpp"
#include "soh/Enhancements/debugger/performanceTimer.h"
#include <string>
#include <vector>
@ -84,7 +87,7 @@ bool fishsanityAgeSplit;
bool initialized;
bool doAreaScroll;
bool previousShowHidden = false;
bool hideShopUnshuffledChecks = true;
bool hideShopUnshuffledChecks = false;
bool alwaysShowGS = false;
std::map<uint32_t, RandomizerCheck> startingShopItem = { { SCENE_KOKIRI_SHOP, RC_KF_SHOP_ITEM_1 },
@ -132,8 +135,10 @@ bool areasFullyChecked[RCAREA_INVALID];
u32 areasSpoiled = 0;
bool showVOrMQ;
s8 areaChecksGotten[RCAREA_INVALID]; //| "Kokiri Forest (4/9)"
s8 areaChecksAvailable[RCAREA_INVALID];
s8 areaCheckTotals[RCAREA_INVALID];
uint16_t totalChecks = 0;
uint16_t totalChecksAvailable = 0;
uint16_t totalChecksGotten = 0;
bool optCollapseAll; // A bool that will collapse all checks once
bool optExpandAll; // A bool that will expand all checks once
@ -166,6 +171,8 @@ bool hideCollected = false;
bool showHidden = true;
bool mystery = false;
bool showLogicTooltip = false;
bool enableAvailableChecks = false;
bool onlyShowAvailable = false;
SceneID DungeonSceneLookupByArea(RandomizerCheckArea area) {
switch (area) {
@ -235,10 +242,12 @@ void TrySetAreas() {
void CalculateTotals() {
totalChecks = 0;
totalChecksAvailable = 0;
totalChecksGotten = 0;
for (uint8_t i = 0; i < RCAREA_INVALID; i++) {
totalChecks += areaCheckTotals[i];
totalChecksAvailable += areaChecksAvailable[i];
totalChecksGotten += areaChecksGotten[i];
}
}
@ -251,17 +260,43 @@ uint16_t GetTotalChecksGotten() {
return totalChecksGotten;
}
bool IsCheckHidden(RandomizerCheck rc) {
Rando::ItemLocation* itemLocation = OTRGlobals::Instance->gRandoContext->GetItemLocation(rc);
RandomizerCheckStatus status = itemLocation->GetCheckStatus();
bool available = itemLocation->IsAvailable();
bool skipped = itemLocation->GetIsSkipped();
bool obtained = itemLocation->HasObtained();
bool seen = status == RCSHOW_SEEN || status == RCSHOW_IDENTIFIED;
bool scummed = status == RCSHOW_SCUMMED;
bool unchecked = status == RCSHOW_UNCHECKED;
return !showHidden && (
(skipped && hideSkipped) ||
(seen && hideSeen) ||
(scummed && hideScummed) ||
(unchecked && hideUnchecked)
);
}
void RecalculateAreaTotals(RandomizerCheckArea rcArea) {
areaChecksGotten[rcArea] = 0;
areaChecksAvailable[rcArea] = 0;
areaCheckTotals[rcArea] = 0;
for (auto rc : checksByArea.at(rcArea)) {
if (!IsVisibleInCheckTracker(rc)) {
continue;
}
areaCheckTotals[rcArea]++;
if (OTRGlobals::Instance->gRandoContext->GetItemLocation(rc)->GetIsSkipped() || OTRGlobals::Instance->gRandoContext->GetItemLocation(rc)->HasObtained()) {
Rando::ItemLocation* itemLoc = OTRGlobals::Instance->gRandoContext->GetItemLocation(rc);
if (itemLoc->GetIsSkipped() || itemLoc->HasObtained()) {
areaChecksGotten[rcArea]++;
}
if (itemLoc->IsAvailable() && !IsCheckHidden(rc)) {
areaChecksAvailable[rcArea]++;
}
}
CalculateTotals();
}
@ -308,6 +343,7 @@ void SetCheckCollected(RandomizerCheck rc) {
if (IsVisibleInCheckTracker(rc)) {
if (!OTRGlobals::Instance->gRandoContext->GetItemLocation(rc)->GetIsSkipped()) {
areaChecksGotten[loc->GetArea()]++;
areaChecksAvailable[loc->GetArea()]--;
} else {
OTRGlobals::Instance->gRandoContext->GetItemLocation(rc)->SetIsSkipped(false);
}
@ -424,10 +460,12 @@ void ClearAreaChecksAndTotals() {
for (auto& [rcArea, vec] : checksByArea) {
vec.clear();
areaChecksGotten[rcArea] = 0;
areaChecksAvailable[rcArea] = 0;
areaCheckTotals[rcArea] = 0;
}
totalChecks = 0;
totalChecksGotten = 0;
totalChecksAvailable = 0;
}
void SetShopSeen(uint32_t sceneNum, bool prices) {
@ -469,6 +507,9 @@ void CheckTrackerLoadGame(int32_t fileNum) {
if (loc->GetCheckStatus() == RCSHOW_SAVED || loc->GetIsSkipped()) {
areaChecksGotten[entry2->GetArea()]++;
}
if (loc->IsAvailable()) {
areaChecksAvailable[entry2->GetArea()]++;
}
}
if (areaChecksGotten[entry2->GetArea()] != 0 || RandomizerCheckObjects::AreaIsOverworld(entry2->GetArea()) ||
@ -524,6 +565,7 @@ void CheckTrackerLoadGame(int32_t fileNum) {
UpdateAllOrdering();
UpdateInventoryChecks();
UpdateFilters();
RecalculateAvailableChecks();
}
void CheckTrackerShopSlotChange(uint8_t cursorSlot, int16_t basePrice) {
@ -539,6 +581,7 @@ void CheckTrackerShopSlotChange(uint8_t cursorSlot, int16_t basePrice) {
if (status == RCSHOW_SEEN) {
OTRGlobals::Instance->gRandoContext->GetItemLocation(slot)->SetCheckStatus(RCSHOW_IDENTIFIED);
SaveManager::Instance->SaveSection(gSaveContext.fileNum, sectionId, true);
RecalculateAvailableChecks();
}
}
@ -812,6 +855,9 @@ void SaveTrackerData(SaveContext* saveContext, int sectionID, bool fullSave) {
void SaveFile(SaveContext* saveContext, int sectionID, bool fullSave) {
SaveTrackerData(saveContext, sectionID, fullSave);
if (fullSave) {
RecalculateAvailableChecks();
}
}
void LoadFile() {
@ -882,6 +928,8 @@ void CheckTrackerWindow::DrawElement() {
showHidden = CVarGetInteger(CVAR_TRACKER_CHECK("ShowHidden"), 0);
mystery = CVarGetInteger(CVAR_RANDOMIZER_ENHANCEMENT("MysteriousShuffle"), 0);
showLogicTooltip = CVarGetInteger(CVAR_TRACKER_CHECK("ShowLogic"), 0);
enableAvailableChecks = CVarGetInteger(CVAR_TRACKER_CHECK("EnableAvailableChecks"), 0);
onlyShowAvailable = CVarGetInteger(CVAR_TRACKER_CHECK("OnlyShowAvailable"), 0);
hideShopUnshuffledChecks = CVarGetInteger(CVAR_TRACKER_CHECK("HideUnshuffledShopChecks"), 0);
alwaysShowGS = CVarGetInteger(CVAR_TRACKER_CHECK("AlwaysShowGSLocs"), 0);
@ -932,9 +980,21 @@ void CheckTrackerWindow::DrawElement() {
ImGui::TableNextRow(0, headerHeight);
ImGui::TableNextColumn();
UIWidgets::CVarCheckbox(
"Show Hidden Items", CVAR_TRACKER_CHECK("ShowHidden"), UIWidgets::CheckboxOptions({{ .tooltip = "When active, items will show hidden checks by default when updated to this state." }})
.Color(THEME_COLOR));
if (UIWidgets::CVarCheckbox(
"Show Hidden Items", CVAR_TRACKER_CHECK("ShowHidden"), UIWidgets::CheckboxOptions({{.tooltip = "When active, items will show hidden checks by default when updated to this state." }})
.Color(THEME_COLOR))) {
doAreaScroll = true;
showHidden = CVarGetInteger(CVAR_TRACKER_CHECK("ShowHidden"), 0);
RecalculateAllAreaTotals();
}
if (enableAvailableChecks) {
if (UIWidgets::CVarCheckbox(
"Only Show Available Checks", CVAR_TRACKER_CHECK("OnlyShowAvailable"), UIWidgets::CheckboxOptions({{ .tooltip = "When active, unavailable checks will be hidden." }})
.Color(THEME_COLOR))) {
doAreaScroll = true;
RecalculateAllAreaTotals();
}
}
UIWidgets::PaddedSeparator();
if (UIWidgets::Button("Expand All", UIWidgets::ButtonOptions().Color(THEME_COLOR).Size(UIWidgets::Sizes::Inline))) {
optCollapseAll = false;
@ -960,7 +1020,13 @@ void CheckTrackerWindow::DrawElement() {
ImGui::Separator();
ImGui::Text("Total Checks: %d / %d", totalChecksGotten, totalChecks);
std::ostringstream totalChecksSS;
totalChecksSS << "Total Checks: ";
if (enableAvailableChecks) {
totalChecksSS << totalChecksAvailable << " Available / ";
}
totalChecksSS << totalChecksGotten << " Checked / " << totalChecks << " Total";
ImGui::Text(totalChecksSS.str().c_str());
UIWidgets::PaddedSeparator();
@ -1012,7 +1078,8 @@ void CheckTrackerWindow::DrawElement() {
doAreaScroll = true;
}
if ((shouldHideFilteredAreas && filterAreasHidden[rcArea]) ||
(!showHidden && ((hideComplete && thisAreaFullyChecked) || (hideIncomplete && !thisAreaFullyChecked)))
(!showHidden && ((hideComplete && thisAreaFullyChecked) || (hideIncomplete && !thisAreaFullyChecked))) ||
(enableAvailableChecks && onlyShowAvailable && areaChecksAvailable[rcArea] == 0)
) {
doDraw = false;
} else {
@ -1051,14 +1118,27 @@ void CheckTrackerWindow::DrawElement() {
isThisAreaSpoiled = IsAreaSpoiled(rcArea) || mqSpoilers;
if (isThisAreaSpoiled) {
if (showVOrMQ && RandomizerCheckObjects::AreaIsDungeon(rcArea)) {
if (OTRGlobals::Instance->gRandoContext->GetDungeons()->GetDungeonFromScene(DungeonSceneLookupByArea(rcArea))->IsMQ())
ImGui::Text("(%d/%d) - MQ", areaChecksGotten[rcArea], areaCheckTotals[rcArea]);
else
ImGui::Text("(%d/%d) - Vanilla", areaChecksGotten[rcArea], areaCheckTotals[rcArea]);
} else {
ImGui::Text("(%d/%d)", areaChecksGotten[rcArea], areaCheckTotals[rcArea]);
std::ostringstream areaTotalsSS;
std::ostringstream areaTotalsTooltipSS;
areaTotalsSS << "(";
if (enableAvailableChecks) {
areaTotalsSS << static_cast<uint16_t>(areaChecksAvailable[rcArea]) << " / ";
areaTotalsTooltipSS << "Available / ";
}
areaTotalsSS << static_cast<uint16_t>(areaChecksGotten[rcArea]) << " / " << static_cast<uint16_t>(areaCheckTotals[rcArea]) << ")";
areaTotalsTooltipSS << "Checked / Total";
if (showVOrMQ && RandomizerCheckObjects::AreaIsDungeon(rcArea)) {
if (OTRGlobals::Instance->gRandoContext->GetDungeons()->GetDungeonFromScene(DungeonSceneLookupByArea(rcArea))->IsMQ()) {
areaTotalsSS << " - MQ";
} else {
areaTotalsSS << " - Vanilla";
}
}
ImGui::Text(areaTotalsSS.str().c_str());
UIWidgets::Tooltip(areaTotalsTooltipSS.str().c_str());
} else {
ImGui::Text("???");
}
@ -1561,6 +1641,12 @@ void DrawLocation(RandomizerCheck rc) {
Rando::ItemLocation* itemLoc = OTRGlobals::Instance->gRandoContext->GetItemLocation(rc);
RandomizerCheckStatus status = itemLoc->GetCheckStatus();
bool skipped = itemLoc->GetIsSkipped();
bool available = itemLoc->IsAvailable();
if (enableAvailableChecks && onlyShowAvailable && !available) {
return;
}
if (status == RCSHOW_COLLECTED) {
if (!showHidden && hideCollected) {
return;
@ -1637,10 +1723,18 @@ void DrawLocation(RandomizerCheck rc) {
OTRGlobals::Instance->gRandoContext->GetItemLocation(rc)->SetIsSkipped(false);
areaChecksGotten[loc->GetArea()]--;
totalChecksGotten--;
if (available) {
areaChecksAvailable[loc->GetArea()]++;
totalChecksAvailable++;
}
} else {
OTRGlobals::Instance->gRandoContext->GetItemLocation(rc)->SetIsSkipped(true);
areaChecksGotten[loc->GetArea()]++;
totalChecksGotten++;
if (available) {
areaChecksAvailable[loc->GetArea()]--;
totalChecksAvailable--;
}
}
UpdateOrdering(loc->GetArea());
UpdateInventoryChecks();
@ -1654,7 +1748,19 @@ void DrawLocation(RandomizerCheck rc) {
ImGui::SameLine();
//Draw
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(mainColor.r / 255.0f, mainColor.g / 255.0f, mainColor.b / 255.0f, mainColor.a / 255.0f));
ImVec4 styleColor(mainColor.r / 255.0f, mainColor.g / 255.0f, mainColor.b / 255.0f, mainColor.a / 255.0f);
if (enableAvailableChecks) {
if (itemLoc->HasObtained()) {
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(0, 0, 0, 0));
} else {
ImGui::PushStyleColor(ImGuiCol_Text, styleColor);
}
ImGui::Text("%s", available ? ICON_FA_UNLOCK : ICON_FA_LOCK);
ImGui::PopStyleColor();
ImGui::SameLine();
}
ImGui::PushStyleColor(ImGuiCol_Text, styleColor);
ImGui::Text("%s", txt.c_str());
ImGui::PopStyleColor();
@ -1718,7 +1824,7 @@ void DrawLocation(RandomizerCheck rc) {
if (conditionStr != "true") {
UIWidgets::Tooltip(conditionStr.c_str());
}
return;
break;
}
}
}
@ -1796,6 +1902,55 @@ void ImGuiDrawTwoColorPickerSection(const char* text, const char* cvarMainName,
UIWidgets::PopStyleCombobox();
}
void RecalculateAvailableChecks() {
if (!enableAvailableChecks) {
return;
}
ResetPerformanceTimer(PT_RECALCULATE_AVAILABLE_CHECKS);
StartPerformanceTimer(PT_RECALCULATE_AVAILABLE_CHECKS);
std::vector<RandomizerCheck> targetLocations;
targetLocations.reserve(RR_MAX);
for (auto& location : Rando::StaticData::GetLocationTable()) {
RandomizerCheck rc = location.GetRandomizerCheck();
Rando::ItemLocation* itemLocation = OTRGlobals::Instance->gRandoContext->GetItemLocation(rc);
itemLocation->SetAvailable(false);
if (!itemLocation->HasObtained()) {
targetLocations.emplace_back(rc);
}
}
std::vector<RandomizerCheck> availableChecks = ReachabilitySearch(targetLocations, RG_NONE, true);
for (auto& rc : availableChecks) {
const auto& location = Rando::StaticData::GetLocation(rc);
const auto& itemLocation = OTRGlobals::Instance->gRandoContext->GetItemLocation(rc);
if (location->GetRCType() == RCTYPE_SHOP && itemLocation->GetCheckStatus() == RCSHOW_IDENTIFIED) {
if (CanBuyAnother(rc)) {
itemLocation->SetAvailable(true);
}
} else {
itemLocation->SetAvailable(true);
}
}
totalChecksAvailable = 0;
for (auto& [rcArea, vec] : checksByArea) {
areaChecksAvailable[rcArea] = 0;
for (auto& rc : vec) {
Rando::ItemLocation* itemLocation = OTRGlobals::Instance->gRandoContext->GetItemLocation(rc);
if (itemLocation->IsAvailable() && IsVisibleInCheckTracker(rc) && !IsCheckHidden(rc)) {
areaChecksAvailable[rcArea]++;
}
}
totalChecksAvailable += areaChecksAvailable[rcArea];
}
StopPerformanceTimer(PT_RECALCULATE_AVAILABLE_CHECKS);
SPDLOG_INFO("Recalculate Available Checks Time: {}ms", GetPerformanceTimer(PT_RECALCULATE_AVAILABLE_CHECKS).count());
}
void CheckTrackerWindow::Draw() {
if (!IsVisible()) {
return;
@ -1850,7 +2005,7 @@ void CheckTrackerSettingsWindow::DrawElement() {
.Tooltip("If enabled, Vanilla/MQ dungeons will show on the tracker immediately. Otherwise, Vanilla/MQ dungeon locations must be unlocked.").Color(THEME_COLOR));
if (UIWidgets::CVarCheckbox("Hide unshuffled shop item checks", CVAR_TRACKER_CHECK("HideUnshuffledShopChecks"),
UIWidgets::CheckboxOptions().Tooltip("If enabled, will prevent the tracker from displaying slots with non-shop-item shuffles.").Color(THEME_COLOR))) {
hideShopUnshuffledChecks = !hideShopUnshuffledChecks;
hideShopUnshuffledChecks = CVarGetInteger(CVAR_TRACKER_CHECK("HideUnshuffledShopChecks"), 0);
UpdateFilters();
}
if (UIWidgets::CVarCheckbox("Always show gold skulltulas", CVAR_TRACKER_CHECK("AlwaysShowGSLocs"),
@ -1860,6 +2015,11 @@ void CheckTrackerSettingsWindow::DrawElement() {
}
UIWidgets::CVarCheckbox("Show Logic", CVAR_TRACKER_CHECK("ShowLogic"),
UIWidgets::CheckboxOptions().Tooltip("If enabled, will show a check's logic when hovering over it.").Color(THEME_COLOR));
if (UIWidgets::CVarCheckbox("Enable Available Checks", CVAR_TRACKER_CHECK("EnableAvailableChecks"),
UIWidgets::CheckboxOptions().Tooltip("If enabled, will show the checks that are available to be collected with your current progress.").Color(THEME_COLOR))) {
enableAvailableChecks = CVarGetInteger(CVAR_TRACKER_CHECK("EnableAvailableChecks"), 0);
RecalculateAvailableChecks();
}
// Filtering settings
UIWidgets::PaddedSeparator();

View file

@ -61,4 +61,5 @@ void UpdateAllOrdering();
void UpdateAllAreas();
void RecalculateAllAreaTotals();
void SpoilAreaFromCheck(RandomizerCheck rc);
void RecalculateAvailableChecks();
} // namespace CheckTracker

View file

@ -306,11 +306,13 @@ extern "C" void Randomizer_InitSaveFile() {
// Malon/Talon back at ranch.
Flags_SetEventChkInf(EVENTCHKINF_OBTAINED_POCKET_EGG);
Flags_SetRandomizerInf(RAND_INF_WEIRD_EGG);
Flags_SetEventChkInf(EVENTCHKINF_TALON_WOKEN_IN_CASTLE);
Flags_SetEventChkInf(EVENTCHKINF_TALON_RETURNED_FROM_CASTLE);
// Set "Got Zelda's Letter" flag. Also ensures Saria is back at SFM.
Flags_SetEventChkInf(EVENTCHKINF_OBTAINED_ZELDAS_LETTER);
Flags_SetRandomizerInf(RAND_INF_ZELDAS_LETTER);
Flags_SetRandomizerInf(RAND_INF_CHILD_TRADES_HAS_LETTER_ZELDA);
// Got item from Impa.
@ -321,7 +323,6 @@ extern "C" void Randomizer_InitSaveFile() {
// Set this at the end to ensure we always start with the letter.
// This is for the off chance, we got the Weird Egg from Impa (which should never happen).
INV_CONTENT(ITEM_LETTER_ZELDA) = ITEM_LETTER_ZELDA;
Flags_SetRandomizerInf(RAND_INF_CHILD_TRADES_HAS_LETTER_ZELDA);
}
if (Randomizer_GetSettingValue(RSK_SHUFFLE_MASTER_SWORD) && startingAge == RO_AGE_ADULT) {

View file

@ -459,10 +459,16 @@ void SaveManager::LoadRandomizerVersion3() {
});
randoContext->GetTrials()->SkipAll();
SaveManager::Instance->LoadArray("requiredTrials", randoContext->GetOption(RSK_TRIAL_COUNT).Get() + 1, [&](size_t i) {
SaveManager::Instance->LoadArray("requiredTrials", randoContext->GetOption(RSK_TRIAL_COUNT).Get(), [&](size_t i) {
size_t trialId;
SaveManager::Instance->LoadData("", trialId);
randoContext->GetTrial(trialId)->SetAsRequired();
});
SaveManager::Instance->LoadArray("trickOptions", RT_MAX, [&](size_t i) {
uint8_t value = 0;
SaveManager::Instance->LoadData("", value);
randoContext->GetTrickOption(RandomizerTrick(i)).Set(value);
});
}
@ -596,6 +602,10 @@ void SaveManager::SaveRandomizer(SaveContext* saveContext, int sectionID, bool f
SaveManager::Instance->SaveData("", i);
}
});
SaveManager::Instance->SaveArray("trickOptions", RT_MAX, [&](size_t i) {
SaveManager::Instance->SaveData("", randoContext->GetTrickOption(RandomizerTrick(i)).Get());
});
}
// Init() here is an extension of InitSram, and thus not truly an initializer for SaveManager itself. don't put any class initialization stuff here

View file

@ -1775,7 +1775,7 @@ void SohMenu::AddMenuEnhancements() {
.WindowName("Additional Timers")
.Options(WindowButtonOptions().Tooltip("Enables the separate Additional Timers Window."));
AddWidget(path, "Font Scale: %.2fx", WIDGET_CVAR_SLIDER_FLOAT)
.CVar(CVAR_ENHANCEMENT("TimeDisplay.FontScale"))
.CVar(CVAR_TIME_DISPLAY("FontScale"))
.Callback([](WidgetInfo& info) {
TimeDisplayInitSettings();
})
@ -1786,7 +1786,7 @@ void SohMenu::AddMenuEnhancements() {
.Format("%.2fx")
);
AddWidget(path, "Hide Background", WIDGET_CVAR_CHECKBOX)
.CVar(CVAR_ENHANCEMENT("TimeDisplay.ShowWindowBG"))
.CVar(CVAR_TIME_DISPLAY("ShowWindowBG"))
.Callback([](WidgetInfo& info) {
TimeDisplayInitSettings();
});

View file

@ -299,12 +299,8 @@ namespace SOH {
{ MigrationAction::Rename, "gUniformLR", "gEnhancements.FixMenuLR" },
{ MigrationAction::Rename, "gVisualAgony", "gEnhancements.VisualAgony" },
{ MigrationAction::Rename, "gVoidDamageMul", "gEnhancements.VoidDamageMult" },
{ MigrationAction::Rename, "gGameplayStats.ShowAdditionalTimers", "gEnhancements.GameplayStats.ShowAdditionalTimers" },
{ MigrationAction::Rename, "gGameplayStats.ShowDebugInfo", "gEnhancements.GameplayStats.ShowDebugInfo" },
{ MigrationAction::Rename, "gGameplayStats.RoomBreakdown", "gEnhancements.GameplayStats.RoomBreakdown" },
{ MigrationAction::Rename, "gGameplayStats.ShowIngameTimer", "gEnhancements.GameplayStats.ShowInGameTimer" },
{ MigrationAction::Rename, "gGameplayStats.TimestampsReverse", "gEnhancements.GameplayStats.ReverseTimestamps" },
{ MigrationAction::Rename, "gGameplayStats.RTATiming", "gEnhancements.GameplayStats.RTATiming" },
{ MigrationAction::Rename, "gGameplayStats.ShowIngameTimer", "gGameplayStats.ShowInGameTimer" },
{ MigrationAction::Rename, "gGameplayStats.TimestampsReverse", "gGameplayStats.ReverseTimestamps" },
{ MigrationAction::Rename, "gMirroredWorld", "gEnhancements.MirroredWorld" },
{ MigrationAction::Rename, "gBetaQuestWorld", "gCheats.BetaQuestWorld" },
{ MigrationAction::Rename, "gBombTimerMultiplier", "gCheats.BombTimerMultiplier" },

View file

@ -14,4 +14,6 @@
#define CVAR_GENERAL(var) CVAR_PREFIX_GENERAL "." var
#define CVAR_REMOTE(var) CVAR_PREFIX_REMOTE "." var
#define CVAR_REMOTE_CROWD_CONTROL(var) CVAR_REMOTE("CrowdControl." var)
#define CVAR_REMOTE_SAIL(var) CVAR_REMOTE("Sail." var)
#define CVAR_REMOTE_SAIL(var) CVAR_REMOTE("Sail." var)
#define CVAR_GAMEPLAY_STATS(var) CVAR_PREFIX_GAMEPLAY_STATS "." var
#define CVAR_TIME_DISPLAY(var) CVAR_PREFIX_TIME_DISPLAY "." var

View file

@ -6123,7 +6123,7 @@ void Interface_DrawTotalGameplayTimer(PlayState* play) {
// Draw timer based on the Gameplay Stats total time.
if ((IS_BOSS_RUSH && gSaveContext.ship.quest.data.bossRush.options[BR_OPTIONS_TIMER] == BR_CHOICE_TIMER_YES) ||
(CVarGetInteger(CVAR_ENHANCEMENT("GameplayStats.ShowIngameTimer"), 0) && gSaveContext.fileNum >= 0 && gSaveContext.fileNum <= 2)) {
(CVarGetInteger(CVAR_GAMEPLAY_STATS("ShowIngameTimer"), 0) && gSaveContext.fileNum >= 0 && gSaveContext.fileNum <= 2)) {
s32 X_Margins_Timer = 0;
if (CVarGetInteger(CVAR_COSMETIC("HUD.IGT.UseMargins"), 0) != 0) {

View file

@ -317,7 +317,8 @@ void KaleidoScope_HandleItemCycleExtras(PlayState* play, u8 slot, bool canCycle,
bool CanMaskSelect() {
if (IS_RANDO) {
return CVarGetInteger(CVAR_ENHANCEMENT("MaskSelect"), 0) /* || Randomizer_GetSettingValue(RSK_SHUFFLE_CHILD_TRADE) */;
return CVarGetInteger(CVAR_ENHANCEMENT("MaskSelect"), 0)
&& Flags_GetRandomizerInf(RAND_INF_ZELDAS_LETTER);/* || Randomizer_GetSettingValue(RSK_SHUFFLE_CHILD_TRADE) */
}
// only allow mask select when: