diff --git a/soh/soh/Enhancements/randomizer/randomizer_item_tracker.cpp b/soh/soh/Enhancements/randomizer/randomizer_item_tracker.cpp index e1e5892e1b..dc01c4168a 100644 --- a/soh/soh/Enhancements/randomizer/randomizer_item_tracker.cpp +++ b/soh/soh/Enhancements/randomizer/randomizer_item_tracker.cpp @@ -1,5 +1,7 @@ #include +#include #include +#include #include #include @@ -280,65 +282,83 @@ std::vector silverRupeeItems = { ITEM_TRACKER_RG_CUSTOM(RG_GANONS_CASTLE_MQ_SILVER_SHADOW, "ShaTri", ITEM_RUPEE_SILVER, 0, DrawItem), }; -std::vector itemTrackerDungeonsWithMapsHorizontal = { - { SCENE_DEKU_TREE, { ITEM_DUNGEON_MAP, ITEM_COMPASS } }, - { SCENE_DODONGOS_CAVERN, { ITEM_DUNGEON_MAP, ITEM_COMPASS } }, - { SCENE_JABU_JABU, { ITEM_DUNGEON_MAP, ITEM_COMPASS } }, - { SCENE_FOREST_TEMPLE, { ITEM_KEY_SMALL, ITEM_KEY_BOSS, ITEM_DUNGEON_MAP, ITEM_COMPASS } }, - { SCENE_FIRE_TEMPLE, { ITEM_KEY_SMALL, ITEM_KEY_BOSS, ITEM_DUNGEON_MAP, ITEM_COMPASS } }, - { SCENE_WATER_TEMPLE, { ITEM_KEY_SMALL, ITEM_KEY_BOSS, ITEM_DUNGEON_MAP, ITEM_COMPASS } }, - { SCENE_SPIRIT_TEMPLE, { ITEM_KEY_SMALL, ITEM_KEY_BOSS, ITEM_DUNGEON_MAP, ITEM_COMPASS } }, - { SCENE_SHADOW_TEMPLE, { ITEM_KEY_SMALL, ITEM_KEY_BOSS, ITEM_DUNGEON_MAP, ITEM_COMPASS } }, - { SCENE_INSIDE_GANONS_CASTLE, { ITEM_KEY_SMALL, ITEM_KEY_BOSS } }, - { SCENE_BOTTOM_OF_THE_WELL, { ITEM_KEY_SMALL, ITEM_DUNGEON_MAP, ITEM_COMPASS } }, - { SCENE_ICE_CAVERN, { ITEM_DUNGEON_MAP, ITEM_COMPASS } }, - { SCENE_GERUDO_TRAINING_GROUND, { ITEM_KEY_SMALL } }, -}; +// Dungeon items stacked top to bottom in one grid column, scene in data. Unused slots have no drawFunc +using ItemTrackerDungeonColumn = std::array; -std::vector itemTrackerDungeonsHorizontal = { - { SCENE_FOREST_TEMPLE, { ITEM_KEY_SMALL, ITEM_KEY_BOSS } }, - { SCENE_FIRE_TEMPLE, { ITEM_KEY_SMALL, ITEM_KEY_BOSS } }, - { SCENE_WATER_TEMPLE, { ITEM_KEY_SMALL, ITEM_KEY_BOSS } }, - { SCENE_SPIRIT_TEMPLE, { ITEM_KEY_SMALL, ITEM_KEY_BOSS } }, - { SCENE_SHADOW_TEMPLE, { ITEM_KEY_SMALL, ITEM_KEY_BOSS } }, - { SCENE_INSIDE_GANONS_CASTLE, { ITEM_KEY_SMALL, ITEM_KEY_BOSS } }, - { SCENE_BOTTOM_OF_THE_WELL, { ITEM_KEY_SMALL } }, - { SCENE_GERUDO_TRAINING_GROUND, { ITEM_KEY_SMALL } }, -}; +static ItemTrackerItem Key(SceneID scene) { + return ITEM_TRACKER_ITEM(ITEM_KEY_SMALL, "", static_cast(scene), DrawDungeonItem); +} +static ItemTrackerItem Boss(SceneID scene) { + return ITEM_TRACKER_ITEM(ITEM_KEY_BOSS, "", static_cast(scene), DrawDungeonItem); +} +static ItemTrackerItem Map(SceneID scene) { + return ITEM_TRACKER_ITEM(ITEM_DUNGEON_MAP, "", static_cast(scene), DrawDungeonItem); +} +static ItemTrackerItem Compass(SceneID scene) { + return ITEM_TRACKER_ITEM(ITEM_COMPASS, "", static_cast(scene), DrawDungeonItem); +} -std::vector itemTrackerDungeonsWithMapsCompact = { - { SCENE_FOREST_TEMPLE, { ITEM_KEY_SMALL, ITEM_KEY_BOSS, ITEM_DUNGEON_MAP, ITEM_COMPASS } }, - { SCENE_FIRE_TEMPLE, { ITEM_KEY_SMALL, ITEM_KEY_BOSS, ITEM_DUNGEON_MAP, ITEM_COMPASS } }, - { SCENE_WATER_TEMPLE, { ITEM_KEY_SMALL, ITEM_KEY_BOSS, ITEM_DUNGEON_MAP, ITEM_COMPASS } }, - { SCENE_SPIRIT_TEMPLE, { ITEM_KEY_SMALL, ITEM_KEY_BOSS, ITEM_DUNGEON_MAP, ITEM_COMPASS } }, - { SCENE_SHADOW_TEMPLE, { ITEM_KEY_SMALL, ITEM_KEY_BOSS, ITEM_DUNGEON_MAP, ITEM_COMPASS } }, - { SCENE_BOTTOM_OF_THE_WELL, { ITEM_KEY_SMALL, ITEM_DUNGEON_MAP, ITEM_COMPASS } }, - { SCENE_DEKU_TREE, { ITEM_DUNGEON_MAP, ITEM_COMPASS } }, - { SCENE_DODONGOS_CAVERN, { ITEM_DUNGEON_MAP, ITEM_COMPASS } }, - { SCENE_JABU_JABU, { ITEM_DUNGEON_MAP, ITEM_COMPASS } }, - { SCENE_ICE_CAVERN, { ITEM_DUNGEON_MAP, ITEM_COMPASS } }, - { SCENE_INSIDE_GANONS_CASTLE, { ITEM_KEY_SMALL, ITEM_KEY_BOSS } }, - { SCENE_GERUDO_TRAINING_GROUND, { ITEM_KEY_SMALL } }, -}; +static const auto itemTrackerDungeonsWithMapsHorizontal = std::to_array({ + { Map(SCENE_DEKU_TREE), Compass(SCENE_DEKU_TREE) }, + { Map(SCENE_DODONGOS_CAVERN), Compass(SCENE_DODONGOS_CAVERN) }, + { Map(SCENE_JABU_JABU), Compass(SCENE_JABU_JABU) }, + { Key(SCENE_FOREST_TEMPLE), Boss(SCENE_FOREST_TEMPLE), Map(SCENE_FOREST_TEMPLE), Compass(SCENE_FOREST_TEMPLE) }, + { Key(SCENE_FIRE_TEMPLE), Boss(SCENE_FIRE_TEMPLE), Map(SCENE_FIRE_TEMPLE), Compass(SCENE_FIRE_TEMPLE) }, + { Key(SCENE_WATER_TEMPLE), Boss(SCENE_WATER_TEMPLE), Map(SCENE_WATER_TEMPLE), Compass(SCENE_WATER_TEMPLE) }, + { Key(SCENE_SPIRIT_TEMPLE), Boss(SCENE_SPIRIT_TEMPLE), Map(SCENE_SPIRIT_TEMPLE), Compass(SCENE_SPIRIT_TEMPLE) }, + { Key(SCENE_SHADOW_TEMPLE), Boss(SCENE_SHADOW_TEMPLE), Map(SCENE_SHADOW_TEMPLE), Compass(SCENE_SHADOW_TEMPLE) }, + { Key(SCENE_INSIDE_GANONS_CASTLE), Boss(SCENE_GANONS_TOWER) }, + { Key(SCENE_BOTTOM_OF_THE_WELL), Map(SCENE_BOTTOM_OF_THE_WELL), Compass(SCENE_BOTTOM_OF_THE_WELL) }, + { Map(SCENE_ICE_CAVERN), Compass(SCENE_ICE_CAVERN) }, + { Key(SCENE_GERUDO_TRAINING_GROUND), Key(SCENE_THIEVES_HIDEOUT), Key(SCENE_TREASURE_BOX_SHOP) }, +}); -std::vector itemTrackerDungeonsCompact = { - { SCENE_FOREST_TEMPLE, { ITEM_KEY_SMALL, ITEM_KEY_BOSS } }, - { SCENE_FIRE_TEMPLE, { ITEM_KEY_SMALL, ITEM_KEY_BOSS } }, - { SCENE_WATER_TEMPLE, { ITEM_KEY_SMALL, ITEM_KEY_BOSS } }, - { SCENE_SPIRIT_TEMPLE, { ITEM_KEY_SMALL, ITEM_KEY_BOSS } }, - { SCENE_SHADOW_TEMPLE, { ITEM_KEY_SMALL, ITEM_KEY_BOSS } }, - { SCENE_INSIDE_GANONS_CASTLE, { ITEM_KEY_SMALL, ITEM_KEY_BOSS } }, - { SCENE_BOTTOM_OF_THE_WELL, { ITEM_KEY_SMALL } }, - { SCENE_GERUDO_TRAINING_GROUND, { ITEM_KEY_SMALL } }, - { SCENE_THIEVES_HIDEOUT, { ITEM_KEY_SMALL } }, -}; +static const auto itemTrackerDungeonsHorizontal = std::to_array({ + { Key(SCENE_FOREST_TEMPLE), Boss(SCENE_FOREST_TEMPLE) }, + { Key(SCENE_FIRE_TEMPLE), Boss(SCENE_FIRE_TEMPLE) }, + { Key(SCENE_WATER_TEMPLE), Boss(SCENE_WATER_TEMPLE) }, + { Key(SCENE_SPIRIT_TEMPLE), Boss(SCENE_SPIRIT_TEMPLE) }, + { Key(SCENE_SHADOW_TEMPLE), Boss(SCENE_SHADOW_TEMPLE) }, + { Key(SCENE_INSIDE_GANONS_CASTLE), Boss(SCENE_GANONS_TOWER) }, + { Key(SCENE_BOTTOM_OF_THE_WELL), Key(SCENE_TREASURE_BOX_SHOP) }, + { Key(SCENE_GERUDO_TRAINING_GROUND), Key(SCENE_THIEVES_HIDEOUT) }, +}); + +static const auto itemTrackerDungeonsWithMapsCompact = std::to_array({ + { Key(SCENE_FOREST_TEMPLE), Boss(SCENE_FOREST_TEMPLE), Map(SCENE_FOREST_TEMPLE), Compass(SCENE_FOREST_TEMPLE) }, + { Key(SCENE_FIRE_TEMPLE), Boss(SCENE_FIRE_TEMPLE), Map(SCENE_FIRE_TEMPLE), Compass(SCENE_FIRE_TEMPLE) }, + { Key(SCENE_WATER_TEMPLE), Boss(SCENE_WATER_TEMPLE), Map(SCENE_WATER_TEMPLE), Compass(SCENE_WATER_TEMPLE) }, + { Key(SCENE_SPIRIT_TEMPLE), Boss(SCENE_SPIRIT_TEMPLE), Map(SCENE_SPIRIT_TEMPLE), Compass(SCENE_SPIRIT_TEMPLE) }, + { Key(SCENE_SHADOW_TEMPLE), Boss(SCENE_SHADOW_TEMPLE), Map(SCENE_SHADOW_TEMPLE), Compass(SCENE_SHADOW_TEMPLE) }, + { Key(SCENE_BOTTOM_OF_THE_WELL), Map(SCENE_BOTTOM_OF_THE_WELL), Compass(SCENE_BOTTOM_OF_THE_WELL), + Key(SCENE_TREASURE_BOX_SHOP) }, + { Map(SCENE_DEKU_TREE), Compass(SCENE_DEKU_TREE) }, + { Map(SCENE_DODONGOS_CAVERN), Compass(SCENE_DODONGOS_CAVERN) }, + { Map(SCENE_JABU_JABU), Compass(SCENE_JABU_JABU) }, + { Map(SCENE_ICE_CAVERN), Compass(SCENE_ICE_CAVERN) }, + { Key(SCENE_INSIDE_GANONS_CASTLE), Boss(SCENE_GANONS_TOWER) }, + { Key(SCENE_GERUDO_TRAINING_GROUND), Key(SCENE_THIEVES_HIDEOUT) }, +}); + +static const auto itemTrackerDungeonsCompact = std::to_array({ + { Key(SCENE_FOREST_TEMPLE), Boss(SCENE_FOREST_TEMPLE) }, + { Key(SCENE_FIRE_TEMPLE), Boss(SCENE_FIRE_TEMPLE) }, + { Key(SCENE_WATER_TEMPLE), Boss(SCENE_WATER_TEMPLE) }, + { Key(SCENE_SPIRIT_TEMPLE), Boss(SCENE_SPIRIT_TEMPLE) }, + { Key(SCENE_SHADOW_TEMPLE), Boss(SCENE_SHADOW_TEMPLE) }, + { Key(SCENE_INSIDE_GANONS_CASTLE), Boss(SCENE_GANONS_TOWER) }, + { Key(SCENE_BOTTOM_OF_THE_WELL) }, + { Key(SCENE_GERUDO_TRAINING_GROUND) }, + { Key(SCENE_THIEVES_HIDEOUT) }, + { Key(SCENE_TREASURE_BOX_SHOP) }, +}); std::map itemTrackerDungeonShortNames = { { SCENE_FOREST_TEMPLE, "FRST" }, { SCENE_FIRE_TEMPLE, "FIRE" }, { SCENE_WATER_TEMPLE, "WATR" }, { SCENE_SPIRIT_TEMPLE, "SPRT" }, { SCENE_SHADOW_TEMPLE, "SHDW" }, { SCENE_BOTTOM_OF_THE_WELL, "BOTW" }, { SCENE_DEKU_TREE, "DEKU" }, { SCENE_DODONGOS_CAVERN, "DCVN" }, { SCENE_JABU_JABU, "JABU" }, { SCENE_ICE_CAVERN, "ICE" }, { SCENE_INSIDE_GANONS_CASTLE, "GANON" }, { SCENE_GERUDO_TRAINING_GROUND, "GTG" }, - { SCENE_THIEVES_HIDEOUT, "HIDE" }, + { SCENE_THIEVES_HIDEOUT, "HIDE" }, { SCENE_TREASURE_BOX_SHOP, "TCG" }, }; std::vector dungeonItems = {}; @@ -1424,55 +1444,56 @@ void DrawItemsInACircle(std::vector items) { } } +static bool ShowDungeonItem(const ItemTrackerItem& item) { + if (item.data == SCENE_TREASURE_BOX_SHOP) { + return IS_RANDO && RAND_GET_OPTION(RSK_SHUFFLE_CHEST_MINIGAME); + } + if (item.data == SCENE_THIEVES_HIDEOUT) { + return !IS_RANDO || RAND_GET_OPTION(RSK_GERUDO_FORTRESS).IsNot(RO_GF_CARPENTERS_FREE); + } + return true; +} + /** * GetDungeonItemsVector - * Loops over dungeons and creates vectors of items in the correct order - * to then call DrawItemsInRows + * Lays out columns in blocks of `columns` wide, each block as tall as its tallest column, + * and flattens them row by row for DrawItemsInRows */ -std::vector GetDungeonItemsVector(std::vector dungeons, size_t columns = 6) { - std::vector dungeonItems = {}; - - size_t rowCount = 0; - for (size_t i = 0; i < dungeons.size(); i++) { - if (dungeons[i].items.size() > rowCount) - rowCount = static_cast(dungeons[i].items.size()); - } - - for (size_t i = 0; i < rowCount; i++) { - for (size_t j = 0; j < std::min(dungeons.size(), columns); j++) { - if (dungeons[j].items.size() > i) { - switch (dungeons[j].items[i]) { - case ITEM_KEY_SMALL: - dungeonItems.push_back(ITEM_TRACKER_ITEM(ITEM_KEY_SMALL, "", dungeons[j].id, DrawDungeonItem)); - break; - case ITEM_KEY_BOSS: - // Swap Ganon's Castle boss key to the right scene ID manually - if (dungeons[j].id == SCENE_INSIDE_GANONS_CASTLE) { - dungeonItems.push_back( - ITEM_TRACKER_ITEM(ITEM_KEY_BOSS, "", SCENE_GANONS_TOWER, DrawDungeonItem)); - } else { - dungeonItems.push_back( - ITEM_TRACKER_ITEM(ITEM_KEY_BOSS, "", dungeons[j].id, DrawDungeonItem)); - } - break; - case ITEM_DUNGEON_MAP: - dungeonItems.push_back( - ITEM_TRACKER_ITEM(ITEM_DUNGEON_MAP, "", dungeons[j].id, DrawDungeonItem)); - break; - case ITEM_COMPASS: - dungeonItems.push_back(ITEM_TRACKER_ITEM(ITEM_COMPASS, "", dungeons[j].id, DrawDungeonItem)); - break; - } - } else { - dungeonItems.push_back(ITEM_TRACKER_ITEM(ITEM_NONE, "", 0, DrawItem)); +std::vector GetDungeonItemsVector(std::span layout, + size_t columns = 6) { + struct ShownColumn { + ItemTrackerDungeonColumn items; + size_t size = 0; + }; + std::vector shown; + for (const auto& column : layout) { + ShownColumn kept; + for (const auto& item : column) { + if (item.drawFunc != nullptr && ShowDungeonItem(item)) { + kept.items[kept.size++] = item; } } + if (kept.size > 0) { + shown.push_back(kept); + } } - if (dungeons.size() > columns) { - std::vector nextDungeonItems = - GetDungeonItemsVector(std::vector(dungeons.begin() + columns, dungeons.end()), columns); - dungeonItems.insert(dungeonItems.end(), nextDungeonItems.begin(), nextDungeonItems.end()); + std::vector dungeonItems = {}; + for (size_t start = 0; start < shown.size(); start += columns) { + size_t end = std::min(start + columns, shown.size()); + size_t rowCount = 0; + for (size_t j = start; j < end; j++) { + rowCount = std::max(rowCount, shown[j].size); + } + for (size_t i = 0; i < rowCount; i++) { + for (size_t j = start; j < end; j++) { + if (i < shown[j].size) { + dungeonItems.push_back(shown[j].items[i]); + } else { + dungeonItems.push_back(ITEM_TRACKER_ITEM(ITEM_NONE, "", 0, DrawItem)); + } + } + } } return dungeonItems; @@ -1548,18 +1569,12 @@ void UpdateVectors() { SECTION_DISPLAY_SEPARATE) { if (CVarGetInteger(CVAR_TRACKER_ITEM("DungeonItems.DisplayMaps"), 1)) { dungeonItems = GetDungeonItemsVector(itemTrackerDungeonsWithMapsHorizontal, 12); - // Manually adding Thieves Hideout to an open spot so we don't get an additional row for one item - dungeonItems[23] = ITEM_TRACKER_ITEM(ITEM_KEY_SMALL, "", SCENE_THIEVES_HIDEOUT, DrawDungeonItem); } else { - // Manually adding Thieves Hideout to an open spot so we don't get an additional row for one item dungeonItems = GetDungeonItemsVector(itemTrackerDungeonsHorizontal, 8); - dungeonItems[15] = ITEM_TRACKER_ITEM(ITEM_KEY_SMALL, "", SCENE_THIEVES_HIDEOUT, DrawDungeonItem); } } else { if (CVarGetInteger(CVAR_TRACKER_ITEM("DungeonItems.DisplayMaps"), 1)) { dungeonItems = GetDungeonItemsVector(itemTrackerDungeonsWithMapsCompact); - // Manually adding Thieves Hideout to an open spot so we don't get an additional row for one item - dungeonItems[35] = ITEM_TRACKER_ITEM(ITEM_KEY_SMALL, "", SCENE_THIEVES_HIDEOUT, DrawDungeonItem); } else { dungeonItems = GetDungeonItemsVector(itemTrackerDungeonsCompact); } diff --git a/soh/soh/Enhancements/randomizer/randomizer_item_tracker.h b/soh/soh/Enhancements/randomizer/randomizer_item_tracker.h index 5e9b136bfb..c1d2146599 100644 --- a/soh/soh/Enhancements/randomizer/randomizer_item_tracker.h +++ b/soh/soh/Enhancements/randomizer/randomizer_item_tracker.h @@ -72,11 +72,6 @@ static std::array itemTrackerWindowIDs = { "Item Tracker", "Total Checks" }; void ItemTracker_LoadFromPreset(const nlohmann::json& trackerInfo); -typedef struct ItemTrackerDungeon { - uint32_t id; - std::vector items; -} ItemTrackerDungeon; - class ItemTrackerSettingsWindow final : public Ship::GuiWindow { public: using GuiWindow::GuiWindow;