Shipwright/soh/soh/Enhancements/randomizer/randomizer_check_tracker.cpp

1693 lines
78 KiB
C++

#include "randomizer_check_tracker.h"
#include "randomizer_entrance_tracker.h"
#include "randomizer_item_tracker.h"
#include "randomizerTypes.h"
#include "dungeon.h"
#include "../../OTRGlobals.h"
#include "../../UIWidgets.hpp"
#include <string>
#include <vector>
#include <set>
#include <libultraship/libultraship.h>
#include "location.h"
#include "item_location.h"
#include "soh/Enhancements/game-interactor/GameInteractor.h"
#include "z64item.h"
#include "randomizerTypes.h"
#include "fishsanity.h"
extern "C" {
#include "variables.h"
#include "functions.h"
#include "macros.h"
extern PlayState* gPlayState;
}
extern "C" uint32_t ResourceMgr_IsSceneMasterQuest(s16 sceneNum);
extern "C" GetItemEntry ItemTable_RetrieveEntry(s16 modIndex, s16 getItemID);
extern std::vector<ItemTrackerItem> dungeonRewardStones;
extern std::vector<ItemTrackerItem> dungeonRewardMedallions;
extern std::vector<ItemTrackerItem> songItems;
extern std::vector<ItemTrackerItem> equipmentItems;
#define RCO_RAORU { RC_GIFT_FROM_SAGES, RCVORMQ_BOTH, RCTYPE_DUNGEON_REWARD, RCAREA_MARKET, ACTOR_ID_MAX, SCENE_ID_MAX, 0x00, GI_NONE, false, "Gift from Raoru", "Gift from Raoru", true };
using json = nlohmann::json;
void to_json(json& j, const RandomizerCheckTrackerData& rctd) {
j = json {
{ "status", rctd.status == RCSHOW_COLLECTED ? RCSHOW_SAVED : rctd.status },
{ "skipped", rctd.skipped },
{ "price", rctd.price },
{ "hintItem", rctd.hintItem }};
}
void from_json(const json& j, RandomizerCheckTrackerData& rctd) {
j.at("status").get_to(rctd.status);
j.at("skipped").get_to(rctd.skipped);
j.at("hintItem").get_to(rctd.hintItem);
j.at("price").get_to(rctd.price);
}
namespace CheckTracker {
// settings
bool showShops;
bool showOverworldTokens;
bool showDungeonTokens;
bool showBeans;
bool showScrubs;
bool showMerchants;
bool showBeehives;
bool showCows;
bool showAdultTrade;
bool showKokiriSword;
bool showMasterSword;
bool showWeirdEgg;
bool showGerudoCard;
bool showFrogSongRupees;
bool showStartingMapsCompasses;
bool showKeysanity;
bool showGerudoFortressKeys;
bool showBossKeysanity;
bool showGanonBossKey;
bool showOcarinas;
bool show100SkullReward;
bool showLinksPocket;
bool fortressFast;
bool fortressNormal;
u8 fishsanityMode;
u8 fishsanityPondCount;
bool fishsanityAgeSplit;
// persistent during gameplay
bool initialized;
bool doAreaScroll;
bool previousShowHidden = false;
bool hideShopRightChecks = true;
bool hideTriforceCompleted = true;
bool alwaysShowGS = false;
std::map<uint32_t, RandomizerCheck> startingShopItem = { { SCENE_KOKIRI_SHOP, RC_KF_SHOP_ITEM_1 },
{ SCENE_BAZAAR, RC_MARKET_BAZAAR_ITEM_1 },
{ SCENE_POTION_SHOP_MARKET, RC_MARKET_POTION_SHOP_ITEM_1 },
{ SCENE_BOMBCHU_SHOP, RC_MARKET_BOMBCHU_SHOP_ITEM_1 },
{ SCENE_POTION_SHOP_KAKARIKO, RC_KAK_POTION_SHOP_ITEM_1 },
{ SCENE_ZORA_SHOP, RC_ZD_SHOP_ITEM_1 },
{ SCENE_GORON_SHOP, RC_GC_SHOP_ITEM_1 } };
std::map<SceneID, RandomizerCheckArea> DungeonRCAreasBySceneID = {
{SCENE_DEKU_TREE, RCAREA_DEKU_TREE},
{SCENE_DODONGOS_CAVERN, RCAREA_DODONGOS_CAVERN},
{SCENE_JABU_JABU, RCAREA_JABU_JABUS_BELLY},
{SCENE_FOREST_TEMPLE, RCAREA_FOREST_TEMPLE},
{SCENE_FIRE_TEMPLE, RCAREA_FIRE_TEMPLE},
{SCENE_WATER_TEMPLE, RCAREA_WATER_TEMPLE},
{SCENE_SHADOW_TEMPLE, RCAREA_SHADOW_TEMPLE},
{SCENE_SPIRIT_TEMPLE, RCAREA_SPIRIT_TEMPLE},
{SCENE_BOTTOM_OF_THE_WELL, RCAREA_BOTTOM_OF_THE_WELL},
{SCENE_ICE_CAVERN, RCAREA_ICE_CAVERN},
{SCENE_GERUDO_TRAINING_GROUND, RCAREA_GERUDO_TRAINING_GROUND},
{SCENE_INSIDE_GANONS_CASTLE, RCAREA_GANONS_CASTLE},
};
std::map<RandomizerCheckArea, std::vector<RandomizerCheck>> checksByArea;
bool areasFullyChecked[RCAREA_INVALID];
u32 areasSpoiled = 0;
bool showVOrMQ;
s8 areaChecksGotten[RCAREA_INVALID]; //| "Kokiri Forest (4/9)"
s8 areaCheckTotals[RCAREA_INVALID];
bool optCollapseAll; // A bool that will collapse all checks once
bool optExpandAll; // A bool that will expand all checks once
RandomizerCheck lastLocationChecked = RC_UNKNOWN_CHECK;
RandomizerCheckArea previousArea = RCAREA_INVALID;
RandomizerCheckArea currentArea = RCAREA_INVALID;
OSContPad* trackerButtonsPressed;
std::unordered_map<RandomizerCheck, std::string> checkNameOverrides;
void BeginFloatWindows(std::string UniqueName, bool& open, ImGuiWindowFlags flags = 0);
bool CompareChecks(RandomizerCheck, RandomizerCheck);
bool CheckByArea(RandomizerCheckArea);
void DrawLocation(RandomizerCheck);
void EndFloatWindows();
bool HasItemBeenCollected(RandomizerCheck);
void LoadSettings();
void RainbowTick();
void UpdateAreas(RandomizerCheckArea area);
void UpdateInventoryChecks();
void UpdateOrdering(RandomizerCheckArea);
int sectionId;
SceneID DungeonSceneLookupByArea(RandomizerCheckArea area) {
switch (area) {
case RCAREA_DEKU_TREE: return SCENE_DEKU_TREE;
case RCAREA_DODONGOS_CAVERN: return SCENE_DODONGOS_CAVERN;
case RCAREA_JABU_JABUS_BELLY: return SCENE_JABU_JABU;
case RCAREA_FOREST_TEMPLE: return SCENE_FOREST_TEMPLE;
case RCAREA_FIRE_TEMPLE: return SCENE_FIRE_TEMPLE;
case RCAREA_WATER_TEMPLE: return SCENE_WATER_TEMPLE;
case RCAREA_SPIRIT_TEMPLE: return SCENE_SPIRIT_TEMPLE;
case RCAREA_SHADOW_TEMPLE: return SCENE_SHADOW_TEMPLE;
case RCAREA_BOTTOM_OF_THE_WELL: return SCENE_BOTTOM_OF_THE_WELL;
case RCAREA_ICE_CAVERN: return SCENE_ICE_CAVERN;
case RCAREA_GERUDO_TRAINING_GROUND: return SCENE_GERUDO_TRAINING_GROUND;
case RCAREA_GANONS_CASTLE: return SCENE_INSIDE_GANONS_CASTLE;
default: return SCENE_ID_MAX;
}
}
Color_RGBA8 Color_Bg_Default = { 0, 0, 0, 255 }; // Black
Color_RGBA8 Color_Main_Default = { 255, 255, 255, 255 }; // White
Color_RGBA8 Color_Area_Incomplete_Extra_Default = { 255, 255, 255, 255 }; // White
Color_RGBA8 Color_Area_Complete_Extra_Default = { 255, 255, 255, 255 }; // White
Color_RGBA8 Color_Unchecked_Extra_Default = { 255, 255, 255, 255 }; // White
Color_RGBA8 Color_Skipped_Main_Default = { 160, 160, 160, 255 }; // Grey
Color_RGBA8 Color_Skipped_Extra_Default = { 160, 160, 160, 255 }; // Grey
Color_RGBA8 Color_Seen_Extra_Default = { 255, 255, 255, 255 }; // TODO
Color_RGBA8 Color_Hinted_Extra_Default = { 255, 255, 255, 255 }; // TODO
Color_RGBA8 Color_Collected_Extra_Default = { 242, 101, 34, 255 }; // Orange
Color_RGBA8 Color_Scummed_Extra_Default = { 0, 174, 239, 255 }; // Blue
Color_RGBA8 Color_Saved_Extra_Default = { 0, 185, 0, 255 }; // Green
Color_RGBA8 Color_Background = { 0, 0, 0, 255 };
Color_RGBA8 Color_Area_Incomplete_Main = { 255, 255, 255, 255 }; // White
Color_RGBA8 Color_Area_Incomplete_Extra = { 255, 255, 255, 255 }; // White
Color_RGBA8 Color_Area_Complete_Main = { 255, 255, 255, 255 }; // White
Color_RGBA8 Color_Area_Complete_Extra = { 255, 255, 255, 255 }; // White
Color_RGBA8 Color_Unchecked_Main = { 255, 255, 255, 255 }; // White
Color_RGBA8 Color_Unchecked_Extra = { 255, 255, 255, 255 }; // Useless
Color_RGBA8 Color_Skipped_Main = { 160, 160, 160, 255 }; // Grey
Color_RGBA8 Color_Skipped_Extra = { 160, 160, 160, 255 }; // Grey
Color_RGBA8 Color_Seen_Main = { 255, 255, 255, 255 }; // TODO
Color_RGBA8 Color_Seen_Extra = { 160, 160, 160, 255 }; // TODO
Color_RGBA8 Color_Hinted_Main = { 255, 255, 255, 255 }; // TODO
Color_RGBA8 Color_Hinted_Extra = { 255, 255, 255, 255 }; // TODO
Color_RGBA8 Color_Collected_Main = { 255, 255, 255, 255 }; // White
Color_RGBA8 Color_Collected_Extra = { 242, 101, 34, 255 }; // Orange
Color_RGBA8 Color_Scummed_Main = { 255, 255, 255, 255 }; // White
Color_RGBA8 Color_Scummed_Extra = { 0, 174, 239, 255 }; // Blue
Color_RGBA8 Color_Saved_Main = { 255, 255, 255, 255 }; // White
Color_RGBA8 Color_Saved_Extra = { 0, 185, 0, 255 }; // Green
std::vector<uint32_t> buttons = { BTN_A, BTN_B, BTN_CUP, BTN_CDOWN, BTN_CLEFT, BTN_CRIGHT, BTN_L,
BTN_Z, BTN_R, BTN_START, BTN_DUP, BTN_DDOWN, BTN_DLEFT, BTN_DRIGHT };
void DefaultCheckData(RandomizerCheck rc) {
gSaveContext.checkTrackerData[rc].status = RCSHOW_UNCHECKED;
gSaveContext.checkTrackerData[rc].skipped = 0;
gSaveContext.checkTrackerData[rc].hintItem = RC_UNKNOWN_CHECK;
}
void SongFromImpa() {
if (IS_RANDO) {
if (OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_SKIP_CHILD_ZELDA) == RO_GENERIC_ON && IS_RANDO) {
if (gSaveContext.checkTrackerData[RC_SONG_FROM_IMPA].status != RCSHOW_SAVED) {
gSaveContext.checkTrackerData[RC_SONG_FROM_IMPA].status = RCSHOW_SAVED;
}
}
}
}
void GiftFromSages() {
if (!IS_RANDO) {
DefaultCheckData(RC_GIFT_FROM_SAGES);
}
}
std::vector<RandomizerCheck> checks;
// Function for adding Link's Pocket check
void LinksPocket() {
if (IS_RANDO) {
if (OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_LINKS_POCKET) != RO_LINKS_POCKET_NOTHING ||
OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_SHUFFLE_DUNGEON_REWARDS) == RO_DUNGEON_REWARDS_END_OF_DUNGEON) {
DefaultCheckData(RC_LINKS_POCKET);
gSaveContext.checkTrackerData[RC_LINKS_POCKET].status = RCSHOW_SAVED;
}
}
}
void TrySetAreas() {
if (checksByArea.empty()) {
for (int i = RCAREA_KOKIRI_FOREST; i < RCAREA_INVALID; i++) {
checksByArea.emplace(static_cast<RandomizerCheckArea>(i), std::vector<RandomizerCheck>());
}
}
}
void RecalculateAreaTotals() {
for (auto [rcArea, checks] : checksByArea) {
if (rcArea == RCAREA_INVALID) {
return;
}
areaChecksGotten[rcArea] = 0;
areaCheckTotals[rcArea] = 0;
for (auto rc : checks) {
if (!IsVisibleInCheckTracker(rc)) {
continue;
}
areaCheckTotals[rcArea]++;
if (gSaveContext.checkTrackerData[rc].skipped || gSaveContext.checkTrackerData[rc].status == RCSHOW_COLLECTED
|| gSaveContext.checkTrackerData[rc].status == RCSHOW_SAVED) {
areaChecksGotten[rcArea]++;
}
}
}
}
void SetCheckCollected(RandomizerCheck rc) {
gSaveContext.checkTrackerData[rc].status = RCSHOW_COLLECTED;
Rando::Location* loc = Rando::StaticData::GetLocation(rc);
if (IsVisibleInCheckTracker(rc)) {
if (!gSaveContext.checkTrackerData[rc].skipped) {
areaChecksGotten[loc->GetArea()]++;
} else {
gSaveContext.checkTrackerData[rc].skipped = false;
}
}
SaveManager::Instance->SaveSection(gSaveContext.fileNum, sectionId, true);
doAreaScroll = true;
UpdateOrdering(loc->GetArea());
UpdateInventoryChecks();
}
bool IsAreaScene(SceneID sceneNum) {
switch (sceneNum) {
case SCENE_HYRULE_FIELD:
case SCENE_KAKARIKO_VILLAGE:
case SCENE_GRAVEYARD:
case SCENE_ZORAS_RIVER:
case SCENE_KOKIRI_FOREST:
case SCENE_SACRED_FOREST_MEADOW:
case SCENE_LAKE_HYLIA:
case SCENE_ZORAS_DOMAIN:
case SCENE_ZORAS_FOUNTAIN:
case SCENE_GERUDO_VALLEY:
case SCENE_LOST_WOODS:
case SCENE_DESERT_COLOSSUS:
case SCENE_GERUDOS_FORTRESS:
case SCENE_HAUNTED_WASTELAND:
case SCENE_HYRULE_CASTLE:
case SCENE_DEATH_MOUNTAIN_TRAIL:
case SCENE_DEATH_MOUNTAIN_CRATER:
case SCENE_GORON_CITY:
case SCENE_LON_LON_RANCH:
case SCENE_DEKU_TREE:
case SCENE_DODONGOS_CAVERN:
case SCENE_JABU_JABU:
case SCENE_FOREST_TEMPLE:
case SCENE_FIRE_TEMPLE:
case SCENE_WATER_TEMPLE:
case SCENE_SPIRIT_TEMPLE:
case SCENE_SHADOW_TEMPLE:
case SCENE_BOTTOM_OF_THE_WELL:
case SCENE_ICE_CAVERN:
case SCENE_GERUDO_TRAINING_GROUND:
case SCENE_GANONS_TOWER:
case SCENE_INSIDE_GANONS_CASTLE:
case SCENE_BACK_ALLEY_DAY:
case SCENE_BACK_ALLEY_NIGHT:
case SCENE_MARKET_DAY:
case SCENE_MARKET_NIGHT:
case SCENE_MARKET_RUINS:
return true;
default:
return false;
}
}
RandomizerCheckArea AreaFromEntranceGroup[] = {
RCAREA_INVALID,
RCAREA_KOKIRI_FOREST,
RCAREA_LOST_WOODS,
RCAREA_SACRED_FOREST_MEADOW,
RCAREA_KAKARIKO_VILLAGE,
RCAREA_GRAVEYARD,
RCAREA_DEATH_MOUNTAIN_TRAIL,
RCAREA_DEATH_MOUNTAIN_CRATER,
RCAREA_GORON_CITY,
RCAREA_ZORAS_RIVER,
RCAREA_ZORAS_DOMAIN,
RCAREA_ZORAS_FOUNTAIN,
RCAREA_HYRULE_FIELD,
RCAREA_LON_LON_RANCH,
RCAREA_LAKE_HYLIA,
RCAREA_GERUDO_VALLEY,
RCAREA_WASTELAND,
RCAREA_MARKET,
RCAREA_HYRULE_CASTLE,
};
RandomizerCheckArea GetCheckArea() {
auto scene = static_cast<SceneID>(gPlayState->sceneNum);
bool grottoScene = (scene == SCENE_GROTTOS || scene == SCENE_FAIRYS_FOUNTAIN);
const EntranceData* ent = GetEntranceData(grottoScene ? ENTRANCE_RANDO_GROTTO_EXIT_START + GetCurrentGrottoId() : gSaveContext.entranceIndex);
RandomizerCheckArea area = RCAREA_INVALID;
if (ent != nullptr && !IsAreaScene(scene) && ent->type != ENTRANCE_TYPE_DUNGEON) {
if (ent->source == "Desert Colossus" || ent->destination == "Desert Colossus") {
area = RCAREA_DESERT_COLOSSUS;
} else if (ent->source == "Gerudo Fortress" || ent->destination == "Gerudo Fortress") {
area = RCAREA_GERUDO_FORTRESS;
} else {
area = AreaFromEntranceGroup[ent->dstGroup];
}
}
if (area == RCAREA_INVALID) {
if (grottoScene && (GetCurrentGrottoId() == -1) && (OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_SHUFFLE_GROTTO_ENTRANCES) == RO_GENERIC_OFF)) {
area = previousArea;
} else {
area = RandomizerCheckObjects::GetRCAreaBySceneID(scene);
}
}
return area;
}
bool vector_contains_scene(std::vector<SceneID> vec, const int16_t scene) {
return std::any_of(vec.begin(), vec.end(), [&](const auto& x) { return x == scene; });
}
std::vector<SceneID> skipScenes = {SCENE_GANON_BOSS, SCENE_GANONS_TOWER_COLLAPSE_EXTERIOR, SCENE_GANON_BOSS, SCENE_INSIDE_GANONS_CASTLE_COLLAPSE, SCENE_GANONS_TOWER_COLLAPSE_INTERIOR};
void ClearAreaChecksAndTotals() {
for (auto& [rcArea, vec] : checksByArea) {
vec.clear();
areaChecksGotten[rcArea] = 0;
areaCheckTotals[rcArea] = 0;
}
}
void SetShopSeen(uint32_t sceneNum, bool prices) {
RandomizerCheck start = startingShopItem.find(sceneNum)->second;
if (sceneNum == SCENE_POTION_SHOP_KAKARIKO && !LINK_IS_ADULT) {
return;
}
if (GetCheckArea() == RCAREA_KAKARIKO_VILLAGE && sceneNum == SCENE_BAZAAR) {
start = RC_KAK_BAZAAR_ITEM_1;
}
bool statusChanged = false;
for (int i = start; i < start + 8; i++) {
if (gSaveContext.checkTrackerData[i].status == RCSHOW_UNCHECKED) {
gSaveContext.checkTrackerData[i].status = RCSHOW_SEEN;
statusChanged = true;
}
}
if (statusChanged) {
SaveManager::Instance->SaveSection(gSaveContext.fileNum, sectionId, true);
}
}
bool HasItemBeenCollected(RandomizerCheck rc) {
if (gPlayState == nullptr) {
return false;
}
Rando::Location* x = Rando::StaticData::GetLocation(rc);
Rando::SpoilerCollectionCheck check = x->GetCollectionCheck();
auto flag = check.flag;
auto scene = check.scene;
auto type = check.type;
switch (type) {
case SpoilerCollectionCheckType::SPOILER_CHK_ALWAYS_COLLECTED:
return true;
case SpoilerCollectionCheckType::SPOILER_CHK_CHEST:
return (gPlayState->sceneNum == scene && gPlayState->actorCtx.flags.chest & (1 << flag)) ||
gSaveContext.sceneFlags[scene].chest & (1 << flag);
case SpoilerCollectionCheckType::SPOILER_CHK_COLLECTABLE:
return (gPlayState->sceneNum == scene && gPlayState->actorCtx.flags.collect & (1 << flag)) ||
gSaveContext.sceneFlags[scene].collect & (1 << flag);
case SpoilerCollectionCheckType::SPOILER_CHK_MERCHANT:
case SpoilerCollectionCheckType::SPOILER_CHK_SHOP_ITEM:
case SpoilerCollectionCheckType::SPOILER_CHK_COW:
case SpoilerCollectionCheckType::SPOILER_CHK_FISH:
case SpoilerCollectionCheckType::SPOILER_CHK_SCRUB:
case SpoilerCollectionCheckType::SPOILER_CHK_RANDOMIZER_INF:
case SpoilerCollectionCheckType::SPOILER_CHK_MASTER_SWORD:
return Flags_GetRandomizerInf(OTRGlobals::Instance->gRandomizer->GetRandomizerInfFromCheck(rc));
case SpoilerCollectionCheckType::SPOILER_CHK_EVENT_CHK_INF:
return gSaveContext.eventChkInf[flag / 16] & (0x01 << flag % 16);
case SpoilerCollectionCheckType::SPOILER_CHK_GERUDO_MEMBERSHIP_CARD:
return CHECK_FLAG_ALL(gSaveContext.eventChkInf[0x09], 0x0F);
case SpoilerCollectionCheckType::SPOILER_CHK_GOLD_SKULLTULA:
return GET_GS_FLAGS(scene) & flag;
case SpoilerCollectionCheckType::SPOILER_CHK_INF_TABLE:
return gSaveContext.infTable[scene] & INDEX_TO_16BIT_LITTLE_ENDIAN_BITMASK(flag);
case SpoilerCollectionCheckType::SPOILER_CHK_ITEM_GET_INF:
return gSaveContext.itemGetInf[flag / 16] & INDEX_TO_16BIT_LITTLE_ENDIAN_BITMASK(flag);
case SpoilerCollectionCheckType::SPOILER_CHK_MAGIC_BEANS:
return BEANS_BOUGHT >= 10;
case SpoilerCollectionCheckType::SPOILER_CHK_NONE:
return false;
case SpoilerCollectionCheckType::SPOILER_CHK_GRAVEDIGGER:
// Gravedigger has a fix in place that means one of two save locations. Check both.
return (gSaveContext.itemGetInf[1] & 0x1000) || // vanilla flag
((IS_RANDO || CVarGetInteger("gGravediggingTourFix", 0)) &&
gSaveContext.sceneFlags[scene].collect & (1 << flag) || (gPlayState->actorCtx.flags.collect & (1 << flag))); // rando/fix flag
default:
return false;
}
return false;
}
void CheckTrackerLoadGame(int32_t fileNum) {
LoadSettings();
TrySetAreas();
for (auto& entry : Rando::StaticData::GetLocationTable()) {
RandomizerCheck rc = entry.GetRandomizerCheck();
RandomizerCheckTrackerData rcTrackerData = gSaveContext.checkTrackerData[rc];
if (rc == RC_UNKNOWN_CHECK || rc == RC_MAX || rc == RC_LINKS_POCKET ||
!Rando::StaticData::GetLocation(rc) != RC_UNKNOWN_CHECK) {
continue;
}
Rando::Location* entry2;
if (rc == RC_GIFT_FROM_SAGES && !IS_RANDO) {
entry2 = Rando::StaticData::GetLocation(rc);
} else {
entry2 = Rando::StaticData::GetLocation(rc);
}
checksByArea.find(entry2->GetArea())->second.push_back(entry2->GetRandomizerCheck());
if (IsVisibleInCheckTracker(entry2->GetRandomizerCheck())) {
areaCheckTotals[entry2->GetArea()]++;
if (rcTrackerData.status == RCSHOW_SAVED || rcTrackerData.skipped) {
areaChecksGotten[entry2->GetArea()]++;
}
}
if (areaChecksGotten[entry2->GetArea()] != 0 || RandomizerCheckObjects::AreaIsOverworld(entry2->GetArea())) {
areasSpoiled |= (1 << entry2->GetArea());
}
// Create check name overrides for child pond fish if age split is disabled
if (fishsanityMode != RO_FISHSANITY_OFF && fishsanityMode != RO_FISHSANITY_OVERWORLD && entry.GetRCType() == RCTYPE_FISH && entry.GetScene() == SCENE_FISHING_POND &&
entry.GetActorParams() != 116 && !fishsanityAgeSplit) {
if (entry.GetShortName().starts_with("Child")) {
checkNameOverrides[rc] = entry.GetShortName().substr(6);
}
}
}
if (OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_LINKS_POCKET) != RO_LINKS_POCKET_NOTHING && IS_RANDO) {
s8 startingAge = OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_STARTING_AGE);
RandomizerCheckArea startingArea;
switch (startingAge) {
case RO_AGE_CHILD:
startingArea = RCAREA_KOKIRI_FOREST;
break;
case RO_AGE_ADULT:
startingArea = RCAREA_MARKET;
break;
default:
startingArea = RCAREA_KOKIRI_FOREST;
break;
}
checksByArea.find(startingArea)->second.push_back(RC_LINKS_POCKET);
areaChecksGotten[startingArea]++;
areaCheckTotals[startingArea]++;
}
showVOrMQ = (OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_MQ_DUNGEON_RANDOM) == RO_MQ_DUNGEONS_RANDOM_NUMBER ||
(OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_MQ_DUNGEON_RANDOM) == RO_MQ_DUNGEONS_SET_NUMBER &&
OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_MQ_DUNGEON_COUNT) < 12));
LinksPocket();
SongFromImpa();
GiftFromSages();
initialized = true;
UpdateAllOrdering();
UpdateInventoryChecks();
}
void CheckTrackerShopSlotChange(uint8_t cursorSlot, int16_t basePrice) {
if (gPlayState->sceneNum == SCENE_HAPPY_MASK_SHOP) { // Happy Mask Shop is not used in rando, so is not tracked
return;
}
auto slot = startingShopItem.find(gPlayState->sceneNum)->second + cursorSlot;
if (GetCheckArea() == RCAREA_KAKARIKO_VILLAGE && gPlayState->sceneNum == SCENE_BAZAAR) {
slot = RC_KAK_BAZAAR_ITEM_1 + cursorSlot;
}
auto status = gSaveContext.checkTrackerData[slot].status;
if (status == RCSHOW_SEEN) {
gSaveContext.checkTrackerData[slot].status = RCSHOW_IDENTIFIED;
gSaveContext.checkTrackerData[slot].price = basePrice;
SaveManager::Instance->SaveSection(gSaveContext.fileNum, sectionId, true);
}
}
void CheckTrackerTransition(uint32_t sceneNum) {
if (!GameInteractor::IsSaveLoaded()) {
return;
}
doAreaScroll = true;
previousArea = currentArea;
currentArea = GetCheckArea();
switch (sceneNum) {
case SCENE_KOKIRI_SHOP:
case SCENE_BAZAAR:
case SCENE_POTION_SHOP_MARKET:
case SCENE_BOMBCHU_SHOP:
case SCENE_POTION_SHOP_KAKARIKO:
case SCENE_GORON_SHOP:
case SCENE_ZORA_SHOP:
SetShopSeen(sceneNum, false);
break;
}
}
void CheckTrackerFrame() {
if (!GameInteractor::IsSaveLoaded()) {
return;
}
// TODO: Move to OnAmmoChange hook once it gets added.
if (gSaveContext.checkTrackerData[RC_ZR_MAGIC_BEAN_SALESMAN].status != RCSHOW_COLLECTED &&
gSaveContext.checkTrackerData[RC_ZR_MAGIC_BEAN_SALESMAN].status != RCSHOW_SAVED) {
if (BEANS_BOUGHT >= 10) {
SetCheckCollected(RC_ZR_MAGIC_BEAN_SALESMAN);
}
}
}
void CheckTrackerItemReceive(GetItemEntry giEntry) {
if (!GameInteractor::IsSaveLoaded() || vector_contains_scene(skipScenes, gPlayState->sceneNum)) {
return;
}
auto scene = static_cast<SceneID>(gPlayState->sceneNum);
// Vanilla special item checks
if (!IS_RANDO) {
if (giEntry.itemId == ITEM_SHIELD_DEKU) {
SetCheckCollected(RC_KF_SHOP_ITEM_1);
return;
}else if (giEntry.itemId == ITEM_KOKIRI_EMERALD) {
SetCheckCollected(RC_QUEEN_GOHMA);
return;
} else if (giEntry.itemId == ITEM_GORON_RUBY) {
SetCheckCollected(RC_KING_DODONGO);
return;
} else if (giEntry.itemId == ITEM_ZORA_SAPPHIRE) {
SetCheckCollected(RC_BARINADE);
return;
} else if (giEntry.itemId == ITEM_MEDALLION_FOREST) {
SetCheckCollected(RC_PHANTOM_GANON);
return;
} else if (giEntry.itemId == ITEM_MEDALLION_FIRE) {
SetCheckCollected(RC_VOLVAGIA);
return;
} else if (giEntry.itemId == ITEM_MEDALLION_WATER) {
SetCheckCollected(RC_MORPHA);
return;
} else if (giEntry.itemId == ITEM_MEDALLION_SHADOW) {
SetCheckCollected(RC_BONGO_BONGO);
return;
} else if (giEntry.itemId == ITEM_MEDALLION_SPIRIT) {
SetCheckCollected(RC_TWINROVA);
return;
} else if (giEntry.itemId == ITEM_MEDALLION_LIGHT) {
SetCheckCollected(RC_GIFT_FROM_SAGES);
return;
} else if (giEntry.itemId == ITEM_SONG_EPONA) {
SetCheckCollected(RC_SONG_FROM_MALON);
return;
} else if (giEntry.itemId == ITEM_SONG_SARIA) {
SetCheckCollected(RC_SONG_FROM_SARIA);
return;
} else if (giEntry.itemId == ITEM_BEAN) {
SetCheckCollected(RC_ZR_MAGIC_BEAN_SALESMAN);
return;
} else if (giEntry.itemId == ITEM_BRACELET) {
SetCheckCollected(RC_GC_DARUNIAS_JOY);
return;
}/* else if (giEntry.itemId == ITEM_SONG_SUN) {
SetCheckCollected(RC_SONG_FROM_ROYAL_FAMILYS_TOMB);
return;
} else if (giEntry.itemId == ITEM_SONG_TIME) {
SetCheckCollected(RC_SONG_FROM_OCARINA_OF_TIME);
return;
} else if (giEntry.itemId == ITEM_SONG_STORMS) {
SetCheckCollected(RC_SONG_FROM_WINDMILL);
return;
} else if (giEntry.itemId == ITEM_SONG_MINUET) {
SetCheckCollected(RC_SHEIK_IN_FOREST);
return;
} else if (giEntry.itemId == ITEM_SONG_BOLERO) {
SetCheckCollected(RC_SHEIK_IN_CRATER);
return;
} else if (giEntry.itemId == ITEM_SONG_SERENADE) {
SetCheckCollected(RC_SHEIK_IN_ICE_CAVERN);
return;
} else if (giEntry.itemId == ITEM_SONG_NOCTURNE) {
SetCheckCollected(RC_SHEIK_IN_KAKARIKO);
return;
} else if (giEntry.itemId == ITEM_SONG_REQUIEM) {
SetCheckCollected(RC_SHEIK_AT_COLOSSUS);
return;
} else if (giEntry.itemId == ITEM_SONG_PRELUDE) {
SetCheckCollected(RC_SHEIK_AT_TEMPLE);
return;
}*/
}
}
void CheckTrackerSceneFlagSet(int16_t sceneNum, int16_t flagType, int32_t flag) {
if (flagType != FLAG_SCENE_TREASURE && flagType != FLAG_SCENE_COLLECTIBLE) {
return;
}
if (sceneNum == SCENE_GRAVEYARD && flag == 0x19 && flagType == FLAG_SCENE_COLLECTIBLE) { // Gravedigging tour special case
SetCheckCollected(RC_GRAVEYARD_DAMPE_GRAVEDIGGING_TOUR);
return;
}
for (auto& loc : Rando::StaticData::GetLocationTable()) {
if (!IsVisibleInCheckTracker(loc.GetRandomizerCheck())) {
continue;
}
SpoilerCollectionCheckType checkMatchType = flagType == FLAG_SCENE_TREASURE ? SpoilerCollectionCheckType::SPOILER_CHK_CHEST : SpoilerCollectionCheckType::SPOILER_CHK_COLLECTABLE;
Rando::SpoilerCollectionCheck scCheck = loc.GetCollectionCheck();
if (scCheck.scene == sceneNum && scCheck.flag == flag && scCheck.type == checkMatchType) {
SetCheckCollected(loc.GetRandomizerCheck());
return;
}
}
}
void CheckTrackerFlagSet(int16_t flagType, int32_t flag) {
SpoilerCollectionCheckType checkMatchType = SpoilerCollectionCheckType::SPOILER_CHK_NONE;
switch (flagType) {
case FLAG_GS_TOKEN:
checkMatchType = SpoilerCollectionCheckType::SPOILER_CHK_GOLD_SKULLTULA;
break;
case FLAG_EVENT_CHECK_INF:
if ((flag == EVENTCHKINF_CARPENTERS_FREE(0) || flag == EVENTCHKINF_CARPENTERS_FREE(1) ||
flag == EVENTCHKINF_CARPENTERS_FREE(2) || flag == EVENTCHKINF_CARPENTERS_FREE(3))
&& GET_EVENTCHKINF_CARPENTERS_FREE_ALL()) {
SetCheckCollected(RC_GF_GERUDO_MEMBERSHIP_CARD);
return;
}
checkMatchType = SpoilerCollectionCheckType::SPOILER_CHK_EVENT_CHK_INF;
break;
case FLAG_INF_TABLE:
if (flag == INFTABLE_190) {
SetCheckCollected(RC_GF_HBA_1000_POINTS);
return;
} else if (flag == INFTABLE_11E) {
SetCheckCollected(RC_GC_ROLLING_GORON_AS_CHILD);
return;
} else if (flag == INFTABLE_GORON_CITY_DOORS_UNLOCKED) {
SetCheckCollected(RC_GC_ROLLING_GORON_AS_ADULT);
return;
} else if (flag == INFTABLE_139) {
SetCheckCollected(RC_ZD_KING_ZORA_THAWED);
return;
} else if (flag == INFTABLE_191) {
SetCheckCollected(RC_MARKET_LOST_DOG);
return;
}
if (!IS_RANDO) {
if (flag == INFTABLE_192) {
SetCheckCollected(RC_LW_DEKU_SCRUB_NEAR_BRIDGE);
return;
} else if (flag == INFTABLE_193) {
SetCheckCollected(RC_LW_DEKU_SCRUB_GROTTO_FRONT);
return;
}
}
break;
case FLAG_ITEM_GET_INF:
if (!IS_RANDO) {
if (flag == ITEMGETINF_OBTAINED_STICK_UPGRADE_FROM_STAGE) {
SetCheckCollected(RC_DEKU_THEATER_SKULL_MASK);
return;
} else if (flag == ITEMGETINF_OBTAINED_NUT_UPGRADE_FROM_STAGE) {
SetCheckCollected(RC_DEKU_THEATER_MASK_OF_TRUTH);
return;
} else if (flag == ITEMGETINF_0B) {
SetCheckCollected(RC_HF_DEKU_SCRUB_GROTTO);
return;
}
}
checkMatchType = SpoilerCollectionCheckType::SPOILER_CHK_ITEM_GET_INF;
break;
case FLAG_RANDOMIZER_INF:
checkMatchType = SpoilerCollectionCheckType::SPOILER_CHK_RANDOMIZER_INF;
break;
}
if (checkMatchType == SpoilerCollectionCheckType::SPOILER_CHK_NONE) {
return;
}
for (auto& loc : Rando::StaticData::GetLocationTable()) {
if ((!IS_RANDO && ((loc.GetQuest() == RCQUEST_MQ && !IS_MASTER_QUEST) ||
(loc.GetQuest() == RCQUEST_VANILLA && IS_MASTER_QUEST))) ||
(IS_RANDO && !(OTRGlobals::Instance->gRandoContext->GetDungeons()->GetDungeonFromScene(loc.GetScene()) == nullptr) &&
((OTRGlobals::Instance->gRandoContext->GetDungeons()->GetDungeonFromScene(loc.GetScene())->IsMQ() &&
loc.GetQuest() == RCQUEST_VANILLA) ||
OTRGlobals::Instance->gRandoContext->GetDungeons()->GetDungeonFromScene(loc.GetScene())->IsVanilla() &&
loc.GetQuest() == RCQUEST_MQ))) {
continue;
}
Rando::SpoilerCollectionCheck scCheck = loc.GetCollectionCheck();
SpoilerCollectionCheckType scCheckType = scCheck.type;
if (checkMatchType == SpoilerCollectionCheckType::SPOILER_CHK_RANDOMIZER_INF &&
(scCheckType == SpoilerCollectionCheckType::SPOILER_CHK_MERCHANT ||
scCheckType == SpoilerCollectionCheckType::SPOILER_CHK_SHOP_ITEM ||
scCheckType == SpoilerCollectionCheckType::SPOILER_CHK_COW ||
scCheckType == SpoilerCollectionCheckType::SPOILER_CHK_FISH ||
scCheckType == SpoilerCollectionCheckType::SPOILER_CHK_SCRUB ||
scCheckType == SpoilerCollectionCheckType::SPOILER_CHK_MASTER_SWORD ||
scCheckType == SpoilerCollectionCheckType::SPOILER_CHK_RANDOMIZER_INF)) {
if (flag == OTRGlobals::Instance->gRandomizer->GetRandomizerInfFromCheck(loc.GetRandomizerCheck())) {
SetCheckCollected(loc.GetRandomizerCheck());
return;
}
continue;
}
int16_t checkFlag = scCheck.flag;
if (checkMatchType == SpoilerCollectionCheckType::SPOILER_CHK_GOLD_SKULLTULA) {
checkFlag = loc.GetActorParams();
}
if (checkFlag == flag && scCheck.type == checkMatchType) {
SetCheckCollected(loc.GetRandomizerCheck());
return;
}
}
}
void InitTrackerData(bool isDebug) {
TrySetAreas();
for (auto& loc : Rando::StaticData::GetLocationTable()) {
if (loc.GetRandomizerCheck() != RC_UNKNOWN_CHECK && loc.GetRandomizerCheck() != RC_MAX) {
DefaultCheckData(loc.GetRandomizerCheck());
}
}
UpdateAllOrdering();
}
void SaveTrackerData(SaveContext* saveContext, int sectionID, bool gameSave) {
SaveManager::Instance->SaveArray("checks", ARRAY_COUNT(saveContext->checkTrackerData), [&](size_t i) {
if (saveContext->checkTrackerData[i].status == RCSHOW_COLLECTED) {
if (gameSave) {
gSaveContext.checkTrackerData[i].status = saveContext->checkTrackerData[i].status = RCSHOW_SAVED;
UpdateAllOrdering();
UpdateInventoryChecks();
} else {
saveContext->checkTrackerData[i].status = RCSHOW_SCUMMED;
}
}
SaveManager::Instance->SaveStruct("", [&]() {
SaveManager::Instance->SaveData("status", saveContext->checkTrackerData[i].status);
SaveManager::Instance->SaveData("skipped", saveContext->checkTrackerData[i].skipped);
SaveManager::Instance->SaveData("price", saveContext->checkTrackerData[i].price);
SaveManager::Instance->SaveData("hintItem", saveContext->checkTrackerData[i].hintItem);
});
});
}
void SaveFile(SaveContext* saveContext, int sectionID, bool fullSave) {
SaveTrackerData(saveContext, sectionID, fullSave);
}
void LoadFile() {
SaveManager::Instance->LoadArray("checks", RC_MAX, [](size_t i) {
SaveManager::Instance->LoadStruct("", [&]() {
SaveManager::Instance->LoadData("status", gSaveContext.checkTrackerData[i].status);
SaveManager::Instance->LoadData("skipped", gSaveContext.checkTrackerData[i].skipped);
SaveManager::Instance->LoadData("price", gSaveContext.checkTrackerData[i].price);
SaveManager::Instance->LoadData("hintItem", gSaveContext.checkTrackerData[i].hintItem);
});
});
}
void Teardown() {
initialized = false;
ClearAreaChecksAndTotals();
checksByArea.clear();
areasSpoiled = 0;
lastLocationChecked = RC_UNKNOWN_CHECK;
}
void UpdateCheck(uint32_t check, RandomizerCheckTrackerData data) {
auto area = Rando::StaticData::GetLocation(static_cast<RandomizerCheck>(check))->GetArea();
if ((!gSaveContext.checkTrackerData[check].skipped && data.skipped) ||
((gSaveContext.checkTrackerData[check].status != RCSHOW_COLLECTED && gSaveContext.checkTrackerData[check].status != RCSHOW_SAVED) &&
(data.status == RCSHOW_COLLECTED || data.status == RCSHOW_SAVED))) {
areaChecksGotten[area]++;
} else if ((gSaveContext.checkTrackerData[check].skipped && !data.skipped) ||
((gSaveContext.checkTrackerData[check].status == RCSHOW_COLLECTED || gSaveContext.checkTrackerData[check].status == RCSHOW_SAVED) &&
(data.status != RCSHOW_COLLECTED && data.status != RCSHOW_SAVED))) {
areaChecksGotten[area]--;
}
gSaveContext.checkTrackerData[check] = data;
UpdateOrdering(area);
}
void CheckTrackerWindow::DrawElement() {
ImGui::SetNextWindowSize(ImVec2(400, 540), ImGuiCond_FirstUseEver);
if (!initialized && (gPlayState == nullptr || gSaveContext.fileNum < 0 || gSaveContext.fileNum > 2)) {
return;
}
if (CVarGetInteger("gCheckTrackerWindowType", TRACKER_WINDOW_WINDOW) == TRACKER_WINDOW_FLOATING) {
if (CVarGetInteger("gCheckTrackerShowOnlyPaused", 0) && (gPlayState == nullptr || gPlayState->pauseCtx.state == 0)) {
return;
}
if (CVarGetInteger("gCheckTrackerDisplayType", TRACKER_DISPLAY_ALWAYS) == TRACKER_DISPLAY_COMBO_BUTTON) {
int comboButton1Mask = buttons[CVarGetInteger("gCheckTrackerComboButton1", TRACKER_COMBO_BUTTON_L)];
int comboButton2Mask = buttons[CVarGetInteger("gCheckTrackerComboButton2", TRACKER_COMBO_BUTTON_R)];
OSContPad* trackerButtonsPressed = LUS::Context::GetInstance()->GetControlDeck()->GetPads();
bool comboButtonsHeld = trackerButtonsPressed != nullptr &&
trackerButtonsPressed[0].button & comboButton1Mask &&
trackerButtonsPressed[0].button & comboButton2Mask;
if (!comboButtonsHeld) {
return;
}
}
}
BeginFloatWindows("Check Tracker", mIsVisible, ImGuiWindowFlags_NoScrollbar);
if (!GameInteractor::IsSaveLoaded()) {
ImGui::Text("Waiting for file load..."); //TODO Language
EndFloatWindows();
return;
}
SceneID sceneId = SCENE_ID_MAX;
if (gPlayState != nullptr) {
sceneId = (SceneID)gPlayState->sceneNum;
}
areasSpoiled |= (1 << currentArea);
//Quick Options
#ifdef __WIIU__
float headerHeight = 40.0f;
#else
float headerHeight = 20.0f;
#endif
ImVec2 size = ImGui::GetContentRegionMax();
size.y -= headerHeight;
if (!ImGui::BeginTable("Check Tracker", 1, 0, size)) {
EndFloatWindows();
return;
}
ImGui::TableNextRow(0, headerHeight);
ImGui::TableNextColumn();
UIWidgets::EnhancementCheckbox(
"Show Hidden Items", "gCheckTrackerOptionShowHidden", false,
"When active, items will show hidden checks by default when updated to this state.");
UIWidgets::PaddedSeparator();
if (ImGui::Button("Expand All")) {
optCollapseAll = false;
optExpandAll = true;
doAreaScroll = true;
}
ImGui::SameLine();
if (ImGui::Button("Collapse All")) {
optExpandAll = false;
optCollapseAll = true;
}
UIWidgets::PaddedSeparator();
//Checks Section Lead-in
ImGui::TableNextRow();
ImGui::TableNextColumn();
size = ImGui::GetContentRegionAvail();
if (!ImGui::BeginTable("CheckTracker##Checks", 1, ImGuiTableFlags_ScrollY, size)) {
ImGui::EndTable();
EndFloatWindows();
return;
}
ImGui::TableNextRow();
ImGui::TableNextColumn();
//Prep for loop
RainbowTick();
bool doDraw = false;
bool thisAreaFullyChecked = false;
bool showHidden = CVarGetInteger("gCheckTrackerOptionShowHidden", 0);
bool hideIncomplete = CVarGetInteger("gCheckTrackerAreaIncompleteHide", 0);
bool hideComplete = CVarGetInteger("gCheckTrackerAreaCompleteHide", 0);
bool collapseLogic;
bool doingCollapseOrExpand = optExpandAll || optCollapseAll;
bool isThisAreaSpoiled;
RandomizerCheckArea lastArea = RCAREA_INVALID;
Color_RGBA8 areaCompleteColor = CVarGetColor("gCheckTrackerAreaMainCompleteColor", Color_Main_Default);
Color_RGBA8 areaIncompleteColor = CVarGetColor("gCheckTrackerAreaMainIncompleteColor", Color_Main_Default);
Color_RGBA8 extraCompleteColor = CVarGetColor("gCheckTrackerAreaExtraCompleteColor", Color_Area_Complete_Extra_Default);
Color_RGBA8 extraIncompleteColor = CVarGetColor("gCheckTrackerAreaExtraIncompleteColor", Color_Area_Incomplete_Extra_Default);
Color_RGBA8 mainColor;
Color_RGBA8 extraColor;
std::string stemp;
s32 areaMask = 1;
for (auto& [rcArea, checks] : checksByArea) {
RandomizerCheckArea thisArea = currentArea;
thisAreaFullyChecked = (areaChecksGotten[rcArea] == areaCheckTotals[rcArea]);
//Last Area needs to be cleaned up
if (lastArea != RCAREA_INVALID && doDraw) {
UIWidgets::PaddedSeparator();
}
lastArea = rcArea;
if (previousShowHidden != showHidden) {
previousShowHidden = showHidden;
doAreaScroll = true;
}
if (!showHidden && (
hideComplete && thisAreaFullyChecked ||
hideIncomplete && !thisAreaFullyChecked
)) {
doDraw = false;
} else {
//Get the colour for the area
if (thisAreaFullyChecked) {
mainColor = areaCompleteColor;
extraColor = extraCompleteColor;
} else {
mainColor = areaIncompleteColor;
extraColor = extraIncompleteColor;
}
//Draw the area
collapseLogic = !thisAreaFullyChecked;
if (doingCollapseOrExpand) {
if (optExpandAll) {
collapseLogic = true;
} else if (optCollapseAll) {
collapseLogic = false;
}
}
stemp = RandomizerCheckObjects::GetRCAreaName(rcArea) + "##TreeNode";
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(mainColor.r / 255.0f, mainColor.g / 255.0f,
mainColor.b / 255.0f, mainColor.a / 255.0f));
if (doingCollapseOrExpand) {
ImGui::SetNextItemOpen(collapseLogic, ImGuiCond_Always);
} else {
ImGui::SetNextItemOpen(!thisAreaFullyChecked, ImGuiCond_Once);
}
doDraw = ImGui::TreeNode(stemp.c_str());
ImGui::PopStyleColor();
ImGui::SameLine();
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(extraColor.r / 255.0f, extraColor.g / 255.0f,
extraColor.b / 255.0f, extraColor.a / 255.0f));
isThisAreaSpoiled = areasSpoiled & areaMask || CVarGetInteger("gCheckTrackerOptionMQSpoilers", 0) || !IS_RANDO ||
OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_MQ_DUNGEON_RANDOM) == RO_MQ_DUNGEONS_NONE ||
OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_MQ_DUNGEON_RANDOM) == RO_MQ_DUNGEONS_SELECTION ||
(OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_MQ_DUNGEON_RANDOM) == RO_MQ_DUNGEONS_SET_NUMBER &&
OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_MQ_DUNGEON_COUNT) == 12);
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]);
}
} else {
ImGui::Text("???");
}
ImGui::PopStyleColor();
//Keep areas loaded between transitions
if (thisArea == rcArea && doAreaScroll) {
ImGui::SetScrollHereY(0.0f);
doAreaScroll = false;
}
for (auto rc : checks) {
if (doDraw && isThisAreaSpoiled && IsVisibleInCheckTracker(rc)) {
DrawLocation(rc);
}
}
if (doDraw) {
ImGui::TreePop();
}
}
areaMask <<= 1;
}
ImGui::EndTable(); //Checks Lead-out
ImGui::EndTable(); //Quick Options Lead-out
EndFloatWindows();
if (doingCollapseOrExpand) {
optCollapseAll = false;
optExpandAll = false;
}
}
// Windowing stuff
void BeginFloatWindows(std::string UniqueName, bool& open, ImGuiWindowFlags flags) {
ImGuiWindowFlags windowFlags = flags;
if (windowFlags == 0) {
windowFlags |=
ImGuiWindowFlags_AlwaysVerticalScrollbar | ImGuiWindowFlags_NoFocusOnAppearing;
}
if (CVarGetInteger("gCheckTrackerWindowType", TRACKER_WINDOW_WINDOW) == TRACKER_WINDOW_FLOATING) {
ImGui::SetNextWindowViewport(ImGui::GetMainViewport()->ID);
windowFlags |= ImGuiWindowFlags_NoDocking | ImGuiWindowFlags_NoNav | ImGuiWindowFlags_NoTitleBar |
ImGuiWindowFlags_NoScrollWithMouse | ImGuiWindowFlags_NoScrollbar;
if (!CVarGetInteger("gCheckTrackerHudEditMode", 0)) {
windowFlags |= ImGuiWindowFlags_NoInputs | ImGuiWindowFlags_NoMove;
}
}
ImGui::PushStyleColor(ImGuiCol_WindowBg, ImVec4(Color_Background.r / 255.0f, Color_Background.g / 255.0f,
Color_Background.b / 255.0f, Color_Background.a / 255.0f));
ImGui::PushStyleColor(ImGuiCol_Border, ImVec4(0, 0, 0, 0));
ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 4.0f);
ImGui::Begin(UniqueName.c_str(), &open, windowFlags);
}
void EndFloatWindows() {
ImGui::PopStyleVar();
ImGui::PopStyleColor();
ImGui::PopStyleColor();
ImGui::End();
}
void LoadSettings() {
//If in randomzer (n64ddFlag), then get the setting and check if in general we should be showing the settings
//If in vanilla, _try_ to show items that at least are needed for 100%
showShops = IS_RANDO ? (
OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_SHOPSANITY) != RO_SHOPSANITY_OFF &&
OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_SHOPSANITY) != RO_SHOPSANITY_ZERO_ITEMS)
: false;
showBeans = IS_RANDO ?
OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_SHUFFLE_MAGIC_BEANS) == RO_GENERIC_YES
: true;
showScrubs = IS_RANDO ?
OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_SHUFFLE_SCRUBS) != RO_SCRUBS_OFF
: false;
showMerchants = IS_RANDO ?
OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_SHUFFLE_MERCHANTS) != RO_SHUFFLE_MERCHANTS_OFF
: true;
showBeehives = IS_RANDO ?
OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_SHUFFLE_BEEHIVES) == RO_GENERIC_YES
: false;
showCows = IS_RANDO ?
OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_SHUFFLE_COWS) == RO_GENERIC_YES
: false;
showAdultTrade = IS_RANDO ?
OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_SHUFFLE_ADULT_TRADE) == RO_GENERIC_YES
: true;
showKokiriSword = IS_RANDO ?
OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_SHUFFLE_KOKIRI_SWORD) == RO_GENERIC_YES
: true;
showMasterSword = IS_RANDO ?
OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_SHUFFLE_MASTER_SWORD) == RO_GENERIC_YES
: true;
showWeirdEgg = IS_RANDO ?
OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_SHUFFLE_WEIRD_EGG) == RO_GENERIC_YES
: true;
showGerudoCard = IS_RANDO ?
OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_SHUFFLE_GERUDO_MEMBERSHIP_CARD) == RO_GENERIC_YES
: true;
showFrogSongRupees = IS_RANDO ?
OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_SHUFFLE_FROG_SONG_RUPEES) == RO_GENERIC_YES
: false;
showStartingMapsCompasses = IS_RANDO ?
OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_SHUFFLE_MAPANDCOMPASS) != RO_DUNGEON_ITEM_LOC_VANILLA
: false;
showKeysanity = IS_RANDO ?
OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_KEYSANITY) != RO_DUNGEON_ITEM_LOC_VANILLA
: false;
showBossKeysanity = IS_RANDO ?
OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_BOSS_KEYSANITY) != RO_DUNGEON_ITEM_LOC_VANILLA
: false;
showGerudoFortressKeys = IS_RANDO ?
OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_GERUDO_KEYS) != RO_GERUDO_KEYS_VANILLA
: false;
showGanonBossKey = IS_RANDO ?
OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_GANONS_BOSS_KEY) != RO_GANON_BOSS_KEY_VANILLA
: false;
showOcarinas = IS_RANDO ?
OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_SHUFFLE_OCARINA) == RO_GENERIC_YES
: false;
show100SkullReward = IS_RANDO ?
OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_SHUFFLE_100_GS_REWARD) == RO_GENERIC_YES
: false;
showLinksPocket = IS_RANDO ? // don't show Link's Pocket if not randomizer, or if rando and pocket is disabled
OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_LINKS_POCKET) != RO_LINKS_POCKET_NOTHING
:false;
hideTriforceCompleted = IS_RANDO ?
OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_TRIFORCE_HUNT) != RO_GENERIC_ON : false;
if (IS_RANDO) {
switch (OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_SHUFFLE_TOKENS)) {
case RO_TOKENSANITY_ALL:
showOverworldTokens = true;
showDungeonTokens = true;
break;
case RO_TOKENSANITY_OVERWORLD:
showOverworldTokens = true;
showDungeonTokens = false;
break;
case RO_TOKENSANITY_DUNGEONS:
showOverworldTokens = false;
showDungeonTokens = true;
break;
default:
showOverworldTokens = false;
showDungeonTokens = false;
break;
}
} else { // Vanilla
showOverworldTokens = true;
showDungeonTokens = true;
}
fortressFast = false;
fortressNormal = false;
switch (OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_GERUDO_FORTRESS)) {
case RO_GF_OPEN:
showGerudoFortressKeys = false;
showGerudoCard = false;
break;
case RO_GF_FAST:
fortressFast = true;
break;
case RO_GF_NORMAL:
fortressNormal = true;
break;
}
fishsanityMode = OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_FISHSANITY);
fishsanityPondCount = OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_FISHSANITY_POND_COUNT);
fishsanityAgeSplit = OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_FISHSANITY_AGE_SPLIT);
}
bool IsVisibleInCheckTracker(RandomizerCheck rc) {
Rando::Location* loc = Rando::StaticData::GetLocation(rc);
if (IS_RANDO && OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_LOGIC_RULES) != RO_LOGIC_VANILLA) {
return
(loc->GetArea() != RCAREA_INVALID) && // don't show Invalid locations
(loc->GetRCType() != RCTYPE_GOSSIP_STONE) && //TODO: Don't show hints until tracker supports them
(loc->GetRCType() != RCTYPE_CHEST_GAME) && // don't show non final reward chest game checks until we support shuffling them
(rc != RC_HC_ZELDAS_LETTER) && // don't show zeldas letter until we support shuffling it
(rc != RC_LINKS_POCKET || showLinksPocket) &&
(!RandomizerCheckObjects::AreaIsDungeon(loc->GetArea()) ||
loc->GetQuest() == RCQUEST_BOTH ||
loc->GetQuest() == RCQUEST_MQ && OTRGlobals::Instance->gRandoContext->GetDungeons()->GetDungeonFromScene(loc->GetScene())->IsMQ() ||
loc->GetQuest() == RCQUEST_VANILLA && OTRGlobals::Instance->gRandoContext->GetDungeons()->GetDungeonFromScene(loc->GetScene())->IsVanilla()
) &&
(loc->GetRCType() != RCTYPE_SHOP || (showShops && (!hideShopRightChecks || hideShopRightChecks && loc->GetActorParams() > 0x03))) &&
(loc->GetRandomizerCheck() != RC_MARKET_BOMBCHU_BOWLING_BOMBCHUS) &&
(rc != RC_TRIFORCE_COMPLETED || !hideTriforceCompleted) &&
(rc != RC_GIFT_FROM_SAGES || !IS_RANDO) &&
(loc->GetRCType() != RCTYPE_SCRUB ||
showScrubs ||
rc == RC_LW_DEKU_SCRUB_NEAR_BRIDGE || // The 3 scrubs that are always randomized
rc == RC_HF_DEKU_SCRUB_GROTTO ||
rc == RC_LW_DEKU_SCRUB_GROTTO_FRONT
) &&
(loc->GetRCType() != RCTYPE_MERCHANT || showMerchants) &&
(loc->GetRCType() != RCTYPE_BEEHIVE || showBeehives) &&
(loc->GetRCType() != RCTYPE_OCARINA || showOcarinas) &&
(loc->GetRCType() != RCTYPE_SKULL_TOKEN || alwaysShowGS ||
(showOverworldTokens && RandomizerCheckObjects::AreaIsOverworld(loc->GetArea())) ||
(showDungeonTokens && RandomizerCheckObjects::AreaIsDungeon(loc->GetArea()))
) &&
(loc->GetRCType() != RCTYPE_COW || showCows) &&
(loc->GetRCType() != RCTYPE_FISH || OTRGlobals::Instance->gRandoContext->GetFishsanity()->GetFishLocationIncluded(loc)) &&
(loc->GetRCType() != RCTYPE_ADULT_TRADE ||
showAdultTrade ||
rc == RC_KAK_ANJU_AS_ADULT || // adult trade checks that are always shuffled
rc == RC_DMT_TRADE_CLAIM_CHECK // even when shuffle adult trade is off
) &&
(rc != RC_KF_KOKIRI_SWORD_CHEST || showKokiriSword) &&
(rc != RC_TOT_MASTER_SWORD || showMasterSword) &&
(rc != RC_ZR_MAGIC_BEAN_SALESMAN || showBeans) &&
(rc != RC_HC_MALON_EGG || showWeirdEgg) &&
(loc->GetRCType() != RCTYPE_FROG_SONG || showFrogSongRupees) &&
(loc->GetRCType() != RCTYPE_MAP_COMPASS || showStartingMapsCompasses) &&
(loc->GetRCType() != RCTYPE_SMALL_KEY || showKeysanity) &&
(loc->GetRCType() != RCTYPE_BOSS_KEY || showBossKeysanity) &&
(loc->GetRCType() != RCTYPE_GANON_BOSS_KEY || showGanonBossKey) &&
(rc != RC_KAK_100_GOLD_SKULLTULA_REWARD || show100SkullReward) &&
(loc->GetRCType() != RCTYPE_GF_KEY && rc != RC_GF_GERUDO_MEMBERSHIP_CARD ||
(showGerudoCard && rc == RC_GF_GERUDO_MEMBERSHIP_CARD) ||
(fortressNormal && showGerudoFortressKeys && loc->GetRCType() == RCTYPE_GF_KEY) ||
(fortressFast && showGerudoFortressKeys && rc == RC_GF_NORTH_F1_CARPENTER)
);
}
else if (loc->IsVanillaCompletion()) {
return (loc->GetQuest() == RCQUEST_BOTH ||
(loc->GetQuest() == RCQUEST_MQ && OTRGlobals::Instance->gRandoContext->GetDungeons()->GetDungeonFromScene(loc->GetScene())->IsMQ()) ||
(loc->GetQuest() == RCQUEST_VANILLA && OTRGlobals::Instance->gRandoContext->GetDungeons()->GetDungeonFromScene(loc->GetScene())->IsVanilla()) ||
rc == RC_GIFT_FROM_SAGES) && rc != RC_LINKS_POCKET;
}
return false;
}
void UpdateInventoryChecks() {
//For all the areas with compasses, if you have one, spoil the area
for (auto [scene, area] : DungeonRCAreasBySceneID) {
if (CHECK_DUNGEON_ITEM(DUNGEON_MAP, scene)) {
areasSpoiled |= (1 << area);
}
}
}
void UpdateAreaFullyChecked(RandomizerCheckArea area) {
}
void UpdateAreas(RandomizerCheckArea area) {
areasFullyChecked[area] = areaChecksGotten[area] == checksByArea.find(area)->second.size();
if (areaChecksGotten[area] != 0 || RandomizerCheckObjects::AreaIsOverworld(area)) {
areasSpoiled |= (1 << area);
}
}
void UpdateAllOrdering() {
// Sort the entire thing
for (int i = 0; i < RCAREA_INVALID; i++) {
UpdateOrdering(static_cast<RandomizerCheckArea>(i));
}
}
void UpdateOrdering(RandomizerCheckArea rcArea) {
// Sort a single area
if(checksByArea.contains(rcArea)) {
std::sort(checksByArea.find(rcArea)->second.begin(), checksByArea.find(rcArea)->second.end(), CompareChecks);
}
}
bool IsEoDCheck(RandomizerCheckType type) {
return type == RCTYPE_BOSS_HEART_OR_OTHER_REWARD || type == RCTYPE_DUNGEON_REWARD;
}
bool CompareChecks(RandomizerCheck i, RandomizerCheck j) {
RandomizerCheckTrackerData iShow = gSaveContext.checkTrackerData[i];
RandomizerCheckTrackerData jShow = gSaveContext.checkTrackerData[j];
Rando::Location* x = Rando::StaticData::GetLocation(i);
Rando::Location* y = Rando::StaticData::GetLocation(j);
bool iCollected = iShow.status == RCSHOW_COLLECTED || iShow.status == RCSHOW_SAVED;
bool iSaved = iShow.status == RCSHOW_SAVED;
bool jCollected = jShow.status == RCSHOW_COLLECTED || jShow.status == RCSHOW_SAVED;
bool jSaved = jShow.status == RCSHOW_SAVED;
if (!iCollected && jCollected) {
return true;
} else if (iCollected && !jCollected) {
return false;
}
if (!iSaved && jSaved) {
return true;
} else if (iSaved && !jSaved) {
return false;
}
if (!iShow.skipped && jShow.skipped) {
return true;
} else if (iShow.skipped && !jShow.skipped) {
return false;
}
if (!IsEoDCheck(x->GetRCType()) && IsEoDCheck(y->GetRCType())) {
return true;
} else if (IsEoDCheck(x->GetRCType()) && !IsEoDCheck(y->GetRCType())) {
return false;
}
if (i < j) {
return true;
} else if (i > j) {
return false;
}
return false;
}
bool IsHeartPiece(GetItemID giid) {
return giid == GI_HEART_PIECE || giid == GI_HEART_PIECE_WIN;
}
void DrawLocation(RandomizerCheck rc) {
Color_RGBA8 mainColor;
Color_RGBA8 extraColor;
std::string txt;
bool showHidden = CVarGetInteger("gCheckTrackerOptionShowHidden", 0);
Rando::Location* loc = Rando::StaticData::GetLocation(rc);
Rando::ItemLocation* itemLoc = OTRGlobals::Instance->gRandoContext->GetItemLocation(rc);
RandomizerCheckTrackerData checkData = gSaveContext.checkTrackerData[rc];
RandomizerCheckStatus status = checkData.status;
bool skipped = checkData.skipped;
if (status == RCSHOW_COLLECTED) {
if (!showHidden && CVarGetInteger("gCheckTrackerCollectedHide", 0)) {
return;
}
mainColor =
!IsHeartPiece((GetItemID)Rando::StaticData::RetrieveItem(loc->GetVanillaItem()).GetItemID()) && !IS_RANDO
? CVarGetColor("gCheckTrackerCollectedExtraColor", Color_Collected_Extra_Default)
: CVarGetColor("gCheckTrackerCollectedMainColor", Color_Main_Default);
extraColor = CVarGetColor("gCheckTrackerCollectedExtraColor", Color_Collected_Extra_Default);
} else if (status == RCSHOW_SAVED) {
if (!showHidden && CVarGetInteger("gCheckTrackerSavedHide", 0)) {
return;
}
mainColor =
!IsHeartPiece((GetItemID)Rando::StaticData::RetrieveItem(loc->GetVanillaItem()).GetItemID()) && !IS_RANDO
? CVarGetColor("gCheckTrackerSavedExtraColor", Color_Saved_Extra_Default)
: CVarGetColor("gCheckTrackerSavedMainColor", Color_Main_Default);
extraColor = CVarGetColor("gCheckTrackerSavedExtraColor", Color_Saved_Extra_Default);
} else if (skipped) {
if (!showHidden && CVarGetInteger("gCheckTrackerSkippedHide", 0)) {
return;
}
mainColor =
!IsHeartPiece((GetItemID)Rando::StaticData::RetrieveItem(loc->GetVanillaItem()).GetItemID()) && !IS_RANDO
? CVarGetColor("gCheckTrackerSkippedExtraColor", Color_Skipped_Extra_Default)
: CVarGetColor("gCheckTrackerSkippedMainColor", Color_Main_Default);
extraColor = CVarGetColor("gCheckTrackerSkippedExtraColor", Color_Skipped_Extra_Default);
} else if (status == RCSHOW_SEEN || status == RCSHOW_IDENTIFIED) {
if (!showHidden && CVarGetInteger("gCheckTrackerSeenHide", 0)) {
return;
}
mainColor =
!IsHeartPiece((GetItemID)Rando::StaticData::RetrieveItem(loc->GetVanillaItem()).GetItemID()) && !IS_RANDO
? CVarGetColor("gCheckTrackerSeenExtraColor", Color_Seen_Extra_Default)
: CVarGetColor("gCheckTrackerSeenMainColor", Color_Main_Default);
extraColor = CVarGetColor("gCheckTrackerSeenExtraColor", Color_Seen_Extra_Default);
} else if (status == RCSHOW_SCUMMED) {
if (!showHidden && CVarGetInteger("gCheckTrackerScummedHide", 0)) {
return;
}
mainColor =
!IsHeartPiece((GetItemID)Rando::StaticData::RetrieveItem(loc->GetVanillaItem()).GetItemID()) && !IS_RANDO
? CVarGetColor("gCheckTrackerScummedExtraColor", Color_Scummed_Extra_Default)
: CVarGetColor("gCheckTrackerScummedMainColor", Color_Main_Default);
extraColor = CVarGetColor("gCheckTrackerScummedExtraColor", Color_Scummed_Extra_Default);
} else if (status == RCSHOW_UNCHECKED) {
if (!showHidden && CVarGetInteger("gCheckTrackerUncheckedHide", 0)) {
return;
}
mainColor =
!IsHeartPiece((GetItemID)Rando::StaticData::RetrieveItem(loc->GetVanillaItem()).GetItemID()) && !IS_RANDO
? CVarGetColor("gCheckTrackerUncheckedExtraColor", Color_Unchecked_Extra_Default)
: CVarGetColor("gCheckTrackerUncheckedMainColor", Color_Main_Default);
extraColor = CVarGetColor("gCheckTrackerUncheckedExtraColor", Color_Unchecked_Extra_Default);
}
//Main Text
if (checkNameOverrides.contains(loc->GetRandomizerCheck())) {
txt = checkNameOverrides[loc->GetRandomizerCheck()];
} else {
txt = loc->GetShortName();
}
if (lastLocationChecked == loc->GetRandomizerCheck()) {
txt = "* " + txt;
}
// Draw button - for Skipped/Seen/Scummed/Unchecked only
if (status == RCSHOW_UNCHECKED || status == RCSHOW_SEEN || status == RCSHOW_IDENTIFIED || status == RCSHOW_SCUMMED || skipped) {
if (UIWidgets::StateButton(std::to_string(rc).c_str(), skipped ? ICON_FA_PLUS : ICON_FA_TIMES)) {
if (skipped) {
gSaveContext.checkTrackerData[rc].skipped = false;
areaChecksGotten[loc->GetArea()]--;
} else {
gSaveContext.checkTrackerData[rc].skipped = true;
areaChecksGotten[loc->GetArea()]++;
}
UpdateOrdering(loc->GetArea());
UpdateInventoryChecks();
SaveManager::Instance->SaveSection(gSaveContext.fileNum, sectionId, true);
}
} else {
ImGui::InvisibleButton("", ImVec2(20.0f, 10.0f));
}
ImGui::SameLine();
//Draw
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(mainColor.r / 255.0f, mainColor.g / 255.0f, mainColor.b / 255.0f, mainColor.a / 255.0f));
ImGui::Text("%s", txt.c_str());
ImGui::PopStyleColor();
//Draw the extra info
txt = "";
if (checkData.hintItem != 0) {
// TODO hints
} else if (status != RCSHOW_UNCHECKED) {
switch (status) {
case RCSHOW_SAVED:
case RCSHOW_COLLECTED:
case RCSHOW_SCUMMED:
if (IS_RANDO) {
txt = itemLoc->GetPlacedItem().GetName().GetForLanguage(gSaveContext.language);
} else {
if (IsHeartPiece((GetItemID)Rando::StaticData::RetrieveItem(loc->GetVanillaItem()).GetItemID())) {
if (gSaveContext.language == LANGUAGE_ENG || gSaveContext.language == LANGUAGE_GER) {
txt = Rando::StaticData::RetrieveItem(loc->GetVanillaItem()).GetName().english;
} else if (gSaveContext.language == LANGUAGE_FRA) {
txt = Rando::StaticData::RetrieveItem(loc->GetVanillaItem()).GetName().french;
}
}
}
break;
case RCSHOW_IDENTIFIED:
case RCSHOW_SEEN:
if (IS_RANDO) {
if (itemLoc->GetPlacedRandomizerGet() == RG_ICE_TRAP) {
if (status == RCSHOW_IDENTIFIED) {
txt = OTRGlobals::Instance->gRandoContext->overrides[rc].GetTrickName().GetForLanguage(gSaveContext.language);
} else {
txt = Rando::StaticData::RetrieveItem(OTRGlobals::Instance->gRandoContext->overrides[rc].LooksLike()).GetName().GetForLanguage(gSaveContext.language);
}
} else {
txt = itemLoc->GetPlacedItem().GetName().GetForLanguage(gSaveContext.language);
}
if (status == RCSHOW_IDENTIFIED) {
txt += fmt::format(" - {}", gSaveContext.checkTrackerData[rc].price);
}
} else {
if (IsHeartPiece((GetItemID)Rando::StaticData::RetrieveItem(loc->GetVanillaItem()).GetItemID())) {
if (gSaveContext.language == LANGUAGE_ENG || gSaveContext.language == LANGUAGE_GER) {
txt = Rando::StaticData::RetrieveItem(loc->GetVanillaItem()).GetName().english;
} else if (gSaveContext.language == LANGUAGE_FRA) {
txt = Rando::StaticData::RetrieveItem(loc->GetVanillaItem()).GetName().french;
}
}
}
break;
}
}
if (txt == "" && skipped) {
txt = "Skipped"; // TODO language
}
if (txt != "") {
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(extraColor.r / 255.0f, extraColor.g / 255.0f, extraColor.b / 255.0f, extraColor.a / 255.0f));
ImGui::SameLine();
ImGui::Text(" (%s)", txt.c_str());
ImGui::PopStyleColor();
}
}
static std::set<std::string> rainbowCVars = {
"gCheckTrackerAreaMainIncompleteColor", "gCheckTrackerAreaExtraIncompleteColor",
"gCheckTrackerAreaMainCompleteColor", "gCheckTrackerAreaExtraCompleteColor",
"gCheckTrackerUncheckedMainColor", "gCheckTrackerUncheckedExtraColor",
"gCheckTrackerSkippedMainColor", "gCheckTrackerSkippedExtraColor",
"gCheckTrackerSeenMainColor", "gCheckTrackerSeenExtraColor",
"gCheckTrackerHintedMainColor", "gCheckTrackerHintedExtraColor",
"gCheckTrackerCollectedMainColor", "gCheckTrackerCollectedExtraColor",
"gCheckTrackerScummedMainColor", "gCheckTrackerScummedExtraColor",
"gCheckTrackerSavedMainColor", "gCheckTrackerSavedExtraColor",
};
int hue = 0;
void RainbowTick() {
float freqHue = hue * 2 * M_PI / (360 * CVarGetFloat("gCosmetics.RainbowSpeed", 0.6f));
for (auto& cvar : rainbowCVars) {
if (CVarGetInteger((cvar + "RBM").c_str(), 0) == 0) {
continue;
}
Color_RGBA8 newColor;
newColor.r = sin(freqHue + 0) * 127 + 128;
newColor.g = sin(freqHue + (2 * M_PI / 3)) * 127 + 128;
newColor.b = sin(freqHue + (4 * M_PI / 3)) * 127 + 128;
newColor.a = 255;
CVarSetColor(cvar.c_str(), newColor);
}
hue++;
hue %= 360;
}
void ImGuiDrawTwoColorPickerSection(const char* text, const char* cvarMainName, const char* cvarExtraName,
Color_RGBA8& main_color, Color_RGBA8& extra_color, Color_RGBA8& main_default_color,
Color_RGBA8& extra_default_color, const char* cvarHideName, const char* tooltip) {
Color_RGBA8 cvarMainColor = CVarGetColor(cvarMainName, main_default_color);
Color_RGBA8 cvarExtraColor = CVarGetColor(cvarExtraName, extra_default_color);
main_color = cvarMainColor;
extra_color = cvarExtraColor;
if (ImGui::CollapsingHeader(text)) {
if (*cvarHideName != '\0') {
std::string label = cvarHideName;
label += "##Hidden";
ImGui::PushID(label.c_str());
UIWidgets::EnhancementCheckbox("Hidden", cvarHideName, false,
"When active, checks will hide by default when updated to this state. Can "
"be overriden with the \"Show Hidden Items\" option.");
ImGui::PopID();
}
if (ImGui::BeginTable(text, 2, ImGuiTableFlags_BordersH | ImGuiTableFlags_BordersV | ImGuiTableFlags_Hideable)) {
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::PushItemWidth(ImGui::GetContentRegionAvail().x);
if (UIWidgets::EnhancementColor("Check", cvarMainName,
ImVec4(main_color.r, main_color.g, main_color.b, main_color.a),
ImVec4(main_default_color.r, main_default_color.g, main_default_color.b, main_default_color.a)))
{
main_color = CVarGetColor(cvarMainName, main_default_color);
};
ImGui::PopItemWidth();
ImGui::TableNextColumn();
ImGui::AlignTextToFramePadding();
ImGui::PushItemWidth(ImGui::GetContentRegionAvail().x);
if (UIWidgets::EnhancementColor("Details", cvarExtraName,
ImVec4(extra_color.r, extra_color.g, extra_color.b, extra_color.a),
ImVec4(extra_default_color.r, extra_default_color.g, extra_default_color.b, extra_default_color.a)))
{
extra_color = CVarGetColor(cvarExtraName, extra_default_color);
}
ImGui::PopItemWidth();
ImGui::EndTable();
}
}
if (tooltip != "") {
ImGui::SameLine();
ImGui::Text(" ?");
UIWidgets::Tooltip(tooltip);
}
}
static const char* windowType[] = { "Floating", "Window" };
static const char* displayType[] = { "Always", "Combo Button Hold" };
static const char* buttonStrings[] = { "A Button", "B Button", "C-Up", "C-Down", "C-Left", "C-Right", "L Button",
"Z Button", "R Button", "Start", "D-Up", "D-Down", "D-Left", "D-Right" };
void CheckTrackerSettingsWindow::DrawElement() {
ImGui::SetNextWindowSize(ImVec2(600, 375), ImGuiCond_FirstUseEver);
if (!ImGui::Begin("Check Tracker Settings", &mIsVisible, ImGuiWindowFlags_NoFocusOnAppearing)) {
ImGui::End();
return;
}
ImGui::PushStyleVar(ImGuiStyleVar_CellPadding, { 8.0f, 8.0f });
ImGui::BeginTable("CheckTrackerSettingsTable", 2, ImGuiTableFlags_BordersH | ImGuiTableFlags_BordersV);
ImGui::TableSetupColumn("General settings", ImGuiTableColumnFlags_WidthStretch, 200.0f);
ImGui::TableSetupColumn("Section settings", ImGuiTableColumnFlags_WidthStretch, 200.0f);
ImGui::TableHeadersRow();
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::PushItemWidth(ImGui::GetContentRegionAvail().x);
if (UIWidgets::EnhancementColor("BG Color", "gCheckTrackerBgColor",
ImVec4(Color_Background.r, Color_Background.g, Color_Background.b, Color_Background.a),
ImVec4(Color_Bg_Default.r, Color_Bg_Default.g, Color_Bg_Default.b, Color_Bg_Default.a),
false, true))
{
Color_Background = CVarGetColor("gCheckTrackerBgColor", Color_Bg_Default);
}
ImGui::PopItemWidth();
UIWidgets::LabeledRightAlignedEnhancementCombobox("Window Type", "gCheckTrackerWindowType", windowType, TRACKER_WINDOW_WINDOW);
if (CVarGetInteger("gCheckTrackerWindowType", TRACKER_WINDOW_WINDOW) == TRACKER_WINDOW_FLOATING) {
UIWidgets::EnhancementCheckbox("Enable Dragging", "gCheckTrackerHudEditMode");
UIWidgets::EnhancementCheckbox("Only enable while paused", "gCheckTrackerShowOnlyPaused");
UIWidgets::LabeledRightAlignedEnhancementCombobox("Display Mode", "gCheckTrackerDisplayType", displayType, 0);
if (CVarGetInteger("gCheckTrackerDisplayType", TRACKER_DISPLAY_ALWAYS) == TRACKER_DISPLAY_COMBO_BUTTON) {
UIWidgets::LabeledRightAlignedEnhancementCombobox("Combo Button 1", "gCheckTrackerComboButton1", buttonStrings, TRACKER_COMBO_BUTTON_L);
UIWidgets::LabeledRightAlignedEnhancementCombobox("Combo Button 2", "gCheckTrackerComboButton2", buttonStrings, TRACKER_COMBO_BUTTON_R);
}
}
UIWidgets::EnhancementCheckbox("Vanilla/MQ Dungeon Spoilers", "gCheckTrackerOptionMQSpoilers");
UIWidgets::Tooltip("If enabled, Vanilla/MQ dungeons will show on the tracker immediately. Otherwise, Vanilla/MQ dungeon locations must be unlocked.");
if (UIWidgets::EnhancementCheckbox("Hide right-side shop item checks", "gCheckTrackerOptionHideRightShopChecks", false, "", UIWidgets::CheckboxGraphics::Cross, true)) {
hideShopRightChecks = !hideShopRightChecks;
RecalculateAreaTotals();
}
UIWidgets::Tooltip("If enabled, will prevent the tracker from displaying slots 1-4 in all shops.");
if (UIWidgets::EnhancementCheckbox("Always show gold skulltulas", "gCheckTrackerOptionAlwaysShowGSLocs", false, "")) {
alwaysShowGS = !alwaysShowGS;
RecalculateAreaTotals();
}
UIWidgets::Tooltip("If enabled, will show GS locations in the tracker regardless of tokensanity settings.");
ImGui::TableNextColumn();
CheckTracker::ImGuiDrawTwoColorPickerSection("Area Incomplete", "gCheckTrackerAreaMainIncompleteColor", "gCheckTrackerAreaExtraIncompleteColor", Color_Area_Incomplete_Main, Color_Area_Incomplete_Extra, Color_Main_Default, Color_Area_Incomplete_Extra_Default, "gCheckTrackerAreaIncompleteHide", "");
CheckTracker::ImGuiDrawTwoColorPickerSection("Area Complete", "gCheckTrackerAreaMainCompleteColor", "gCheckTrackerAreaExtraCompleteColor", Color_Area_Complete_Main, Color_Area_Complete_Extra, Color_Main_Default, Color_Area_Complete_Extra_Default, "gCheckTrackerAreaCompleteHide", "");
CheckTracker::ImGuiDrawTwoColorPickerSection("Unchecked", "gCheckTrackerUncheckedMainColor", "gCheckTrackerUncheckedExtraColor", Color_Unchecked_Main, Color_Unchecked_Extra, Color_Main_Default, Color_Unchecked_Extra_Default, "gCheckTrackerUncheckedHide", "Checks you have not interacted with at all.");
CheckTracker::ImGuiDrawTwoColorPickerSection("Skipped", "gCheckTrackerSkippedMainColor", "gCheckTrackerSkippedExtraColor", Color_Skipped_Main, Color_Skipped_Extra, Color_Main_Default, Color_Skipped_Extra_Default, "gCheckTrackerSkippedHide", "");
CheckTracker::ImGuiDrawTwoColorPickerSection("Seen", "gCheckTrackerSeenMainColor", "gCheckTrackerSeenExtraColor", Color_Seen_Main, Color_Seen_Extra, Color_Main_Default, Color_Seen_Extra_Default, "gCheckTrackerSeenHide", "Used for shops. Shows item names for shop slots when walking in, and prices when highlighting them in buy mode.");
CheckTracker::ImGuiDrawTwoColorPickerSection("Scummed", "gCheckTrackerScummedMainColor", "gCheckTrackerScummedExtraColor", Color_Scummed_Main, Color_Scummed_Extra, Color_Main_Default, Color_Scummed_Extra_Default, "gCheckTrackerScummedHide", "Checks you collect, but then reload before saving so you no longer have them.");
//CheckTracker::ImGuiDrawTwoColorPickerSection("Hinted (WIP)", "gCheckTrackerHintedMainColor", "gCheckTrackerHintedExtraColor", Color_Hinted_Main, Color_Hinted_Extra, Color_Main_Default, Color_Hinted_Extra_Default, "gCheckTrackerHintedHide", "");
CheckTracker::ImGuiDrawTwoColorPickerSection("Collected", "gCheckTrackerCollectedMainColor", "gCheckTrackerCollectedExtraColor", Color_Collected_Main, Color_Collected_Extra, Color_Main_Default, Color_Collected_Extra_Default, "gCheckTrackerCollectedHide", "Checks you have collected without saving or reloading yet.");
CheckTracker::ImGuiDrawTwoColorPickerSection("Saved", "gCheckTrackerSavedMainColor", "gCheckTrackerSavedExtraColor", Color_Saved_Main, Color_Saved_Extra, Color_Main_Default, Color_Saved_Extra_Default, "gCheckTrackerSavedHide", "Checks that you saved the game while having collected.");
ImGui::PopStyleVar(1);
ImGui::EndTable();
ImGui::End();
}
void CheckTrackerWindow::InitElement() {
Color_Background = CVarGetColor("gCheckTrackerBgColor", Color_Bg_Default);
Color_Area_Incomplete_Main = CVarGetColor("gCheckTrackerAreaMainIncompleteColor", Color_Main_Default);
Color_Area_Incomplete_Extra = CVarGetColor("gCheckTrackerAreaExtraIncompleteColor", Color_Area_Incomplete_Extra_Default);
Color_Area_Complete_Main = CVarGetColor("gCheckTrackerAreaMainCompleteColor", Color_Main_Default);
Color_Area_Complete_Extra = CVarGetColor("gCheckTrackerAreaExtraCompleteColor", Color_Area_Complete_Extra_Default);
Color_Unchecked_Main = CVarGetColor("gCheckTrackerUncheckedMainColor", Color_Main_Default);
Color_Unchecked_Extra = CVarGetColor("gCheckTrackerUncheckedExtraColor", Color_Unchecked_Extra_Default);
Color_Skipped_Main = CVarGetColor("gCheckTrackerSkippedMainColor", Color_Main_Default);
Color_Skipped_Extra = CVarGetColor("gCheckTrackerSkippedExtraColor", Color_Skipped_Extra_Default);
Color_Seen_Main = CVarGetColor("gCheckTrackerSeenMainColor", Color_Main_Default);
Color_Seen_Extra = CVarGetColor("gCheckTrackerSeenExtraColor", Color_Seen_Extra_Default);
Color_Hinted_Main = CVarGetColor("gCheckTrackerHintedMainColor", Color_Main_Default);
Color_Hinted_Extra = CVarGetColor("gCheckTrackerHintedExtraColor", Color_Hinted_Extra_Default);
Color_Collected_Main = CVarGetColor("gCheckTrackerCollectedMainColor", Color_Main_Default);
Color_Collected_Extra = CVarGetColor("gCheckTrackerCollectedExtraColor", Color_Collected_Extra_Default);
Color_Scummed_Main = CVarGetColor("gCheckTrackerScummedMainColor", Color_Main_Default);
Color_Scummed_Extra = CVarGetColor("gCheckTrackerScummedExtraColor", Color_Scummed_Extra_Default);
Color_Saved_Main = CVarGetColor("gCheckTrackerSavedMainColor", Color_Main_Default);
Color_Saved_Extra = CVarGetColor("gCheckTrackerSavedExtraColor", Color_Saved_Extra_Default);
SaveManager::Instance->AddInitFunction(InitTrackerData);
sectionId = SaveManager::Instance->AddSaveFunction("trackerData", 1, SaveFile, true, -1);
SaveManager::Instance->AddLoadFunction("trackerData", 1, LoadFile);
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnLoadGame>(CheckTrackerLoadGame);
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnExitGame>([](uint32_t fileNum) {
Teardown();
});
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnItemReceive>(CheckTrackerItemReceive);
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnGameFrameUpdate>(CheckTrackerFrame);
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnTransitionEnd>(CheckTrackerTransition);
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnShopSlotChange>(CheckTrackerShopSlotChange);
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnSceneFlagSet>(CheckTrackerSceneFlagSet);
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnFlagSet>(CheckTrackerFlagSet);
hideShopRightChecks = CVarGetInteger("gCheckTrackerOptionHideRightShopChecks", 1);
alwaysShowGS = CVarGetInteger("gCheckTrackerOptionAlwaysShowGSLocs", 0);
//LocationTable_Init();
}
} // namespace CheckTracker