Rando: Entrance Tracker v1 (#2005)

* initial pass for entrance tracker

* add search meta tags to entrance tracker data; clear entrance tracker on title screen

* rename to use playstate/play

* fix lus imports

* move discovered entrance info to SohStats struct

* Add scene info and highlighting to entrance tracker

* hide undiscovered text when searching

* add comments for entrance tracker

* fix merge conflict error

* account for zora river -> hyrule field water entrance in tracker

* fix assignement error

* remove unneeded defaults from debug file init

* adjust entrance tracker settings and add more search tags

* convert magic numbers to defines; add more comments to entrance tracker; clarify variable names

* add reverse index to entrance tracker data to compare with instead of using strings

* rename variables
This commit is contained in:
Adam Bird 2022-12-06 23:44:14 -05:00 committed by GitHub
parent f9fe3f8fb3
commit 8337e4e24f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 1134 additions and 14 deletions

View File

@ -192,6 +192,7 @@ source_group("Header Files\\soh\\Enhancements\\debugger" FILES ${Header_Files__s
set(Header_Files__soh__Enhancements__randomizer
"soh/Enhancements/randomizer/randomizer.h"
"soh/Enhancements/randomizer/randomizer_entrance.h"
"soh/Enhancements/randomizer/randomizer_entrance_tracker.h"
"soh/Enhancements/randomizer/randomizer_grotto.h"
"soh/Enhancements/randomizer/randomizer_inf.h"
"soh/Enhancements/randomizer/randomizer_item_tracker.h"
@ -323,6 +324,7 @@ source_group("Source Files\\soh\\Enhancements\\debugger" FILES ${Source_Files__s
set(Source_Files__soh__Enhancements__randomizer
"soh/Enhancements/randomizer/randomizer.cpp"
"soh/Enhancements/randomizer/randomizer_entrance.c"
"soh/Enhancements/randomizer/randomizer_entrance_tracker.cpp"
"soh/Enhancements/randomizer/randomizer_grotto.c"
"soh/Enhancements/randomizer/randomizer_item_tracker.cpp"
"soh/Enhancements/randomizer/adult_trade_shuffle.c"

View File

@ -62,6 +62,8 @@ typedef struct {
/* */ bool gameComplete;
/* */ u32 timestamp[TIMESTAMP_MAX];
/* */ u32 count[COUNT_MAX];
/* */ u32 entrancesDiscovered[SAVEFILE_ENTRANCES_DISCOVERED_IDX_COUNT];
/* */ u32 scenesDiscovered[SAVEFILE_SCENES_DISCOVERED_IDX_COUNT];
} SohStats;
typedef struct {

View File

@ -26,7 +26,7 @@ s16 dynamicExitList[] = { 0x045B, 0x0482, 0x03E8, 0x044B, 0x02A2, 0x0201, 0x03B8
static s16 entranceOverrideTable[ENTRANCE_TABLE_SIZE] = {0};
EntranceInfo originalEntranceTable[1556] = {0};
EntranceInfo originalEntranceTable[ENTRANCE_TABLE_SIZE] = {0};
//These variables store the new entrance indices for dungeons so that
//savewarping and game overs respawn players at the proper entrance.
@ -46,6 +46,8 @@ static s16 newIceCavernEntrance = ICE_CAVERN_ENTRANCE;
static s8 hasCopiedEntranceTable = 0;
static s8 hasModifiedEntranceTable = 0;
void Entrance_SetEntranceDiscovered(u16 entranceIndex);
u8 Entrance_EntranceIsNull(EntranceOverride* entranceOverride) {
return entranceOverride->index == 0 && entranceOverride->destination == 0 && entranceOverride->blueWarp == 0
&& entranceOverride->override == 0 && entranceOverride->overrideDestination == 0;
@ -69,14 +71,14 @@ static void Entrance_SeparateAdultSpawnAndPrelude() {
void Entrance_CopyOriginalEntranceTable(void) {
if (!hasCopiedEntranceTable) {
memcpy(originalEntranceTable, gEntranceTable, sizeof(EntranceInfo) * 1556);
memcpy(originalEntranceTable, gEntranceTable, sizeof(EntranceInfo) * ENTRANCE_TABLE_SIZE);
hasCopiedEntranceTable = 1;
}
}
void Entrance_ResetEntranceTable(void) {
if (hasCopiedEntranceTable && hasModifiedEntranceTable) {
memcpy(gEntranceTable, originalEntranceTable, sizeof(EntranceInfo) * 1556);
memcpy(gEntranceTable, originalEntranceTable, sizeof(EntranceInfo) * ENTRANCE_TABLE_SIZE);
hasModifiedEntranceTable = 0;
}
}
@ -122,12 +124,12 @@ void Entrance_Init(void) {
s16 overrideIndex = gSaveContext.entranceOverrides[i].override;
//Overwrite grotto related indices
if (originalIndex >= 0x0800) {
if (originalIndex >= ENTRANCE_RANDO_GROTTO_EXIT_START) {
Grotto_SetExitOverride(originalIndex, overrideIndex);
continue;
}
if (originalIndex >= 0x0700 && originalIndex < 0x0800) {
if (originalIndex >= ENTRANCE_RANDO_GROTTO_LOAD_START && originalIndex < ENTRANCE_RANDO_GROTTO_EXIT_START) {
Grotto_SetLoadOverride(originalIndex, overrideIndex);
continue;
}
@ -201,15 +203,36 @@ s16 Entrance_OverrideNextIndex(s16 nextEntranceIndex) {
return nextEntranceIndex;
}
Entrance_SetEntranceDiscovered(nextEntranceIndex);
EntranceTracker_SetLastEntranceOverride(nextEntranceIndex);
return Grotto_OverrideSpecialEntrance(Entrance_GetOverride(nextEntranceIndex));
}
s16 Entrance_OverrideDynamicExit(s16 dynamicExitIndex) {
Entrance_SetEntranceDiscovered(dynamicExitList[dynamicExitIndex]);
EntranceTracker_SetLastEntranceOverride(dynamicExitList[dynamicExitIndex]);
return Grotto_OverrideSpecialEntrance(Entrance_GetOverride(dynamicExitList[dynamicExitIndex]));
}
u32 Entrance_SceneAndSpawnAre(u8 scene, u8 spawn) {
EntranceInfo currentEntrance = gEntranceTable[gSaveContext.entranceIndex];
s16 computedEntranceIndex;
// Adjust the entrance to acount for the exact scene/spawn combination for child/adult and day/night
if (!IS_DAY) {
if (!LINK_IS_ADULT) {
computedEntranceIndex = gSaveContext.entranceIndex + 1;
} else {
computedEntranceIndex = gSaveContext.entranceIndex + 3;
}
} else {
if (!LINK_IS_ADULT) {
computedEntranceIndex = gSaveContext.entranceIndex;
} else {
computedEntranceIndex = gSaveContext.entranceIndex + 2;
}
}
EntranceInfo currentEntrance = gEntranceTable[computedEntranceIndex];
return currentEntrance.scene == scene && currentEntrance.spawn == spawn;
}
@ -578,3 +601,58 @@ void Entrance_OverrideSpawnScene(s32 sceneNum, s32 spawn) {
}
}
}
u8 Entrance_GetIsSceneDiscovered(u8 sceneNum) {
u16 bitsPerIndex = sizeof(u32) * 8;
u32 idx = sceneNum / bitsPerIndex;
if (idx < SAVEFILE_SCENES_DISCOVERED_IDX_COUNT) {
u32 sceneBit = 1 << (sceneNum - (idx * bitsPerIndex));
return (gSaveContext.sohStats.scenesDiscovered[idx] & sceneBit) != 0;
}
return 0;
}
void Entrance_SetSceneDiscovered(u8 sceneNum) {
if (Entrance_GetIsSceneDiscovered(sceneNum)) {
return;
}
u16 bitsPerIndex = sizeof(u32) * 8;
u32 idx = sceneNum / bitsPerIndex;
if (idx < SAVEFILE_SCENES_DISCOVERED_IDX_COUNT) {
u32 sceneBit = 1 << (sceneNum - (idx * bitsPerIndex));
gSaveContext.sohStats.scenesDiscovered[idx] |= sceneBit;
}
}
u8 Entrance_GetIsEntranceDiscovered(u16 entranceIndex) {
u16 bitsPerIndex = sizeof(u32) * 8;
u32 idx = entranceIndex / bitsPerIndex;
if (idx < SAVEFILE_ENTRANCES_DISCOVERED_IDX_COUNT) {
u32 entranceBit = 1 << (entranceIndex - (idx * bitsPerIndex));
return (gSaveContext.sohStats.entrancesDiscovered[idx] & entranceBit) != 0;
}
return 0;
}
void Entrance_SetEntranceDiscovered(u16 entranceIndex) {
// Skip if already set to save time from setting the connected entrance or
// if this entrance is outside of the randomized entrance range (i.e. is a dynamic entrance)
if (entranceIndex > MAX_ENTRANCE_RANDO_USED_INDEX || Entrance_GetIsEntranceDiscovered(entranceIndex)) {
return;
}
u16 bitsPerIndex = sizeof(u32) * 8;
u32 idx = entranceIndex / bitsPerIndex;
if (idx < SAVEFILE_ENTRANCES_DISCOVERED_IDX_COUNT) {
u32 entranceBit = 1 << (entranceIndex - (idx * bitsPerIndex));
gSaveContext.sohStats.entrancesDiscovered[idx] |= entranceBit;
// Set connected
for (size_t i = 0; i < ENTRANCE_OVERRIDES_MAX_COUNT; i++) {
if (entranceIndex == gSaveContext.entranceOverrides[i].index) {
Entrance_SetEntranceDiscovered(gSaveContext.entranceOverrides[i].overrideDestination);
break;
}
}
}
}

View File

@ -22,8 +22,15 @@
#define GANONS_CASTLE_ENTRANCE 0x0467
#define LINK_HOUSE_SAVEWARP_ENTRANCE 0x00BB
#define ENTRANCE_RANDO_GROTTO_LOAD_START 0x0700
#define ENTRANCE_RANDO_GROTTO_EXIT_START 0x0800
#define MAX_ENTRANCE_RANDO_USED_INDEX 0x0820
#define ENTRANCE_OVERRIDES_MAX_COUNT 256
#define SAVEFILE_ENTRANCES_DISCOVERED_IDX_COUNT 66 // Max entrance rando index is 0x0820, (2080 / 32 == 65) + 1
#define SAVEFILE_SCENES_DISCOVERED_IDX_COUNT 4 // Max scene ID is 0x6E, (110 / 32 == 3) + 1
typedef struct {
int16_t index;
int16_t destination;
@ -47,5 +54,8 @@ void Entrance_HandleEponaState(void);
void Entrance_OverrideWeatherState(void);
void Entrance_OverrideGeurdoGuardCapture(void);
void Entrance_OverrideSpawnScene(int32_t sceneNum, int32_t spawn);
void Entrance_EnableFW(void);
uint8_t Entrance_GetIsEntranceDiscovered(uint16_t entranceIndex);
void Entrance_SetEntranceDiscovered(uint16_t entranceIndex);
#endif //_RANDO_ENTRANCE_H_

View File

@ -0,0 +1,891 @@
#include "randomizer_entrance_tracker.h"
#include "../../util.h"
#include "../../OTRGlobals.h"
#include <ImGuiImpl.h>
#include "../../UIWidgets.hpp"
#include <map>
#include <string>
#include <vector>
#include <Cvar.h>
#include <Hooks.h>
extern "C" {
#include <z64.h>
#include "variables.h"
#include "functions.h"
#include "macros.h"
extern PlayState* gPlayState;
#include "randomizer_entrance.h"
#include "randomizer_grotto.h"
}
#define COLOR_ORANGE IM_COL32(230, 159, 0, 255)
#define COLOR_GREEN IM_COL32(0, 158, 115, 255)
#define COLOR_GRAY IM_COL32(155, 155, 155, 255)
EntranceOverride srcListSortedByArea[ENTRANCE_OVERRIDES_MAX_COUNT] = {0};
EntranceOverride destListSortedByArea[ENTRANCE_OVERRIDES_MAX_COUNT] = {0};
EntranceOverride srcListSortedByType[ENTRANCE_OVERRIDES_MAX_COUNT] = {0};
EntranceOverride destListSortedByType[ENTRANCE_OVERRIDES_MAX_COUNT] = {0};
EntranceTrackingData gEntranceTrackingData = {0};
static const EntranceOverride emptyOverride = {0};
static s16 lastEntranceIndex = -1;
static s16 currentGrottoId = -1;
static s16 lastSceneOrEntranceDetected = -1;
static std::string spoilerEntranceGroupNames[] = {
"Spawns/Warp Songs/Owls",
"Kokiri Forest",
"Lost Woods",
"Sacred Forest Meadow",
"Kakariko Village",
"Graveyard",
"Death Mountain Trail",
"Death Mountain Crater",
"Goron City",
"Zora's River",
"Zora's Domain",
"Zora's Fountain",
"Hyrule Field",
"Lon Lon Ranch",
"Lake Hylia",
"Gerudo Valley",
"Haunted Wasteland",
"Market",
"Hyrule Castle",
};
static std::string groupTypeNames[] = {
"One Way",
"Overworld",
"Interior",
"Grotto",
"Dungeon",
};
// Entrance data for the tracker taken from the 3ds rando entrance tracker, and supplemented with scene/spawn info and meta search tags
const EntranceData entranceData[] = {
//index, reverse, scenes (and spawns), source name, destination name, source group, destination group, type, metaTag, oneExit
{ 0x00BB, -1, SINGLE_SCENE_INFO(0x34), "Child Spawn", "Link's House", ENTRANCE_GROUP_ONE_WAY, ENTRANCE_GROUP_ONE_WAY, ENTRANCE_TYPE_ONE_WAY},
{ 0x0282, -1, SINGLE_SCENE_INFO(0x43), "Adult Spawn", "Temple of Time", ENTRANCE_GROUP_ONE_WAY, ENTRANCE_GROUP_ONE_WAY, ENTRANCE_TYPE_ONE_WAY},
{ 0x0600, -1, {{ -1 }}, "Minuet of Forest", "SFM Warp Pad", ENTRANCE_GROUP_ONE_WAY, ENTRANCE_GROUP_ONE_WAY, ENTRANCE_TYPE_ONE_WAY},
{ 0x04F6, -1, {{ -1 }}, "Bolero of Fire", "DMC Warp Pad", ENTRANCE_GROUP_ONE_WAY, ENTRANCE_GROUP_ONE_WAY, ENTRANCE_TYPE_ONE_WAY},
{ 0x0604, -1, {{ -1 }}, "Serenade of Water", "Lake Hylia Warp Pad", ENTRANCE_GROUP_ONE_WAY, ENTRANCE_GROUP_ONE_WAY, ENTRANCE_TYPE_ONE_WAY},
{ 0x01F1, -1, {{ -1 }}, "Requiem of Spirit", "Desert Colossus Warp Pad", ENTRANCE_GROUP_ONE_WAY, ENTRANCE_GROUP_ONE_WAY, ENTRANCE_TYPE_ONE_WAY},
{ 0x0568, -1, {{ -1 }}, "Nocturne of Shadow", "Graveyard Warp Pad", ENTRANCE_GROUP_ONE_WAY, ENTRANCE_GROUP_ONE_WAY, ENTRANCE_TYPE_ONE_WAY},
{ 0x05F4, -1, {{ -1 }}, "Prelude of Light", "Temple of Time Warp Pad", ENTRANCE_GROUP_ONE_WAY, ENTRANCE_GROUP_ONE_WAY, ENTRANCE_TYPE_ONE_WAY},
{ 0x0554, -1, SINGLE_SCENE_INFO(0x60), "DMT Owl Flight", "Kakariko Village Owl Drop", ENTRANCE_GROUP_ONE_WAY, ENTRANCE_GROUP_ONE_WAY, ENTRANCE_TYPE_ONE_WAY},
{ 0x027E, -1, SINGLE_SCENE_INFO(0x57), "LH Owl Flight", "Hyrule Field Owl Drop", ENTRANCE_GROUP_ONE_WAY, ENTRANCE_GROUP_ONE_WAY, ENTRANCE_TYPE_ONE_WAY},
// Kokiri Forest
{ 0x05E0, 0x020D, SINGLE_SCENE_INFO(0x55), "KF", "Lost Woods Bridge", ENTRANCE_GROUP_KOKIRI_FOREST, ENTRANCE_GROUP_LOST_WOODS, ENTRANCE_TYPE_OVERWORLD, "lw"},
{ 0x011E, 0x0286, SINGLE_SCENE_INFO(0x55), "KF", "Lost Woods", ENTRANCE_GROUP_KOKIRI_FOREST, ENTRANCE_GROUP_LOST_WOODS, ENTRANCE_TYPE_OVERWORLD, "lw"},
{ 0x0272, 0x0211, SINGLE_SCENE_INFO(0x55), "KF", "Link's House", ENTRANCE_GROUP_KOKIRI_FOREST, ENTRANCE_GROUP_KOKIRI_FOREST, ENTRANCE_TYPE_INTERIOR, "", 1},
{ 0x0433, 0x0443, SINGLE_SCENE_INFO(0x55), "KF", "Mido's House", ENTRANCE_GROUP_KOKIRI_FOREST, ENTRANCE_GROUP_KOKIRI_FOREST, ENTRANCE_TYPE_INTERIOR, "", 1},
{ 0x0437, 0x0447, SINGLE_SCENE_INFO(0x55), "KF", "Saria's House", ENTRANCE_GROUP_KOKIRI_FOREST, ENTRANCE_GROUP_KOKIRI_FOREST, ENTRANCE_TYPE_INTERIOR, "", 1},
{ 0x009C, 0x033C, SINGLE_SCENE_INFO(0x55), "KF", "House of Twins", ENTRANCE_GROUP_KOKIRI_FOREST, ENTRANCE_GROUP_KOKIRI_FOREST, ENTRANCE_TYPE_INTERIOR, "", 1},
{ 0x00C9, 0x026A, SINGLE_SCENE_INFO(0x55), "KF", "Know-It-All House", ENTRANCE_GROUP_KOKIRI_FOREST, ENTRANCE_GROUP_KOKIRI_FOREST, ENTRANCE_TYPE_INTERIOR, "", 1},
{ 0x00C1, 0x0266, SINGLE_SCENE_INFO(0x55), "KF", "KF Shop", ENTRANCE_GROUP_KOKIRI_FOREST, ENTRANCE_GROUP_KOKIRI_FOREST, ENTRANCE_TYPE_INTERIOR, "", 1},
{ 0x071B, 0x081B, SINGLE_SCENE_INFO(0x55), "KF", "KF Storms Grotto", ENTRANCE_GROUP_KOKIRI_FOREST, ENTRANCE_GROUP_KOKIRI_FOREST, ENTRANCE_TYPE_GROTTO, "chest", 1},
{ 0x0000, 0x0209, SINGLE_SCENE_INFO(0x55), "KF", "Deku Tree", ENTRANCE_GROUP_KOKIRI_FOREST, ENTRANCE_GROUP_KOKIRI_FOREST, ENTRANCE_TYPE_DUNGEON, "", 1},
{ 0x0211, 0x0272, SINGLE_SCENE_INFO(0x34), "Link's House", "KF", ENTRANCE_GROUP_KOKIRI_FOREST, ENTRANCE_GROUP_KOKIRI_FOREST, ENTRANCE_TYPE_INTERIOR, ""},
{ 0x0443, 0x0433, SINGLE_SCENE_INFO(0x28), "Mido's House", "KF", ENTRANCE_GROUP_KOKIRI_FOREST, ENTRANCE_GROUP_KOKIRI_FOREST, ENTRANCE_TYPE_INTERIOR, ""},
{ 0x0447, 0x0437, SINGLE_SCENE_INFO(0x29), "Saria's House", "KF", ENTRANCE_GROUP_KOKIRI_FOREST, ENTRANCE_GROUP_KOKIRI_FOREST, ENTRANCE_TYPE_INTERIOR, ""},
{ 0x033C, 0x009C, SINGLE_SCENE_INFO(0x27), "House of Twins", "KF", ENTRANCE_GROUP_KOKIRI_FOREST, ENTRANCE_GROUP_KOKIRI_FOREST, ENTRANCE_TYPE_INTERIOR, ""},
{ 0x026A, 0x00C9, SINGLE_SCENE_INFO(0x26), "Know-It-All House", "KF", ENTRANCE_GROUP_KOKIRI_FOREST, ENTRANCE_GROUP_KOKIRI_FOREST, ENTRANCE_TYPE_INTERIOR, ""},
{ 0x0266, 0x00C1, SINGLE_SCENE_INFO(0x2D), "KF Shop", "KF", ENTRANCE_GROUP_KOKIRI_FOREST, ENTRANCE_GROUP_KOKIRI_FOREST, ENTRANCE_TYPE_INTERIOR, ""},
{ 0x081B, 0x071B, {{ 0x3E, 0x00 }}, "KF Storms Grotto", "KF", ENTRANCE_GROUP_KOKIRI_FOREST, ENTRANCE_GROUP_KOKIRI_FOREST, ENTRANCE_TYPE_GROTTO, "chest"},
{ 0x0209, 0x0000, SINGLE_SCENE_INFO(0x00), "Deku Tree", "KF", ENTRANCE_GROUP_KOKIRI_FOREST, ENTRANCE_GROUP_KOKIRI_FOREST, ENTRANCE_TYPE_DUNGEON, ""},
// Lost Woods
{ 0x020D, 0x05E0, SINGLE_SCENE_INFO(0x5B), "Lost Woods Bridge", "KF", ENTRANCE_GROUP_LOST_WOODS, ENTRANCE_GROUP_KOKIRI_FOREST, ENTRANCE_TYPE_OVERWORLD, "lw"},
{ 0x0185, 0x04DE, SINGLE_SCENE_INFO(0x5B), "Lost Woods Bridge", "Hyrule Field", ENTRANCE_GROUP_LOST_WOODS, ENTRANCE_GROUP_HYRULE_FIELD, ENTRANCE_TYPE_OVERWORLD, "lw,hf"},
{ 0x0286, 0x011E, SINGLE_SCENE_INFO(0x5B), "Lost Woods", "KF", ENTRANCE_GROUP_LOST_WOODS, ENTRANCE_GROUP_KOKIRI_FOREST, ENTRANCE_TYPE_OVERWORLD, "lw"},
{ 0x04E2, 0x04D6, SINGLE_SCENE_INFO(0x5B), "Lost Woods", "Goron City", ENTRANCE_GROUP_LOST_WOODS, ENTRANCE_GROUP_GORON_CITY, ENTRANCE_TYPE_OVERWORLD, "lw,gc"},
{ 0x01DD, 0x04DA, SINGLE_SCENE_INFO(0x5B), "Lost Woods", "ZR", ENTRANCE_GROUP_LOST_WOODS, ENTRANCE_GROUP_ZORAS_RIVER, ENTRANCE_TYPE_OVERWORLD, "lw"},
{ 0x00FC, 0x01A9, SINGLE_SCENE_INFO(0x5B), "Lost Woods", "SFM", ENTRANCE_GROUP_LOST_WOODS, ENTRANCE_GROUP_SFM, ENTRANCE_TYPE_OVERWORLD, "lw"},
{ 0x071A, 0x081A, SINGLE_SCENE_INFO(0x5B), "Lost Woods", "LW Near Shortcuts Grotto", ENTRANCE_GROUP_LOST_WOODS, ENTRANCE_GROUP_LOST_WOODS, ENTRANCE_TYPE_GROTTO, "lw,chest", 1},
{ 0x0719, 0x0819, SINGLE_SCENE_INFO(0x5B), "Lost Woods", "LW Scrubs Grotto", ENTRANCE_GROUP_LOST_WOODS, ENTRANCE_GROUP_LOST_WOODS, ENTRANCE_TYPE_GROTTO, "lw", 1},
{ 0x0720, 0x0820, SINGLE_SCENE_INFO(0x5B), "Lost Woods", "Deku Theater", ENTRANCE_GROUP_LOST_WOODS, ENTRANCE_GROUP_LOST_WOODS, ENTRANCE_TYPE_GROTTO, "lw,mask,stage", 1},
{ 0x081A, 0x071A, {{ 0x3E, 0x00 }}, "LW Near Shortcuts Grotto", "Lost Woods", ENTRANCE_GROUP_LOST_WOODS, ENTRANCE_GROUP_LOST_WOODS, ENTRANCE_TYPE_GROTTO, "lw,chest"},
{ 0x0819, 0x0719, {{ 0x3E, 0x07 }}, "LW Scrubs Grotto", "Lost Woods", ENTRANCE_GROUP_LOST_WOODS, ENTRANCE_GROUP_LOST_WOODS, ENTRANCE_TYPE_GROTTO, "lw"},
{ 0x0820, 0x0720, {{ 0x3E, 0x0C }}, "Deku Theater", "Lost Woods", ENTRANCE_GROUP_LOST_WOODS, ENTRANCE_GROUP_LOST_WOODS, ENTRANCE_TYPE_GROTTO, "lw,mask,stage"},
// Sacred Forest Meadow
{ 0x01A9, 0x00FC, SINGLE_SCENE_INFO(0x56), "SFM", "Lost Woods", ENTRANCE_GROUP_SFM, ENTRANCE_GROUP_LOST_WOODS, ENTRANCE_TYPE_OVERWORLD, "lw"},
{ 0x0716, 0x0816, SINGLE_SCENE_INFO(0x56), "SFM", "SFM Wolfos Grotto", ENTRANCE_GROUP_SFM, ENTRANCE_GROUP_SFM, ENTRANCE_TYPE_GROTTO, "chest", 1},
{ 0x0718, 0x0818, SINGLE_SCENE_INFO(0x56), "SFM", "SFM Fairy Grotto", ENTRANCE_GROUP_SFM, ENTRANCE_GROUP_SFM, ENTRANCE_TYPE_GROTTO, "", 1},
{ 0x0717, 0x0817, SINGLE_SCENE_INFO(0x56), "SFM", "SFM Storms Grotto", ENTRANCE_GROUP_SFM, ENTRANCE_GROUP_SFM, ENTRANCE_TYPE_GROTTO, "scrubs", 1},
{ 0x0169, 0x0215, SINGLE_SCENE_INFO(0x56), "SFM", "Forest Temple", ENTRANCE_GROUP_SFM, ENTRANCE_GROUP_SFM, ENTRANCE_TYPE_DUNGEON, "", 1},
{ 0x0816, 0x0716, {{ 0x3E, 0x08 }}, "SFM Wolfos Grotto", "SFM", ENTRANCE_GROUP_SFM, ENTRANCE_GROUP_SFM, ENTRANCE_TYPE_GROTTO},
{ 0x0818, 0x0718, {{ 0x3C, 0x00 }}, "SFM Fairy Grotto", "SFM", ENTRANCE_GROUP_SFM, ENTRANCE_GROUP_SFM, ENTRANCE_TYPE_GROTTO},
{ 0x0817, 0x0717, {{ 0x3E, 0x0A }}, "SFM Storms Grotto", "SFM", ENTRANCE_GROUP_SFM, ENTRANCE_GROUP_SFM, ENTRANCE_TYPE_GROTTO, "scrubs"},
{ 0x0215, 0x0169, SINGLE_SCENE_INFO(0x03), "Forest Temple", "SFM", ENTRANCE_GROUP_SFM, ENTRANCE_GROUP_SFM, ENTRANCE_TYPE_DUNGEON},
// Kakariko Village
{ 0x017D, 0x00DB, SINGLE_SCENE_INFO(0x52), "Kakariko", "Hyrule Field", ENTRANCE_GROUP_KAKARIKO, ENTRANCE_GROUP_HYRULE_FIELD, ENTRANCE_TYPE_OVERWORLD, "hf"},
{ 0x00E4, 0x0195, SINGLE_SCENE_INFO(0x52), "Kakariko", "Graveyard", ENTRANCE_GROUP_KAKARIKO, ENTRANCE_GROUP_GRAVEYARD, ENTRANCE_TYPE_OVERWORLD},
{ 0x013D, 0x0191, SINGLE_SCENE_INFO(0x52), "Kakariko", "DMT", ENTRANCE_GROUP_KAKARIKO, ENTRANCE_GROUP_DEATH_MOUNTAIN_TRAIL, ENTRANCE_TYPE_OVERWORLD},
{ 0x02FD, 0x0349, SINGLE_SCENE_INFO(0x52), "Kakariko", "Carpenter Boss House", ENTRANCE_GROUP_KAKARIKO, ENTRANCE_GROUP_KAKARIKO, ENTRANCE_TYPE_INTERIOR, "", 1},
{ 0x0550, 0x04EE, SINGLE_SCENE_INFO(0x52), "Kakariko", "House of Skulltula", ENTRANCE_GROUP_KAKARIKO, ENTRANCE_GROUP_KAKARIKO, ENTRANCE_TYPE_INTERIOR, "", 1},
{ 0x039C, 0x0345, SINGLE_SCENE_INFO(0x52), "Kakariko", "Impa's House Front", ENTRANCE_GROUP_KAKARIKO, ENTRANCE_GROUP_KAKARIKO, ENTRANCE_TYPE_INTERIOR, "", 1},
{ 0x05C8, 0x05DC, SINGLE_SCENE_INFO(0x52), "Kakariko", "Impa's House Back", ENTRANCE_GROUP_KAKARIKO, ENTRANCE_GROUP_KAKARIKO, ENTRANCE_TYPE_INTERIOR, "cow", 1},
{ 0x0453, 0x0351, SINGLE_SCENE_INFO(0x52), "Kakariko", "Windmill", ENTRANCE_GROUP_KAKARIKO, ENTRANCE_GROUP_KAKARIKO, ENTRANCE_TYPE_INTERIOR, "", 1},
{ 0x003B, 0x0463, SINGLE_SCENE_INFO(0x52), "Kakariko", "Kak Shooting Gallery", ENTRANCE_GROUP_KAKARIKO, ENTRANCE_GROUP_KAKARIKO, ENTRANCE_TYPE_INTERIOR, "adult", 1},
{ 0x0072, 0x034D, SINGLE_SCENE_INFO(0x52), "Kakariko", "Granny's Potion Shop", ENTRANCE_GROUP_KAKARIKO, ENTRANCE_GROUP_KAKARIKO, ENTRANCE_TYPE_INTERIOR, "", 1},
{ 0x00B7, 0x0201, SINGLE_SCENE_INFO(0x52), "Kakariko", "Kak Bazaar", ENTRANCE_GROUP_KAKARIKO, ENTRANCE_GROUP_KAKARIKO, ENTRANCE_TYPE_INTERIOR, "shop", 1},
{ 0x0384, 0x044B, SINGLE_SCENE_INFO(0x52), "Kakariko", "Kak Potion Shop Front", ENTRANCE_GROUP_KAKARIKO, ENTRANCE_GROUP_KAKARIKO, ENTRANCE_TYPE_INTERIOR, "", 1},
{ 0x03EC, 0x04FF, SINGLE_SCENE_INFO(0x52), "Kakariko", "Kak Potion Shop Back", ENTRANCE_GROUP_KAKARIKO, ENTRANCE_GROUP_KAKARIKO, ENTRANCE_TYPE_INTERIOR, "", 1},
{ 0x070A, 0x080A, SINGLE_SCENE_INFO(0x52), "Kakariko", "Kak Open Grotto", ENTRANCE_GROUP_KAKARIKO, ENTRANCE_GROUP_KAKARIKO, ENTRANCE_TYPE_GROTTO, "chest", 1},
{ 0x070B, 0x080B, SINGLE_SCENE_INFO(0x52), "Kakariko", "Kak Redead Grotto", ENTRANCE_GROUP_KAKARIKO, ENTRANCE_GROUP_KAKARIKO, ENTRANCE_TYPE_GROTTO, "chest", 1},
{ 0x0098, 0x02A6, SINGLE_SCENE_INFO(0x52), "Kakariko", "Bottom of the Well", ENTRANCE_GROUP_KAKARIKO, ENTRANCE_GROUP_KAKARIKO, ENTRANCE_TYPE_DUNGEON, "botw", 1},
{ 0x0349, 0x02FD, SINGLE_SCENE_INFO(0x2A), "Carpenter Boss House", "Kakariko", ENTRANCE_GROUP_KAKARIKO, ENTRANCE_GROUP_KAKARIKO, ENTRANCE_TYPE_INTERIOR},
{ 0x04EE, 0x0550, SINGLE_SCENE_INFO(0x50), "House of Skulltula", "Kakariko", ENTRANCE_GROUP_KAKARIKO, ENTRANCE_GROUP_KAKARIKO, ENTRANCE_TYPE_INTERIOR},
{ 0x0345, 0x039C, SINGLE_SCENE_INFO(0x37), "Impa's House Front", "Kakariko", ENTRANCE_GROUP_KAKARIKO, ENTRANCE_GROUP_KAKARIKO, ENTRANCE_TYPE_INTERIOR},
{ 0x05DC, 0x05C8, SINGLE_SCENE_INFO(0x37), "Impa's House Back", "Kakariko", ENTRANCE_GROUP_KAKARIKO, ENTRANCE_GROUP_KAKARIKO, ENTRANCE_TYPE_INTERIOR, "cow"},
{ 0x0351, 0x0453, SINGLE_SCENE_INFO(0x48), "Windmill", "Kakariko", ENTRANCE_GROUP_KAKARIKO, ENTRANCE_GROUP_KAKARIKO, ENTRANCE_TYPE_INTERIOR},
{ 0x0463, 0x003B, {{ 0x42, 0x00 }}, "Kak Shooting Gallery", "Kakariko", ENTRANCE_GROUP_KAKARIKO, ENTRANCE_GROUP_KAKARIKO, ENTRANCE_TYPE_INTERIOR},
{ 0x034D, 0x0072, SINGLE_SCENE_INFO(0x4E), "Granny's Potion Shop", "Kakariko", ENTRANCE_GROUP_KAKARIKO, ENTRANCE_GROUP_KAKARIKO, ENTRANCE_TYPE_INTERIOR},
{ 0x0201, 0x00B7, {{ 0x2C, 0x00 }}, "Kak Bazaar", "Kakariko", ENTRANCE_GROUP_KAKARIKO, ENTRANCE_GROUP_KAKARIKO, ENTRANCE_TYPE_INTERIOR, "shop"},
{ 0x044B, 0x0384, SINGLE_SCENE_INFO(0x30), "Kak Potion Shop Front", "Kakariko", ENTRANCE_GROUP_KAKARIKO, ENTRANCE_GROUP_KAKARIKO, ENTRANCE_TYPE_INTERIOR},
{ 0x04FF, 0x03EC, SINGLE_SCENE_INFO(0x30), "Kak Potion Shop Back", "Kakariko", ENTRANCE_GROUP_KAKARIKO, ENTRANCE_GROUP_KAKARIKO, ENTRANCE_TYPE_INTERIOR},
{ 0x080A, 0x070A, {{ 0x3E, 0x00 }}, "Kak Open Grotto", "Kakariko", ENTRANCE_GROUP_KAKARIKO, ENTRANCE_GROUP_KAKARIKO, ENTRANCE_TYPE_GROTTO, "chest"},
{ 0x080B, 0x070B, {{ 0x3E, 0x03 }}, "Kak Redead Grotto", "Kakariko", ENTRANCE_GROUP_KAKARIKO, ENTRANCE_GROUP_KAKARIKO, ENTRANCE_TYPE_GROTTO, "chest"},
{ 0x02A6, 0x0098, SINGLE_SCENE_INFO(0x08), "Bottom of the Well", "Kakariko", ENTRANCE_GROUP_KAKARIKO, ENTRANCE_GROUP_KAKARIKO, ENTRANCE_TYPE_DUNGEON, "botw"},
// The Graveyard
{ 0x0195, 0x00E4, SINGLE_SCENE_INFO(0x53), "Graveyard", "Kakariko", ENTRANCE_GROUP_GRAVEYARD, ENTRANCE_GROUP_KAKARIKO, ENTRANCE_TYPE_OVERWORLD},
{ 0x030D, 0x0355, SINGLE_SCENE_INFO(0x53), "Graveyard", "Dampe's Shack", ENTRANCE_GROUP_GRAVEYARD, ENTRANCE_GROUP_GRAVEYARD, ENTRANCE_TYPE_INTERIOR, "", 1},
{ 0x004B, 0x035D, SINGLE_SCENE_INFO(0x53), "Graveyard", "Shield Grave", ENTRANCE_GROUP_GRAVEYARD, ENTRANCE_GROUP_GRAVEYARD, ENTRANCE_TYPE_GROTTO, "", 1},
{ 0x031C, 0x0361, SINGLE_SCENE_INFO(0x53), "Graveyard", "Heart Piece Grave", ENTRANCE_GROUP_GRAVEYARD, ENTRANCE_GROUP_GRAVEYARD, ENTRANCE_TYPE_GROTTO, "", 1},
{ 0x002D, 0x050B, SINGLE_SCENE_INFO(0x53), "Graveyard", "Composer's Grave", ENTRANCE_GROUP_GRAVEYARD, ENTRANCE_GROUP_GRAVEYARD, ENTRANCE_TYPE_GROTTO, "", 1},
{ 0x044F, 0x0359, SINGLE_SCENE_INFO(0x53), "Graveyard", "Dampe's Grave", ENTRANCE_GROUP_GRAVEYARD, ENTRANCE_GROUP_GRAVEYARD, ENTRANCE_TYPE_GROTTO, "race", 1},
{ 0x0037, 0x0205, SINGLE_SCENE_INFO(0x53), "Graveyard", "Shadow Temple", ENTRANCE_GROUP_GRAVEYARD, ENTRANCE_GROUP_GRAVEYARD, ENTRANCE_TYPE_DUNGEON, "", 1},
{ 0x0355, 0x030D, SINGLE_SCENE_INFO(0x3A), "Dampe's Shack", "Graveyard", ENTRANCE_GROUP_GRAVEYARD, ENTRANCE_GROUP_GRAVEYARD, ENTRANCE_TYPE_INTERIOR},
{ 0x035D, 0x004B, SINGLE_SCENE_INFO(0x40), "Shield Grave", "Graveyard", ENTRANCE_GROUP_GRAVEYARD, ENTRANCE_GROUP_GRAVEYARD, ENTRANCE_TYPE_GROTTO},
{ 0x0361, 0x031C, SINGLE_SCENE_INFO(0x3F), "Heart Piece Grave", "Graveyard", ENTRANCE_GROUP_GRAVEYARD, ENTRANCE_GROUP_GRAVEYARD, ENTRANCE_TYPE_GROTTO},
{ 0x050B, 0x002D, SINGLE_SCENE_INFO(0x41), "Composer's Grave", "Graveyard", ENTRANCE_GROUP_GRAVEYARD, ENTRANCE_GROUP_GRAVEYARD, ENTRANCE_TYPE_GROTTO},
{ 0x0359, 0x044F, SINGLE_SCENE_INFO(0x48), "Dampe's Grave", "Graveyard", ENTRANCE_GROUP_GRAVEYARD, ENTRANCE_GROUP_GRAVEYARD, ENTRANCE_TYPE_GROTTO, "race"},
{ 0x0205, 0x0037, SINGLE_SCENE_INFO(0x07), "Shadow Temple", "Graveyard", ENTRANCE_GROUP_GRAVEYARD, ENTRANCE_GROUP_GRAVEYARD, ENTRANCE_TYPE_DUNGEON},
// Death Mountain Trail
{ 0x0191, 0x013D, SINGLE_SCENE_INFO(0x60), "DMT", "Kakariko", ENTRANCE_GROUP_DEATH_MOUNTAIN_TRAIL, ENTRANCE_GROUP_KAKARIKO, ENTRANCE_TYPE_OVERWORLD},
{ 0x014D, 0x01B9, SINGLE_SCENE_INFO(0x60), "DMT", "Goron City", ENTRANCE_GROUP_DEATH_MOUNTAIN_TRAIL, ENTRANCE_GROUP_GORON_CITY, ENTRANCE_TYPE_OVERWORLD, "gc"},
{ 0x0147, 0x01BD, SINGLE_SCENE_INFO(0x60), "DMT", "DMC", ENTRANCE_GROUP_DEATH_MOUNTAIN_TRAIL, ENTRANCE_GROUP_DEATH_MOUNTAIN_CRATER, ENTRANCE_TYPE_OVERWORLD},
{ 0x0315, 0x045B, SINGLE_SCENE_INFO(0x60), "DMT", "DMT Great Fairy Fountain", ENTRANCE_GROUP_DEATH_MOUNTAIN_TRAIL, ENTRANCE_GROUP_DEATH_MOUNTAIN_TRAIL, ENTRANCE_TYPE_INTERIOR, "", 1},
{ 0x0708, 0x0808, SINGLE_SCENE_INFO(0x60), "DMT", "DMT Storms Grotto", ENTRANCE_GROUP_DEATH_MOUNTAIN_TRAIL, ENTRANCE_GROUP_DEATH_MOUNTAIN_TRAIL, ENTRANCE_TYPE_GROTTO, "chest", 1},
{ 0x0709, 0x0809, SINGLE_SCENE_INFO(0x60), "DMT", "DMT Cow Grotto", ENTRANCE_GROUP_DEATH_MOUNTAIN_TRAIL, ENTRANCE_GROUP_DEATH_MOUNTAIN_TRAIL, ENTRANCE_TYPE_GROTTO, "", 1},
{ 0x0004, 0x0242, SINGLE_SCENE_INFO(0x60), "DMT", "Dodongo's Cavern", ENTRANCE_GROUP_DEATH_MOUNTAIN_TRAIL, ENTRANCE_GROUP_DEATH_MOUNTAIN_TRAIL, ENTRANCE_TYPE_DUNGEON, "dc", 1},
{ 0x045B, 0x0315, {{ 0x3B, 0x00 }}, "DMT Great Fairy Fountain", "DMT", ENTRANCE_GROUP_DEATH_MOUNTAIN_TRAIL, ENTRANCE_GROUP_DEATH_MOUNTAIN_TRAIL, ENTRANCE_TYPE_INTERIOR},
{ 0x0808, 0x0708, {{ 0x3E, 0x00 }}, "DMT Storms Grotto", "DMT", ENTRANCE_GROUP_DEATH_MOUNTAIN_TRAIL, ENTRANCE_GROUP_DEATH_MOUNTAIN_TRAIL, ENTRANCE_TYPE_GROTTO, "chest"},
{ 0x0809, 0x0709, {{ 0x3E, 0x0D }}, "DMT Cow Grotto", "DMT", ENTRANCE_GROUP_DEATH_MOUNTAIN_TRAIL, ENTRANCE_GROUP_DEATH_MOUNTAIN_TRAIL, ENTRANCE_TYPE_GROTTO},
{ 0x0242, 0x0004, SINGLE_SCENE_INFO(0x01), "Dodongo's Cavern", "DMT", ENTRANCE_GROUP_DEATH_MOUNTAIN_TRAIL, ENTRANCE_GROUP_DEATH_MOUNTAIN_TRAIL, ENTRANCE_TYPE_DUNGEON, "dc"},
// Death Mountain Crater
{ 0x01C1, 0x0246, SINGLE_SCENE_INFO(0x61), "DMC", "Goron City", ENTRANCE_GROUP_DEATH_MOUNTAIN_CRATER, ENTRANCE_GROUP_GORON_CITY, ENTRANCE_TYPE_OVERWORLD, "gc"},
{ 0x01BD, 0x0147, SINGLE_SCENE_INFO(0x61), "DMC", "DMT", ENTRANCE_GROUP_DEATH_MOUNTAIN_CRATER, ENTRANCE_GROUP_DEATH_MOUNTAIN_TRAIL, ENTRANCE_TYPE_OVERWORLD},
{ 0x04BE, 0x0482, SINGLE_SCENE_INFO(0x61), "DMC", "DMC Great Fairy Fountain", ENTRANCE_GROUP_DEATH_MOUNTAIN_CRATER, ENTRANCE_GROUP_DEATH_MOUNTAIN_CRATER, ENTRANCE_TYPE_INTERIOR, "", 1},
{ 0x0706, 0x0806, SINGLE_SCENE_INFO(0x61), "DMC", "DMC Upper Grotto", ENTRANCE_GROUP_DEATH_MOUNTAIN_CRATER, ENTRANCE_GROUP_DEATH_MOUNTAIN_CRATER, ENTRANCE_TYPE_GROTTO, "chest", 1},
{ 0x0705, 0x0805, SINGLE_SCENE_INFO(0x61), "DMC", "DMC Hammer Grotto", ENTRANCE_GROUP_DEATH_MOUNTAIN_CRATER, ENTRANCE_GROUP_DEATH_MOUNTAIN_CRATER, ENTRANCE_TYPE_GROTTO, "scrubs", 1},
{ 0x0165, 0x024A, SINGLE_SCENE_INFO(0x61), "DMC", "Fire Temple", ENTRANCE_GROUP_DEATH_MOUNTAIN_CRATER, ENTRANCE_GROUP_DEATH_MOUNTAIN_CRATER, ENTRANCE_TYPE_DUNGEON, "", 1},
{ 0x0482, 0x04BE, {{ 0x3B, 0x01 }}, "DMC Great Fairy Fountain", "DMC", ENTRANCE_GROUP_DEATH_MOUNTAIN_CRATER, ENTRANCE_GROUP_DEATH_MOUNTAIN_CRATER, ENTRANCE_TYPE_INTERIOR},
{ 0x0806, 0x0706, {{ 0x3E, 0x00 }}, "DMC Upper Grotto", "DMC", ENTRANCE_GROUP_DEATH_MOUNTAIN_CRATER, ENTRANCE_GROUP_DEATH_MOUNTAIN_CRATER, ENTRANCE_TYPE_GROTTO, "chest"},
{ 0x0805, 0x0705, {{ 0x3E, 0x04 }}, "DMC Hammer Grotto", "DMC", ENTRANCE_GROUP_DEATH_MOUNTAIN_CRATER, ENTRANCE_GROUP_DEATH_MOUNTAIN_CRATER, ENTRANCE_TYPE_GROTTO, "scrubs"},
{ 0x024A, 0x0165, SINGLE_SCENE_INFO(0x04), "Fire Temple", "DMC", ENTRANCE_GROUP_DEATH_MOUNTAIN_CRATER, ENTRANCE_GROUP_DEATH_MOUNTAIN_CRATER, ENTRANCE_TYPE_DUNGEON},
// Goron City
{ 0x01B9, 0x014D, SINGLE_SCENE_INFO(0x62), "Goron City", "DMT", ENTRANCE_GROUP_GORON_CITY, ENTRANCE_GROUP_DEATH_MOUNTAIN_TRAIL, ENTRANCE_TYPE_OVERWORLD, "gc"},
{ 0x0246, 0x01C1, SINGLE_SCENE_INFO(0x62), "Goron City", "DMC", ENTRANCE_GROUP_GORON_CITY, ENTRANCE_GROUP_DEATH_MOUNTAIN_CRATER, ENTRANCE_TYPE_OVERWORLD, "gc"},
{ 0x04D6, 0x04E2, SINGLE_SCENE_INFO(0x62), "Goron City", "Lost Woods", ENTRANCE_GROUP_GORON_CITY, ENTRANCE_GROUP_LOST_WOODS, ENTRANCE_TYPE_OVERWORLD, "gc,lw"},
{ 0x037C, 0x03FC, SINGLE_SCENE_INFO(0x62), "Goron City", "Goron Shop", ENTRANCE_GROUP_GORON_CITY, ENTRANCE_GROUP_GORON_CITY, ENTRANCE_TYPE_INTERIOR, "gc", 1},
{ 0x0707, 0x0807, SINGLE_SCENE_INFO(0x62), "Goron City", "Goron City Grotto", ENTRANCE_GROUP_GORON_CITY, ENTRANCE_GROUP_GORON_CITY, ENTRANCE_TYPE_GROTTO, "gc,scrubs", 1},
{ 0x03FC, 0x037C, SINGLE_SCENE_INFO(0x2E), "Goron Shop", "Goron City", ENTRANCE_GROUP_GORON_CITY, ENTRANCE_GROUP_GORON_CITY, ENTRANCE_TYPE_INTERIOR, "gc"},
{ 0x0807, 0x0707, {{ 0x3E, 0x04 }}, "Goron City Grotto", "Goron City", ENTRANCE_GROUP_GORON_CITY, ENTRANCE_GROUP_GORON_CITY, ENTRANCE_TYPE_GROTTO, "gc,scrubs"},
// Zora's River
{ 0x0181, 0x00EA, SINGLE_SCENE_INFO(0x54), "ZR", "Hyrule Field", ENTRANCE_GROUP_ZORAS_RIVER, ENTRANCE_GROUP_HYRULE_FIELD, ENTRANCE_TYPE_OVERWORLD, "hf"},
{ 0x04DA, 0x01DD, SINGLE_SCENE_INFO(0x54), "ZR", "Lost Woods", ENTRANCE_GROUP_ZORAS_RIVER, ENTRANCE_GROUP_LOST_WOODS, ENTRANCE_TYPE_OVERWORLD, "lw"},
{ 0x0108, 0x019D, SINGLE_SCENE_INFO(0x54), "ZR", "Zora's Domain", ENTRANCE_GROUP_ZORAS_RIVER, ENTRANCE_GROUP_ZORAS_DOMAIN, ENTRANCE_TYPE_OVERWORLD},
{ 0x0702, 0x0802, SINGLE_SCENE_INFO(0x54), "ZR", "ZR Storms Grotto", ENTRANCE_GROUP_ZORAS_RIVER, ENTRANCE_GROUP_ZORAS_RIVER, ENTRANCE_TYPE_GROTTO, "scrubs", 1},
{ 0x0703, 0x0803, SINGLE_SCENE_INFO(0x54), "ZR", "ZR Fairy Grotto", ENTRANCE_GROUP_ZORAS_RIVER, ENTRANCE_GROUP_ZORAS_RIVER, ENTRANCE_TYPE_GROTTO, "", 1},
{ 0x0704, 0x0804, SINGLE_SCENE_INFO(0x54), "ZR", "ZR Open Grotto", ENTRANCE_GROUP_ZORAS_RIVER, ENTRANCE_GROUP_ZORAS_RIVER, ENTRANCE_TYPE_GROTTO, "chest", 1},
{ 0x0802, 0x0702, {{ 0x3E, 0x0A }}, "ZR Storms Grotto", "ZR", ENTRANCE_GROUP_ZORAS_RIVER, ENTRANCE_GROUP_ZORAS_RIVER, ENTRANCE_TYPE_GROTTO, "scrubs"},
{ 0x0803, 0x0703, {{ 0x3C, 0x00 }}, "ZR Fairy Grotto", "ZR", ENTRANCE_GROUP_ZORAS_RIVER, ENTRANCE_GROUP_ZORAS_RIVER, ENTRANCE_TYPE_GROTTO},
{ 0x0804, 0x0704, {{ 0x3E, 0x00 }}, "ZR Open Grotto", "ZR", ENTRANCE_GROUP_ZORAS_RIVER, ENTRANCE_GROUP_ZORAS_RIVER, ENTRANCE_TYPE_GROTTO, "chest"},
// Zora's Domain
{ 0x019D, 0x0108, SINGLE_SCENE_INFO(0x58), "Zora's Domain", "ZR", ENTRANCE_GROUP_ZORAS_DOMAIN, ENTRANCE_GROUP_ZORAS_RIVER, ENTRANCE_TYPE_OVERWORLD},
{ 0x0560, 0x0328, SINGLE_SCENE_INFO(0x58), "Zora's Domain", "Lake Hylia", ENTRANCE_GROUP_ZORAS_DOMAIN, ENTRANCE_GROUP_LAKE_HYLIA, ENTRANCE_TYPE_OVERWORLD, "lh"},
{ 0x0225, 0x01A1, SINGLE_SCENE_INFO(0x58), "Zora's Domain", "ZF", ENTRANCE_GROUP_ZORAS_DOMAIN, ENTRANCE_GROUP_ZORAS_FOUNTAIN, ENTRANCE_TYPE_OVERWORLD},
{ 0x0380, 0x03C4, SINGLE_SCENE_INFO(0x58), "Zora's Domain", "Zora Shop", ENTRANCE_GROUP_ZORAS_DOMAIN, ENTRANCE_GROUP_ZORAS_DOMAIN, ENTRANCE_TYPE_INTERIOR, "", 1},
{ 0x071C, 0x081C, SINGLE_SCENE_INFO(0x58), "Zora's Domain", "ZD Storms Grotto", ENTRANCE_GROUP_ZORAS_DOMAIN, ENTRANCE_GROUP_ZORAS_DOMAIN, ENTRANCE_TYPE_GROTTO, "fairy", 1},
{ 0x03C4, 0x0380, SINGLE_SCENE_INFO(0x2F), "Zora Shop", "Zora's Domain", ENTRANCE_GROUP_ZORAS_DOMAIN, ENTRANCE_GROUP_ZORAS_DOMAIN, ENTRANCE_TYPE_INTERIOR},
{ 0x081C, 0x071C, {{ 0x3C, 0x00 }}, "ZD Storms Grotto", "Zora's Domain", ENTRANCE_GROUP_ZORAS_DOMAIN, ENTRANCE_GROUP_ZORAS_DOMAIN, ENTRANCE_TYPE_GROTTO, "fairy"},
// Zora's Fountain
{ 0x01A1, 0x0225, SINGLE_SCENE_INFO(0x59), "ZF", "Zora's Domain", ENTRANCE_GROUP_ZORAS_FOUNTAIN, ENTRANCE_GROUP_ZORAS_DOMAIN, ENTRANCE_TYPE_OVERWORLD},
{ 0x0371, 0x0394, SINGLE_SCENE_INFO(0x59), "ZF", "ZF Great Fairy Fountain", ENTRANCE_GROUP_ZORAS_FOUNTAIN, ENTRANCE_GROUP_ZORAS_FOUNTAIN, ENTRANCE_TYPE_INTERIOR, "", 1},
{ 0x0028, 0x0221, SINGLE_SCENE_INFO(0x59), "ZF", "Jabu Jabu's Belly", ENTRANCE_GROUP_ZORAS_FOUNTAIN, ENTRANCE_GROUP_ZORAS_FOUNTAIN, ENTRANCE_TYPE_DUNGEON, "", 1},
{ 0x0088, 0x03D4, SINGLE_SCENE_INFO(0x59), "ZF", "Ice Cavern", ENTRANCE_GROUP_ZORAS_FOUNTAIN, ENTRANCE_GROUP_ZORAS_FOUNTAIN, ENTRANCE_TYPE_DUNGEON, "", 1},
{ 0x0394, 0x0371, {{ 0x3D, 0x00 }}, "ZF Great Fairy Fountain", "ZF", ENTRANCE_GROUP_ZORAS_FOUNTAIN, ENTRANCE_GROUP_ZORAS_FOUNTAIN, ENTRANCE_TYPE_INTERIOR},
{ 0x0221, 0x0028, SINGLE_SCENE_INFO(0x02), "Jabu Jabu's Belly", "ZF", ENTRANCE_GROUP_ZORAS_FOUNTAIN, ENTRANCE_GROUP_ZORAS_FOUNTAIN, ENTRANCE_TYPE_DUNGEON},
{ 0x03D4, 0x0088, SINGLE_SCENE_INFO(0x09), "Ice Cavern", "ZF", ENTRANCE_GROUP_ZORAS_FOUNTAIN, ENTRANCE_GROUP_ZORAS_FOUNTAIN, ENTRANCE_TYPE_DUNGEON},
// Hyrule Field
{ 0x04DE, 0x0185, SINGLE_SCENE_INFO(0x51), "Hyrule Field", "Lost Woods Bridge", ENTRANCE_GROUP_HYRULE_FIELD, ENTRANCE_GROUP_LOST_WOODS, ENTRANCE_TYPE_OVERWORLD, "hf,lw"},
{ 0x0276, 0x01FD, SINGLE_SCENE_INFO(0x51), "Hyrule Field", "Market Entrance", ENTRANCE_GROUP_HYRULE_FIELD, ENTRANCE_GROUP_MARKET, ENTRANCE_TYPE_OVERWORLD, "hf"},
{ 0x0157, 0x01F9, SINGLE_SCENE_INFO(0x51), "Hyrule Field", "Lon Lon Ranch", ENTRANCE_GROUP_HYRULE_FIELD, ENTRANCE_GROUP_LON_LON_RANCH, ENTRANCE_TYPE_OVERWORLD, "hf,llr"},
{ 0x00DB, 0x017D, SINGLE_SCENE_INFO(0x51), "Hyrule Field", "Kakariko", ENTRANCE_GROUP_HYRULE_FIELD, ENTRANCE_GROUP_KAKARIKO, ENTRANCE_TYPE_OVERWORLD, "hf"},
{ 0x00EA, 0x0181, SINGLE_SCENE_INFO(0x51), "Hyrule Field", "ZR", ENTRANCE_GROUP_HYRULE_FIELD, ENTRANCE_GROUP_ZORAS_RIVER, ENTRANCE_TYPE_OVERWORLD, "hf"},
{ 0x0102, 0x0189, SINGLE_SCENE_INFO(0x51), "Hyrule Field", "Lake Hylia", ENTRANCE_GROUP_HYRULE_FIELD, ENTRANCE_GROUP_LAKE_HYLIA, ENTRANCE_TYPE_OVERWORLD, "hf,lh"},
{ 0x0117, 0x018D, SINGLE_SCENE_INFO(0x51), "Hyrule Field", "GV", ENTRANCE_GROUP_HYRULE_FIELD, ENTRANCE_GROUP_GERUDO_VALLEY, ENTRANCE_TYPE_OVERWORLD, "hf"},
{ 0x0710, 0x0810, SINGLE_SCENE_INFO(0x51), "Hyrule Field", "HF Near Market Grotto", ENTRANCE_GROUP_HYRULE_FIELD, ENTRANCE_GROUP_HYRULE_FIELD, ENTRANCE_TYPE_GROTTO, "chest", 1},
{ 0x070E, 0x080E, SINGLE_SCENE_INFO(0x51), "Hyrule Field", "HF Near Kak Grotto", ENTRANCE_GROUP_HYRULE_FIELD, ENTRANCE_GROUP_HYRULE_FIELD, ENTRANCE_TYPE_GROTTO, "spider", 1},
{ 0x070D, 0x080D, SINGLE_SCENE_INFO(0x51), "Hyrule Field", "HF Tektite Grotto", ENTRANCE_GROUP_HYRULE_FIELD, ENTRANCE_GROUP_HYRULE_FIELD, ENTRANCE_TYPE_GROTTO, "water", 1},
{ 0x070F, 0x080F, SINGLE_SCENE_INFO(0x51), "Hyrule Field", "HF Fairy Grotto", ENTRANCE_GROUP_HYRULE_FIELD, ENTRANCE_GROUP_HYRULE_FIELD, ENTRANCE_TYPE_GROTTO, "", 1},
{ 0x0711, 0x0811, SINGLE_SCENE_INFO(0x51), "Hyrule Field", "HF Cow Grotto", ENTRANCE_GROUP_HYRULE_FIELD, ENTRANCE_GROUP_HYRULE_FIELD, ENTRANCE_TYPE_GROTTO, "webbed", 1},
{ 0x0713, 0x0813, SINGLE_SCENE_INFO(0x51), "Hyrule Field", "HF Open Grotto", ENTRANCE_GROUP_HYRULE_FIELD, ENTRANCE_GROUP_HYRULE_FIELD, ENTRANCE_TYPE_GROTTO, "chest", 1},
{ 0x0712, 0x0812, SINGLE_SCENE_INFO(0x51), "Hyrule Field", "HF Inside Fence Grotto", ENTRANCE_GROUP_HYRULE_FIELD, ENTRANCE_GROUP_HYRULE_FIELD, ENTRANCE_TYPE_GROTTO, "scrubs", 1},
{ 0x0714, 0x0814, SINGLE_SCENE_INFO(0x51), "Hyrule Field", "HF Southeast Grotto", ENTRANCE_GROUP_HYRULE_FIELD, ENTRANCE_GROUP_HYRULE_FIELD, ENTRANCE_TYPE_GROTTO, "chest", 1},
{ 0x0810, 0x0710, {{ 0x3E, 0x00 }}, "HF Near Market Grotto", "Hyrule Field", ENTRANCE_GROUP_HYRULE_FIELD, ENTRANCE_GROUP_HYRULE_FIELD, ENTRANCE_TYPE_GROTTO},
{ 0x080E, 0x070E, {{ 0x3E, 0x01 }}, "HF Near Kak Grotto", "Hyrule Field", ENTRANCE_GROUP_HYRULE_FIELD, ENTRANCE_GROUP_HYRULE_FIELD, ENTRANCE_TYPE_GROTTO, "spider"},
{ 0x080D, 0x070D, {{ 0x3E, 0x0B }}, "HF Tektite Grotto", "Hyrule Field", ENTRANCE_GROUP_HYRULE_FIELD, ENTRANCE_GROUP_HYRULE_FIELD, ENTRANCE_TYPE_GROTTO, "water"},
{ 0x080F, 0x070F, {{ 0x3C, 0x00 }}, "HF Fairy Grotto", "Hyrule Field", ENTRANCE_GROUP_HYRULE_FIELD, ENTRANCE_GROUP_HYRULE_FIELD, ENTRANCE_TYPE_GROTTO},
{ 0x0811, 0x0711, {{ 0x3E, 0x05 }}, "HF Cow Grotto", "Hyrule Field", ENTRANCE_GROUP_HYRULE_FIELD, ENTRANCE_GROUP_HYRULE_FIELD, ENTRANCE_TYPE_GROTTO, "webbed"},
{ 0x0813, 0x0713, {{ 0x3E, 0x00 }}, "HF Open Grotto", "Hyrule Field", ENTRANCE_GROUP_HYRULE_FIELD, ENTRANCE_GROUP_HYRULE_FIELD, ENTRANCE_TYPE_GROTTO, "chest"},
{ 0x0812, 0x0712, {{ 0x3E, 0x02 }}, "HF Inside Fence Grotto", "Hyrule Field", ENTRANCE_GROUP_HYRULE_FIELD, ENTRANCE_GROUP_HYRULE_FIELD, ENTRANCE_TYPE_GROTTO, "srubs"},
{ 0x0814, 0x0714, {{ 0x3E, 0x00 }}, "HF Southeast Grotto", "Hyrule Field", ENTRANCE_GROUP_HYRULE_FIELD, ENTRANCE_GROUP_HYRULE_FIELD, ENTRANCE_TYPE_GROTTO, "chest"},
// Lon Lon Ranch
{ 0x01F9, 0x0157, SINGLE_SCENE_INFO(0x63), "Lon Lon Ranch", "Hyrule Field", ENTRANCE_GROUP_LON_LON_RANCH, ENTRANCE_GROUP_HYRULE_FIELD, ENTRANCE_TYPE_OVERWORLD, "hf"},
{ 0x004F, 0x0378, SINGLE_SCENE_INFO(0x63), "Lon Lon Ranch", "Talon's House", ENTRANCE_GROUP_LON_LON_RANCH, ENTRANCE_GROUP_LON_LON_RANCH, ENTRANCE_TYPE_INTERIOR, "llr", 1},
{ 0x02F9, 0x042F, SINGLE_SCENE_INFO(0x63), "Lon Lon Ranch", "LLR Stables", ENTRANCE_GROUP_LON_LON_RANCH, ENTRANCE_GROUP_LON_LON_RANCH, ENTRANCE_TYPE_INTERIOR, "cow", 1},
{ 0x05D0, 0x05D4, SINGLE_SCENE_INFO(0x63), "Lon Lon Ranch", "LLR Tower", ENTRANCE_GROUP_LON_LON_RANCH, ENTRANCE_GROUP_LON_LON_RANCH, ENTRANCE_TYPE_INTERIOR, "cow", 1},
{ 0x0715, 0x0815, SINGLE_SCENE_INFO(0x63), "Lon Lon Ranch", "LLR Grotto", ENTRANCE_GROUP_LON_LON_RANCH, ENTRANCE_GROUP_LON_LON_RANCH, ENTRANCE_TYPE_GROTTO, "scrubs", 1},
{ 0x0378, 0x004F, {{ 0x4C, 0x00 }}, "Talon's House", "Lon Lon Ranch", ENTRANCE_GROUP_LON_LON_RANCH, ENTRANCE_GROUP_LON_LON_RANCH, ENTRANCE_TYPE_INTERIOR, "llr"},
{ 0x042F, 0x02F9, SINGLE_SCENE_INFO(0x36), "LLR Stables", "Lon Lon Ranch", ENTRANCE_GROUP_LON_LON_RANCH, ENTRANCE_GROUP_LON_LON_RANCH, ENTRANCE_TYPE_INTERIOR, "cow"},
{ 0x05D4, 0x05D0, {{ 0x4C, 0x01 }}, "LLR Tower", "Lon Lon Ranch", ENTRANCE_GROUP_LON_LON_RANCH, ENTRANCE_GROUP_LON_LON_RANCH, ENTRANCE_TYPE_INTERIOR, "cow"},
{ 0x0815, 0x0715, {{ 0x3E, 0x04 }}, "LLR Grotto", "Lon Lon Ranch", ENTRANCE_GROUP_LON_LON_RANCH, ENTRANCE_GROUP_LON_LON_RANCH, ENTRANCE_TYPE_GROTTO, "scrubs"},
// Lake Hylia
{ 0x0189, 0x0102, SINGLE_SCENE_INFO(0x57), "Lake Hylia", "Hyrule Field", ENTRANCE_GROUP_LAKE_HYLIA, ENTRANCE_GROUP_HYRULE_FIELD, ENTRANCE_TYPE_OVERWORLD, "lh"},
{ 0x0328, 0x0560, SINGLE_SCENE_INFO(0x57), "Lake Hylia", "Zora's Domain", ENTRANCE_GROUP_LAKE_HYLIA, ENTRANCE_GROUP_ZORAS_DOMAIN, ENTRANCE_TYPE_OVERWORLD, "lh"},
{ 0x0043, 0x03CC, SINGLE_SCENE_INFO(0x57), "Lake Hylia", "LH Lab", ENTRANCE_GROUP_LAKE_HYLIA, ENTRANCE_GROUP_LAKE_HYLIA, ENTRANCE_TYPE_INTERIOR, "lh", 1},
{ 0x045F, 0x0309, SINGLE_SCENE_INFO(0x57), "Lake Hylia", "Fishing Hole", ENTRANCE_GROUP_LAKE_HYLIA, ENTRANCE_GROUP_LAKE_HYLIA, ENTRANCE_TYPE_INTERIOR, "lh", 1},
{ 0x0701, 0x0801, SINGLE_SCENE_INFO(0x57), "Lake Hylia", "LH Grotto", ENTRANCE_GROUP_LAKE_HYLIA, ENTRANCE_GROUP_LAKE_HYLIA, ENTRANCE_TYPE_GROTTO, "scrubs", 1},
{ 0x0010, 0x021D, SINGLE_SCENE_INFO(0x57), "Lake Hylia", "Water Temple", ENTRANCE_GROUP_LAKE_HYLIA, ENTRANCE_GROUP_LAKE_HYLIA, ENTRANCE_TYPE_DUNGEON, "lh", 1},
{ 0x03CC, 0x0043, SINGLE_SCENE_INFO(0x38), "LH Lab", "Lake Hylia", ENTRANCE_GROUP_LAKE_HYLIA, ENTRANCE_GROUP_LAKE_HYLIA, ENTRANCE_TYPE_INTERIOR, "lh"},
{ 0x0309, 0x045F, SINGLE_SCENE_INFO(0x49), "Fishing Hole", "Lake Hylia", ENTRANCE_GROUP_LAKE_HYLIA, ENTRANCE_GROUP_LAKE_HYLIA, ENTRANCE_TYPE_INTERIOR, "lh"},
{ 0x0801, 0x0701, {{ 0x3E, 0x04 }}, "LH Grotto", "Lake Hylia", ENTRANCE_GROUP_LAKE_HYLIA, ENTRANCE_GROUP_LAKE_HYLIA, ENTRANCE_TYPE_GROTTO, "lh,scrubs"},
{ 0x021D, 0x0010, SINGLE_SCENE_INFO(0x05), "Water Temple", "Lake Hylia", ENTRANCE_GROUP_LAKE_HYLIA, ENTRANCE_GROUP_LAKE_HYLIA, ENTRANCE_TYPE_DUNGEON, "lh"},
// Gerudo Area
{ 0x018D, 0x0117, SINGLE_SCENE_INFO(0x5A), "GV", "Hyrule Field", ENTRANCE_GROUP_GERUDO_VALLEY, ENTRANCE_GROUP_HYRULE_FIELD, ENTRANCE_TYPE_OVERWORLD, "hf"},
{ 0x0129, 0x022D, SINGLE_SCENE_INFO(0x5A), "GV", "GF", ENTRANCE_GROUP_GERUDO_VALLEY, ENTRANCE_GROUP_GERUDO_VALLEY, ENTRANCE_TYPE_OVERWORLD, "gerudo fortress"},
{ 0x0219, -1, SINGLE_SCENE_INFO(0x5A), "GV", "Lake Hylia", ENTRANCE_GROUP_GERUDO_VALLEY, ENTRANCE_GROUP_LAKE_HYLIA, ENTRANCE_TYPE_OVERWORLD, "lh"},
{ 0x03A0, 0x03D0, SINGLE_SCENE_INFO(0x5A), "GV", "Carpenters' Tent", ENTRANCE_GROUP_GERUDO_VALLEY, ENTRANCE_GROUP_GERUDO_VALLEY, ENTRANCE_TYPE_INTERIOR, "", 1},
{ 0x071F, 0x081F, SINGLE_SCENE_INFO(0x5A), "GV", "GV Octorok Grotto", ENTRANCE_GROUP_GERUDO_VALLEY, ENTRANCE_GROUP_GERUDO_VALLEY, ENTRANCE_TYPE_GROTTO, "", 1},
{ 0x071E, 0x081E, SINGLE_SCENE_INFO(0x5A), "GV", "GV Storms Grotto", ENTRANCE_GROUP_GERUDO_VALLEY, ENTRANCE_GROUP_GERUDO_VALLEY, ENTRANCE_TYPE_GROTTO, "scrubs", 1},
{ 0x022D, 0x0129, SINGLE_SCENE_INFO(0x5D), "GF", "GV", ENTRANCE_GROUP_GERUDO_VALLEY, ENTRANCE_GROUP_GERUDO_VALLEY, ENTRANCE_TYPE_OVERWORLD, "gerudo fortress"},
{ 0x0130, 0x03AC, SINGLE_SCENE_INFO(0x5D), "GF", "Haunted Wasteland", ENTRANCE_GROUP_GERUDO_VALLEY, ENTRANCE_GROUP_HAUNTED_WASTELAND, ENTRANCE_TYPE_OVERWORLD, "gerudo fortress"},
{ 0x071D, 0x081D, SINGLE_SCENE_INFO(0x5D), "GF", "GF Storms Grotto", ENTRANCE_GROUP_GERUDO_VALLEY, ENTRANCE_GROUP_GERUDO_VALLEY, ENTRANCE_TYPE_GROTTO, "gerudo fortress", 1},
{ 0x0008, 0x03A8, SINGLE_SCENE_INFO(0x5D), "GF", "Gerudo Training Grounds", ENTRANCE_GROUP_GERUDO_VALLEY, ENTRANCE_GROUP_GERUDO_VALLEY, ENTRANCE_TYPE_DUNGEON, "gerudo fortress,gtg", 1},
{ 0x03D0, 0x03A0, SINGLE_SCENE_INFO(0x39), "Carpenters' Tent", "GV", ENTRANCE_GROUP_GERUDO_VALLEY, ENTRANCE_GROUP_GERUDO_VALLEY, ENTRANCE_TYPE_INTERIOR},
{ 0x081F, 0x071F, {{ 0x3E, 0x06 }}, "GV Octorok Grotto", "GV", ENTRANCE_GROUP_GERUDO_VALLEY, ENTRANCE_GROUP_GERUDO_VALLEY, ENTRANCE_TYPE_GROTTO},
{ 0x081E, 0x071E, {{ 0x3E, 0x0A }}, "GV Storms Grotto", "GV", ENTRANCE_GROUP_GERUDO_VALLEY, ENTRANCE_GROUP_GERUDO_VALLEY, ENTRANCE_TYPE_GROTTO, "scrubs"},
{ 0x081D, 0x071D, {{ 0x3C, 0x00 }}, "GF Storms Grotto", "GF", ENTRANCE_GROUP_GERUDO_VALLEY, ENTRANCE_GROUP_GERUDO_VALLEY, ENTRANCE_TYPE_GROTTO, "gerudo fortress"},
{ 0x03A8, 0x0008, SINGLE_SCENE_INFO(0x0B), "Gerudo Training Grounds", "GF", ENTRANCE_GROUP_GERUDO_VALLEY, ENTRANCE_GROUP_GERUDO_VALLEY, ENTRANCE_TYPE_DUNGEON, "gerudo fortress,gtg"},
// The Wasteland
{ 0x03AC, 0x0130, SINGLE_SCENE_INFO(0x5E), "Haunted Wasteland", "GF", ENTRANCE_GROUP_HAUNTED_WASTELAND, ENTRANCE_GROUP_GERUDO_VALLEY, ENTRANCE_TYPE_OVERWORLD, "hw,gerudo fortress"},
{ 0x0123, 0x0365, SINGLE_SCENE_INFO(0x5E), "Haunted Wasteland", "Desert Colossus", ENTRANCE_GROUP_HAUNTED_WASTELAND, ENTRANCE_GROUP_HAUNTED_WASTELAND, ENTRANCE_TYPE_OVERWORLD, "dc,hw"},
{ 0x0365, 0x0123, SINGLE_SCENE_INFO(0x5C), "Desert Colossus", "Haunted Wasteland", ENTRANCE_GROUP_HAUNTED_WASTELAND, ENTRANCE_GROUP_HAUNTED_WASTELAND, ENTRANCE_TYPE_OVERWORLD, "dc,hw"},
{ 0x0588, 0x057C, SINGLE_SCENE_INFO(0x5C), "Colossus", "Colossus Great Fairy Fountain", ENTRANCE_GROUP_HAUNTED_WASTELAND, ENTRANCE_GROUP_HAUNTED_WASTELAND, ENTRANCE_TYPE_INTERIOR, "dc", 1},
{ 0x0700, 0x0800, SINGLE_SCENE_INFO(0x5C), "Desert Colossus", "Colossus Grotto", ENTRANCE_GROUP_HAUNTED_WASTELAND, ENTRANCE_GROUP_HAUNTED_WASTELAND, ENTRANCE_TYPE_GROTTO, "dc,scrubs", 1},
{ 0x0082, 0x01E1, SINGLE_SCENE_INFO(0x5C), "Desert Colossus", "Spirit Temple", ENTRANCE_GROUP_HAUNTED_WASTELAND, ENTRANCE_GROUP_HAUNTED_WASTELAND, ENTRANCE_TYPE_DUNGEON, "dc", 1},
{ 0x057C, 0x0588, {{ 0x3D, 0x02 }}, "Colossus Great Fairy Fountain", "Colossus", ENTRANCE_GROUP_HAUNTED_WASTELAND, ENTRANCE_GROUP_HAUNTED_WASTELAND, ENTRANCE_TYPE_INTERIOR, "dc"},
{ 0x0800, 0x0700, {{ 0x3E, 0x0A }}, "Colossus Grotto", "Desert Colossus", ENTRANCE_GROUP_HAUNTED_WASTELAND, ENTRANCE_GROUP_HAUNTED_WASTELAND, ENTRANCE_TYPE_GROTTO, "dc,scrubs"},
{ 0x01E1, 0x0082, SINGLE_SCENE_INFO(0x06), "Spirit Temple", "Desert Colossus", ENTRANCE_GROUP_HAUNTED_WASTELAND, ENTRANCE_GROUP_HAUNTED_WASTELAND, ENTRANCE_TYPE_DUNGEON, "dc"},
// Market
{ 0x01FD, 0x0276, {SCENE_NO_SPAWN(0x1B), SCENE_NO_SPAWN(0x1C), SCENE_NO_SPAWN(0x1D)}, "Market Entrance", "Hyrule Field", ENTRANCE_GROUP_MARKET, ENTRANCE_GROUP_HYRULE_FIELD, ENTRANCE_TYPE_OVERWORLD, "hf"},
{ 0x00B1, 0x0033, {SCENE_NO_SPAWN(0x1B), SCENE_NO_SPAWN(0x1C), SCENE_NO_SPAWN(0x1D)}, "Market Entrance", "Market", ENTRANCE_GROUP_MARKET, ENTRANCE_GROUP_MARKET, ENTRANCE_TYPE_OVERWORLD},
{ 0x007E, 0x026E, {SCENE_NO_SPAWN(0x1B), SCENE_NO_SPAWN(0x1C), SCENE_NO_SPAWN(0x1D)}, "Market Entrance", "Guard House", ENTRANCE_GROUP_MARKET, ENTRANCE_GROUP_MARKET, ENTRANCE_TYPE_INTERIOR, "pots,poe", 1},
{ 0x0033, 0x00B1, {SCENE_NO_SPAWN(0x20), SCENE_NO_SPAWN(0x21), SCENE_NO_SPAWN(0x22), SCENE_NO_SPAWN(0x1E), SCENE_NO_SPAWN(0x1F)}, "Market", "Market Entrance", ENTRANCE_GROUP_MARKET, ENTRANCE_GROUP_MARKET, ENTRANCE_TYPE_OVERWORLD},
{ 0x0138, 0x025A, {SCENE_NO_SPAWN(0x20), SCENE_NO_SPAWN(0x21), SCENE_NO_SPAWN(0x22), SCENE_NO_SPAWN(0x1E), SCENE_NO_SPAWN(0x1F)}, "Market", "HC Grounds / OGC", ENTRANCE_GROUP_MARKET, ENTRANCE_GROUP_HYRULE_CASTLE, ENTRANCE_TYPE_OVERWORLD, "outside ganon's castle"},
{ 0x0171, 0x025E, {SCENE_NO_SPAWN(0x20), SCENE_NO_SPAWN(0x21), SCENE_NO_SPAWN(0x22), SCENE_NO_SPAWN(0x1E), SCENE_NO_SPAWN(0x1F)}, "Market", "Outside Temple of Time", ENTRANCE_GROUP_MARKET, ENTRANCE_GROUP_MARKET, ENTRANCE_TYPE_OVERWORLD},
{ 0x016D, 0x01CD, {SCENE_NO_SPAWN(0x20), SCENE_NO_SPAWN(0x21), SCENE_NO_SPAWN(0x22), SCENE_NO_SPAWN(0x1E), SCENE_NO_SPAWN(0x1F)}, "Market", "MK Shooting Gallery", ENTRANCE_GROUP_MARKET, ENTRANCE_GROUP_MARKET, ENTRANCE_TYPE_INTERIOR, "child", 1},
{ 0x0507, 0x03BC, {SCENE_NO_SPAWN(0x20), SCENE_NO_SPAWN(0x21), SCENE_NO_SPAWN(0x22), SCENE_NO_SPAWN(0x1E), SCENE_NO_SPAWN(0x1F)}, "Market", "Bombchu Bowling", ENTRANCE_GROUP_MARKET, ENTRANCE_GROUP_MARKET, ENTRANCE_TYPE_INTERIOR, "", 1},
{ 0x0063, 0x01D5, {SCENE_NO_SPAWN(0x20), SCENE_NO_SPAWN(0x21), SCENE_NO_SPAWN(0x22), SCENE_NO_SPAWN(0x1E), SCENE_NO_SPAWN(0x1F)}, "Market", "Treasure Chest Game", ENTRANCE_GROUP_MARKET, ENTRANCE_GROUP_MARKET, ENTRANCE_TYPE_INTERIOR, "", 1},
{ 0x043B, 0x0067, {SCENE_NO_SPAWN(0x20), SCENE_NO_SPAWN(0x21), SCENE_NO_SPAWN(0x22), SCENE_NO_SPAWN(0x1E), SCENE_NO_SPAWN(0x1F)}, "Market", "Man-in-Green's House", ENTRANCE_GROUP_MARKET, ENTRANCE_GROUP_MARKET, ENTRANCE_TYPE_INTERIOR, "", 1},
{ 0x0530, 0x01D1, {SCENE_NO_SPAWN(0x20), SCENE_NO_SPAWN(0x21), SCENE_NO_SPAWN(0x22), SCENE_NO_SPAWN(0x1E), SCENE_NO_SPAWN(0x1F)}, "Market", "Mask Shop", ENTRANCE_GROUP_MARKET, ENTRANCE_GROUP_MARKET, ENTRANCE_TYPE_INTERIOR, "", 1},
{ 0x052C, 0x03B8, {SCENE_NO_SPAWN(0x20), SCENE_NO_SPAWN(0x21), SCENE_NO_SPAWN(0x22), SCENE_NO_SPAWN(0x1E), SCENE_NO_SPAWN(0x1F)}, "Market", "MK Bazaar", ENTRANCE_GROUP_MARKET, ENTRANCE_GROUP_MARKET, ENTRANCE_TYPE_INTERIOR, "shop", 1},
{ 0x0388, 0x02A2, {SCENE_NO_SPAWN(0x20), SCENE_NO_SPAWN(0x21), SCENE_NO_SPAWN(0x22), SCENE_NO_SPAWN(0x1E), SCENE_NO_SPAWN(0x1F)}, "Market", "MK Potion Shop", ENTRANCE_GROUP_MARKET, ENTRANCE_GROUP_MARKET, ENTRANCE_TYPE_INTERIOR, "", 1},
{ 0x0528, 0x03C0, {SCENE_NO_SPAWN(0x20), SCENE_NO_SPAWN(0x21), SCENE_NO_SPAWN(0x22), SCENE_NO_SPAWN(0x1E), SCENE_NO_SPAWN(0x1F)}, "Market", "Bombchu Shop", ENTRANCE_GROUP_MARKET, ENTRANCE_GROUP_MARKET, ENTRANCE_TYPE_INTERIOR, "", 1},
{ 0x026E, 0x007E, {{ 0x4D }}, "Guard House", "Market Entrance", ENTRANCE_GROUP_MARKET, ENTRANCE_GROUP_MARKET, ENTRANCE_TYPE_INTERIOR, "pots,poe"},
{ 0x01CD, 0x016D, {{ 0x42, 0x01 }}, "MK Shooting Gallery", "Market", ENTRANCE_GROUP_MARKET, ENTRANCE_GROUP_MARKET, ENTRANCE_TYPE_INTERIOR},
{ 0x03BC, 0x0507, SINGLE_SCENE_INFO(0x4B), "Bombchu Bowling", "Market", ENTRANCE_GROUP_MARKET, ENTRANCE_GROUP_MARKET, ENTRANCE_TYPE_INTERIOR},
{ 0x01D5, 0x0063, SINGLE_SCENE_INFO(0x10), "Treasure Chest Game", "Market", ENTRANCE_GROUP_MARKET, ENTRANCE_GROUP_MARKET, ENTRANCE_TYPE_INTERIOR},
{ 0x0067, 0x043B, SINGLE_SCENE_INFO(0x2B), "Man-in-Green's House", "Market", ENTRANCE_GROUP_MARKET, ENTRANCE_GROUP_MARKET, ENTRANCE_TYPE_INTERIOR},
{ 0x01D1, 0x0530, SINGLE_SCENE_INFO(0x33), "Mask Shop", "Market", ENTRANCE_GROUP_MARKET, ENTRANCE_GROUP_MARKET, ENTRANCE_TYPE_INTERIOR},
{ 0x03B8, 0x052C, {{ 0x2C, 0x01 }}, "MK Bazaar", "Market", ENTRANCE_GROUP_MARKET, ENTRANCE_GROUP_MARKET, ENTRANCE_TYPE_INTERIOR, "shop"},
{ 0x02A2, 0x0388, SINGLE_SCENE_INFO(0x31), "MK Potion Shop", "Market", ENTRANCE_GROUP_MARKET, ENTRANCE_GROUP_MARKET, ENTRANCE_TYPE_INTERIOR},
{ 0x03C0, 0x0528, SINGLE_SCENE_INFO(0x32), "Bombchu Shop", "Market", ENTRANCE_GROUP_MARKET, ENTRANCE_GROUP_MARKET, ENTRANCE_TYPE_INTERIOR},
{ 0x025E, 0x0171, {SCENE_NO_SPAWN(0x23), SCENE_NO_SPAWN(0x24), SCENE_NO_SPAWN(0x25)}, "Outside Temple of Time", "Market", ENTRANCE_GROUP_MARKET, ENTRANCE_GROUP_MARKET, ENTRANCE_TYPE_OVERWORLD, "tot"},
{ 0x0053, 0x0472, {SCENE_NO_SPAWN(0x23), SCENE_NO_SPAWN(0x24), SCENE_NO_SPAWN(0x25)}, "Outside Temple of Time", "Temple of Time", ENTRANCE_GROUP_MARKET, ENTRANCE_GROUP_MARKET, ENTRANCE_TYPE_INTERIOR, "tot", 1},
{ 0x0472, 0x0053, SINGLE_SCENE_INFO(0x43), "Temple of Time", "Outside Temple of Time", ENTRANCE_GROUP_MARKET, ENTRANCE_GROUP_MARKET, ENTRANCE_TYPE_INTERIOR, "tot"},
// Hyrule Castle
{ 0x025A, 0x0138, {SCENE_NO_SPAWN(0x5F), SCENE_NO_SPAWN(0x64)}, "HC Grounds / OGC", "Market", ENTRANCE_GROUP_HYRULE_CASTLE, ENTRANCE_GROUP_MARKET, ENTRANCE_TYPE_OVERWORLD, "outside ganon's castle"},
{ 0x0578, 0x0340, SINGLE_SCENE_INFO(0x5F), "HC Grounds", "HC Great Fairy Fountain", ENTRANCE_GROUP_HYRULE_CASTLE, ENTRANCE_GROUP_HYRULE_CASTLE, ENTRANCE_TYPE_INTERIOR, "", 1},
{ 0x070C, 0x080C, SINGLE_SCENE_INFO(0x5F), "HC Grounds", "HC Storms Grotto", ENTRANCE_GROUP_HYRULE_CASTLE, ENTRANCE_GROUP_HYRULE_CASTLE, ENTRANCE_TYPE_GROTTO, "bombable", 1},
{ 0x0340, 0x0578, {{ 0x3D, 0x01 }}, "HC Great Fairy Fountain", "HC Grounds", ENTRANCE_GROUP_HYRULE_CASTLE, ENTRANCE_GROUP_HYRULE_CASTLE, ENTRANCE_TYPE_INTERIOR},
{ 0x080C, 0x070C, {{ 0x3E, 0x09 }}, "HC Storms Grotto", "HC Grounds", ENTRANCE_GROUP_HYRULE_CASTLE, ENTRANCE_GROUP_HYRULE_CASTLE, ENTRANCE_TYPE_GROTTO, "bombable"},
{ 0x04C2, 0x03E8, SINGLE_SCENE_INFO(0x64), "OGC", "OGC Great Fairy Fountain", ENTRANCE_GROUP_HYRULE_CASTLE, ENTRANCE_GROUP_HYRULE_CASTLE, ENTRANCE_TYPE_INTERIOR, "outside ganon's castle", 1},
{ 0x0467, 0x023D, SINGLE_SCENE_INFO(0x64), "OGC", "Ganon's Castle", ENTRANCE_GROUP_HYRULE_CASTLE, ENTRANCE_GROUP_HYRULE_CASTLE, ENTRANCE_TYPE_DUNGEON, "outside ganon's castle,gc", 1},
{ 0x03E8, 0x04C2, {{ 0x3B, 0x02 }}, "OGC Great Fairy Fountain", "OGC", ENTRANCE_GROUP_HYRULE_CASTLE, ENTRANCE_GROUP_HYRULE_CASTLE, ENTRANCE_TYPE_INTERIOR, "outside ganon's castle"},
{ 0x023D, 0x0467, SINGLE_SCENE_INFO(0x0D), "Ganon's Castle", "OGC", ENTRANCE_GROUP_HYRULE_CASTLE, ENTRANCE_GROUP_HYRULE_CASTLE, ENTRANCE_TYPE_DUNGEON, "outside ganon's castle,gc"}
};
// Check if Link is in the area and return that scene/entrance for tracking
s8 LinkIsInArea(const EntranceData* entrance) {
bool result = false;
if (gPlayState == nullptr) {
return -1;
}
// Handle detecting the current grotto
if ((gPlayState->sceneNum == SCENE_YOUSEI_IZUMI_TATE || gPlayState->sceneNum == SCENE_KAKUSIANA) &&
entrance->type == ENTRANCE_TYPE_GROTTO) {
if (entrance->index == (ENTRANCE_RANDO_GROTTO_EXIT_START + currentGrottoId)) {
// Return the grotto entrance for tracking
return entrance->index;
} else {
return -1;
}
}
// Otherwise check all scenes/spawns
// Not all areas require a spawn position to differeniate between another area
for (auto info : entrance->scenes) {
// When a spawn position is specified, check that combination
if (info.spawn != -1) {
result = Entrance_SceneAndSpawnAre(info.scene, info.spawn);
} else { // Otherwise just check the current scene
result = gPlayState->sceneNum == info.scene;
}
// Return the scene for tracking
if (result) {
return info.scene;
}
}
return -1;
}
bool IsEntranceDiscovered(s16 index) {
bool isDiscovered = Entrance_GetIsEntranceDiscovered(index);
if (!isDiscovered) {
// If the pair included one of the hyrule field <-> zora's river entrances,
// the randomizer will have also overriden the water-based entrances, so check those too
if ((index == 0x00EA && Entrance_GetIsEntranceDiscovered(0x01D9)) || (index == 0x01D9 && Entrance_GetIsEntranceDiscovered(0x00EA))) {
isDiscovered = true;
} else if ((index == 0x0181 && Entrance_GetIsEntranceDiscovered(0x0311)) || (index == 0x0311 && Entrance_GetIsEntranceDiscovered(0x0181))) {
isDiscovered = true;
}
}
return isDiscovered;
}
const EntranceData* GetEntranceData(s16 index) {
for (size_t i = 0; i < ARRAY_COUNT(entranceData); i++) {
if (index == entranceData[i].index) {
return &entranceData[i];
}
}
// Shouldn't be reached
return nullptr;
}
void SortEntranceListByType(EntranceOverride* entranceList, u8 byDest) {
EntranceOverride tempList[ENTRANCE_OVERRIDES_MAX_COUNT] = { 0 };
for (size_t i = 0; i < ENTRANCE_OVERRIDES_MAX_COUNT; i++) {
tempList[i] = entranceList[i];
}
size_t idx = 0;
for (size_t k = 0; k < ENTRANCE_TYPE_COUNT; k++) {
for (size_t i = 0; i < ARRAY_COUNT(entranceData); i++) {
for (size_t j = 0; j < ENTRANCE_OVERRIDES_MAX_COUNT; j++) {
if (Entrance_EntranceIsNull(&tempList[j])) {
break;
}
size_t entranceIndex = byDest ? tempList[j].override : tempList[j].index;
if (entranceData[i].type == k && entranceIndex == entranceData[i].index) {
entranceList[idx] = tempList[j];
idx++;
break;
}
}
}
}
}
void SortEntranceListByArea(EntranceOverride* entranceList, u8 byDest) {
EntranceOverride tempList[ENTRANCE_OVERRIDES_MAX_COUNT] = { 0 };
// Store to temp
for (size_t i = 0; i < ENTRANCE_OVERRIDES_MAX_COUNT; i++) {
tempList[i] = entranceList[i];
// Don't include one-way indexes in the tempList if we're sorting by destination
// so that we keep them at the beginning.
if (byDest) {
if (GetEntranceData(tempList[i].index)->srcGroup == ENTRANCE_GROUP_ONE_WAY) {
tempList[i] = emptyOverride;
}
}
}
size_t idx = 0;
// Sort Source List based on entranceData order
if (!byDest) {
for (size_t i = 0; i < ARRAY_COUNT(entranceData); i++) {
for (size_t j = 0; j < ENTRANCE_OVERRIDES_MAX_COUNT; j++) {
if (Entrance_EntranceIsNull(&tempList[j])) {
break;
}
if (tempList[j].index == entranceData[i].index) {
entranceList[idx] = tempList[j];
idx++;
break;
}
}
}
} else {
// Increment the idx by however many one-way entrances are shuffled since these
// will still be displayed at the beginning
idx += gEntranceTrackingData.GroupEntranceCounts[ENTRANCE_SOURCE_AREA][ENTRANCE_GROUP_ONE_WAY];
// Sort the rest of the Destination List by matching destination strings with source strings when possible
// and otherwise by group
for (size_t group = ENTRANCE_GROUP_KOKIRI_FOREST; group < SPOILER_ENTRANCE_GROUP_COUNT; group++) {
for (size_t i = 0; i < ENTRANCE_OVERRIDES_MAX_COUNT; i++) {
if (Entrance_EntranceIsNull(&gSaveContext.entranceOverrides[i])) {
continue;
}
const EntranceData* curEntrance = GetEntranceData(gSaveContext.entranceOverrides[i].index);
if (curEntrance->srcGroup != group) {
continue;
}
// First, search the list for the matching reverse entrance if it exists
for (size_t j = 0; j < ENTRANCE_OVERRIDES_MAX_COUNT; j++) {
const EntranceData* curOverride = GetEntranceData(tempList[j].override);
if (Entrance_EntranceIsNull(&tempList[j]) || curOverride->dstGroup != group) {
continue;
}
if (curEntrance->reverseIndex == curOverride->index) {
entranceList[idx] = tempList[j];
// "Remove" this entrance from the tempList by setting it's values to zero
tempList[j] = emptyOverride;
idx++;
break;
}
}
}
// Then find any remaining entrances in the same group and add them to the end
for (size_t i = 0; i < ENTRANCE_OVERRIDES_MAX_COUNT; i++) {
if (Entrance_EntranceIsNull(&tempList[i])) {
continue;
}
const EntranceData* curOverride = GetEntranceData(tempList[i].override);
if (curOverride->dstGroup == group) {
entranceList[idx] = tempList[i];
tempList[i] = emptyOverride;
idx++;
}
}
}
}
}
void SetCurrentGrottoIDForTracker(s16 entranceIndex) {
currentGrottoId = entranceIndex;
}
void SetLastEntranceOverrideForTracker(s16 entranceIndex) {
lastEntranceIndex = entranceIndex;
}
void ClearEntranceTrackingData() {
currentGrottoId = -1;
lastEntranceIndex = -1;
lastSceneOrEntranceDetected = -1;
gEntranceTrackingData = {0};
}
void InitEntranceTrackingData() {
gEntranceTrackingData = {0};
// Check if entrance randomization is disabled
if (!OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_SHUFFLE_ENTRANCES)) {
return;
}
// Set total and group counts
for (size_t i = 0; i < ENTRANCE_OVERRIDES_MAX_COUNT; i++) {
if (Entrance_EntranceIsNull(&gSaveContext.entranceOverrides[i])) {
break;
}
const EntranceData* index = GetEntranceData(gSaveContext.entranceOverrides[i].index);
const EntranceData* override = GetEntranceData(gSaveContext.entranceOverrides[i].override);
if (index->srcGroup == ENTRANCE_GROUP_ONE_WAY) {
gEntranceTrackingData.GroupEntranceCounts[ENTRANCE_SOURCE_AREA][ENTRANCE_GROUP_ONE_WAY]++;
gEntranceTrackingData.GroupEntranceCounts[ENTRANCE_DESTINATION_AREA][ENTRANCE_GROUP_ONE_WAY]++;
gEntranceTrackingData.GroupEntranceCounts[ENTRANCE_SOURCE_TYPE][ENTRANCE_TYPE_ONE_WAY]++;
gEntranceTrackingData.GroupEntranceCounts[ENTRANCE_DESTINATION_TYPE][ENTRANCE_TYPE_ONE_WAY]++;
} else {
gEntranceTrackingData.GroupEntranceCounts[ENTRANCE_SOURCE_AREA][index->srcGroup]++;
gEntranceTrackingData.GroupEntranceCounts[ENTRANCE_DESTINATION_AREA][override->dstGroup]++;
gEntranceTrackingData.GroupEntranceCounts[ENTRANCE_SOURCE_TYPE][index->type]++;
gEntranceTrackingData.GroupEntranceCounts[ENTRANCE_DESTINATION_TYPE][override->type]++;
}
gEntranceTrackingData.EntranceCount++;
}
// The entrance data is sorted and grouped in a one dimensional array, so we need to track offsets
// Set offsets for areas starting at 0
u16 srcOffsetTotal = 0;
u16 dstOffsetTotal = 0;
for (size_t i = 0; i < SPOILER_ENTRANCE_GROUP_COUNT; i++) {
// Set the offset for the current group
gEntranceTrackingData.GroupOffsets[ENTRANCE_SOURCE_AREA][i] = srcOffsetTotal;
gEntranceTrackingData.GroupOffsets[ENTRANCE_DESTINATION_AREA][i] = dstOffsetTotal;
// Increment the offset by the areas entrance count
srcOffsetTotal += gEntranceTrackingData.GroupEntranceCounts[ENTRANCE_SOURCE_AREA][i];
dstOffsetTotal += gEntranceTrackingData.GroupEntranceCounts[ENTRANCE_DESTINATION_AREA][i];
}
// Set offsets for types starting at 0
srcOffsetTotal = 0;
dstOffsetTotal = 0;
for (size_t i = 0; i < ENTRANCE_TYPE_COUNT; i++) {
// Set the offset for the current group
gEntranceTrackingData.GroupOffsets[ENTRANCE_SOURCE_TYPE][i] = srcOffsetTotal;
gEntranceTrackingData.GroupOffsets[ENTRANCE_DESTINATION_TYPE][i] = dstOffsetTotal;
// Increment the offset by the areas entrance count
srcOffsetTotal += gEntranceTrackingData.GroupEntranceCounts[ENTRANCE_SOURCE_TYPE][i];
dstOffsetTotal += gEntranceTrackingData.GroupEntranceCounts[ENTRANCE_DESTINATION_TYPE][i];
}
// Sort entrances by group and type in entranceData
for (size_t i = 0; i < ENTRANCE_OVERRIDES_MAX_COUNT; i++) {
srcListSortedByArea[i] = gSaveContext.entranceOverrides[i];
destListSortedByArea[i] = gSaveContext.entranceOverrides[i];
srcListSortedByType[i] = gSaveContext.entranceOverrides[i];
destListSortedByType[i] = gSaveContext.entranceOverrides[i];
}
SortEntranceListByArea(srcListSortedByArea, 0);
SortEntranceListByArea(destListSortedByArea, 1);
SortEntranceListByType(srcListSortedByType, 0);
SortEntranceListByType(destListSortedByType, 1);
}
void DrawEntranceTracker(bool& open) {
if (!open) {
CVar_SetS32("gEntranceTrackerEnabled", 0);
return;
}
ImGui::SetNextWindowSize(ImVec2(600, 375), ImGuiCond_FirstUseEver);
if (!ImGui::Begin("Entrance Tracker", &open, ImGuiWindowFlags_NoFocusOnAppearing)) {
ImGui::End();
return;
}
// Begin tracker settings
ImGui::SetNextItemOpen(false, ImGuiCond_Once);
if (ImGui::TreeNode("Tracker Settings")) {
// Reduce indentation from the tree node for the table
ImGui::SetCursorPosX((ImGui::GetCursorPosX() / 2) + 4.0f);
if (ImGui::BeginTable("entranceTrackerSettings", 1, ImGuiTableFlags_BordersInnerH)) {
ImGui::TableNextColumn();
UIWidgets::Spacer(0);
ImGui::TextWrapped("The entrance tracker will only track shuffled entrances");
UIWidgets::Spacer(0);
ImGui::TableNextColumn();
if (ImGui::BeginTable("entranceTrackerSubSettings", 2, ImGuiTableFlags_BordersInnerV | ImGuiTableFlags_SizingStretchProp)) {
ImGui::TableNextColumn();
ImGui::Text("Sort By");
UIWidgets::EnhancementRadioButton("To", "gEntranceTrackerSortBy", 0);
UIWidgets::Tooltip("Sort entrances by the original source entrance");
UIWidgets::EnhancementRadioButton("From", "gEntranceTrackerSortBy", 1);
UIWidgets::Tooltip("Sort entrances by the overrided destination");
UIWidgets::Spacer(2.0f);
ImGui::Text("List Items");
UIWidgets::PaddedEnhancementCheckbox("Auto scroll", "gEntranceTrackerAutoScroll", true, false);
UIWidgets::Tooltip("Automatically scroll to the first aviable entrance in the current scene");
UIWidgets::PaddedEnhancementCheckbox("Highlight previous", "gEntranceTrackerHighlightPrevious", true, false);
UIWidgets::Tooltip("Highlight the previous entrance that Link came from");
UIWidgets::PaddedEnhancementCheckbox("Highlight available", "gEntranceTrackerHighlightAvailable", true, false);
UIWidgets::Tooltip("Highlight available entrances in the current scene");
UIWidgets::PaddedEnhancementCheckbox("Hide undiscovered", "gEntranceTrackerCollapseUndiscovered", true, true);
UIWidgets::Tooltip("Collapse undiscovered entrances towards the bottom of each group");
ImGui::TableNextColumn();
ImGui::Text("Group By");
UIWidgets::EnhancementRadioButton("Area", "gEntranceTrackerGroupBy", 0);
UIWidgets::Tooltip("Group entrances by their area");
UIWidgets::EnhancementRadioButton("Type", "gEntranceTrackerGroupBy", 1);
UIWidgets::Tooltip("Group entrances by their entrance type");
UIWidgets::Spacer(2.0f);
ImGui::Text("Spoiler Reveal");
UIWidgets::PaddedEnhancementCheckbox("Show \"To\"", "gEntranceTrackerShowTo", true, false);
UIWidgets::Tooltip("Reveal the \"To\" entrance for undiscovered entrances");
UIWidgets::PaddedEnhancementCheckbox("Show \"From\"", "gEntranceTrackerShowFrom", true, false);
UIWidgets::Tooltip("Reveal the \"From\" entrance for undiscovered entrances");
ImGui::EndTable();
}
ImGui::TableNextColumn();
ImGui::SetNextItemOpen(false, ImGuiCond_Once);
if (ImGui::TreeNode("Legend")) {
ImGui::TextColored(ImColor(COLOR_ORANGE), "Last Entrance");
ImGui::TextColored(ImColor(COLOR_GREEN), "Available Entrances");
ImGui::TextColored(ImColor(COLOR_GRAY), "Undiscovered Entrances");
ImGui::TreePop();
}
UIWidgets::Spacer(0);
ImGui::EndTable();
}
ImGui::TreePop();
} else {
UIWidgets::PaddedSeparator();
}
static ImGuiTextFilter locationSearch;
uint8_t nextTreeState = 0;
if (ImGui::Button("Collapse All")) {
nextTreeState = 1;
}
UIWidgets::Tooltip("Collapse all entrance groups");
ImGui::SameLine();
if (ImGui::Button("Expand All")) {
nextTreeState = 2;
}
UIWidgets::Tooltip("Expand all entrance groups");
ImGui::SameLine();
if (ImGui::Button("Clear")) {
locationSearch.Clear();
}
UIWidgets::Tooltip("Clear the search field");
if (locationSearch.Draw()) {
nextTreeState = 2;
}
uint8_t destToggle = CVar_GetS32("gEntranceTrackerSortBy", 0);
uint8_t groupToggle = CVar_GetS32("gEntranceTrackerGroupBy", 0);
// Combine destToggle and groupToggle to get a range of 0-3
uint8_t groupType = destToggle + (groupToggle * 2);
size_t groupCount = groupToggle ? ENTRANCE_TYPE_COUNT : SPOILER_ENTRANCE_GROUP_COUNT;
auto groupNames = groupToggle ? groupTypeNames : spoilerEntranceGroupNames;
EntranceOverride *entranceList;
switch (groupType) {
case ENTRANCE_SOURCE_AREA:
entranceList = srcListSortedByArea;
break;
case ENTRANCE_DESTINATION_AREA:
entranceList = destListSortedByArea;
break;
case ENTRANCE_SOURCE_TYPE:
entranceList = srcListSortedByType;
break;
case ENTRANCE_DESTINATION_TYPE:
entranceList = destListSortedByType;
break;
}
// Begin tracker list
ImGui::BeginChild("ChildEntranceTrackerLocations", ImVec2(0, -8));
for (size_t i = 0; i < groupCount; i++) {
std::string groupName = groupNames[i];
uint16_t entranceCount = gEntranceTrackingData.GroupEntranceCounts[groupType][i];
uint16_t startIndex = gEntranceTrackingData.GroupOffsets[groupType][i];
bool doAreaScroll = false;
size_t undiscovered = 0;
std::vector<EntranceOverride> displayEntrances = {};
// Loop over entrances first for filtering
for (size_t entranceIdx = 0; entranceIdx < entranceCount; entranceIdx++) {
size_t trueIdx = entranceIdx + startIndex;
EntranceOverride entrance = entranceList[trueIdx];
const EntranceData* original = GetEntranceData(entrance.index);
const EntranceData* override = GetEntranceData(entrance.override);
bool isDiscovered = IsEntranceDiscovered(entrance.index);
bool showOriginal = (!destToggle ? CVar_GetS32("gEntranceTrackerShowTo", 0) : CVar_GetS32("gEntranceTrackerShowFrom", 0)) || isDiscovered;
bool showOverride = (!destToggle ? CVar_GetS32("gEntranceTrackerShowFrom", 0) : CVar_GetS32("gEntranceTrackerShowTo", 0)) || isDiscovered;
const char* origSrcAreaName = spoilerEntranceGroupNames[original->srcGroup].c_str();
const char* origTypeName = groupTypeNames[original->type].c_str();
const char* rplcSrcAreaName = spoilerEntranceGroupNames[override->srcGroup].c_str();
const char* rplcTypeName = groupTypeNames[override->type].c_str();
const char* origSrcName = showOriginal ? original->source.c_str() : "";
const char* origDstName = showOriginal ? original->destination.c_str() : "";
const char* rplcSrcName = showOverride ? override->source.c_str() : "";
const char* rplcDstName = showOverride ? override->destination.c_str() : "";
// Filter for entrances by group name, type, source/destination names, and meta tags
if ((!locationSearch.IsActive() && (showOriginal || showOverride || !CVar_GetS32("gEntranceTrackerCollapseUndiscovered", 0))) ||
((showOriginal && (locationSearch.PassFilter(origSrcName) ||
locationSearch.PassFilter(origDstName) || locationSearch.PassFilter(origSrcAreaName) ||
locationSearch.PassFilter(origTypeName) || locationSearch.PassFilter(original->metaTag.c_str()))) ||
(showOverride && (locationSearch.PassFilter(rplcSrcName) ||
locationSearch.PassFilter(rplcDstName) || locationSearch.PassFilter(rplcSrcAreaName) ||
locationSearch.PassFilter(rplcTypeName) || locationSearch.PassFilter(override->metaTag.c_str()))))) {
// Detect if a scroll should happen and remember the scene for that scroll
if (!doAreaScroll && (lastSceneOrEntranceDetected != LinkIsInArea(original) && LinkIsInArea(original) != -1)) {
lastSceneOrEntranceDetected = LinkIsInArea(original);
doAreaScroll = true;
}
displayEntrances.push_back(entrance);
} else {
if (!isDiscovered) {
undiscovered++;
}
}
}
// Then display the entrances in groups
if (displayEntrances.size() != 0 || (!locationSearch.IsActive() && undiscovered > 0)) {
// Handle opening/closing trees based on auto scroll or collapse/expand buttons
if (nextTreeState == 1) {
ImGui::SetNextItemOpen(false, ImGuiCond_None);
} else {
ImGui::SetNextItemOpen(true, nextTreeState == 0 && !doAreaScroll ? ImGuiCond_Once : ImGuiCond_None);
}
if (ImGui::TreeNode(groupName.c_str())) {
for (auto entrance : displayEntrances) {
const EntranceData* original = GetEntranceData(entrance.index);
const EntranceData* override = GetEntranceData(entrance.override);
bool isDiscovered = IsEntranceDiscovered(entrance.index);
bool showOriginal = (!destToggle ? CVar_GetS32("gEntranceTrackerShowTo", 0) : CVar_GetS32("gEntranceTrackerShowFrom", 0)) || isDiscovered;
bool showOverride = (!destToggle ? CVar_GetS32("gEntranceTrackerShowFrom", 0) : CVar_GetS32("gEntranceTrackerShowTo", 0)) || isDiscovered;
const char* unknown = "???";
const char* origSrcName = showOriginal ? original->source.c_str() : unknown;
const char* origDstName = showOriginal ? original->destination.c_str() : unknown;
const char* rplcSrcName = showOverride ? override->source.c_str() : unknown;
const char* rplcDstName = showOverride ? override->destination.c_str() : unknown;
uint32_t color = isDiscovered ? IM_COL32_WHITE : COLOR_GRAY;
// Handle highlighting and auto scroll
if (LinkIsInArea(original) != -1) {
if (CVar_GetS32("gEntranceTrackerHighlightAvailable", 0)) {
color = COLOR_GREEN;
}
if (doAreaScroll) {
doAreaScroll = false;
if (CVar_GetS32("gEntranceTrackerAutoScroll", 0)) {
ImGui::SetScrollHereY(0.0f);
}
}
} else if (original->index == lastEntranceIndex) {
if (CVar_GetS32("gEntranceTrackerHighlightPrevious", 0)) {
color = COLOR_ORANGE;
}
}
ImGui::PushStyleColor(ImGuiCol_Text, color);
// Use a non-breaking space to keep the arrow from wrapping to a newline by itself
auto nbsp = u8"\u00A0";
if (original->srcGroup != ENTRANCE_GROUP_ONE_WAY) {
ImGui::TextWrapped("%s to %s%s->", origSrcName, origDstName, nbsp);
} else {
ImGui::TextWrapped("%s%s->", origSrcName, nbsp);
}
// Indent the destination
ImGui::SetCursorPosX(ImGui::GetCursorPosX() * 2);
if (!showOverride || (showOverride && (!override->oneExit && override->srcGroup != ENTRANCE_GROUP_ONE_WAY))) {
ImGui::TextWrapped("%s from %s", rplcDstName, rplcSrcName);
} else {
ImGui::TextWrapped("%s", rplcDstName);
}
ImGui::PopStyleColor();
}
// Write collapsed undiscovered info
if (!locationSearch.IsActive() && undiscovered > 0) {
UIWidgets::Spacer(0);
ImGui::PushStyleColor(ImGuiCol_Text, COLOR_GRAY);
ImGui::TextWrapped("%d Undiscovered", undiscovered);
ImGui::PopStyleColor();
}
UIWidgets::Spacer(0);
ImGui::TreePop();
}
}
}
ImGui::EndChild();
ImGui::End();
}
void InitEntranceTracker() {
SohImGui::AddWindow("Randomizer", "Entrance Tracker", DrawEntranceTracker, CVar_GetS32("gEntranceTrackerEnabled", 0) == 1);
}

View File

@ -0,0 +1,82 @@
#pragma once
#include <string>
#include <vector>
typedef enum {
// ENTRANCE_GROUP_NO_GROUP,
ENTRANCE_GROUP_ONE_WAY,
ENTRANCE_GROUP_KOKIRI_FOREST,
ENTRANCE_GROUP_LOST_WOODS,
ENTRANCE_GROUP_SFM,
ENTRANCE_GROUP_KAKARIKO,
ENTRANCE_GROUP_GRAVEYARD,
ENTRANCE_GROUP_DEATH_MOUNTAIN_TRAIL,
ENTRANCE_GROUP_DEATH_MOUNTAIN_CRATER,
ENTRANCE_GROUP_GORON_CITY,
ENTRANCE_GROUP_ZORAS_RIVER,
ENTRANCE_GROUP_ZORAS_DOMAIN,
ENTRANCE_GROUP_ZORAS_FOUNTAIN,
ENTRANCE_GROUP_HYRULE_FIELD,
ENTRANCE_GROUP_LON_LON_RANCH,
ENTRANCE_GROUP_LAKE_HYLIA,
ENTRANCE_GROUP_GERUDO_VALLEY,
ENTRANCE_GROUP_HAUNTED_WASTELAND,
ENTRANCE_GROUP_MARKET,
ENTRANCE_GROUP_HYRULE_CASTLE,
SPOILER_ENTRANCE_GROUP_COUNT,
} SpoilerEntranceGroup;
typedef enum {
ENTRANCE_TYPE_ONE_WAY,
ENTRANCE_TYPE_OVERWORLD,
ENTRANCE_TYPE_INTERIOR,
ENTRANCE_TYPE_GROTTO,
ENTRANCE_TYPE_DUNGEON,
ENTRANCE_TYPE_COUNT,
} TrackerEntranceType;
typedef struct {
int8_t scene;
int8_t spawn;
} EntranceDataSceneAndSpawn;
typedef struct {
int16_t index;
int16_t reverseIndex;
std::vector<EntranceDataSceneAndSpawn> scenes;
std::string source;
std::string destination;
SpoilerEntranceGroup srcGroup;
SpoilerEntranceGroup dstGroup;
TrackerEntranceType type;
std::string metaTag;
uint8_t oneExit;
} EntranceData;
typedef enum {
ENTRANCE_SOURCE_AREA,
ENTRANCE_DESTINATION_AREA,
ENTRANCE_SOURCE_TYPE,
ENTRANCE_DESTINATION_TYPE,
TRACKER_GROUP_TYPE_COUNT,
} TrackerEntranceGroupingType;
typedef struct {
uint8_t EntranceCount;
uint16_t GroupEntranceCounts[TRACKER_GROUP_TYPE_COUNT][SPOILER_ENTRANCE_GROUP_COUNT];
uint16_t GroupOffsets[TRACKER_GROUP_TYPE_COUNT][SPOILER_ENTRANCE_GROUP_COUNT];
} EntranceTrackingData;
extern EntranceTrackingData gEntranceTrackingData;
#define SINGLE_SCENE_INFO(scene) {{ scene, -1 }}
#define SCENE_NO_SPAWN(scene) { scene, -1 }
void SetCurrentGrottoIDForTracker(int16_t entranceIndex);
void SetLastEntranceOverrideForTracker(int16_t entranceIndex);
void ClearEntranceTrackingData();
void InitEntranceTrackingData();
void DrawEntranceTracker(bool& open);
void InitEntranceTracker();

View File

@ -96,8 +96,8 @@ static u8 overridingNextEntrance = false;
// For the grotto exit list, the entrance index is 0x0800 + the grotto id
void Grotto_InitExitAndLoadLists(void) {
for (u8 i = 0; i < NUM_GROTTOS; i++) {
grottoLoadList[i] = 0x0700 + i;
grottoExitList[i] = 0x0800 + i;
grottoLoadList[i] = ENTRANCE_RANDO_GROTTO_LOAD_START + i;
grottoExitList[i] = ENTRANCE_RANDO_GROTTO_EXIT_START + i;
}
}
@ -140,7 +140,8 @@ s16 Grotto_OverrideSpecialEntrance(s16 nextEntranceIndex) {
// If Link hits a grotto exit, load the entrance index from the grotto exit list
// based on the current grotto ID
if (nextEntranceIndex == 0x7FFF) {
// SaveFile_SetEntranceDiscovered(0x0800 + grottoId);
Entrance_SetEntranceDiscovered(ENTRANCE_RANDO_GROTTO_EXIT_START + grottoId);
EntranceTracker_SetLastEntranceOverride(ENTRANCE_RANDO_GROTTO_EXIT_START + grottoId);
nextEntranceIndex = grottoExitList[grottoId];
}
@ -148,7 +149,7 @@ s16 Grotto_OverrideSpecialEntrance(s16 nextEntranceIndex) {
grottoId = nextEntranceIndex & 0x00FF;
// Grotto Returns
if (nextEntranceIndex >= 0x0800 && nextEntranceIndex < 0x0800 + NUM_GROTTOS) {
if (nextEntranceIndex >= ENTRANCE_RANDO_GROTTO_EXIT_START && nextEntranceIndex < ENTRANCE_RANDO_GROTTO_EXIT_START + NUM_GROTTOS) {
GrottoReturnInfo grotto = grottoReturnTable[grottoId];
Grotto_SetupReturnInfo(grotto, RESPAWN_MODE_RETURN);
@ -173,13 +174,15 @@ s16 Grotto_OverrideSpecialEntrance(s16 nextEntranceIndex) {
lastEntranceType = GROTTO_RETURN;
// Grotto Loads
} else if (nextEntranceIndex >= 0x0700 && nextEntranceIndex < 0x0800) {
} else if (nextEntranceIndex >= ENTRANCE_RANDO_GROTTO_LOAD_START && nextEntranceIndex < ENTRANCE_RANDO_GROTTO_EXIT_START) {
// Set the respawn data to load the correct grotto
GrottoLoadInfo grotto = grottoLoadTable[grottoId];
gSaveContext.respawn[RESPAWN_MODE_RETURN].data = grotto.content;
nextEntranceIndex = grotto.entranceIndex;
EntranceTracker_SetCurrentGrottoID(grottoId);
lastEntranceType = NOT_GROTTO;
// Otherwise just unset the current grotto ID
} else {
@ -208,7 +211,8 @@ void Grotto_OverrideActorEntrance(Actor* thisx) {
if (grottoContent == grottoLoadTable[index].content && gPlayState->sceneNum == grottoLoadTable[index].scene) {
// Find the override for the matching index from the grotto Load List
// SaveFile_SetEntranceDiscovered(0x0700 + index);
Entrance_SetEntranceDiscovered(ENTRANCE_RANDO_GROTTO_LOAD_START + index);
EntranceTracker_SetLastEntranceOverride(ENTRANCE_RANDO_GROTTO_LOAD_START + index);
index = grottoLoadList[index];
// Run the index through the special entrances override check
@ -281,9 +285,9 @@ void Grotto_SanitizeEntranceType(void) {
s16 Grotto_GetRenamedGrottoIndexFromOriginal(s8 content, s8 scene) {
for (s16 index = 0; index < NUM_GROTTOS; index++) {
if (content == grottoLoadTable[index].content && scene == grottoLoadTable[index].scene) {
return 0x0700 | index;
return ENTRANCE_RANDO_GROTTO_LOAD_START | index;
}
}
return 0x0700;
return ENTRANCE_RANDO_GROTTO_LOAD_START;
}

View File

@ -1170,6 +1170,14 @@ namespace GameMenuBar {
SohImGui::EnableWindow("Item Tracker", CVar_GetS32("gItemTrackerEnabled", 0));
}
ImGui::Dummy(ImVec2(0.0f, 0.0f));
if (ImGui::Button(GetWindowButtonText("Entrance Tracker", CVar_GetS32("gEntranceTrackerEnabled", 0)).c_str(), buttonSize))
{
bool currentValue = CVar_GetS32("gEntranceTrackerEnabled", 0);
CVar_SetS32("gEntranceTrackerEnabled", !currentValue);
SohImGui::RequestCvarSaveOnNextTick();
SohImGui::EnableWindow("Entrance Tracker", CVar_GetS32("gEntranceTrackerEnabled", 0));
}
ImGui::Dummy(ImVec2(0.0f, 0.0f));
if (ImGui::Button(GetWindowButtonText("Item Tracker Settings", CVar_GetS32("gItemTrackerSettingsEnabled", 0)).c_str(), buttonSize))
{
bool currentValue = CVar_GetS32("gItemTrackerSettingsEnabled", 0);

View File

@ -37,6 +37,7 @@
#include "Enhancements/debugconsole.h"
#include "Enhancements/debugger/debugger.h"
#include "Enhancements/randomizer/randomizer.h"
#include "Enhancements/randomizer/randomizer_entrance_tracker.h"
#include "Enhancements/randomizer/randomizer_item_tracker.h"
#include "Enhancements/randomizer/3drando/random.hpp"
#include "Enhancements/gameplaystats.h"
@ -439,6 +440,7 @@ extern "C" void InitOTR() {
Debug_Init();
Rando_Init();
InitItemTracker();
InitEntranceTracker();
InitStatTracker();
OTRExtScanner();
VanillaItemTable_Init();
@ -2048,3 +2050,19 @@ extern "C" int CustomMessage_RetrieveIfExists(PlayState* play) {
extern "C" void Overlay_DisplayText(float duration, const char* text) {
SohImGui::GetGameOverlay()->TextDrawNotification(duration, true, text);
}
extern "C" void Entrance_ClearEntranceTrackingData(void) {
ClearEntranceTrackingData();
}
extern "C" void Entrance_InitEntranceTrackingData(void) {
InitEntranceTrackingData();
}
extern "C" void EntranceTracker_SetCurrentGrottoID(s16 entranceIndex) {
SetCurrentGrottoIDForTracker(entranceIndex);
}
extern "C" void EntranceTracker_SetLastEntranceOverride(s16 entranceIndex) {
SetLastEntranceOverrideForTracker(entranceIndex);
}

View File

@ -134,6 +134,10 @@ int CustomMessage_RetrieveIfExists(PlayState* play);
void Overlay_DisplayText(float duration, const char* text);
GetItemEntry ItemTable_Retrieve(int16_t getItemID);
GetItemEntry ItemTable_RetrieveEntry(s16 modIndex, s16 getItemID);
void Entrance_ClearEntranceTrackingData(void);
void Entrance_InitEntranceTrackingData(void);
void EntranceTracker_SetCurrentGrottoID(s16 entranceIndex);
void EntranceTracker_SetLastEntranceOverride(s16 entranceIndex);
#endif
#endif

View File

@ -472,6 +472,12 @@ void SaveManager::InitFileNormal() {
gSaveContext.sohStats.count[count] = 0;
}
gSaveContext.sohStats.gameComplete = false;
for (int scenesIdx = 0; scenesIdx < ARRAY_COUNT(gSaveContext.sohStats.scenesDiscovered); scenesIdx++) {
gSaveContext.sohStats.scenesDiscovered[scenesIdx] = 0;
}
for (int entrancesIdx = 0; entrancesIdx < ARRAY_COUNT(gSaveContext.sohStats.entrancesDiscovered); entrancesIdx++) {
gSaveContext.sohStats.entrancesDiscovered[entrancesIdx] = 0;
}
for (int scene = 0; scene < ARRAY_COUNT(gSaveContext.sceneFlags); scene++) {
gSaveContext.sceneFlags[scene].chest = 0;
gSaveContext.sceneFlags[scene].swch = 0;
@ -1030,6 +1036,12 @@ void SaveManager::LoadBaseVersion2() {
SaveManager::Instance->LoadArray("counts", ARRAY_COUNT(gSaveContext.sohStats.count), [](size_t i) {
SaveManager::Instance->LoadData("", gSaveContext.sohStats.count[i]);
});
SaveManager::Instance->LoadArray("scenesDiscovered", ARRAY_COUNT(gSaveContext.sohStats.scenesDiscovered), [](size_t i) {
SaveManager::Instance->LoadData("", gSaveContext.sohStats.scenesDiscovered[i]);
});
SaveManager::Instance->LoadArray("entrancesDiscovered", ARRAY_COUNT(gSaveContext.sohStats.entrancesDiscovered), [](size_t i) {
SaveManager::Instance->LoadData("", gSaveContext.sohStats.entrancesDiscovered[i]);
});
});
SaveManager::Instance->LoadArray("sceneFlags", ARRAY_COUNT(gSaveContext.sceneFlags), [](size_t i) {
SaveManager::Instance->LoadStruct("", [&i]() {
@ -1199,6 +1211,12 @@ void SaveManager::SaveBase() {
SaveManager::Instance->SaveArray("counts", ARRAY_COUNT(gSaveContext.sohStats.count), [](size_t i) {
SaveManager::Instance->SaveData("", gSaveContext.sohStats.count[i]);
});
SaveManager::Instance->SaveArray("scenesDiscovered", ARRAY_COUNT(gSaveContext.sohStats.scenesDiscovered), [](size_t i) {
SaveManager::Instance->SaveData("", gSaveContext.sohStats.scenesDiscovered[i]);
});
SaveManager::Instance->SaveArray("entrancesDiscovered", ARRAY_COUNT(gSaveContext.sohStats.entrancesDiscovered), [](size_t i) {
SaveManager::Instance->SaveData("", gSaveContext.sohStats.entrancesDiscovered[i]);
});
});
SaveManager::Instance->SaveArray("sceneFlags", ARRAY_COUNT(gSaveContext.sceneFlags), [](size_t i) {
SaveManager::Instance->SaveStruct("", [&i]() {

View File

@ -200,6 +200,7 @@ void Sram_OpenSave() {
// Setup the modified entrance table and entrance shuffle table for rando
if (gSaveContext.n64ddFlag) {
Entrance_Init();
Entrance_InitEntranceTrackingData();
}
osSyncPrintf("scene_no = %d\n", gSaveContext.entranceIndex);
@ -583,4 +584,6 @@ void Sram_InitSram(GameState* gameState) {
// When going from a rando save to a vanilla save within the same game instance
// we need to reset the entrance table back to its vanilla state
Entrance_ResetEntranceTable();
// Clear out the entrance tracker
Entrance_ClearEntranceTrackingData();
}