backend tracker updates

This commit is contained in:
gymnast86
2026-05-20 17:41:03 -07:00
parent 7cebc42266
commit ed458dc2b3
15 changed files with 1046 additions and 595 deletions
+32
View File
@@ -305,6 +305,10 @@ int item_getcheck_func_ARROW_20();
int item_getcheck_func_ARROW_30();
int item_getcheck_func_ARROW_1();
int item_getcheck_func_PACHINKO_SHOT();
#if TARGET_PC
int item_getcheck_func_ORDON_PORTAL();
int item_getcheck_func_SOUTH_FARON_PORTAL();
#endif
int item_getcheck_func_WATER_BOMB_5();
int item_getcheck_func_WATER_BOMB_10();
int item_getcheck_func_WATER_BOMB_20();
@@ -338,6 +342,12 @@ int item_getcheck_func_DUNGEON_EXIT_2();
int item_getcheck_func_WALLET_LV1();
int item_getcheck_func_WALLET_LV2();
int item_getcheck_func_WALLET_LV3();
#if TARGET_PC
int item_getcheck_func_UPPER_ZORAS_RIVER_PORTAL();
int item_getcheck_func_CASTLE_TOWN_PORTAL();
int item_getcheck_func_GERUDO_DESERT_PORTAL();
int item_getcheck_func_NORTH_FARON_PORTAL();
#endif
int item_getcheck_func_ZORAS_JEWEL();
int item_getcheck_func_HAWK_EYE();
int item_getcheck_func_WOOD_STICK();
@@ -354,13 +364,23 @@ int item_getcheck_func_LIGHT_SWORD();
int item_getcheck_func_FISHING_ROD_1();
int item_getcheck_func_PACHINKO();
int item_getcheck_func_COPY_ROD_2();
#if TARGET_PC
int item_getcheck_func_KAKARIKO_GORGE_PORTAL();
int item_getcheck_func_KAKARIKO_VILLAGE_PORTAL();
#endif
int item_getcheck_func_BOMB_BAG_LV2();
int item_getcheck_func_BOMB_BAG_LV1();
int item_getcheck_func_BOMB_IN_BAG();
#if TARGET_PC
int item_getcheck_func_DEATH_MOUNTAIN_PORTAL();
#endif
int item_getcheck_func_LIGHT_ARROW();
int item_getcheck_func_ARROW_LV1();
int item_getcheck_func_ARROW_LV2();
int item_getcheck_func_ARROW_LV3();
#if TARGET_PC
int item_getcheck_func_ZORAS_DOMAIN_PORTAL();
#endif
int item_getcheck_func_LURE_ROD();
int item_getcheck_func_BOMB_ARROW();
int item_getcheck_func_HAWK_ARROW();
@@ -406,6 +426,10 @@ int item_getcheck_func_BILL();
int item_getcheck_func_WOOD_STATUE();
int item_getcheck_func_IRIAS_PENDANT();
int item_getcheck_func_HORSE_FLUTE();
#if TARGET_PC
int item_getcheck_func_CAMP_SMALL_KEY();
int item_getcheck_func_LAKE_HYLIA_PORTAL();
#endif
int item_getcheck_func_RAFRELS_MEMO();
int item_getcheck_func_ASHS_SCRIBBLING();
int item_getcheck_func_CHUCHU_YELLOW2();
@@ -420,12 +444,19 @@ int item_getcheck_func_FILLED_CONTAINER();
int item_getcheck_func_MIRROR_PIECE_2();
int item_getcheck_func_MIRROR_PIECE_3();
int item_getcheck_func_MIRROR_PIECE_4();
#if TARGET_PC
int item_getcheck_func_MIRROR_CHAMBER_PORTAL();
int item_getcheck_func_SNOWPEAK_PORTAL();
#endif
int item_getcheck_func_SMELL_YELIA_POUCH();
int item_getcheck_func_SMELL_PUMPKIN();
int item_getcheck_func_SMELL_POH();
int item_getcheck_func_SMELL_FISH();
int item_getcheck_func_SMELL_CHILDREN();
int item_getcheck_func_SMELL_MEDICINE();
#if TARGET_PC
int item_getcheck_func_SACRED_GROVE_PORTAL();
#endif
int item_getcheck_func_M_BEETLE();
int item_getcheck_func_F_BEETLE();
int item_getcheck_func_M_BUTTERFLY();
@@ -465,6 +496,7 @@ int item_getcheck_func_HELM_SPLITTER();
int item_getcheck_func_MORTAL_DRAW();
int item_getcheck_func_JUMP_STRIKE();
int item_getcheck_func_GREAT_SPIN();
int item_getcheck_func_ELDIN_BRIDGE_PORTAL();
#endif
int item_getcheck_func_ANCIENT_DOCUMENT();
int item_getcheck_func_AIR_LETTER();
+595 -549
View File
File diff suppressed because it is too large Load Diff
+4
View File
@@ -3021,7 +3021,11 @@ int dStage_changeScene4Event(int i_exitId, s8 room_no, int i_wipe, bool param_3,
// If randomizer is active and we're loading the first spawn, set our starting time of day
if (randomizer_IsActive() && strcmp(scls_info->mStage, "F_SP103") == 0 &&
scls_info->mRoom == 1 && scls_info->mStart == 1)
{
timeH = randomizer_GetContext().mStartHour;
g_randomizerState.mUpdateTracker = true;
}
#endif
dKy_set_nexttime(15.0f * timeH);
}
+34 -21
View File
@@ -3,19 +3,17 @@
#include "ImGuiConsole.hpp"
#include "ImGuiMenuRandomizer.hpp"
#include "dusk/app_info.hpp"
#include "dusk/logging.h"
#include "dusk/data.hpp"
#include "dusk/randomizer/generator/logic/search.hpp"
#include "dusk/randomizer/game/randomizer_context.hpp"
#include "dusk/randomizer/game/tools.h"
#include "SDL3/SDL_filesystem.h"
#include <mutex>
#include <thread>
#include <filesystem>
#include "dusk/data.hpp"
#include "dusk/randomizer/generator/logic/search.hpp"
#include "dusk/randomizer/generator/utility/string.hpp"
namespace dusk {
@@ -221,9 +219,10 @@ namespace dusk {
if (ImGui::Begin("Rando Tracker", nullptr, windowFlags)) {
auto trackerRando = getTrackerRando();
ImGui::Text("Here's where the tracker will be");
if (ImGui::Button("Update Tracker")) {
// Uncomment button for manual updating
if (/*ImGui::Button("Update Tracker") || */g_randomizerState.mUpdateTracker) {
g_randomizerState.mUpdateTracker = false;
auto trackerRando = getTrackerRando();
// Generate tracker world if it doesn't exist
@@ -233,37 +232,49 @@ namespace dusk {
if (trackerHash.empty() || (trackerHash != contextHash && !contextHash.empty())) {
*trackerRando = randomizer::Randomizer(data::configured_data_path());
trackerRando->GenerateTrackerWorld();
auto trackerWorld = trackerRando->GetWorlds()[0].get();
auto currentItems = trackerWorld->GetStartingItemPool();
m_currentSearch = randomizer::logic::search::Search::Accessible(&trackerRando->GetWorlds(), currentItems);
m_currentSearch.SearchWorlds();
} else {
// Don't try to update inventory when on the title screen
if (!playerIsOnTitleScreen()) {
// TODO: Translate game save inventory into ItemPool that can be used for searching
randomizer::logic::item_pool::ItemPool currentItems = {};
m_currentSearch = randomizer::logic::search::Search::Accessible(&trackerRando->GetWorlds(), currentItems);
if (randomizer_IsActive()) {
auto currentItems = getSaveItemPool(trackerRando->GetWorlds()[0].get());
m_currentSearch = randomizer::logic::search::Search::AccessibleNoStartingInventory(&trackerRando->GetWorlds(), currentItems);
}
m_currentSearch.SearchWorlds();
}
}
if (trackerRando->GetConfig().GetHash(false).empty()) {
ImGui::Text("There is currently no tracker world");
ImGui::Text("Load a seed and start a file to activate the tracker");
} else {
ImGui::Text("Tracker world loaded from seed %s", trackerRando->GetConfig().GetHash().c_str());
ImGui::Checkbox("Show Only Accessible Locations", &m_onlyAccessible);
ImGui::Checkbox("Show Location Requirements", &m_showRequirements);
ImGui::InputText("Location Filter", m_locationFilter, 100);
// Show total number of available locations
auto locations = trackerRando->GetWorlds()[0]->GetAllLocations();
auto numProgressionLocations = std::ranges::count_if(locations, [](auto* location) {return location->IsProgression();});
auto numAvailableLocations = m_currentSearch._visitedLocations.size();
ImGui::Text("Locations Available: %zu / %zu", numAvailableLocations, locations.size());
ImGui::Text("Locations Available: %zu / %zu", numAvailableLocations, numProgressionLocations);
if (ImGui::BeginChild("ScrollRegion", ImVec2(500, 500), true))
{
std::string filter = m_locationFilter;
// Show all locations. Green for accessible. Red for Unaccessible
for (auto location : locations) {
// Don't show locations which aren't progression
// Don't show any locations which aren't accessible if only accessible is checked
// Don't show any locations which don't meet the filter
if (!location->IsProgression() ||
(m_onlyAccessible && !m_currentSearch._visitedLocations.contains(location)) ||
!randomizer::utility::str::Contains(location->GetName(), filter)) {
continue;
}
// Don't show warp portals for now either
if (location->HasCategories("Warp Portal")) {
continue;
}
// Color red
auto color = ImVec4(1.f, 0.f, 0.f, 1.f);
@@ -275,7 +286,9 @@ namespace dusk {
ImGui::TextColored(color, "%s", location->GetName().c_str());
// Show requirements for the location below it (formatting isn't pretty right now)
ImGui::Text(" %s", location->GetComputedRequirement().to_string().c_str());
if (m_showRequirements) {
ImGui::Text(" %s", location->GetComputedRequirement().to_string().c_str());
}
}
}
ImGui::EndChild();
+4
View File
@@ -27,6 +27,10 @@ private:
bool m_showRandoGeneration{false};
bool m_showRandoTracker{false};
bool m_onlyAccessible{false};
bool m_showRequirements{false};
char m_locationFilter[100];
randomizer::logic::search::Search m_currentSearch = randomizer::logic::search::Search();
};
}
@@ -385,7 +385,7 @@ static bool checkFoolishItemEffectReady()
}
static void handleFoolishItem() {
u32 count = g_randomizerState.foolishItemCount;
u32 count = g_randomizerState.mFoolishItemCount;
if (count == 0) {
return;
}
@@ -401,7 +401,7 @@ static void handleFoolishItem() {
}
// Reset count
g_randomizerState.foolishItemCount = 0;
g_randomizerState.mFoolishItemCount = 0;
/* Store the currently loaded sound wave to local variables as we will need to load them back later.
* We use this method because if we just loaded the sound waves every time the item was gotten, we'd
@@ -172,7 +172,8 @@ public:
// things like the sound of the rupee counter going up.
u8 mFlowMessageItemId{0};
int foolishItemCount{0};
int mFoolishItemCount{0};
bool mUpdateTracker{false};
};
extern RandomizerState g_randomizerState;
+317 -2
View File
@@ -1,10 +1,12 @@
#include "tools.h"
#include "stages.h"
#include "d/d_com_inf_game.h"
#include "d/actor/d_a_alink.h"
#include "d/d_com_inf_game.h"
#include "d/d_item.h"
#include "d/d_item_data.h"
#include "dusk/logging.h"
#include "f_op/f_op_actor_mng.h"
#include "stages.h"
#include "verify_item_functions.h"
bool playerIsInRoomStage(s32 room, const char* stage)
{
@@ -149,4 +151,317 @@ int numMirrorShards() {
numMirrorShards += dComIfGs_isCollectMirror(i);
}
return numMirrorShards;
}
int getTempleKeysFound(int stage) {
static std::unordered_map<int, std::vector<int>> keyDoorFlags = {
{0xA, {0x0}},
{0x10, {0x7, 0xB, 0x2B, 0x3E}},
{0x11, {0x33, 0x3D, 0x3F}},
{0x12, {0x23, 0x24, 0x34}},
{0x13, {0x27, 0x46, 0x4D, 0x5A, 0x5B}},
{0x14, {0x2B, 0x2C, 0x2F, 0x30}},
{0x15, {0x1B, 0x1C, 0x1D}},
{0x16, {0x6}},
{0x17, {0x6, 0x7, 0x8, 0xB, 0x23, 0x24, 0x25}},
{0x18, {0x4C, 0x6F, 0x7C}}
};
int count = dComIfGs_getKeyNum(stage);
// Add number of unlocked key doors for this dungeon to current key count
for (auto flag : keyDoorFlags[stage]) {
if (dComIfGs_isSwitch(stage, flag)) {
count += 1;
}
}
return count;
}
bool isTempleBigKeyFound(int stage) {
// The boss key never gets taken away unlike small keys
return dComIfGs_isDungeonItemBossKey(stage);
}
randomizer::logic::item_pool::ItemPool getSaveItemPool(randomizer::logic::world::World* world) {
randomizer::logic::item_pool::ItemPool pool{};
// Item wheel items
for (int i = 0; i < MAX_ITEM_SLOTS; ++i) {
switch (dComIfGs_getItem(i, false)) {
case dItemNo_Randomizer_HAWK_EYE_e:
pool.push_back(world->GetItem("Hawkeye", true));
break;
case dItemNo_Randomizer_BOOMERANG_e:
pool.push_back(world->GetItem("Gale Boomerang", true));
break;
case dItemNo_Randomizer_SPINNER_e:
pool.push_back(world->GetItem("Spinner", true));
break;
case dItemNo_Randomizer_IRONBALL_e:
pool.push_back(world->GetItem("Ball and Chain", true));
break;
case dItemNo_Randomizer_BOW_e:
pool.push_back(world->GetItem("Progressive Bow", true));
break;
case dItemNo_Randomizer_W_HOOKSHOT_e:
pool.push_back(world->GetItem("Progressive Clawshot", true));
[[fallthrough]];
case dItemNo_Randomizer_HOOKSHOT_e:
pool.push_back(world->GetItem("Progressive Clawshot", true));
break;
case dItemNo_Randomizer_HVY_BOOTS_e:
pool.push_back(world->GetItem("Iron Boots", true));
break;
case dItemNo_Randomizer_COPY_ROD_e:
pool.push_back(world->GetItem("Progressive Dominion Rod", true));
// Powered up dominion rod
if (dComIfGs_isEventBit(0x2580)) {
pool.push_back(world->GetItem("Progressive Dominion Rod", true));
}
break;
case dItemNo_Randomizer_KANTERA_e:
pool.push_back(world->GetItem("Lantern", true));
break;
case dItemNo_Randomizer_JEWEL_ROD_e:
case dItemNo_Randomizer_JEWEL_BEE_ROD_e:
case dItemNo_Randomizer_JEWEL_WORM_ROD_e:
pool.push_back(world->GetItem("Progressive Fishing Rod", true));
[[fallthrough]];
case dItemNo_Randomizer_FISHING_ROD_1_e:
case dItemNo_Randomizer_BEE_ROD_e:
case dItemNo_Randomizer_WORM_ROD_e:
pool.push_back(world->GetItem("Progressive Fishing Rod", true));
break;
case dItemNo_Randomizer_PACHINKO_e:
pool.push_back(world->GetItem("Slingshot", true));
break;
case dItemNo_Randomizer_BOMB_BAG_LV1_e:
pool.push_back(world->GetItem("Bomb Bag", true));
break;
case dItemNo_Randomizer_RAFRELS_MEMO_e:
pool.push_back(world->GetItem("Aurus Memo", true));
break;
case dItemNo_Randomizer_ASHS_SCRIBBLING_e:
pool.push_back(world->GetItem("Asheis Sketch", true));
break;
case dItemNo_Randomizer_EMPTY_BOTTLE_e:
case dItemNo_Randomizer_RED_BOTTLE_e:
case dItemNo_Randomizer_GREEN_BOTTLE_e:
case dItemNo_Randomizer_BLUE_BOTTLE_e:
case dItemNo_Randomizer_MILK_BOTTLE_e:
case dItemNo_Randomizer_HALF_MILK_BOTTLE_e:
case dItemNo_Randomizer_OIL_BOTTLE_e:
case dItemNo_Randomizer_WATER_BOTTLE_e:
case dItemNo_Randomizer_OIL_BOTTLE_2_e:
case dItemNo_Randomizer_RED_BOTTLE_2_e:
case dItemNo_Randomizer_UGLY_SOUP_e:
case dItemNo_Randomizer_HOT_SPRING_e:
case dItemNo_Randomizer_FAIRY_e:
case dItemNo_Randomizer_HOT_SPRING_2_e:
case dItemNo_Randomizer_OIL2_e:
case dItemNo_Randomizer_OIL_e:
case dItemNo_Randomizer_FAIRY_DROP_e:
case dItemNo_Randomizer_DROP_BOTTLE_e:
case dItemNo_Randomizer_BEE_CHILD_e:
case dItemNo_Randomizer_CHUCHU_RARE_e:
case dItemNo_Randomizer_CHUCHU_RED_e:
case dItemNo_Randomizer_CHUCHU_BLUE_e:
case dItemNo_Randomizer_CHUCHU_GREEN_e:
case dItemNo_Randomizer_CHUCHU_YELLOW_e:
case dItemNo_Randomizer_CHUCHU_PURPLE_e:
case dItemNo_Randomizer_LV1_SOUP_e:
case dItemNo_Randomizer_LV2_SOUP_e:
case dItemNo_Randomizer_LV3_SOUP_e:
case dItemNo_Randomizer_OIL_BOTTLE3_e:
case dItemNo_Randomizer_CHUCHU_BLACK_e:
pool.push_back(world->GetItem("Empty Bottle", true));
break;
default:
break;
}
}
// Shadow Crystal
if (dComIfGs_isEventBit(0xD04)) {
pool.push_back(world->GetItem("Shadow Crystal", true));
}
// Fused Shadows
for (int i = 0; i < numFusedShadows(); ++i) {
pool.push_back(world->GetItem("Progressive Fused Shadow", true));
}
// Mirror Shards
for (int i = 0; i < numMirrorShards(); ++i) {
pool.push_back(world->GetItem("Progressive Mirror Shard", true));
}
// Poe Souls
for (int i = 0; i < dComIfGs_getPohSpiritNum(); ++i) {
pool.push_back(world->GetItem("Poe Soul", true));
}
// Hearts
for (int i = 0; i < dComIfGs_getMaxLife(); ++i) {
pool.push_back(world->GetItem("Piece of Heart", true));
}
// Sky Book characters
for (int i = 0; i < dComIfGs_getAncientDocumentNum(); ++i) {
pool.push_back(world->GetItem("Progressive Sky Book", true));
}
// Small Keys
static std::unordered_map<int, std::string> keyRegionItemNameMap = {
{0xA, "Gerudo Desert Bulblin Camp Key"},
{0x10, "Forest Temple Small Key"},
{0x11, "Goron Mines Small Key"},
{0x12, "Lakebed Temple Small Key"},
{0x13, "Arbiters Grounds Small Key"},
{0x14, "Snowpeak Ruins Small Key"},
{0x15, "Temple of Time Small Key"},
{0x16, "City in the Sky Small Key"},
{0x17, "Palace of Twilight Small Key"},
{0x18, "Hyrule Castle Small Key"},
};
for (auto& [stage, keyName] : keyRegionItemNameMap) {
for (int i = 0; i < getTempleKeysFound(stage); ++i) {
pool.push_back(world->GetItem(keyName, true));
}
}
// Gate Keys
if (haveItem(dItemNo_Randomizer_BOSSRIDER_KEY_e)) {
pool.push_back(world->GetItem(dItemNo_Randomizer_BOSSRIDER_KEY_e, true));
}
// Big Keys
static std::unordered_map<int, std::string> bigKeyRegionItemNameMap = {
{0x10, "Forest Temple Big Key"},
{0x12, "Lakebed Temple Big Key"},
{0x13, "Arbiters Grounds Big Key"},
{0x14, "Snowpeak Ruins Bedroom Key"},
{0x15, "Temple of Time Big Key"},
{0x16, "City in the Sky Big Key"},
{0x17, "Palace of Twilight Big Key"},
{0x18, "Hyrule Castle Big Key"},
};
for (auto& [stage, keyName] : bigKeyRegionItemNameMap) {
if (isTempleBigKeyFound(stage)) {
pool.push_back(world->GetItem(keyName, true));
}
}
// Goron Mines Key Shards
for (int i = dItemNo_Randomizer_L2_KEY_PIECES1_e; i < dItemNo_Randomizer_L2_KEY_PIECES3_e; ++i) {
if (haveItem(i)) {
pool.push_back(world->GetItem("Goron Mines Key Shard", true));
}
}
// Ordon Pumpkin
if (haveItem(dItemNo_Randomizer_TOMATO_PUREE_e)) {
pool.push_back(world->GetItem(dItemNo_Randomizer_TOMATO_PUREE_e, true));
}
// Ordon Cheese
if (haveItem(dItemNo_Randomizer_TASTE_e)) {
pool.push_back(world->GetItem(dItemNo_Randomizer_TASTE_e, true));
}
// Golden Bugs
for (int i = dItemNo_Randomizer_M_BEETLE_e; i < dItemNo_Randomizer_F_MAYFLY_e; ++i) {
if (haveItem(i)) {
pool.push_back(world->GetItem(i, true));
}
}
// Ilia quest items
for (int i = dItemNo_Randomizer_LETTER_e; i < dItemNo_Randomizer_HORSE_FLUTE_e; ++i) {
if (haveItem(i)) {
pool.push_back(world->GetItem(i, true));
}
}
// Warp Portals
// Item ids are scattered so we have to explicitly list them all
static const int portals[] = {
dItemNo_Randomizer_ORDON_PORTAL_e,
dItemNo_Randomizer_SOUTH_FARON_PORTAL_e,
dItemNo_Randomizer_NORTH_FARON_PORTAL_e,
dItemNo_Randomizer_SACRED_GROVE_PORTAL_e,
dItemNo_Randomizer_KAKARIKO_GORGE_PORTAL_e,
dItemNo_Randomizer_KAKARIKO_VILLAGE_PORTAL_e,
dItemNo_Randomizer_DEATH_MOUNTAIN_PORTAL_e,
dItemNo_Randomizer_ELDIN_BRIDGE_PORTAL_e,
dItemNo_Randomizer_CASTLE_TOWN_PORTAL_e,
dItemNo_Randomizer_UPPER_ZORAS_RIVER_PORTAL_e,
dItemNo_Randomizer_ZORAS_DOMAIN_PORTAL_e,
dItemNo_Randomizer_SNOWPEAK_PORTAL_e,
dItemNo_Randomizer_GERUDO_DESERT_PORTAL_e,
dItemNo_Randomizer_MIRROR_CHAMBER_PORTAL_e,
};
for (auto portal : portals) {
if (haveItem(portal)) {
pool.push_back(world->GetItem(portal, true));
}
}
// Swords
static const int swords[] = {
dItemNo_Randomizer_WOOD_STICK_e,
dItemNo_Randomizer_SWORD_e,
dItemNo_Randomizer_MASTER_SWORD_e,
dItemNo_Randomizer_LIGHT_SWORD_e,
};
for (auto sword : swords) {
if (haveItem(sword)) {
pool.push_back(world->GetItem("Progressive Sword", true));
}
}
// Other Equipment
static const int equipment[] = {
dItemNo_Randomizer_WOOD_SHIELD_e,
dItemNo_Randomizer_HYLIA_SHIELD_e,
dItemNo_Randomizer_WEAR_ZORA_e,
dItemNo_Randomizer_ARMOR_e,
};
for (auto item : equipment) {
if (haveItem(item)) {
pool.push_back(world->GetItem(item, true));
}
}
// Hidden Skills
static const int hiddenSkills[] = {
dItemNo_Randomizer_ENDING_BLOW_e,
dItemNo_Randomizer_SHIELD_ATTACK_e,
dItemNo_Randomizer_BACK_SLICE_e,
dItemNo_Randomizer_HELM_SPLITTER_e,
dItemNo_Randomizer_MORTAL_DRAW_e,
dItemNo_Randomizer_JUMP_STRIKE_e,
dItemNo_Randomizer_GREAT_SPIN_e,
};
for (auto skill : hiddenSkills) {
if (haveItem(skill)) {
pool.push_back(world->GetItem("Progressive Hidden Skill", true));
}
}
// Wallets
switch (dComIfGs_getWalletSize()) {
case GIANT_WALLET:
pool.push_back(world->GetItem("Progressive Wallet", true));
[[fallthrough]];
case BIG_WALLET:
pool.push_back(world->GetItem("Progressive Wallet", true));
[[fallthrough]];
default:
break;
}
return pool;
}
+7 -1
View File
@@ -16,4 +16,10 @@ bool playerIsOnTitleScreen();
u16 getItemMessageID(u8 itemId);
int numCompletedDungeons();
int numFusedShadows();
int numMirrorShards();
int numMirrorShards();
/*
* Reads the current player inventory and returns an ItemPool that can be used for logic searches
*
*/
randomizer::logic::item_pool::ItemPool getSaveItemPool(randomizer::logic::world::World* world);
@@ -281,7 +281,7 @@
- Name: Castle Town Doctors Office Entrance
Can Transform: If Transform Anywhere
Exits:
Castle Town Doctors Office Lower: Invoice
Castle Town Doctors Office Lower: Invoice and Can_Talk_to_Humans
Castle Town Doctors Office West Door Interior: Nothing
Castle Town Doctors Office East Door Interior: Nothing
@@ -290,7 +290,7 @@
Medicine Scent: Can_Sniff
Exits:
Castle Town Doctors Office Upper: Wolf_Link
Castle Town Doctors Office Entrance: Invoice
Castle Town Doctors Office Entrance: Invoice and Can_Talk_to_Humans
- Name: Castle Town Doctors Office Upper
Exits:
@@ -28,6 +28,9 @@ namespace randomizer::logic::item_pool
{"Aurus Memo", 1},
{"Asheis Sketch", 1},
{"Renados Letter", 1},
{"Invoice", 1},
{"Wooden Statue", 1},
{"Ilias Charm", 1},
{"Zora Armor", 1},
{"Hylian Shield", 1},
{"Ordon Shield", 1},
@@ -127,6 +130,11 @@ namespace randomizer::logic::item_pool
{"Gerudo Desert Portal", 1},
{"Mirror Chamber Portal", 1},
// Tears
{"Faron Twilight Tear", 16},
{"Eldin Twilight Tear", 16},
{"Lanayru Twilight Tear", 16},
// Junk we should always have
{"Purple Rupee Links House", 1},
{"Green Rupee", 2},
+10 -7
View File
@@ -17,19 +17,22 @@ namespace randomizer::logic::search
Search::Search(const SearchMode& searchMode,
world::WorldPool* worlds,
const item_pool::ItemPool& items /* = {} */,
const int& worldToSearch /* = -1 */):
_searchMode(searchMode), _worlds(worlds)
const int& worldToSearch /* = -1 */,
bool startingInventory /*= true */):
_searchMode(searchMode), _worlds(worlds), _startingInventory(startingInventory)
{
// Set the items we should already own
this->_ownedItems.insert(items.begin(), items.end());
// Add starting inventory items for each world
for (const auto& world : *(this->_worlds))
{
if (worldToSearch == -1 || world->GetID() == worldToSearch)
if (this->_startingInventory) {
for (const auto& world : *(this->_worlds))
{
const auto& startingInventory = world->GetStartingItemPool();
this->_ownedItems.insert(startingInventory.begin(), startingInventory.end());
if (worldToSearch == -1 || world->GetID() == worldToSearch)
{
const auto& startingInventory = world->GetStartingItemPool();
this->_ownedItems.insert(startingInventory.begin(), startingInventory.end());
}
}
}
+10 -1
View File
@@ -64,7 +64,8 @@ namespace randomizer::logic::search
Search(const SearchMode& searchMode,
world::WorldPool* worlds,
const item_pool::ItemPool& items = {},
const int& worldToSearch = -1);
const int& worldToSearch = -1,
bool startingInventory = true);
static auto Accessible(world::WorldPool* worlds,
const item_pool::ItemPool& items = {},
@@ -73,6 +74,13 @@ namespace randomizer::logic::search
return Search(SearchMode::ACCESSIBLE_LOCATIONS, worlds, items, worldToSearch);
}
static auto AccessibleNoStartingInventory(world::WorldPool* worlds,
const item_pool::ItemPool& items = {},
const int& worldToSearch = -1)
{
return Search(SearchMode::ACCESSIBLE_LOCATIONS, worlds, items, worldToSearch, false);
}
static auto AllLocationsReachable(world::WorldPool* worlds,
const item_pool::ItemPool& items = {},
const int& worldToSearch = -1)
@@ -136,6 +144,7 @@ namespace randomizer::logic::search
bool _newThingsFound = true;
bool _isBeatable = false;
bool _collectItems = true;
bool _startingInventory = true;
std::unordered_set<int> _ownedEvents;
std::unordered_multiset<item::Item*> _ownedItems;
+17 -9
View File
@@ -116,15 +116,8 @@ namespace randomizer::logic::world
auto dungeonMap = itemNode["Dungeon Map"].as<std::string>("");
// Make the item and insert it into the item table
auto item = std::make_unique<item::Item>(id,
name,
this,
importance,
gameWinningItem,
dungeonSmallKey != "",
dungeonBigKey != "",
dungeonCompass != "",
dungeonMap != "");
auto item = std::make_unique<item::Item>(id, name, this, importance, gameWinningItem,
dungeonSmallKey != "", dungeonBigKey != "", dungeonCompass != "", dungeonMap != "");
this->_itemTable.try_emplace(name, std::move(item));
@@ -146,6 +139,9 @@ namespace randomizer::logic::world
{
this->GetDungeon(dungeonMap)->SetDungeonMap(curItem);
}
// Put item into itemIdTable as well
this->_itemIdTable.try_emplace(id, curItem);
}
}
@@ -992,6 +988,18 @@ namespace randomizer::logic::world
return this->_itemTable.at(name).get();
}
item::Item* World::GetItem(uint8_t id, const bool& ignoreError /*= false*/) {
if (!this->_itemIdTable.contains(id))
{
if (!ignoreError)
{
throw std::runtime_error("Unknown item id \"" + std::to_string(id) + "\"");
}
return item::Nothing.get();
}
return this->_itemIdTable.at(id);
}
item::Item* World::GetGameWinningItem() const
{
return this->_itemTable.at("Game Beatable").get();
@@ -113,6 +113,7 @@ namespace randomizer::logic::world
dungeon::Dungeon* GetDungeon(const std::string& name);
const std::map<std::string, std::unique_ptr<dungeon::Dungeon>>& GetDungeonTable() const;
item::Item* GetItem(const std::string& name, const bool& ignoreError = false);
item::Item* GetItem(uint8_t id, const bool& ignoreError = false);
item::Item* GetShadowCrystal();
item::Item* GetGameWinningItem() const;
item_pool::ItemPool& GetItemPool();
@@ -144,6 +145,7 @@ namespace randomizer::logic::world
seedgen::settings::Settings _settings;
std::map<std::string, std::unique_ptr<item::Item>> _itemTable = {};
std::map<int, item::Item*> _itemIdTable = {};
std::map<std::string, std::unique_ptr<location::Location>> _locationTable = {};
std::unordered_set<std::string> _intentionallyRemovedLocations = {};
std::unordered_set<std::string> _registeredLocationCategories = {};