Complete Refactor of Check Tracker ImGui

This commit is contained in:
sonoftunk 2022-11-21 21:15:15 -05:00
parent 5307457ab0
commit f638ae0a05
10 changed files with 887 additions and 305 deletions

View File

@ -38,7 +38,13 @@ const char* RainbowColorCvarList[] = {
"gStickTrailTopCol", "gStickTrailBottomCol","gHammerTrailTopCol", "gHammerTrailBottomCol",
"gCharged1Col", "gCharged1ColEnv", "gCharged2Col", "gCharged2ColEnv",
"gCCFileChoosePrim", "gCCFileChooseTextPrim", "gCCEquipmentsPrim", "gCCItemsPrim",
"gCCMapsPrim", "gCCQuestsPrim", "gCCSavePrim", "gCCGameoverPrim"
"gCCMapsPrim", "gCCQuestsPrim", "gCCSavePrim", "gCCGameoverPrim",
"gCheckTrackerAreaMainIncompleteColor", "gCheckTrackerAreaExtraIncompleteColor",
"gCheckTrackerAreaMainCompleteColor", "gCheckTrackerAreaExtraCompleteColor",
"gCheckTrackerUncheckedMainColor", "gCheckTrackerUncheckedExtraColor",
"gCheckTrackerSkippedMainColor", "gCheckTrackerSkippedExtraColor", "gCheckTrackerSeenMainColor", "gCheckTrackerSeenExtraColor",
"gCheckTrackerHintedMainColor", "gCheckTrackerHintedExtraColor", "gCheckTrackerCheckedMainColor", "gCheckTrackerCheckedExtraColor",
"gCheckTrackerScummedMainColor", "gCheckTrackerScummedExtraColor", "gCheckTrackerSavedMainColor", "gCheckTrackerSavedExtraColor",
};
const char* MarginCvarList[] {
"gHearts", "gHeartsCount", "gMagicBar", "gVSOA", "gBBtn", "gABtn", "gStartBtn",

View File

@ -2523,6 +2523,14 @@ RandomizerCheck Randomizer::GetCheckFromActor(s16 actorId, s16 sceneNum, s16 act
return GetCheckObjectFromActor(actorId, sceneNum, actorParams).rc;
}
RandomizerInf Randomizer::GetRandomizerInfFromCheck(RandomizerCheck rc) {
auto rcIt = rcToRandomizerInf.find(rc);
if (rcIt == rcToRandomizerInf.end())
return RAND_INF_MAX;
return rcIt->second;
}
RandomizerCheck Randomizer::GetCheckFromRandomizerInf(RandomizerInf randomizerInf) {
for (auto const& [key, value] : rcToRandomizerInf) {
if (value == randomizerInf) return key;

View File

@ -65,6 +65,7 @@ class Randomizer {
u8 GetRandoSettingValue(RandomizerSettingKey randoSettingKey);
RandomizerCheck GetCheckFromActor(s16 actorId, s16 sceneNum, s16 actorParams);
RandomizerCheck GetCheckFromRandomizerInf(RandomizerInf randomizerInf);
RandomizerInf GetRandomizerInfFromCheck(RandomizerCheck rc);
RandomizerGetData GetRandomizerGetDataFromActor(s16 actorId, s16 sceneNum, s16 actorParams);
RandomizerGetData GetRandomizerGetDataFromKnownCheck(RandomizerCheck randomizerCheck);
std::string GetChildAltarText() const;

View File

@ -3,6 +3,7 @@
#include <string>
#include <Cvar.h>
#include "z64.h"
#include "../../OTRGlobals.h"
/*
typedef struct {
@ -950,35 +951,48 @@ void RandomizerCheckObjects::UpdateImGuiVisibility() {
}
}
void RandomizerCheckObjects::UpdateTrackerImGuiVisibility() {
for (auto& [randomizerCheck, locationIt] : rcObjects) {
locationIt.visibleInImgui = (
(locationIt.rcArea != RCAREA_INVALID) && // don't show Invalid locations
(locationIt.vOrMQ != RCVORMQ_MQ) && // don't show MQ checks until we support MQ
((locationIt.rcType != RCTYPE_SHOP) || CVar_GetS32("gRandomizeShopsanity", 0) > 1) && // 1 is the value for "0 random items" for shop
(locationIt.rcType != RCTYPE_GOSSIP_STONE) && // don't show gossip stones (maybe gossipsanity will be a thing eventually?)
(locationIt.rcType != RCTYPE_CHEST_GAME) && // don't show non final reward chest game checks until we support shuffling them
((locationIt.rcType != RCTYPE_SKULL_TOKEN) ||
(CVar_GetS32("gRandomizeShuffleTokens", 0) == 3) || // all tokens
((CVar_GetS32("gRandomizeShuffleTokens", 0) == 2) && RandomizerCheckObjects::AreaIsOverworld(locationIt.rcArea)) || // overworld tokens
((CVar_GetS32("gRandomizeShuffleTokens", 0) == 1) && RandomizerCheckObjects::AreaIsDungeon(locationIt.rcArea)) // dungeon tokens
) &&
((locationIt.rcType != RCTYPE_COW) || CVar_GetS32("gRandomizeShuffleCows", 0)) &&
((locationIt.rcType != RCTYPE_ADULT_TRADE) || CVar_GetS32("gRandomizeShuffleAdultTrade", 0)) &&
((locationIt.rc != RC_KF_KOKIRI_SWORD_CHEST) || CVar_GetS32("gRandomizeShuffleKokiriSword", 0)) &&
((locationIt.rc != RC_HC_MALON_EGG) || CVar_GetS32("gRandomizeShuffleWeirdEgg", 0)) &&
((locationIt.rc != RC_GF_GERUDO_MEMBERSHIP_CARD) || CVar_GetS32("gRandomizeShuffleGerudoToken", 0)) &&
((locationIt.rcType != RCTYPE_FROG_SONG) || CVar_GetS32("gRandomizeShuffleFrogSongRupees", 0)) &&
((locationIt.rcType != RCTYPE_MAP_COMPASS) || CVar_GetS32("gRandomizeStartingMapsCompasses", 0) != 1) && // 1 is the value for "vanilla" maps/compasses
((locationIt.rcType != RCTYPE_SMALL_KEY) || CVar_GetS32("gRandomizeKeysanity", 0) != 1) && // 1 is the value for "vanilla" small keys
((locationIt.rcType != RCTYPE_GF_KEY) || CVar_GetS32("randoShuffleGerudoFortressKeys", 0) != 0) && // 0 is the value for "vanilla" gf keys
((locationIt.rcType != RCTYPE_BOSS_KEY) || CVar_GetS32("gRandomizeBossKeysanity", 0) != 1) && // 1 is the value for "vanilla" boss keys
((locationIt.rcType != RCTYPE_GANON_BOSS_KEY) || CVar_GetS32("gRandomizeShuffleGanonBossKey", 0) != 0) && // 0 is the value for "vanilla" ganon's boss key
((!RC_IS_CARPENTER(locationIt.rc) && locationIt.rc != RC_GF_GERUDO_MEMBERSHIP_CARD) ||
(CVar_GetS32("gRandomizeGerudoFortress", 0) == 2 && !RC_IS_CARPENTER(locationIt.rc) && locationIt.rc != RC_GF_GERUDO_MEMBERSHIP_CARD) || //2 is the value for "open" gerudo's fortress
(CVar_GetS32("gRandomizeGerudoFortress", 0) == 1 && (locationIt.rc == RC_GF_NORTH_F1_CARPENTER || locationIt.rc == RC_GF_GERUDO_MEMBERSHIP_CARD)) || //1 is the value for "fast" gerudo's fortress
(CVar_GetS32("gRandomizeGerudoFortress", 0) == 0) //0 is the value for "normal" gerudo's fortress
)
bool RandomizerCheckObjects::IsVisibleInCheckTracker(RandomizerCheckObject rcObj) {
uint8_t gRandomizeShopsanity = OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_SHOPSANITY);
uint8_t gRandomizeShuffleTokens = OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_SHUFFLE_TOKENS);
uint8_t gRandomizeShuffleCows = OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_SHUFFLE_COWS);
uint8_t gRandomizeShuffleAdultTrade = OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_SHUFFLE_ADULT_TRADE);
uint8_t gRandomizeShuffleKokiriSword = OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_STARTING_KOKIRI_SWORD);
uint8_t gRandomizeShuffleWeirdEgg = OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_SHUFFLE_WEIRD_EGG);
uint8_t gRandomizeShuffleGerudoToken = OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_SHUFFLE_GERUDO_MEMBERSHIP_CARD);
uint8_t gRandomizeShuffleFrogSongRupees = OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_SHUFFLE_FROG_SONG_RUPEES);
uint8_t gRandomizeStartingMapsCompasses = OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_STARTING_MAPS_COMPASSES);
uint8_t gRandomizeKeysanity = OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_KEYSANITY);
uint8_t randoShuffleGerudoFortressKeys = OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_GERUDO_KEYS);
uint8_t gRandomizeBossKeysanity = OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_BOSS_KEYSANITY);
uint8_t gRandomizeShuffleGanonBossKey = OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_GANONS_BOSS_KEY);
uint8_t gRandomizeGerudoFortress = OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_GERUDO_FORTRESS);
return
(rcObj.rcArea != RCAREA_INVALID) && // don't show Invalid locations
(rcObj.vOrMQ != RCVORMQ_MQ) && // don't show MQ checks until we support MQ
((rcObj.rcType != RCTYPE_SHOP) || gRandomizeShopsanity > 1) && // 1 is the value for "0 random items" for shop
(rcObj.rcType != RCTYPE_GOSSIP_STONE) && // don't show gossip stones (maybe gossipsanity will be a thing eventually?)
(rcObj.rcType != RCTYPE_CHEST_GAME) && // don't show non final reward chest game checks until we support shuffling them
((rcObj.rcType != RCTYPE_SKULL_TOKEN) ||
(gRandomizeShuffleTokens == 3) || // all tokens
((gRandomizeShuffleTokens == 2) && RandomizerCheckObjects::AreaIsOverworld(rcObj.rcArea)) || // overworld tokens
((gRandomizeShuffleTokens == 1) && RandomizerCheckObjects::AreaIsDungeon(rcObj.rcArea)) // dungeon tokens
) &&
((rcObj.rcType != RCTYPE_COW) || gRandomizeShuffleCows) &&
((rcObj.rcType != RCTYPE_ADULT_TRADE) || gRandomizeShuffleAdultTrade) &&
((rcObj.rc != RC_KF_KOKIRI_SWORD_CHEST) || gRandomizeShuffleKokiriSword) &&
((rcObj.rc != RC_HC_MALON_EGG) || gRandomizeShuffleWeirdEgg) &&
((rcObj.rc != RC_GF_GERUDO_MEMBERSHIP_CARD) || gRandomizeShuffleGerudoToken) &&
((rcObj.rcType != RCTYPE_FROG_SONG) || gRandomizeShuffleFrogSongRupees) &&
((rcObj.rcType != RCTYPE_MAP_COMPASS) || gRandomizeStartingMapsCompasses != 1) && // 1 is the value for "vanilla" maps/compasses
((rcObj.rcType != RCTYPE_SMALL_KEY) || gRandomizeKeysanity != 1) && // 1 is the value for "vanilla" small keys
((rcObj.rcType != RCTYPE_GF_KEY) || gRandomizeKeysanity != 0) && // 0 is the value for "vanilla" gf keys
((rcObj.rcType != RCTYPE_BOSS_KEY) || gRandomizeBossKeysanity != 1) && // 1 is the value for "vanilla" boss keys
((rcObj.rcType != RCTYPE_GANON_BOSS_KEY) || gRandomizeShuffleGanonBossKey != 0) && // 0 is the value for "vanilla" ganon's boss key
((!RC_IS_CARPENTER(rcObj.rc) && rcObj.rc != RC_GF_GERUDO_MEMBERSHIP_CARD) ||
(gRandomizeGerudoFortress == 2 && !RC_IS_CARPENTER(rcObj.rc) && rcObj.rc != RC_GF_GERUDO_MEMBERSHIP_CARD) || //2 is the value for "open" gerudo's fortress
(gRandomizeGerudoFortress == 1 && (rcObj.rc == RC_GF_NORTH_F1_CARPENTER || rcObj.rc == RC_GF_GERUDO_MEMBERSHIP_CARD)) || //1 is the value for "fast" gerudo's fortress
(gRandomizeGerudoFortress == 0) //0 is the value for "normal" gerudo's fortress
);
}
}

View File

@ -64,6 +64,7 @@ typedef enum {
RCAREA_ICE_CAVERN,
RCAREA_GERUDO_TRAINING_GROUND,
RCAREA_GANONS_CASTLE,
//If adding any more areas, Check Tracker will need a refactor
RCAREA_INVALID
} RandomizerCheckArea;
@ -103,4 +104,5 @@ namespace RandomizerCheckObjects {
RandomizerCheckArea GetRCAreaBySceneID(SceneID sceneId);
void UpdateImGuiVisibility();
void UpdateTrackerImGuiVisibility();
}
bool IsVisibleInCheckTracker(RandomizerCheckObject rcObj);
}

View File

@ -0,0 +1,789 @@
#include "randomizer_check_tracker.h"
#include "../../OTRGlobals.h"
#include <ImGuiImpl.h>
#include "../../UIWidgets.hpp"
#include <string>
#include <vector>
#include <Cvar.h>
#include <Hooks.h>
#include "3drando/item_location.hpp"
extern "C" {
#include "variables.h"
#include "functions.h"
#include "macros.h"
extern PlayState* gPlayState;
}
extern "C" uint32_t ResourceMgr_IsSceneMasterQuest(s16 sceneNum);
void InitializeChecks();
void UpdateChecks();
void DrawLocation(RandomizerCheckObject rcObj, RandomizerCheckShow* thisCheckStatus);
void BeginFloatWindows(std::string UniqueName, ImGuiWindowFlags flags = 0);
void EndFloatWindows();
void UpdateOrdering(bool init = false);
bool ShouldUpdateChecks();
bool CompareCheckObject(RandomizerCheckObject i, RandomizerCheckObject j);
bool HasItemBeenCollected(RandomizerCheckObject obj);
ImVec4 Color_RGBA8_to_ImVec4(Color_RGBA8& color);
Color_RGBA8 ImVec4_to_Color_RGBA8(ImVec4& color);
RandomizerCheckShow GetCheckStatus(RandomizerCheckObject rcObj, int idx);
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_Checked_Extra_Default = { 255, 255, 255, 255 }; // TODO
Color_RGBA8 Color_Scummed_Extra_Default = { 255, 255, 255, 255 }; // TODO
Color_RGBA8 Color_Saved_Extra_Default = { 0, 185, 0, 255 }; // Green
ImVec4 Color_Background = { 0, 0, 0, 0 };
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 = { 255, 255, 255, 255 }; //Grey
Color_RGBA8 Color_Skipped_Extra = { 255, 255, 255, 255 }; //Grey
Color_RGBA8 Color_Seen_Main = { 255, 255, 255, 255 }; //TODO
Color_RGBA8 Color_Seen_Extra = { 255, 255, 255, 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_Checked_Main = { 255, 255, 255, 255 }; //TODO
Color_RGBA8 Color_Checked_Extra = { 255, 255, 255, 255 }; //TODO
Color_RGBA8 Color_Scummed_Main = { 255, 255, 255, 255 }; //TODO
Color_RGBA8 Color_Scummed_Extra = { 255, 255, 255, 255 }; //TODO
Color_RGBA8 Color_Saved_Main = { 255, 255, 255, 255 }; //White
Color_RGBA8 Color_Saved_Extra = { 0, 185, 0, 255 }; //Green
// persistent during gameplay
bool initialized = false;
bool doInitialize = false;
std::map<RandomizerCheck, RandomizerCheckShow> checkStatusMap;
u32 areasFullyChecked = 0;
s8 areaChecksTotal[32]; //| For sorting and showing
s8 areaChecksGotten[32]; //| "Kokiri Forest (4/9)"
std::vector<RandomizerCheckObject> checks;
bool optCollapseAll; // A bool that will collapse all checks once
bool optExpandAll; // A bool that will expand all checks once
s32 lastSaveCount = -1;
RandomizerCheck lastLocationChecked = RC_UNKNOWN_CHECK;
RandomizerCheckArea previousArea = RCAREA_INVALID;
RandomizerCheckArea currentArea = RCAREA_INVALID;
OSContPad* trackerButtonsPressed;
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 DrawCheckTracker(bool& open) {
if (doInitialize)
InitializeChecks();
if (!initialized)
return;
if (!open) {
CVar_SetS32("gCheckTrackerEnabled", 0);
return;
}
if (CVar_GetS32("gCheckTrackerWindowType", 1) == 0) {
if (CVar_GetS32("gCheckTrackerShowOnlyPaused", 0) == 1)
if (gPlayState == nullptr || gPlayState->pauseCtx.state == 0)
return;
if (CVar_GetS32("gCheckTrackerDisplayType", 0) == 1) {
int comboButton1Mask = buttons[CVar_GetS32("gCheckTrackerComboButton1", 6)];
int comboButton2Mask = buttons[CVar_GetS32("gCheckTrackerComboButton2", 8)];
bool comboButtonsHeld = trackerButtonsPressed != nullptr &&
trackerButtonsPressed[0].button & comboButton1Mask &&
trackerButtonsPressed[0].button & comboButton2Mask;
if (!comboButtonsHeld)
return;
}
}
SceneID sceneId = SCENE_ID_MAX;
if (gPlayState != nullptr) {
sceneId = (SceneID)gPlayState->sceneNum;
currentArea = RandomizerCheckObjects::GetRCAreaBySceneID(sceneId);
}
bool doAreaScroll =
(currentArea != RCAREA_INVALID && currentArea != previousArea &&
sceneId != SCENE_KAKUSIANA && // Don't move for grottos
sceneId != SCENE_YOUSEI_IZUMI_TATE && sceneId != SCENE_YOUSEI_IZUMI_YOKO && sceneId != SCENE_DAIYOUSEI_IZUMI && // Don't move for fairy fountains
sceneId != SCENE_SHOP1 && sceneId != SCENE_SYATEKIJYOU // Don't move for Bazaar/Gallery, as it moves between Kak and Market
);
previousArea = currentArea;
//Only update the checks if something has changed
if (ShouldUpdateChecks()) {
UpdateChecks();
UpdateOrdering();
}
BeginFloatWindows("Check Tracker", ImGuiWindowFlags_NoScrollbar);
//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)) {
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.");
ImGui::SameLine();
if (ImGui::Button("Expand All")) {
optCollapseAll = false;
optExpandAll = 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
bool doDraw = false;
bool thisAreaFullyChecked = false;
bool showHidden = CVar_GetS32("gCheckTrackerOptionShowHidden", 0);
bool hideIncomplete = CVar_GetS32("gCheckTrackerAreaIncompleteHide", 0);
bool hideComplete = CVar_GetS32("gCheckTrackerAreaCompleteHide", 0);
bool collapseLogic;
bool doingCollapseOrExpand = optExpandAll || optCollapseAll;
RandomizerCheckArea lastArea = RCAREA_INVALID;
Color_RGBA8 areaCompleteColor = CVar_GetRGBA("gCheckTrackerAreaMainCompleteColor", Color_Main_Default);
Color_RGBA8 areaIncompleteColor = CVar_GetRGBA("gCheckTrackerAreaMainIncompleteColor", Color_Main_Default);
Color_RGBA8 extraCompleteColor = CVar_GetRGBA("gCheckTrackerAreaExtraCompleteColor", Color_Area_Complete_Extra_Default);
Color_RGBA8 extraIncompleteColor = CVar_GetRGBA("gCheckTrackerAreaExtraIncompleteColor", Color_Area_Incomplete_Extra_Default);
Color_RGBA8 mainColor;
Color_RGBA8 extraColor;
std::string stemp;
s32 areaMask = 1;
// Logic for each check
for (auto& obj : checks)
{
// Second Column mode
// if (twoColumnMode && areaChecksGotten[obj.rcArea] != areaChecksTotal[obj.rcArea]) {
// //TODO draw left column
// ImGui::TableNextColumn();
// //TODO draw right column
// }
//
//New Area to be drawn
if (obj.rcArea != lastArea)
{
//Last Area needs to be cleaned up
if (lastArea != RCAREA_INVALID && doDraw) {
ImGui::TreePop();
UIWidgets::PaddedSeparator();
}
lastArea = obj.rcArea;
//Decide if we should skip because of hiding rules
thisAreaFullyChecked = ((areasFullyChecked & areaMask) != 0);
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(obj.rcArea) + "##TreeNode";
ImGui::PushStyleColor(ImGuiCol_Text, Color_RGBA8_to_ImVec4(mainColor));
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, Color_RGBA8_to_ImVec4(extraColor));
ImGui::Text("(%d/%d)", areaChecksGotten[obj.rcArea], areaChecksTotal[obj.rcArea]);
ImGui::PopStyleColor();
//Keep areas loaded between transitions
if (currentArea == obj.rcArea && doAreaScroll)
ImGui::SetScrollHereY(0.0f);
}
areaMask <<= 1;
}
if (doDraw)
DrawLocation(obj, &checkStatusMap.find(obj.rc)->second);
}
//Clean up last area
if (doDraw)
ImGui::TreePop();
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, ImGuiWindowFlags flags) {
ImGuiWindowFlags windowFlags = flags;
if (windowFlags == 0) {
windowFlags |=
ImGuiWindowFlags_AlwaysVerticalScrollbar | ImGuiWindowFlags_NoFocusOnAppearing;
}
if (!CVar_GetS32("gCheckTrackerWindowType", 0)) {
ImGui::SetNextWindowViewport(ImGui::GetMainViewport()->ID);
windowFlags |= ImGuiWindowFlags_NoDocking | ImGuiWindowFlags_NoNav | ImGuiWindowFlags_NoTitleBar |
ImGuiWindowFlags_NoScrollWithMouse | ImGuiWindowFlags_NoScrollbar;
if (!CVar_GetS32("gCheckTrackerHudEditMode", 0)) {
windowFlags |= ImGuiWindowFlags_NoInputs | ImGuiWindowFlags_NoMove;
}
}
ImGui::PushStyleColor(ImGuiCol_WindowBg, Color_Background);
ImGui::PushStyleColor(ImGuiCol_Border, ImVec4(0, 0, 0, 0));
ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 4.0f);
ImGui::Begin(UniqueName.c_str(), nullptr, windowFlags);
}
void EndFloatWindows() {
ImGui::PopStyleVar();
ImGui::PopStyleColor();
ImGui::PopStyleColor();
ImGui::End();
}
void InitializeChecks() {
if (gPlayState == nullptr || gSaveContext.fileNum < 0 || gSaveContext.fileNum > 2)
return;
int count = 0;
for (auto& [rcCheck, rcObj] : RandomizerCheckObjects::GetAllRCObjects()) {
if (!RandomizerCheckObjects::IsVisibleInCheckTracker(rcObj))
continue;
checks.push_back(rcObj);
checkStatusMap.emplace(rcObj.rc, RCSHOW_UNCHECKED);
count++;
areaChecksTotal[rcObj.rcArea]++;
}
UpdateChecks();
UpdateOrdering(true);
doInitialize = false;
initialized = true;
}
void Teardown() {
initialized = false;
//delete checkStatus; //TODO may cause crashes due to race conditions during a draw cycle, but if you don't have it, then there's a memory leak when switching files
areasFullyChecked = 0;
checks.clear();
lastSaveCount = -1;
lastLocationChecked = RC_UNKNOWN_CHECK;
for (int i = 0; i < sizeof(areaChecksTotal); i++) {
areaChecksTotal[i] = 0;
areaChecksGotten[i] = 0;
}
}
bool ShouldUpdateChecks() {
// TODO eventually will need to be hooked into game elements rather than just save file
// TODO, saveCount isn't actually a thing. If not allowed to add it, will need to iterate over save bits and see if any changed
// TODO, or enhance hooks, but that is a LUS change
//return lastSaveCount != gSaveContext.sohStats.saveCount;
return true;
}
void UpdateChecks() {
int idx = 0;
RandomizerCheckObject* lastCheck;
RandomizerCheckShow lastStatus;
for (auto& rcObj : checks) {
RandomizerCheckShow* checkStatusPtr = &checkStatusMap.find(rcObj.rc)->second;
lastStatus = *checkStatusPtr;
*checkStatusPtr = GetCheckStatus(rcObj, idx);
//Update areasFullyChecked
if (lastStatus != *checkStatusPtr) {
if (lastStatus != RCSHOW_CHECKED && lastStatus != RCSHOW_SAVED && (*checkStatusPtr == RCSHOW_CHECKED || *checkStatusPtr == RCSHOW_SAVED))
areaChecksGotten[rcObj.rcArea]++;
else if ((lastStatus == RCSHOW_CHECKED || lastStatus == RCSHOW_SAVED) && *checkStatusPtr != RCSHOW_CHECKED && *checkStatusPtr != RCSHOW_SAVED)
areaChecksGotten[rcObj.rcArea]--;
if (areaChecksGotten[rcObj.rcArea] == areaChecksTotal[rcObj.rcArea])
areasFullyChecked |= (1 << rcObj.rcArea);
else
areasFullyChecked &= (0xFFFFFFFF - (1 << rcObj.rcArea));
}
lastCheck = &rcObj;
idx++;
}
//TODO for when using saveCount as a basis
//lastSaveCount = gSaveContext.sohStats.saveCount;
}
void UpdateOrdering(bool init) {
// Sort the entire thing
if (init) {
std::sort(checks.begin(), checks.end(), CompareCheckObject);
return;
}
//TODO benchmark the full sort over the individual sorts, it might just make sense to always sort all?
//sort each area individually
int startOffset = 0;
int endOffset = 0;
for (int x = 0; x < sizeof(areaChecksTotal); x++) {
endOffset = startOffset + areaChecksTotal[x];
std::sort(checks.begin() + startOffset, checks.begin() + endOffset, CompareCheckObject);
startOffset += areaChecksTotal[x];
}
}
bool CompareCheckObject(RandomizerCheckObject i, RandomizerCheckObject j) {
if (i.rcArea < j.rcArea)
return true;
else if (i.rcArea > j.rcArea)
return false;
if (checkStatusMap.find(i.rc)->second < checkStatusMap.find(j.rc)->second)
return true;
else if (checkStatusMap.find(i.rc)->second > checkStatusMap.find(j.rc)->second)
return false;
if (i.rcShortName < j.rcShortName)
return true;
if (i.rcShortName > j.rcShortName)
return false;
return false;
}
RandomizerCheckShow GetCheckStatus(RandomizerCheckObject rcObj, int idx) {
if (HasItemBeenCollected(rcObj))
return RCSHOW_SAVED; // TODO: use SAVED until we hook into game elements without requiring a save
//If the status hasn't updated, keep showing as skipped
if (checkStatusMap.find(rcObj.rc)->second == RCSHOW_SKIPPED)
return RCSHOW_SKIPPED;
return RCSHOW_UNCHECKED;
// TODO Seen, Hinted, Scummed, saved/checked
}
bool HasItemBeenCollected(RandomizerCheckObject obj) {
// TODO doesn't consider vanilla/MQ?
// TODO move all the code to a static function in item_location?
// return Location(obj.rc)->GetCollectionCheck().IsChecked(gSaveContext);
ItemLocation* x = Location(obj.rc);
SpoilerCollectionCheck check = x->GetCollectionCheck();
auto flag = check.flag;
auto scene = check.scene;
auto type = check.type;
int shift;
int mask;
switch (type) {
case SpoilerCollectionCheckType::SPOILER_CHK_ALWAYS_COLLECTED:
return true;
case SpoilerCollectionCheckType::SPOILER_CHK_BIGGORON:
return gSaveContext.bgsFlag & flag;
case SpoilerCollectionCheckType::SPOILER_CHK_CHEST:
return gSaveContext.sceneFlags[scene].chest & (1 << flag);
case SpoilerCollectionCheckType::SPOILER_CHK_COLLECTABLE:
return 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_SCRUB:
case SpoilerCollectionCheckType::SPOILER_CHK_RANDOMIZER_INF:
return Flags_GetRandomizerInf(OTRGlobals::Instance->gRandomizer->GetRandomizerInfFromCheck(obj.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:
// Magic to flip an index `flag` to a lookup for 16bit big endian integers. Probably an easier way.....
shift = 7 - (flag % 8) + ((flag % 16) / 8) * 8;
mask = 0x8000 >> shift;
return gSaveContext.infTable[scene] & mask;
case SpoilerCollectionCheckType::SPOILER_CHK_ITEM_GET_INF:
// Magic to flip an index `flag` to a lookup for 16bit big endian integers. Probably an easier way.....
shift = 7 - (flag % 8) + ((flag % 16) / 8) * 8;
mask = 0x8000 >> shift;
return gSaveContext.itemGetInf[flag / 16] & mask;
case SpoilerCollectionCheckType::SPOILER_CHK_MAGIC_BEANS:
return BEANS_BOUGHT >= 10;
case SpoilerCollectionCheckType::SPOILER_CHK_MINIGAME:
if (obj.rc == RC_LH_CHILD_FISHING)
return HIGH_SCORE(HS_FISHING) & 0x400;
if (obj.rc == RC_LH_ADULT_FISHING)
return HIGH_SCORE(HS_FISHING) & 0x800;
case SpoilerCollectionCheckType::SPOILER_CHK_NONE:
return false;
case SpoilerCollectionCheckType::SPOILER_CHK_POE_POINTS:
return gSaveContext.highScores[HS_POE_POINTS] >= 1000;
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) ||
CVar_GetS32("gGravediggingTourFix", 0) && gSaveContext.sceneFlags[scene].collect & (1 << flag);
default:
return false;
}
return false;
}
void DrawLocation(RandomizerCheckObject rcObj, RandomizerCheckShow* thisCheckStatus) {
Color_RGBA8 mainColor;
Color_RGBA8 extraColor;
std::string txt;
bool showHidden = CVar_GetS32("gCheckTrackerOptionShowHidden", 0);
if (*thisCheckStatus == RCSHOW_UNCHECKED) {
if (!showHidden && CVar_GetS32("gCheckTrackerUncheckedHide", 0))
return;
mainColor = CVar_GetRGBA("gCheckTrackerUncheckedMainColor", Color_Main_Default);
extraColor = CVar_GetRGBA("gCheckTrackerUncheckedExtraColor", Color_Unchecked_Extra_Default);
} else if (*thisCheckStatus == RCSHOW_SKIPPED) {
if (!showHidden && CVar_GetS32("gCheckTrackerSkippedHide", 0))
return;
mainColor = CVar_GetRGBA("gCheckTrackerSkippedMainColor", Color_Main_Default);
extraColor = CVar_GetRGBA("gCheckTrackerSkippedExtraColor", Color_Skipped_Extra_Default);
} else if (*thisCheckStatus == RCSHOW_SEEN) {
if (!showHidden && CVar_GetS32("gCheckTrackerSeenHide", 0))
return;
mainColor = CVar_GetRGBA("gCheckTrackerSeenMainColor", Color_Main_Default);
extraColor = CVar_GetRGBA("gCheckTrackerSeenExtraColor", Color_Seen_Extra_Default);
} else if (*thisCheckStatus == RCSHOW_HINTED) {
if (!showHidden && CVar_GetS32("gCheckTrackerHintedHide", 0))
return;
mainColor = CVar_GetRGBA("gCheckTrackerHintedMainColor", Color_Main_Default);
extraColor = CVar_GetRGBA("gCheckTrackerHintedExtraColor", Color_Hinted_Extra_Default);
} else if (*thisCheckStatus == RCSHOW_CHECKED) {
if (!showHidden && CVar_GetS32("gCheckTrackerCheckedHide", 0))
return;
mainColor = CVar_GetRGBA("gCheckTrackerCheckedMainColor", Color_Main_Default);
extraColor = CVar_GetRGBA("gCheckTrackerCheckedExtraColor", Color_Checked_Extra_Default);
} else if (*thisCheckStatus == RCSHOW_SCUMMED) {
if (!showHidden && CVar_GetS32("gCheckTrackerScummedHide", 0))
return;
mainColor = CVar_GetRGBA("gCheckTrackerScummedMainColor", Color_Main_Default);
extraColor = CVar_GetRGBA("gCheckTrackerScummedExtraColor", Color_Scummed_Extra_Default);
} else if (*thisCheckStatus == RCSHOW_SAVED) {
if (!showHidden && CVar_GetS32("gCheckTrackerSavedHide", 0))
return;
mainColor = CVar_GetRGBA("gCheckTrackerSavedMainColor", Color_Main_Default);
extraColor = CVar_GetRGBA("gCheckTrackerSavedExtraColor", Color_Saved_Extra_Default);
}
//Main Text
txt = rcObj.rcShortName;
if (lastLocationChecked == rcObj.rc)
txt = "* " + txt;
//Second Column mode
//TODO
// if (twoColumnMode) ImGui::TableNextColumn();
// Draw button - for Skipped/Unchecked only
if (*thisCheckStatus == RCSHOW_UNCHECKED || *thisCheckStatus == RCSHOW_SKIPPED) {
bool skipped = (*thisCheckStatus == RCSHOW_SKIPPED);
if (ImGui::ArrowButton(std::to_string(rcObj.rc).c_str(), skipped ? ImGuiDir_Left : ImGuiDir_Right)) {
if (skipped)
*thisCheckStatus = RCSHOW_UNCHECKED;
else
*thisCheckStatus = RCSHOW_SKIPPED;
}
} else {
ImGui::InvisibleButton("", ImVec2(20.0f, 10.0f));
}
ImGui::SameLine();
//Draw
ImGui::PushStyleColor(ImGuiCol_Text, Color_RGBA8_to_ImVec4(mainColor));
ImGui::Text("%s", txt.c_str());
ImGui::PopStyleColor();
//Draw the extra info
if (*thisCheckStatus != RCSHOW_UNCHECKED) {
switch (*thisCheckStatus) {
case RCSHOW_SAVED:
case RCSHOW_CHECKED:
case RCSHOW_SCUMMED:
txt = OTRGlobals::Instance->gRandomizer
->EnumToSpoilerfileGetName[gSaveContext.itemLocations[rcObj.rc].get.rgID][gSaveContext.language];
break;
case RCSHOW_SKIPPED:
txt = "Skipped"; //TODO language
break;
case RCSHOW_SEEN:
txt = OTRGlobals::Instance->gRandomizer
->EnumToSpoilerfileGetName[gSaveContext.itemLocations[rcObj.rc].get.fakeRgID][gSaveContext.language];
break;
case RCSHOW_HINTED:
txt = "Hints are WIP"; // TODO language
break;
}
ImGui::PushStyleColor(ImGuiCol_Text, Color_RGBA8_to_ImVec4(extraColor));
ImGui::SameLine();
ImGui::Text(" (%s)", txt.c_str());
ImGui::PopStyleColor();
}
}
inline void SaveColorToCVar(std::string cvarname, ImVec4 color) {
CVar_SetFloat((cvarname + "R").c_str(), color.x);
CVar_SetFloat((cvarname + "G").c_str(), color.y);
CVar_SetFloat((cvarname + "B").c_str(), color.z);
CVar_SetFloat((cvarname + "A").c_str(), color.w);
SohImGui::RequestCvarSaveOnNextTick();
}
inline ImVec4 GetColorFromCVar(std::string cvarname) {
float R = CVar_GetFloat((cvarname + "R").c_str(), 255);
float G = CVar_GetFloat((cvarname + "G").c_str(), 255);
float B = CVar_GetFloat((cvarname + "B").c_str(), 255);
float A = CVar_GetFloat((cvarname + "A").c_str(), 255);
return ImVec4(R, G, B, A);
}
inline Color_RGBA8 ImVec4_to_Color_RGBA8(ImVec4& color) {
Color_RGBA8 ret;
ret.r = color.x * 255.0f;
ret.g = color.y * 255.0f;
ret.b = color.z * 255.0f;
ret.a = color.w * 255.0f;
return ret;
}
inline ImVec4 Color_RGBA8_to_ImVec4(Color_RGBA8& color) {
ImVec4 ret;
ret.x = color.r / 255.0f;
ret.y = color.g / 255.0f;
ret.z = color.b / 255.0f;
ret.w = color.a;
return ret;
}
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) {
Color_RGBA8 cvarMainColor = CVar_GetRGBA(cvarMainName, main_default_color);
Color_RGBA8 cvarExtraColor = CVar_GetRGBA(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, Color_RGBA8_to_ImVec4(main_color), Color_RGBA8_to_ImVec4(main_default_color))) {
main_color = CVar_GetRGBA(cvarMainName, main_default_color);
};
ImGui::PopItemWidth();
ImGui::TableNextColumn();
ImGui::AlignTextToFramePadding();
ImGui::PushItemWidth(ImGui::GetContentRegionAvail().x);
if (UIWidgets::EnhancementColor("Details", cvarExtraName, Color_RGBA8_to_ImVec4(extra_color),
Color_RGBA8_to_ImVec4(extra_default_color))) {
main_color = CVar_GetRGBA(cvarExtraName, extra_default_color);
}
ImGui::PopItemWidth();
ImGui::EndTable();
}
}
}
const char* windowType[] = { "Floating", "Window" };
const char* displayType[] = { "Always", "Combo Button Hold" };
const char* buttonStrings[] = { "A", "B", "C-Up", "C-Down", "C-Left", "C-Right", "L",
"Z", "R", "Start", "D-Up", "D-Down", "D-Left", "D-Right" };
void DrawCheckTrackerOptions(bool& open) {
if (!open) {
CVar_SetS32("gCheckTrackerSettingsEnabled", 0);
return;
}
ImGui::SetNextWindowSize(ImVec2(600, 375), ImGuiCond_FirstUseEver);
if (!ImGui::Begin("Check Tracker Settings", &open, 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::Text("BG Color");
ImGui::SameLine();
ImGui::PushItemWidth(ImGui::GetContentRegionAvail().x);
if (ImGui::ColorEdit4("BG Color##gCheckTrackerBgColor", (float*)&Color_Background, ImGuiColorEditFlags_AlphaPreview | ImGuiColorEditFlags_AlphaBar | ImGuiColorEditFlags_NoLabel)) {
CVar_SetFloat("gCheckTrackerBgColorR", Color_Background.x);
CVar_SetFloat("gCheckTrackerBgColorG", Color_Background.y);
CVar_SetFloat("gCheckTrackerBgColorB", Color_Background.z);
CVar_SetFloat("gCheckTrackerBgColorA", Color_Background.w);
SohImGui::RequestCvarSaveOnNextTick();
}
ImGui::PopItemWidth();
ImGui::Text("Window Type");
ImGui::SameLine();
UIWidgets::EnhancementCombobox("gCheckTrackerWindowType", windowType, 2, 1);
if (CVar_GetS32("gCheckTrackerWindowType", 0) == 0) {
UIWidgets::EnhancementCheckbox("Enable Dragging", "gCheckTrackerHudEditMode", 0);
UIWidgets::EnhancementCheckbox("Only enable while paused", "gCheckTrackerShowOnlyPaused", 0);
ImGui::Text("Display Mode");
ImGui::SameLine();
UIWidgets::EnhancementCombobox("gCheckTrackerDisplayType", displayType, 2, 0);
if (CVar_GetS32("gCheckTrackerDisplayType", 0) > 0) {
ImGui::Text("Combo Button 2");
ImGui::SameLine();
UIWidgets::EnhancementCombobox("gCheckTrackerComboButton1", buttonStrings, 14, 6);
ImGui::Text("Combo Button 1");
ImGui::SameLine();
UIWidgets::EnhancementCombobox("gCheckTrackerComboButton2", buttonStrings, 14, 8);
}
}
ImGui::TableNextColumn();
ImGuiDrawTwoColorPickerSection("Area Incomplete", "gCheckTrackerAreaMainIncompleteColor", "gCheckTrackerAreaExtraIncompleteColor", Color_Area_Incomplete_Main, Color_Area_Incomplete_Extra, Color_Main_Default, Color_Area_Incomplete_Extra_Default, "gCheckTrackerAreaIncompleteHide" );
ImGuiDrawTwoColorPickerSection("Area Complete", "gCheckTrackerAreaMainCompleteColor", "gCheckTrackerAreaExtraCompleteColor", Color_Area_Complete_Main, Color_Area_Complete_Extra, Color_Main_Default, Color_Area_Complete_Extra_Default, "gCheckTrackerAreaCompleteHide" );
ImGuiDrawTwoColorPickerSection("Unchecked", "gCheckTrackerUncheckedMainColor", "gCheckTrackerUncheckedExtraColor", Color_Unchecked_Main, Color_Unchecked_Extra, Color_Main_Default, Color_Unchecked_Extra_Default, "gCheckTrackerUncheckedHide" );
ImGuiDrawTwoColorPickerSection("Skipped", "gCheckTrackerSkippedMainColor", "gCheckTrackerSkippedExtraColor", Color_Skipped_Main, Color_Skipped_Extra, Color_Main_Default, Color_Skipped_Extra_Default, "gCheckTrackerSkippedHide" );
ImGuiDrawTwoColorPickerSection("Seen (WIP)", "gCheckTrackerSeenMainColor", "gCheckTrackerSeenExtraColor", Color_Seen_Main, Color_Seen_Extra, Color_Main_Default, Color_Seen_Extra_Default, "gCheckTrackerSeenHide" );
ImGuiDrawTwoColorPickerSection("Hinted (WIP)", "gCheckTrackerHintedMainColor", "gCheckTrackerHintedExtraColor", Color_Hinted_Main, Color_Hinted_Extra, Color_Main_Default, Color_Hinted_Extra_Default, "gCheckTrackerHintedHide" );
ImGuiDrawTwoColorPickerSection("Checked (WIP)", "gCheckTrackerCheckedMainColor", "gCheckTrackerCheckedExtraColor", Color_Checked_Main, Color_Checked_Extra, Color_Main_Default, Color_Checked_Extra_Default, "gCheckTrackerCheckedHide" );
ImGuiDrawTwoColorPickerSection("Scummed (WIP)", "gCheckTrackerScummedMainColor", "gCheckTrackerScummedExtraColor", Color_Scummed_Main, Color_Scummed_Extra, Color_Main_Default, Color_Scummed_Extra_Default, "gCheckTrackerScummedHide" );
ImGuiDrawTwoColorPickerSection("Saved", "gCheckTrackerSavedMainColor", "gCheckTrackerSavedExtraColor", Color_Saved_Main, Color_Saved_Extra, Color_Main_Default, Color_Saved_Extra_Default, "gCheckTrackerSavedHide" );
ImGui::PopStyleVar(1);
ImGui::EndTable();
ImGui::End();
}
void InitCheckTracker() {
SohImGui::AddWindow("Randomizer", "Check Tracker", DrawCheckTracker, CVar_GetS32("gCheckTrackerEnabled", 0) == 1);
SohImGui::AddWindow("Randomizer", "Check Tracker Settings", DrawCheckTrackerOptions);
float trackerBgR = CVar_GetFloat("gCheckTrackerBgColorR", 0);
float trackerBgG = CVar_GetFloat("gCheckTrackerBgColorG", 0);
float trackerBgB = CVar_GetFloat("gCheckTrackerBgColorB", 0);
float trackerBgA = CVar_GetFloat("gCheckTrackerBgColorA", 1);
Color_Background = { trackerBgR, trackerBgG, trackerBgB, trackerBgA }; // Float value, 1 = 255 in rgb value.
Color_Area_Incomplete_Main = CVar_GetRGBA("gCheckTrackerAreaMainIncompleteColor", Color_Main_Default);
Color_Area_Incomplete_Extra = CVar_GetRGBA("gCheckTrackerAreaExtraIncompleteColor", Color_Area_Incomplete_Extra_Default);
Color_Area_Complete_Main = CVar_GetRGBA("gCheckTrackerAreaMainCompleteColor", Color_Main_Default);
Color_Area_Complete_Extra = CVar_GetRGBA("gCheckTrackerAreaExtraCompleteColor", Color_Area_Complete_Extra_Default);
Color_Unchecked_Main = CVar_GetRGBA("gCheckTrackerUncheckedMainColor", Color_Main_Default);
Color_Unchecked_Extra = CVar_GetRGBA("gCheckTrackerUncheckedExtraColor", Color_Unchecked_Extra_Default);
Color_Skipped_Main = CVar_GetRGBA("gCheckTrackerSkippedMainColor", Color_Main_Default);
Color_Skipped_Extra = CVar_GetRGBA("gCheckTrackerSkippedExtraColor", Color_Skipped_Extra_Default);
Color_Seen_Main = CVar_GetRGBA("gCheckTrackerSeenMainColor", Color_Main_Default);
Color_Seen_Extra = CVar_GetRGBA("gCheckTrackerSeenExtraColor", Color_Seen_Extra_Default);
Color_Hinted_Main = CVar_GetRGBA("gCheckTrackerHintedMainColor", Color_Main_Default);
Color_Hinted_Extra = CVar_GetRGBA("gCheckTrackerHintedExtraColor", Color_Hinted_Extra_Default);
Color_Checked_Main = CVar_GetRGBA("gCheckTrackerCheckedMainColor", Color_Main_Default);
Color_Checked_Extra = CVar_GetRGBA("gCheckTrackerCheckedExtraColor", Color_Checked_Extra_Default);
Color_Scummed_Main = CVar_GetRGBA("gCheckTrackerScummedMainColor", Color_Main_Default);
Color_Scummed_Extra = CVar_GetRGBA("gCheckTrackerScummedExtraColor", Color_Scummed_Extra_Default);
Color_Saved_Main = CVar_GetRGBA("gCheckTrackerSavedMainColor", Color_Main_Default);
Color_Saved_Extra = CVar_GetRGBA("gCheckTrackerSavedExtraColor", Color_Saved_Extra_Default);
Ship::RegisterHook<Ship::ControllerRead>([](OSContPad* cont_pad) {
trackerButtonsPressed = cont_pad;
});
Ship::RegisterHook<Ship::LoadFile>([](uint32_t fileNum) {
doInitialize = true;
});
Ship::RegisterHook<Ship::DeleteFile>([](uint32_t fileNum) {
Teardown();
});
LocationTable_Init();
}

View File

@ -0,0 +1,15 @@
#pragma once
// Check tracker check visibility categories
typedef enum {
RCSHOW_UNCHECKED,
RCSHOW_SKIPPED,
RCSHOW_SEEN,
RCSHOW_HINTED,
RCSHOW_CHECKED,
RCSHOW_SCUMMED,
RCSHOW_SAVED,
} RandomizerCheckShow;
void InitCheckTracker();
void DrawCheckTracker(bool& open);

View File

@ -7,10 +7,8 @@
#include <map>
#include <string>
#include <vector>
#include <set>
#include <Cvar.h>
#include <Hooks.h>
#include "3drando/item_location.hpp"
extern "C" {
#include <z64.h>
@ -32,10 +30,6 @@ void DrawQuest(ItemTrackerItem item);
void DrawSong(ItemTrackerItem item);
OSContPad* buttonsPressed;
std::set<RandomizerCheck> checkedLocations;
std::set<RandomizerCheck> skippedLocations;
std::set<RandomizerCheck> prevCheckedLocations;
RandomizerCheck lastLocationChecked;
bool shouldUpdateVectors = true;
@ -639,261 +633,6 @@ void DrawNotes(bool resizeable = false) {
ImGui::EndGroup();
}
bool HasItemBeenSkipped(RandomizerCheckObject obj) {
return skippedLocations.find(obj.rc) != skippedLocations.end();
}
bool HasItemBeenCollected(RandomizerCheckObject obj) {
// TODO doesn't consider vanilla/MQ?
// TODO move all the code to a static function in item_location
// return Location(obj.rc)->GetCollectionCheck().IsChecked(gSaveContext);
ItemLocation* x = Location(obj.rc);
SpoilerCollectionCheck check = x->GetCollectionCheck();
auto flag = check.flag;
auto scene = check.scene;
auto type = check.type;
int shift;
int mask;
switch (type) {
case SpoilerCollectionCheckType::SPOILER_CHK_ALWAYS_COLLECTED:
return true;
case SpoilerCollectionCheckType::SPOILER_CHK_BIGGORON:
return gSaveContext.bgsFlag & flag;
case SpoilerCollectionCheckType::SPOILER_CHK_CHEST:
return gSaveContext.sceneFlags[scene].chest & (1 << flag);
case SpoilerCollectionCheckType::SPOILER_CHK_COLLECTABLE:
return 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_SCRUB:
case SpoilerCollectionCheckType::SPOILER_CHK_RANDOMIZER_INF:
return Flags_GetRandomizerInf(randomizerFlagLookup[obj.rc]); //TODO randomizer.cpp has rcToRandomizerInf
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:
// Magic to flip an index `flag` to a lookup for 16bit big endian integers. Probably an easier way.....
shift = 7 - (flag % 8) + ((flag % 16) / 8) * 8;
mask = 0x8000 >> shift;
return gSaveContext.infTable[scene] & mask;
case SpoilerCollectionCheckType::SPOILER_CHK_ITEM_GET_INF:
// Magic to flip an index `flag` to a lookup for 16bit big endian integers. Probably an easier way.....
shift = 7 - (flag % 8) + ((flag % 16) / 8) * 8;
mask = 0x8000 >> shift;
return gSaveContext.itemGetInf[flag / 16] & mask;
case SpoilerCollectionCheckType::SPOILER_CHK_MAGIC_BEANS:
return BEANS_BOUGHT >= 10;
case SpoilerCollectionCheckType::SPOILER_CHK_MINIGAME:
if (obj.rc == RC_LH_CHILD_FISHING)
return HIGH_SCORE(HS_FISHING) & 0x400;
if (obj.rc == RC_LH_ADULT_FISHING)
return HIGH_SCORE(HS_FISHING) & 0x800;
case SpoilerCollectionCheckType::SPOILER_CHK_NONE:
return false;
case SpoilerCollectionCheckType::SPOILER_CHK_POE_POINTS:
return gSaveContext.highScores[HS_POE_POINTS] >= 1000;
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) ||
CVar_GetS32("gGravediggingTourFix", 0) && gSaveContext.sceneFlags[scene].collect & (1 << flag);
default:
return false;
}
return false;
}
RandomizerCheckArea lastArea = RCAREA_INVALID;
void DrawLocations() {
if (ImGui::BeginTable("tableRandoChecks", 2, ImGuiTableFlags_BordersH | ImGuiTableFlags_BordersV)) {
ImGui::TableSetupColumn("To Check", ImGuiTableColumnFlags_WidthStretch, 200.0f);
ImGui::TableSetupColumn("Checked", ImGuiTableColumnFlags_WidthStretch, 200.0f);
ImGui::TableHeadersRow();
ImGui::TableNextRow();
// COLUMN 1 - TO CHECK LOCATIONS
ImGui::TableNextColumn();
static ImGuiTextFilter locationSearch;
locationSearch.Draw();
bool lastItemFound = false;
bool doAreaScroll = false;
bool inGame = gPlayState != nullptr && gSaveContext.fileNum >= 0 && gSaveContext.fileNum <= 2;
RandomizerCheckArea currentArea = RCAREA_INVALID;
SceneID sceneId = SCENE_ID_MAX;
if (gPlayState != nullptr) {
sceneId = (SceneID)gPlayState->sceneNum;
currentArea = RandomizerCheckObjects::GetRCAreaBySceneID(sceneId);
}
ImGui::BeginChild("ChildToCheckLocations", ImVec2(0, -8));
for (auto& [rcArea, rcObjects] : RandomizerCheckObjects::GetAllRCObjectsByArea()) {
bool hasItems = false;
for (auto& locationIt : rcObjects) {
if (!locationIt.second.visibleInImgui)
continue;
if (!checkedLocations.count(locationIt.second.rc) && !skippedLocations.count(locationIt.second.rc) &&
locationSearch.PassFilter(locationIt.second.rcSpoilerName.c_str())) {
hasItems = true;
doAreaScroll =
(currentArea != RCAREA_INVALID && sceneId != SCENE_KAKUSIANA && // Don't move for grottos
sceneId != SCENE_YOUSEI_IZUMI_TATE && sceneId != SCENE_YOUSEI_IZUMI_YOKO && // Don't move for fairy fountains
sceneId != SCENE_SHOP1 && sceneId != SCENE_SYATEKIJYOU && // Don't move for Bazaar/Gallery, as it moves between Kak and Market
currentArea != lastArea && currentArea == rcArea);
break;
}
}
if (hasItems) {
ImGui::SetNextItemOpen(true, ImGuiCond_Once);
if (ImGui::TreeNode(RandomizerCheckObjects::GetRCAreaName(rcArea).c_str())) {
if (doAreaScroll) {
ImGui::SetScrollHereY(0.0f);
doAreaScroll = false;
}
for (auto& locationIt : rcObjects) {
if (!locationIt.second.visibleInImgui)
continue;
bool checked = HasItemBeenCollected(locationIt.second);
bool skipped = HasItemBeenSkipped(locationIt.second);
// If the location has its scene flag set
if (inGame && checked) {
// show it as checked
checkedLocations.insert(locationIt.second.rc);
if (skipped)
skippedLocations.erase(locationIt.second.rc);
if (!lastItemFound &&
prevCheckedLocations.find(locationIt.second.rc) == prevCheckedLocations.end()) {
lastItemFound = true;
prevCheckedLocations.insert(locationIt.second.rc);
lastLocationChecked = locationIt.second.rc;
}
}
if (locationIt.second.visibleInImgui &&
!checkedLocations.count(locationIt.second.rc) && !skippedLocations.count(locationIt.second.rc) &&
locationSearch.PassFilter(locationIt.second.rcSpoilerName.c_str())) {
if (ImGui::ArrowButton(std::to_string(locationIt.second.rc).c_str(), ImGuiDir_Right)) {
skippedLocations.insert(locationIt.second.rc);
} else {
ImGui::SameLine();
ImGui::Text(locationIt.second.rcShortName.c_str());
}
}
}
ImGui::TreePop();
}
}
}
ImGui::EndChild();
// COLUMN 2 - CHECKED LOCATIONS
doAreaScroll = false;
ImGui::TableNextColumn();
ImGui::BeginChild("ChildCheckedLocations", ImVec2(0, -8));
for (auto& [rcArea, rcObjects] : RandomizerCheckObjects::GetAllRCObjectsByArea()) {
bool hasItems = false;
for (auto& locationIt : rcObjects) {
if (!locationIt.second.visibleInImgui)
continue;
if (checkedLocations.count(locationIt.second.rc) || skippedLocations.count(locationIt.second.rc)) {
hasItems = true;
doAreaScroll =
(currentArea != RCAREA_INVALID && sceneId != SCENE_KAKUSIANA && // Don't move for kakusiana/grottos
sceneId != SCENE_YOUSEI_IZUMI_TATE && sceneId != SCENE_YOUSEI_IZUMI_YOKO && // Don't move for fairy fountains
sceneId != SCENE_SHOP1 && sceneId != SCENE_SYATEKIJYOU && // Don't move for Bazaar/Gallery, as it moves between Kak and Market
currentArea != lastArea && currentArea == rcArea);
break;
}
}
if (hasItems) {
ImGui::SetNextItemOpen(true, ImGuiCond_Once);
if (ImGui::TreeNode(RandomizerCheckObjects::GetRCAreaName(rcArea).c_str())) {
if (doAreaScroll) {
ImGui::SetScrollHereY(0.0f);
doAreaScroll = false;
}
for (auto& locationIt : rcObjects) {
if (!locationIt.second.visibleInImgui)
continue;
bool checked = HasItemBeenCollected(locationIt.second);
bool skipped = HasItemBeenSkipped(locationIt.second);
auto elfound = checkedLocations.find(locationIt.second.rc);
auto skfound = skippedLocations.find(locationIt.second.rc);
if (locationIt.second.visibleInImgui && (elfound != checkedLocations.end() || skfound != skippedLocations.end())) {
// If the location has its scene flag set
if (!inGame || (!checked && !skipped)) {
// show it as unchecked
if (!checked && elfound != checkedLocations.end())
checkedLocations.erase(elfound);
if (!skipped && skfound != skippedLocations.end())
skippedLocations.erase(skfound);
} else if (skipped && ImGui::ArrowButton(std::to_string(locationIt.second.rc).c_str(), ImGuiDir_Left)) {
if (skipped)
skippedLocations.erase(skfound);
} else if (!skipped) {
float sz = ImGui::GetFrameHeight();
ImGui::InvisibleButton("", ImVec2(sz, sz));
}
ImGui::SameLine();
std::string txt =
(lastLocationChecked == locationIt.second.rc ? "* " : "") + // Indicate the last location checked (before app reset at least)
locationIt.second.rcShortName;
if (skipped)
ImGui::PushStyleColor(ImGuiCol_Text, IM_COL32(160, 160, 160, 255));
ImGui::Text(txt.c_str());
if (!skipped)
ImGui::PushStyleColor(ImGuiCol_Text, IM_COL32(0, 185, 0, 255));
if (skipped)
txt = "Skipped";
else
txt = OTRGlobals::Instance->gRandomizer
->EnumToSpoilerfileGetName[gSaveContext.itemLocations[locationIt.second.rc].get.rgID][gSaveContext.language];
ImGui::SameLine();
ImGui::Text("(%s)", txt.c_str());
ImGui::PopStyleColor();
}
}
ImGui::TreePop();
}
}
}
ImGui::EndChild();
ImGui::EndTable();
if (sceneId != SCENE_KAKUSIANA &&
sceneId != SCENE_YOUSEI_IZUMI_TATE &&
sceneId != SCENE_YOUSEI_IZUMI_YOKO &&
sceneId != SCENE_SYATEKIJYOU &&
sceneId != SCENE_SHOP1)
lastArea = currentArea;
}
}
// Windowing stuff
ImVec4 ChromaKeyBackground = { 0, 0, 0, 0 }; // Float value, 1 = 255 in rgb value.
void BeginFloatingWindows(std::string UniqueName, ImGuiWindowFlags flags = 0) {
@ -1057,6 +796,7 @@ void UpdateVectors() {
if (!shouldUpdateVectors) {
return;
}
dungeonRewards.clear();
dungeonRewards.insert(dungeonRewards.end(), dungeonRewardStones.begin(), dungeonRewardStones.end());
dungeonRewards.insert(dungeonRewards.end(), dungeonRewardMedallions.begin(), dungeonRewardMedallions.end());
@ -1202,13 +942,6 @@ void DrawItemTracker(bool& open) {
DrawNotes(true);
EndFloatingWindows();
}
if (CVar_GetS32("gItemTrackerLocationDisplayType", 0) == 2 && CVar_GetS32("gItemTrackerDisplayType", 0) == 0) {
ImGui::SetNextWindowSize(ImVec2(600, 1000), ImGuiCond_FirstUseEver);
BeginFloatingWindows("Check Tracker", ImGuiWindowFlags_NoFocusOnAppearing);
DrawLocations();
EndFloatingWindows();
}
}
}
@ -1294,7 +1027,6 @@ void DrawItemTrackerOptions(bool& open) {
if (CVar_GetS32("gItemTrackerDisplayType", 0) != 1) {
LabeledComboBoxRightAligned("Personal notes", "gItemTrackerNotesDisplayType", { "Hidden", "Main Window", "Seperate" }, 0);
}
LabeledComboBoxRightAligned("Location Tracker", "gItemTrackerLocationDisplayType", { "Hidden", "Main Window (WIP)", "Separate" }, 0);
ImGui::PopStyleVar(1);
ImGui::EndTable();
@ -1325,12 +1057,9 @@ void InitItemTracker() {
Ship::RegisterHook<Ship::LoadFile>([](uint32_t fileNum) {
const char* initialTrackerNotes = CVar_GetString(("gItemTrackerNotes" + std::to_string(fileNum)).c_str(), "");
strcpy(itemTrackerNotes.Data, initialTrackerNotes);
RandomizerCheckObjects::UpdateTrackerImGuiVisibility();
});
Ship::RegisterHook<Ship::DeleteFile>([](uint32_t fileNum) {
CVar_SetString(("gItemTrackerNotes" + std::to_string(fileNum)).c_str(), "");
SohImGui::RequestCvarSaveOnNextTick();
});
RandomizerCheckObjects::UpdateTrackerImGuiVisibility();
LocationTable_Init();
}
}

View File

@ -1589,6 +1589,22 @@ namespace GameMenuBar {
SohImGui::RequestCvarSaveOnNextTick();
SohImGui::EnableWindow("Item Tracker Settings", CVar_GetS32("gItemTrackerSettingsEnabled", 0));
}
ImGui::Dummy(ImVec2(0.0f, 0.0f));
if (ImGui::Button(GetWindowButtonText("Check Tracker", CVar_GetS32("gCheckTrackerEnabled", 0)).c_str(), buttonSize))
{
bool currentValue = CVar_GetS32("gCheckTrackerEnabled", 0);
CVar_SetS32("gCheckTrackerEnabled", !currentValue);
SohImGui::RequestCvarSaveOnNextTick();
SohImGui::EnableWindow("Check Tracker", CVar_GetS32("gCheckTrackerEnabled", 0));
}
ImGui::Dummy(ImVec2(0.0f, 0.0f));
if (ImGui::Button(GetWindowButtonText("Check Tracker Settings", CVar_GetS32("gCheckTrackerSettingsEnabled", 0)).c_str(), buttonSize))
{
bool currentValue = CVar_GetS32("gCheckTrackerSettingsEnabled", 0);
CVar_SetS32("gCheckTrackerSettingsEnabled", !currentValue);
SohImGui::RequestCvarSaveOnNextTick();
SohImGui::EnableWindow("Check Tracker Settings", CVar_GetS32("gCheckTrackerSettingsEnabled", 0));
}
ImGui::PopStyleVar(3);
ImGui::PopStyleColor(1);
#ifdef ENABLE_CROWD_CONTROL

View File

@ -38,6 +38,7 @@
#include "Enhancements/debugger/debugger.h"
#include "Enhancements/randomizer/randomizer.h"
#include "Enhancements/randomizer/randomizer_item_tracker.h"
#include "Enhancements/randomizer/randomizer_check_tracker.h"
#include "Enhancements/randomizer/3drando/random.hpp"
#include "Enhancements/n64_weird_frame_data.inc"
#include "frame_interpolation.h"
@ -445,6 +446,7 @@ extern "C" void InitOTR() {
Debug_Init();
Rando_Init();
InitItemTracker();
InitCheckTracker();
OTRExtScanner();
VanillaItemTable_Init();