mirror of
https://github.com/HarbourMasters/Shipwright.git
synced 2025-08-22 06:13:45 -07:00
Merge branch 'develop' into region_scene
This commit is contained in:
commit
2e03fe4caa
93 changed files with 1458 additions and 1048 deletions
|
@ -41,15 +41,28 @@ You can name your branch whatever you want, but it's recommended to name it some
|
|||
|
||||
The limit is your imagination. You can add new features, fix bugs, add new mods, or even change the way the game works. We will demonstrate this by creating a mod that changes the speed of the day/night cycle.
|
||||
|
||||
Let's being by finding where the time is updated. Thankfully in the save editor we have a slider already hooked up to the time of day so we can check there for reference. The save editor file is at `soh/soh/Enhancements/debugger/debugSaveEditor.cpp`, if we do a quick search within that file for time we will find the following at line 400:
|
||||
Let's begin by finding where the time is updated. Thankfully in the save editor we have a slider already hooked up to the time of day so we can check there for reference. The save editor file is at `soh/soh/Enhancements/debugger/debugSaveEditor.cpp`, if we do a quick search within that file for time we will find the following at around line 217:
|
||||
|
||||
```cpp
|
||||
const uint16_t dayTimeMin = 0;
|
||||
const uint16_t dayTimeMax = 0xFFFF;
|
||||
ImGui::SliderScalar("Time", ImGuiDataType_U16, &gSaveContext.dayTime, &dayTimeMin, &dayTimeMax);
|
||||
SliderInt("Time", (int32_t*)&gSaveContext.dayTime, intSliderOptionsBase.Min(0).Max(0xFFFF).Tooltip("Time of day"));
|
||||
if (Button("Dawn", buttonOptionsBase)) {
|
||||
gSaveContext.dayTime = 0x4000;
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (Button("Noon", buttonOptionsBase)) {
|
||||
gSaveContext.dayTime = 0x8000;
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (Button("Sunset", buttonOptionsBase)) {
|
||||
gSaveContext.dayTime = 0xC001;
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (Button("Midnight", buttonOptionsBase)) {
|
||||
gSaveContext.dayTime = 0;
|
||||
}
|
||||
```
|
||||
|
||||
So this tells us that `gSaveContext.dayTime` is what we're looking for. Let's now do a global search for this to see if we can find where it is updated. We find the following in `soh/src/code/z_kankyo.c` line 925:
|
||||
So this tells us that `gSaveContext.dayTime` is what we're looking for. Let's now do a global search for this to see if we can find where it is updated. We find the following in `soh/src/code/z_kankyo.c` around line 925:
|
||||
|
||||
```cpp
|
||||
if (IS_DAY || gTimeIncrement >= 0x190) {
|
||||
|
@ -71,16 +84,19 @@ if (IS_DAY || gTimeIncrement >= 0x190) {
|
|||
}
|
||||
```
|
||||
|
||||
Rebuild the game and launch it, then load a save file. You should see that the time of day is now moving much faster. Terrific! While we could wrap this up and call it a day, we could make this user configurable by making a few more changes. I think a slider would be good for this, there's a slider in the cheat menu that we can use as a reference. Let's find it in `soh/soh/SohMenuBar.cpp` around line 1120:
|
||||
Rebuild the game and launch it, then load a save file. You should see that the time of day is now moving much faster. Terrific! While we could wrap this up and call it a day, we could make this user configurable by making a few more changes. I think a slider would be good for this, there's a slider in the cheat menu that we can use as a reference. Let's find it in `soh/soh/SohGui/SohMenuEnhancements.cpp` around line 1565:
|
||||
|
||||
```cpp
|
||||
UIWidgets::EnhancementSliderFloat("Hookshot Reach Multiplier: %.1fx", "##gCheatHookshotReachMultiplier", "gCheatHookshotReachMultiplier", 1.0f, 5.0f, "", 1.0f, false);
|
||||
AddWidget(path, "Hookshot Reach Multiplier: %.2fx", WIDGET_CVAR_SLIDER_FLOAT)
|
||||
.CVar(CVAR_CHEAT("HookshotReachMultiplier"))
|
||||
.Options(FloatSliderOptions().Format("%.2f").Min(1.0f).Max(5.0f));
|
||||
```
|
||||
|
||||
The float values being passed in here are `minimum`, `maximum`, and `default` respectively. We'll make our minimum 0.2 to allow it to move slower, and our maximum 5.0 to allow it to move up to 5x faster. We'll also set the default to 1.0 so that it doesn't change the behavior by default. Copy this line and paste it below, then make the relevant changes:
|
||||
This adds a `Widget` which sets a CVar, which then sets the options of the slider. We'll make our minimum 0.2 to allow it to move slower, and our maximum 5.0 to allow it to move up to 5x faster. We'll also set the default to 1.0 so that it doesn't change the behavior by default. Copy this line and paste it below, then make the relevant changes:
|
||||
|
||||
```cpp
|
||||
UIWidgets::EnhancementSliderFloat("Time Multiplier: %.1fx", "##gCheatTimeMultiplier", "gCheatTimeMultiplier", 0.2f, 5.0f, "", 1.0f, false);
|
||||
AddWidget(path, "Time Multiplier: %.2fx", WIDGET_CVAR_SLIDER_FLOAT)
|
||||
.CVar(CVAR_CHEAT("TimeOfDayMultiplier"))
|
||||
.Options(FloatSliderOptions().Format("%.2f").Min(0.2f).Max(5.0f).DefaultValue(1.0f));
|
||||
```
|
||||
|
||||
Now we need to replace our hard coded values with the new variable. We can do this by replacing the `10` with a cvar call
|
||||
|
@ -88,10 +104,10 @@ Now we need to replace our hard coded values with the new variable. We can do th
|
|||
```diff
|
||||
if (IS_DAY || gTimeIncrement >= 0x190) {
|
||||
- gSaveContext.dayTime += gTimeIncrement * 10;
|
||||
+ gSaveContext.dayTime += gTimeIncrement * CVarGetFloat("gCheatTimeMultiplier", 1.0f);
|
||||
+ gSaveContext.dayTime += gTimeIncrement * CVarGetFloat(CVAR_CHEAT("TimeOfDayMultiplier"),1.0f);
|
||||
} else {
|
||||
- gSaveContext.dayTime += gTimeIncrement * 2 * 10;
|
||||
+ gSaveContext.dayTime += gTimeIncrement * 2 * CVarGetFloat("gCheatTimeMultiplier", 1.0f);
|
||||
+ gSaveContext.dayTime += gTimeIncrement * 2 * CVarGetFloat(CVAR_CHEAT("TimeOfDayMultiplier"),1.0f);
|
||||
}
|
||||
```
|
||||
|
||||
|
|
|
@ -266,9 +266,6 @@ typedef struct Actor {
|
|||
/* 0x134 */ ActorFunc draw; // Draw Routine. Called by `Actor_Draw`
|
||||
/* 0x138 */ ActorResetFunc reset;
|
||||
/* 0x13C */ char dbgPad[0x10]; // Padding that only exists in the debug rom
|
||||
// #region SOH [General]
|
||||
/* */ u8 maximumHealth; // Max health value for use with health bars, set on actor init
|
||||
// #endregion
|
||||
} Actor; // size = 0x14C
|
||||
|
||||
typedef enum {
|
||||
|
|
19
soh/soh/Enhancements/CuccosToReturn.cpp
Normal file
19
soh/soh/Enhancements/CuccosToReturn.cpp
Normal file
|
@ -0,0 +1,19 @@
|
|||
#include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h"
|
||||
#include "soh/ShipInit.hpp"
|
||||
#include "soh/Enhancements/randomizer/context.h"
|
||||
|
||||
extern "C" {
|
||||
extern PlayState* gPlayState;
|
||||
#include "src/overlays/actors/ovl_En_Niw_Lady/z_en_niw_lady.h"
|
||||
}
|
||||
|
||||
void RegisterCuccosToReturn() {
|
||||
COND_VB_SHOULD(VB_SET_CUCCO_COUNT, CVarGetInteger(CVAR_ENHANCEMENT("CuccosToReturn"), 7) != 7, {
|
||||
EnNiwLady* enNiwLady = va_arg(args, EnNiwLady*);
|
||||
// Override starting Cucco count using setting value
|
||||
enNiwLady->cuccosInPen = 7 - CVarGetInteger(CVAR_ENHANCEMENT("CuccosToReturn"), 7);
|
||||
*should = false;
|
||||
});
|
||||
}
|
||||
|
||||
static RegisterShipInitFunc initFunc(RegisterCuccosToReturn, { CVAR_ENHANCEMENT("CuccosToReturn") });
|
|
@ -110,6 +110,7 @@ void applyPreset(std::string presetName, std::vector<PresetSection> includeSecti
|
|||
}
|
||||
}
|
||||
}
|
||||
ShipInit::InitAll();
|
||||
}
|
||||
|
||||
void DrawPresetSelector(std::vector<PresetSection> includeSections, std::string presetLoc, bool disabled) {
|
||||
|
|
8
soh/soh/Enhancements/SkipAmyPuzzle.cpp
Normal file
8
soh/soh/Enhancements/SkipAmyPuzzle.cpp
Normal file
|
@ -0,0 +1,8 @@
|
|||
#include "soh/Enhancements/game-interactor/GameInteractor.h"
|
||||
#include "soh/ShipInit.hpp"
|
||||
|
||||
void RegisterSkipAmyPuzzle() {
|
||||
COND_VB_SHOULD(VB_AMY_SOLVE, CVarGetInteger(CVAR_ENHANCEMENT("SkipAmyPuzzle"), 0), { *should = true; });
|
||||
}
|
||||
|
||||
static RegisterShipInitFunc initFunc(RegisterSkipAmyPuzzle, { CVAR_ENHANCEMENT("SkipAmyPuzzle") });
|
29
soh/soh/Enhancements/SwitchTimerMultiplier.cpp
Normal file
29
soh/soh/Enhancements/SwitchTimerMultiplier.cpp
Normal file
|
@ -0,0 +1,29 @@
|
|||
#include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h"
|
||||
#include "soh/ShipInit.hpp"
|
||||
|
||||
extern "C" {
|
||||
extern PlayState* gPlayState;
|
||||
}
|
||||
|
||||
void RegisterSwitchTimerMultiplier() {
|
||||
COND_VB_SHOULD(VB_SWITCH_TIMER_TICK, CVarGetInteger(CVAR_ENHANCEMENT("SwitchTimerMultiplier"), 0) != 0, {
|
||||
int multiplier = CVarGetInteger(CVAR_ENHANCEMENT("SwitchTimerMultiplier"), 0);
|
||||
if (multiplier != 0) {
|
||||
Actor* actor = va_arg(args, Actor*);
|
||||
if (multiplier < -3 && actor->id == ACTOR_OBJ_SYOKUDAI) {
|
||||
multiplier = -3;
|
||||
} else if (multiplier < -4 && actor->id == ACTOR_BG_GND_DARKMEIRO) {
|
||||
multiplier = -4;
|
||||
}
|
||||
|
||||
if (multiplier > 0 && gPlayState->gameplayFrames % (multiplier + 1) != 0) {
|
||||
*should = false;
|
||||
} else if (gPlayState->gameplayFrames % (6 + multiplier) == 0) {
|
||||
s16* timer = va_arg(args, s16*);
|
||||
*timer -= *timer > 1;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
static RegisterShipInitFunc initFunc(RegisterSwitchTimerMultiplier, { CVAR_ENHANCEMENT("SwitchTimerMultiplier") });
|
|
@ -10,8 +10,6 @@ extern "C" {
|
|||
#include "variables.h"
|
||||
}
|
||||
|
||||
#define RAND_GET_OPTION(option) Rando::Context::GetInstance()->GetOption(option).Get()
|
||||
|
||||
static bool sEnteredBlueWarp = false;
|
||||
|
||||
/**
|
||||
|
|
31
soh/soh/Enhancements/TreesDropSticks.cpp
Normal file
31
soh/soh/Enhancements/TreesDropSticks.cpp
Normal file
|
@ -0,0 +1,31 @@
|
|||
#include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h"
|
||||
#include "soh/ShipInit.hpp"
|
||||
|
||||
extern "C" {
|
||||
#include "src/overlays/actors/ovl_En_Wood02/z_en_wood02.h"
|
||||
}
|
||||
|
||||
extern PlayState* gPlayState;
|
||||
|
||||
void RegisterTreesDropSticks() {
|
||||
COND_VB_SHOULD(VB_TREE_DROP_COLLECTIBLE, CVarGetInteger(CVAR_ENHANCEMENT("TreesDropSticks"), 0), {
|
||||
if (INV_CONTENT(ITEM_STICK) != ITEM_NONE) {
|
||||
EnWood02* tree = va_arg(args, EnWood02*);
|
||||
Vec3f dropsSpawnPt = tree->actor.world.pos;
|
||||
dropsSpawnPt.y += 200.0f;
|
||||
|
||||
*should = false;
|
||||
for (s32 numDrops = Rand_Next() % 4; numDrops > 0; numDrops--) {
|
||||
Item_DropCollectible(gPlayState, &dropsSpawnPt, ITEM00_STICK);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
COND_VB_SHOULD(VB_PREVENT_ADULT_STICK, CVarGetInteger(CVAR_ENHANCEMENT("TreesDropSticks"), 0), {
|
||||
if (INV_CONTENT(ITEM_STICK) != ITEM_NONE) {
|
||||
*should = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
static RegisterShipInitFunc initFunc(RegisterTreesDropSticks, { CVAR_ENHANCEMENT("TreesDropSticks") });
|
|
@ -163,6 +163,7 @@ typedef enum {
|
|||
TEXT_ANJU_ROUND_THEM_UP_OR_YOULL_PAY = 0x503C,
|
||||
TEXT_ANJU_DONT_TEASE_MY_CUCCOS = 0x503D,
|
||||
TEXT_BIG_POE_COLLECTED_RANDO = 0x5090,
|
||||
TEXT_GERUDO_GUARD_FRIENDLY = 0x6005,
|
||||
TEXT_HBA_NOT_ON_HORSE = 0x603F,
|
||||
TEXT_HBA_INITIAL_EXPLAINATION = 0x6040,
|
||||
TEXT_HBA_WANT_TO_TRY_AGAIN_YES_NO = 0x6041,
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
#include "soh/Enhancements/game-interactor/GameInteractor.h"
|
||||
#include "soh/Enhancements/cosmetics/CosmeticsEditor.h"
|
||||
#include "soh/Enhancements/audio/AudioEditor.h"
|
||||
#include "soh/Enhancements/randomizer/logic.h"
|
||||
|
||||
#define Path _Path
|
||||
#define PATH_HACK
|
||||
|
@ -1450,6 +1451,55 @@ static bool SfxHandler(std::shared_ptr<Ship::Console> Console, const std::vector
|
|||
return 0;
|
||||
}
|
||||
|
||||
static bool AvailableChecksProcessUndiscoveredExitsHandler(std::shared_ptr<Ship::Console> Console,
|
||||
const std::vector<std::string>& args, std::string* output) {
|
||||
const auto& logic = Rando::Context::GetInstance()->GetLogic();
|
||||
bool enabled = false;
|
||||
|
||||
if (args.size() == 1) {
|
||||
enabled = !logic->ACProcessUndiscoveredExits;
|
||||
} else {
|
||||
try {
|
||||
enabled = std::stoi(args[1]);
|
||||
} catch (std::invalid_argument const& ex) {
|
||||
ERROR_MESSAGE("[SOH] Enable should be 0 or 1");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
logic->ACProcessUndiscoveredExits = enabled;
|
||||
INFO_MESSAGE("[SOH] Available Checks - Process Undiscovered Exits %s",
|
||||
logic->ACProcessUndiscoveredExits ? "enabled" : "disabled");
|
||||
|
||||
if (GameInteractor::IsSaveLoaded(true)) {
|
||||
CheckTracker::RecalculateAvailableChecks();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool AvailableChecksRecalculateHandler(std::shared_ptr<Ship::Console> Console,
|
||||
const std::vector<std::string>& args, std::string* output) {
|
||||
RandomizerRegion startingRegion = RR_ROOT;
|
||||
|
||||
if (args.size() > 1) {
|
||||
try {
|
||||
startingRegion = static_cast<RandomizerRegion>(std::stoi(args[1]));
|
||||
} catch (std::invalid_argument const& ex) {
|
||||
ERROR_MESSAGE("[SOH] Region should be a number");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (startingRegion <= RR_NONE || startingRegion >= RR_MAX) {
|
||||
ERROR_MESSAGE("[SOH] Region should be between 1 and %d", RR_MAX - 1);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
CheckTracker::RecalculateAvailableChecks(startingRegion);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void DebugConsole_Init(void) {
|
||||
// Console
|
||||
CMD_REGISTER("file_select", { FileSelectHandler, "Returns to the file select." });
|
||||
|
@ -1708,5 +1758,15 @@ void DebugConsole_Init(void) {
|
|||
{ "group_name", Ship::ArgumentType::TEXT, true },
|
||||
} });
|
||||
|
||||
CMD_REGISTER("acpue", { AvailableChecksProcessUndiscoveredExitsHandler,
|
||||
"Available Checks - Process Undiscovered Exits",
|
||||
{ { "enable", Ship::ArgumentType::NUMBER, true } } });
|
||||
|
||||
CMD_REGISTER("acr", { AvailableChecksRecalculateHandler,
|
||||
"Available Checks - Recalculate",
|
||||
{
|
||||
{ "starting_region", Ship::ArgumentType::NUMBER, true },
|
||||
} });
|
||||
|
||||
Ship::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesNextFrame();
|
||||
}
|
||||
|
|
|
@ -11,17 +11,32 @@
|
|||
|
||||
extern "C" {
|
||||
#include <z64.h>
|
||||
#include "src/overlays/actors/ovl_En_Rr/z_en_rr.h"
|
||||
}
|
||||
|
||||
const char* enemyCVarList[] = {
|
||||
CVAR_ENHANCEMENT("RandomizedEnemyList.Armos"), CVAR_ENHANCEMENT("RandomizedEnemyList.Arwing"),
|
||||
CVAR_ENHANCEMENT("RandomizedEnemyList.BabyDodongo"), CVAR_ENHANCEMENT("RandomizedEnemyList.Bari"),
|
||||
CVAR_ENHANCEMENT("RandomizedEnemyList.Beamos"), CVAR_ENHANCEMENT("RandomizedEnemyList.BigSkulltula"),
|
||||
CVAR_ENHANCEMENT("RandomizedEnemyList.BigStalchild"), CVAR_ENHANCEMENT("RandomizedEnemyList.Biri"),
|
||||
CVAR_ENHANCEMENT("RandomizedEnemyList.BlackKnuckle"), CVAR_ENHANCEMENT("RandomizedEnemyList.BlueTektite"),
|
||||
CVAR_ENHANCEMENT("RandomizedEnemyList.Bubble"), CVAR_ENHANCEMENT("RandomizedEnemyList.ClubMoblin"),
|
||||
CVAR_ENHANCEMENT("RandomizedEnemyList.DarkLink"), CVAR_ENHANCEMENT("RandomizedEnemyList.Dinolfos"),
|
||||
CVAR_ENHANCEMENT("RandomizedEnemyList.Dodongo"), CVAR_ENHANCEMENT("RandomizedEnemyList.FireKeese"),
|
||||
#define CVAR_ENEMY_RANDOMIZER_NAME CVAR_ENHANCEMENT("RandomizedEnemies")
|
||||
#define CVAR_ENEMY_RANDOMIZER_DEFAULT ENEMY_RANDOMIZER_OFF
|
||||
#define CVAR_ENEMY_RANDOMIZER_VALUE CVarGetInteger(CVAR_ENEMY_RANDOMIZER_NAME, CVAR_ENEMY_RANDOMIZER_DEFAULT)
|
||||
|
||||
typedef struct EnemyEntry {
|
||||
int16_t id;
|
||||
int16_t params;
|
||||
} EnemyEntry;
|
||||
|
||||
bool IsEnemyFoundToRandomize(int16_t sceneNum, int8_t roomNum, int16_t actorId, int16_t params, float posX);
|
||||
bool IsEnemyAllowedToSpawn(int16_t sceneNum, int8_t roomNum, EnemyEntry enemy);
|
||||
EnemyEntry GetRandomizedEnemyEntry(uint32_t seed, PlayState* play);
|
||||
|
||||
const char* enemyCVarList[RANDOMIZED_ENEMY_SPAWN_TABLE_SIZE] = {
|
||||
CVAR_ENHANCEMENT("RandomizedEnemyList.Anubis"), CVAR_ENHANCEMENT("RandomizedEnemyList.Armos"),
|
||||
CVAR_ENHANCEMENT("RandomizedEnemyList.Arwing"), CVAR_ENHANCEMENT("RandomizedEnemyList.BabyDodongo"),
|
||||
CVAR_ENHANCEMENT("RandomizedEnemyList.Bari"), CVAR_ENHANCEMENT("RandomizedEnemyList.Beamos"),
|
||||
CVAR_ENHANCEMENT("RandomizedEnemyList.BigSkulltula"), CVAR_ENHANCEMENT("RandomizedEnemyList.BigStalchild"),
|
||||
CVAR_ENHANCEMENT("RandomizedEnemyList.Biri"), CVAR_ENHANCEMENT("RandomizedEnemyList.BlackKnuckle"),
|
||||
CVAR_ENHANCEMENT("RandomizedEnemyList.BlueTektite"), CVAR_ENHANCEMENT("RandomizedEnemyList.Bubble"),
|
||||
CVAR_ENHANCEMENT("RandomizedEnemyList.ClubMoblin"), CVAR_ENHANCEMENT("RandomizedEnemyList.DarkLink"),
|
||||
CVAR_ENHANCEMENT("RandomizedEnemyList.Dinolfos"), CVAR_ENHANCEMENT("RandomizedEnemyList.Dodongo"),
|
||||
CVAR_ENHANCEMENT("RandomizedEnemyList.FireKeese"), /*CVAR_ENHANCEMENT("RandomizedEnemyList.FlareDancer"),*/
|
||||
CVAR_ENHANCEMENT("RandomizedEnemyList.FloorTile"), CVAR_ENHANCEMENT("RandomizedEnemyList.Floormaster"),
|
||||
CVAR_ENHANCEMENT("RandomizedEnemyList.FlyingPeahat"), CVAR_ENHANCEMENT("RandomizedEnemyList.FlyingPot"),
|
||||
CVAR_ENHANCEMENT("RandomizedEnemyList.Freezard"), CVAR_ENHANCEMENT("RandomizedEnemyList.Gibdo"),
|
||||
|
@ -30,18 +45,20 @@ const char* enemyCVarList[] = {
|
|||
CVAR_ENHANCEMENT("RandomizedEnemyList.Keese"), CVAR_ENHANCEMENT("RandomizedEnemyList.LargeBaba"),
|
||||
CVAR_ENHANCEMENT("RandomizedEnemyList.LikeLike"), CVAR_ENHANCEMENT("RandomizedEnemyList.Lizalfos"),
|
||||
CVAR_ENHANCEMENT("RandomizedEnemyList.MadScrub"), CVAR_ENHANCEMENT("RandomizedEnemyList.NormalWolfos"),
|
||||
CVAR_ENHANCEMENT("RandomizedEnemyList.PeahatLarva"), CVAR_ENHANCEMENT("RandomizedEnemyList.Redead"),
|
||||
CVAR_ENHANCEMENT("RandomizedEnemyList.RedTektite"), CVAR_ENHANCEMENT("RandomizedEnemyList.Shabom"),
|
||||
CVAR_ENHANCEMENT("RandomizedEnemyList.ShellBlade"), CVAR_ENHANCEMENT("RandomizedEnemyList.Skulltula"),
|
||||
CVAR_ENHANCEMENT("RandomizedEnemyList.PeahatLarva"), /*CVAR_ENHANCEMENT("RandomizedEnemyList.Poe"),*/
|
||||
CVAR_ENHANCEMENT("RandomizedEnemyList.Redead"), CVAR_ENHANCEMENT("RandomizedEnemyList.RedTektite"),
|
||||
CVAR_ENHANCEMENT("RandomizedEnemyList.Shabom"), CVAR_ENHANCEMENT("RandomizedEnemyList.ShellBlade"),
|
||||
CVAR_ENHANCEMENT("RandomizedEnemyList.Skulltula"), CVAR_ENHANCEMENT("RandomizedEnemyList.SkullKid"),
|
||||
CVAR_ENHANCEMENT("RandomizedEnemyList.SmallBaba"), CVAR_ENHANCEMENT("RandomizedEnemyList.SmallStalchild"),
|
||||
CVAR_ENHANCEMENT("RandomizedEnemyList.Spike"), CVAR_ENHANCEMENT("RandomizedEnemyList.Stalfos"),
|
||||
CVAR_ENHANCEMENT("RandomizedEnemyList.Stinger"), CVAR_ENHANCEMENT("RandomizedEnemyList.Tailparasan"),
|
||||
CVAR_ENHANCEMENT("RandomizedEnemyList.TorchSlug"), CVAR_ENHANCEMENT("RandomizedEnemyList.Wallmaster"),
|
||||
CVAR_ENHANCEMENT("RandomizedEnemyList.WhiteKnuckle"), CVAR_ENHANCEMENT("RandomizedEnemyList.WhiteWolfos"),
|
||||
CVAR_ENHANCEMENT("RandomizedEnemyList.WitheredBaba"),
|
||||
CVAR_ENHANCEMENT("RandomizedEnemyList.SpearMoblin"), CVAR_ENHANCEMENT("RandomizedEnemyList.Spike"),
|
||||
CVAR_ENHANCEMENT("RandomizedEnemyList.Stalfos"), CVAR_ENHANCEMENT("RandomizedEnemyList.Stinger"),
|
||||
CVAR_ENHANCEMENT("RandomizedEnemyList.Tailparasan"), CVAR_ENHANCEMENT("RandomizedEnemyList.TorchSlug"),
|
||||
CVAR_ENHANCEMENT("RandomizedEnemyList.Wallmaster"), CVAR_ENHANCEMENT("RandomizedEnemyList.WhiteKnuckle"),
|
||||
CVAR_ENHANCEMENT("RandomizedEnemyList.WhiteWolfos"), CVAR_ENHANCEMENT("RandomizedEnemyList.WitheredBaba"),
|
||||
};
|
||||
|
||||
const char* enemyNameList[] = {
|
||||
const char* enemyNameList[RANDOMIZED_ENEMY_SPAWN_TABLE_SIZE] = {
|
||||
"Anubis",
|
||||
"Armos",
|
||||
"Arwing",
|
||||
"Baby Dodongo",
|
||||
|
@ -58,6 +75,7 @@ const char* enemyNameList[] = {
|
|||
"Dinolfos",
|
||||
"Dodongo",
|
||||
"Fire Keese",
|
||||
//"Flare Dancer",
|
||||
"Floor Tile",
|
||||
"Floormaster",
|
||||
"Flying Peahat",
|
||||
|
@ -75,13 +93,16 @@ const char* enemyNameList[] = {
|
|||
"Mad Scrub",
|
||||
"Wolfos (Normal)",
|
||||
"Peahat Larva",
|
||||
//"Poe",
|
||||
"Redead",
|
||||
"Red Tektite",
|
||||
"Shabom",
|
||||
"Shell Blade",
|
||||
"Skulltula",
|
||||
"Skull Kid",
|
||||
"Small Deku Baba",
|
||||
"Stalchild (Small)",
|
||||
"Spear Moblin",
|
||||
"Spike",
|
||||
"Stalfos",
|
||||
"Stinger",
|
||||
|
@ -94,98 +115,105 @@ const char* enemyNameList[] = {
|
|||
};
|
||||
|
||||
static EnemyEntry randomizedEnemySpawnTable[RANDOMIZED_ENEMY_SPAWN_TABLE_SIZE] = {
|
||||
{ ACTOR_EN_AM, -1 }, // Armos
|
||||
{ ACTOR_EN_CLEAR_TAG, 1 }, // Arwing
|
||||
{ ACTOR_EN_DODOJR, 0 }, // Baby Dodongo
|
||||
{ ACTOR_EN_VALI, -1 }, // Bari (big jellyfish)
|
||||
{ ACTOR_EN_VM, 1280 }, // Beamos
|
||||
{ ACTOR_EN_ST, 1 }, // Skulltula (big)
|
||||
{ ACTOR_EN_SKB, 20 }, // Stalchild (big)
|
||||
{ ACTOR_EN_BILI, 0 }, // Biri (jellyfish)
|
||||
{ ACTOR_EN_IK, 2 }, // Iron Knuckle (black, standing)
|
||||
{ ACTOR_EN_TITE, -2 }, // Tektite (blue)
|
||||
{ ACTOR_EN_BB, -1 }, // Bubble (flying skull enemy) (blue)
|
||||
{ ACTOR_EN_MB, 0 }, // Moblins (Club)
|
||||
{ ACTOR_EN_TORCH2, 0 }, // Dark Link
|
||||
{ ACTOR_EN_ZF, -2 }, // Dinolfos
|
||||
{ ACTOR_EN_DODONGO, -1 }, // Dodongo
|
||||
{ ACTOR_EN_FIREFLY, 1 }, // Fire Keese
|
||||
{ ACTOR_EN_YUKABYUN, 0 }, // Flying Floor Tile
|
||||
{ ACTOR_EN_FLOORMAS, 0 }, // Floormaster
|
||||
{ ACTOR_EN_PEEHAT, -1 }, // Flying Peahat (big grounded, doesn't spawn larva)
|
||||
{ ACTOR_EN_TUBO_TRAP, 0 }, // Flying pot
|
||||
{ ACTOR_EN_FZ, 0 }, // Freezard
|
||||
{ ACTOR_EN_RD, 32766 }, // Gibdo (standing)
|
||||
{ ACTOR_EN_GOMA, 7 }, // Gohma Larva (Non-Gohma rooms)
|
||||
{ ACTOR_EN_CROW, 0 }, // Guay
|
||||
{ ACTOR_EN_FIREFLY, 4 }, // Ice Keese
|
||||
{ ACTOR_EN_ST, 2 }, // Skulltula (invisible)
|
||||
{ ACTOR_EN_FIREFLY, 2 }, // Regular Keese
|
||||
{ ACTOR_EN_DEKUBABA, 1 }, // Deku Baba (large)
|
||||
{ ACTOR_EN_ANUBICE_TAG, 1 }, // Anubis
|
||||
{ ACTOR_EN_AM, -1 }, // Armos
|
||||
{ ACTOR_EN_CLEAR_TAG, 1 }, // Arwing
|
||||
{ ACTOR_EN_DODOJR, 0 }, // Baby Dodongo
|
||||
{ ACTOR_EN_VALI, -1 }, // Bari (big jellyfish)
|
||||
{ ACTOR_EN_VM, 1280 }, // Beamos
|
||||
{ ACTOR_EN_ST, 1 }, // Skulltula (big)
|
||||
{ ACTOR_EN_SKB, 20 }, // Stalchild (big)
|
||||
{ ACTOR_EN_BILI, 0 }, // Biri (jellyfish)
|
||||
{ ACTOR_EN_IK, 2 }, // Iron Knuckle (black, standing)
|
||||
{ ACTOR_EN_TITE, -2 }, // Tektite (blue)
|
||||
{ ACTOR_EN_BB, -1 }, // Bubble (flying skull enemy) (blue)
|
||||
{ ACTOR_EN_MB, 0 }, // Club Moblin
|
||||
{ ACTOR_EN_TORCH2, 0 }, // Dark Link
|
||||
{ ACTOR_EN_ZF, -2 }, // Dinolfos
|
||||
{ ACTOR_EN_DODONGO, -1 }, // Dodongo
|
||||
{ ACTOR_EN_FIREFLY, 1 }, // Fire Keese
|
||||
// { ACTOR_EN_FD, 0 }, // Flare Dancer (possible cause of crashes because of spawning flame actors on
|
||||
// sloped ground)
|
||||
{ ACTOR_EN_YUKABYUN, 0 }, // Flying Floor Tile
|
||||
{ ACTOR_EN_FLOORMAS, 0 }, // Floormaster
|
||||
{ ACTOR_EN_PEEHAT, -1 }, // Flying Peahat (big grounded, doesn't spawn larva)
|
||||
{ ACTOR_EN_TUBO_TRAP, 0 }, // Flying pot
|
||||
{ ACTOR_EN_FZ, 0 }, // Freezard
|
||||
{ ACTOR_EN_RD, 32766 }, // Gibdo (standing)
|
||||
{ ACTOR_EN_GOMA, 7 }, // Gohma Larva (Non-Gohma rooms)
|
||||
{ ACTOR_EN_CROW, 0 }, // Guay
|
||||
{ ACTOR_EN_FIREFLY, 4 }, // Ice Keese
|
||||
{ ACTOR_EN_ST, 2 }, // Skulltula (invisible)
|
||||
{ ACTOR_EN_FIREFLY, 2 }, // Regular Keese
|
||||
{ ACTOR_EN_DEKUBABA, 1 }, // Deku Baba (large)
|
||||
// Doesn't work (reliant on surface and also normally used in tandem with a leever spawner, kills itself too quickly
|
||||
// otherwise) { ACTOR_EN_REEBA, 0 }, // Leever
|
||||
{ ACTOR_EN_RR, 0 }, // Like-Like
|
||||
{ ACTOR_EN_ZF, -1 }, // Lizalfos
|
||||
{ ACTOR_EN_DEKUNUTS, 768 }, // Mad Scrub (triple attack) (projectiles don't work)
|
||||
{ ACTOR_EN_WF, 0 }, // Wolfos (normal)
|
||||
{ ACTOR_EN_PEEHAT, 1 }, // Flying Peahat Larva
|
||||
{ ACTOR_EN_RD, 1 }, // Redead (standing)
|
||||
{ ACTOR_EN_TITE, -1 }, // Tektite (red)
|
||||
{ ACTOR_EN_BUBBLE, 0 }, // Shabom (bubble)
|
||||
{ ACTOR_EN_SB, 0 }, // Shell Blade
|
||||
{ ACTOR_EN_ST, 0 }, // Skulltula (normal)
|
||||
{ ACTOR_EN_DEKUBABA, 0 }, // Deku Baba (small)
|
||||
{ ACTOR_EN_SKB, 1 }, // Stalchild (small)
|
||||
{ ACTOR_EN_NY, 0 }, // Spike (rolling enemy)
|
||||
{ ACTOR_EN_TEST, 2 }, // Stalfos
|
||||
{ ACTOR_EN_EIYER, 10 }, // Stinger (land) (One in formation, sink under floor and do not activate)
|
||||
{ ACTOR_EN_TP, -1 }, // Electric Tailpasaran
|
||||
{ ACTOR_EN_BW, 0 }, // Torch Slug
|
||||
{ ACTOR_EN_WALLMAS, 1 }, // Wallmaster
|
||||
{ ACTOR_EN_IK, 3 }, // Iron Knuckle (white, standing)
|
||||
{ ACTOR_EN_WF, 1 }, // Wolfos (white)
|
||||
{ ACTOR_EN_KAREBABA, 0 }, // Withered Deku Baba
|
||||
|
||||
// Doesn't work {ACTOR_EN_POH, 0}, // Poe (Seems to rely on other objects?)
|
||||
// Doesn't work {ACTOR_EN_POH, 2}, // Poe (composer Sharp) (Seems to rely on other objects?)
|
||||
// Doesn't work {ACTOR_EN_POH, 3}, // Poe (composer Flat) (Seems to rely on other objects?)
|
||||
// Doesn't work {ACTOR_EN_OKUTA, 0}, // Octorok (actor directly uses water box collision to handle hiding/popping
|
||||
// up) Doesn't work {ACTOR_EN_REEBA, 0}, // Leever (reliant on surface and also normally used in tandem with a
|
||||
// leever spawner, kills itself too quickly otherwise) Kinda doesn't work { ACTOR_EN_FD, 0 }, // Flare Dancer (jumps
|
||||
// out of bounds a lot, and possible cause of crashes because of spawning a ton of flame actors)
|
||||
// Doesn't work (actor directly uses water box collision to handle hiding/popping up)
|
||||
// { ACTOR_EN_OKUTA, 0 }, // Octorok
|
||||
{ ACTOR_EN_PEEHAT, 1 }, // Flying Peahat Larva
|
||||
// Doesn't work (Seems to rely on other objects?)
|
||||
// { ACTOR_EN_POH, 0 }, // Poe
|
||||
// Doesn't work (Seems to rely on other objects?)
|
||||
// { ACTOR_EN_POH, 2 }, // Poe (composer Sharp)
|
||||
// Doesn't work (Seems to rely on other objects?)
|
||||
// { ACTOR_EN_POH, 3 }, // Poe (composer Flat)
|
||||
{ ACTOR_EN_RD, 1 }, // Redead (standing)
|
||||
{ ACTOR_EN_TITE, -1 }, // Tektite (red)
|
||||
{ ACTOR_EN_BUBBLE, 0 }, // Shabom (bubble)
|
||||
{ ACTOR_EN_SB, 0 }, // Shell Blade
|
||||
{ ACTOR_EN_ST, 0 }, // Skulltula (normal)
|
||||
{ ACTOR_EN_SKJ, 4159 }, // Skull Kid
|
||||
{ ACTOR_EN_DEKUBABA, 0 }, // Deku Baba (small)
|
||||
{ ACTOR_EN_SKB, 1 }, // Stalchild (small)
|
||||
{ ACTOR_EN_MB, -1 }, // Spear Moblin
|
||||
{ ACTOR_EN_NY, 0 }, // Spike (rolling enemy)
|
||||
{ ACTOR_EN_TEST, 2 }, // Stalfos
|
||||
{ ACTOR_EN_EIYER, 10 }, // Stinger (land) (One in formation, sink under floor and do not activate)
|
||||
{ ACTOR_EN_TP, -1 }, // Electric Tailpasaran
|
||||
{ ACTOR_EN_BW, 0 }, // Torch Slug
|
||||
{ ACTOR_EN_WALLMAS, 1 }, // Wallmaster
|
||||
{ ACTOR_EN_IK, 3 }, // Iron Knuckle (white, standing)
|
||||
{ ACTOR_EN_WF, 1 }, // Wolfos (white)
|
||||
{ ACTOR_EN_KAREBABA, 0 }, // Withered Deku Baba
|
||||
};
|
||||
|
||||
static int enemiesToRandomize[] = {
|
||||
ACTOR_EN_FIREFLY, // Keese (including fire/ice)
|
||||
ACTOR_EN_TEST, // Stalfos
|
||||
ACTOR_EN_TITE, // Tektite
|
||||
ACTOR_EN_POH, // Poe (normal, blue rupee, composers)
|
||||
ACTOR_EN_OKUTA, // Octorok
|
||||
ACTOR_EN_WALLMAS, // Wallmaster
|
||||
ACTOR_EN_DODONGO, // Dodongo
|
||||
// ACTOR_EN_REEBA, // Leever (reliant on spawner (z_e_encount1.c)
|
||||
ACTOR_EN_PEEHAT, // Flying Peahat, big one spawning larva, larva
|
||||
ACTOR_EN_ZF, // Lizalfos, Dinolfos
|
||||
ACTOR_EN_GOMA, // Gohma Larva (normal, eggs, gohma eggs)
|
||||
ACTOR_EN_BUBBLE, // Shabom (bubble)
|
||||
ACTOR_EN_DODOJR, // Baby Dodongo
|
||||
ACTOR_EN_TORCH2, // Dark Link
|
||||
ACTOR_EN_BILI, // Biri (small jellyfish)
|
||||
ACTOR_EN_TP, // Electric Tailpasaran
|
||||
ACTOR_EN_ST, // Skulltula (normal, big, invisible)
|
||||
ACTOR_EN_BW, // Torch Slug
|
||||
ACTOR_EN_EIYER, // Stinger (land)
|
||||
ACTOR_EN_MB, // Moblins (Club, spear)
|
||||
ACTOR_EN_DEKUBABA, // Deku Baba (small, large)
|
||||
ACTOR_EN_AM, // Armos (enemy variant)
|
||||
ACTOR_EN_DEKUNUTS, // Mad Scrub (single attack, triple attack)
|
||||
ACTOR_EN_VALI, // Bari (big jellyfish) (spawns very high up)
|
||||
ACTOR_EN_BB, // Bubble (flying skull enemy) (all colors)
|
||||
ACTOR_EN_YUKABYUN, // Flying Floor Tile
|
||||
ACTOR_EN_VM, // Beamos
|
||||
ACTOR_EN_FLOORMAS, // Floormaster
|
||||
ACTOR_EN_RD, // Redead, Gibdo
|
||||
ACTOR_EN_SW, // Skullwalltula
|
||||
// ACTOR_EN_FD, // Flare Dancer (can be randomized, but not randomized to, so keeping it in vanilla locations
|
||||
// means it at least shows up in the game)
|
||||
ACTOR_EN_ANUBICE_TAG, // Anubis
|
||||
ACTOR_EN_FIREFLY, // Keese (including fire/ice)
|
||||
ACTOR_EN_TEST, // Stalfos
|
||||
ACTOR_EN_TITE, // Tektite
|
||||
ACTOR_EN_POH, // Poe (normal, blue rupee, composers)
|
||||
ACTOR_EN_OKUTA, // Octorok
|
||||
ACTOR_EN_WALLMAS, // Wallmaster
|
||||
ACTOR_EN_DODONGO, // Dodongo
|
||||
// ACTOR_EN_REEBA, // Leever (reliant on spawner (z_en_encount1.c))
|
||||
ACTOR_EN_PEEHAT, // Flying Peahat, big one spawning larva, larva
|
||||
ACTOR_EN_ZF, // Lizalfos, Dinolfos
|
||||
ACTOR_EN_GOMA, // Gohma Larva (normal, eggs, gohma eggs)
|
||||
ACTOR_EN_BUBBLE, // Shabom (bubble)
|
||||
ACTOR_EN_DODOJR, // Baby Dodongo
|
||||
ACTOR_EN_TORCH2, // Dark Link
|
||||
ACTOR_EN_BILI, // Biri (small jellyfish)
|
||||
ACTOR_EN_TP, // Electric Tailpasaran
|
||||
ACTOR_EN_ST, // Skulltula (normal, big, invisible)
|
||||
ACTOR_EN_BW, // Torch Slug
|
||||
ACTOR_EN_EIYER, // Stinger (land)
|
||||
ACTOR_EN_MB, // Moblins (Club, spear)
|
||||
ACTOR_EN_DEKUBABA, // Deku Baba (small, large)
|
||||
ACTOR_EN_AM, // Armos (enemy variant)
|
||||
ACTOR_EN_DEKUNUTS, // Mad Scrub (single attack, triple attack)
|
||||
ACTOR_EN_VALI, // Bari (big jellyfish) (spawns very high up)
|
||||
ACTOR_EN_BB, // Bubble (flying skull enemy) (all colors)
|
||||
ACTOR_EN_YUKABYUN, // Flying Floor Tile
|
||||
ACTOR_EN_VM, // Beamos
|
||||
ACTOR_EN_FLOORMAS, // Floormaster
|
||||
ACTOR_EN_RD, // Redead, Gibdo
|
||||
ACTOR_EN_SW, // Skullwalltula
|
||||
ACTOR_EN_FD, // Flare Dancer
|
||||
ACTOR_EN_SB, // Shell Blade
|
||||
ACTOR_EN_KAREBABA, // Withered Deku Baba
|
||||
ACTOR_EN_RR, // Like-Like
|
||||
|
@ -198,6 +226,7 @@ static int enemiesToRandomize[] = {
|
|||
ACTOR_EN_WF, // Wolfos
|
||||
ACTOR_EN_SKB, // Stalchild
|
||||
ACTOR_EN_CROW, // Guay
|
||||
ACTOR_EN_SKJ, // Skull Kid
|
||||
};
|
||||
|
||||
extern "C" uint8_t GetRandomizedEnemy(PlayState* play, int16_t* actorId, f32* posX, f32* posY, f32* posZ, int16_t* rotX,
|
||||
|
@ -270,15 +299,7 @@ extern "C" uint8_t GetRandomizedEnemy(PlayState* play, int16_t* actorId, f32* po
|
|||
// Get randomized enemy ID and parameter.
|
||||
uint32_t seed =
|
||||
play->sceneNum + *actorId + (int)*posX + (int)*posY + (int)*posZ + *rotX + *rotY + *rotZ + *params;
|
||||
EnemyEntry randomEnemy = GetRandomizedEnemyEntry(seed);
|
||||
|
||||
int8_t timesRandomized = 1;
|
||||
|
||||
// While randomized enemy isn't allowed in certain situations, randomize again.
|
||||
while (!IsEnemyAllowedToSpawn(play->sceneNum, play->roomCtx.curRoom.num, randomEnemy)) {
|
||||
randomEnemy = GetRandomizedEnemyEntry(seed + timesRandomized);
|
||||
timesRandomized++;
|
||||
}
|
||||
EnemyEntry randomEnemy = GetRandomizedEnemyEntry(seed, play);
|
||||
|
||||
*actorId = randomEnemy.id;
|
||||
*params = randomEnemy.params;
|
||||
|
@ -322,7 +343,7 @@ static std::vector<EnemyEntry> selectedEnemyList;
|
|||
|
||||
void GetSelectedEnemies() {
|
||||
selectedEnemyList.clear();
|
||||
for (int i = 0; i < 49; i++) {
|
||||
for (int i = 0; i < RANDOMIZED_ENEMY_SPAWN_TABLE_SIZE; i++) {
|
||||
if (CVarGetInteger(CVAR_ENHANCEMENT("RandomizedEnemyList.All"), 0)) {
|
||||
selectedEnemyList.push_back(randomizedEnemySpawnTable[i]);
|
||||
} else if (CVarGetInteger(enemyCVarList[i], 1)) {
|
||||
|
@ -334,19 +355,28 @@ void GetSelectedEnemies() {
|
|||
}
|
||||
}
|
||||
|
||||
EnemyEntry GetRandomizedEnemyEntry(uint32_t seed) {
|
||||
EnemyEntry GetRandomizedEnemyEntry(uint32_t seed, PlayState* play) {
|
||||
std::vector<EnemyEntry> filteredEnemyList = {};
|
||||
if (selectedEnemyList.size() == 0) {
|
||||
GetSelectedEnemies();
|
||||
}
|
||||
if (CVarGetInteger(CVAR_ENHANCEMENT("RandomizedEnemies"), ENEMY_RANDOMIZER_OFF) == ENEMY_RANDOMIZER_RANDOM_SEEDED) {
|
||||
for (EnemyEntry enemy : selectedEnemyList) {
|
||||
if (IsEnemyAllowedToSpawn(play->sceneNum, play->roomCtx.curRoom.num, enemy)) {
|
||||
filteredEnemyList.push_back(enemy);
|
||||
}
|
||||
}
|
||||
if (filteredEnemyList.size() == 0) {
|
||||
filteredEnemyList = selectedEnemyList;
|
||||
}
|
||||
if (CVAR_ENEMY_RANDOMIZER_VALUE == ENEMY_RANDOMIZER_RANDOM_SEEDED) {
|
||||
uint32_t finalSeed =
|
||||
seed + (IS_RANDO ? Rando::Context::GetInstance()->GetSeed() : gSaveContext.ship.stats.fileCreatedAt);
|
||||
Random_Init(finalSeed);
|
||||
uint32_t randomNumber = Random(0, selectedEnemyList.size());
|
||||
return selectedEnemyList[randomNumber];
|
||||
uint32_t randomNumber = Random(0, filteredEnemyList.size());
|
||||
return filteredEnemyList[randomNumber];
|
||||
} else {
|
||||
uint32_t randomSelectedEnemy = Random(0, selectedEnemyList.size());
|
||||
return selectedEnemyList[randomSelectedEnemy];
|
||||
uint32_t randomSelectedEnemy = Random(0, filteredEnemyList.size());
|
||||
return filteredEnemyList[randomSelectedEnemy];
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -408,6 +438,8 @@ bool IsEnemyFoundToRandomize(int16_t sceneNum, int8_t roomNum, int16_t actorId,
|
|||
case ACTOR_EN_SB:
|
||||
case ACTOR_EN_NY:
|
||||
return (!(!isMQ && sceneNum == SCENE_WATER_TEMPLE && roomNum == 2));
|
||||
case ACTOR_EN_SKJ:
|
||||
return !(sceneNum == SCENE_LOST_WOODS && LINK_IS_CHILD);
|
||||
default:
|
||||
return 1;
|
||||
}
|
||||
|
@ -419,19 +451,19 @@ bool IsEnemyFoundToRandomize(int16_t sceneNum, int8_t roomNum, int16_t actorId,
|
|||
}
|
||||
|
||||
bool IsEnemyAllowedToSpawn(int16_t sceneNum, int8_t roomNum, EnemyEntry enemy) {
|
||||
|
||||
uint32_t isMQ = ResourceMgr_IsSceneMasterQuest(sceneNum);
|
||||
|
||||
// Freezard - Child Link can only kill this with jump slash Deku Sticks or other equipment like bombs.
|
||||
// Beamos - Needs bombs.
|
||||
// Anubis - Needs fire.
|
||||
// Shell Blade & Spike - Child Link can't kill these with sword or Deku Stick.
|
||||
// Arwing & Dark Link - Both go out of bounds way too easily, softlocking the player.
|
||||
// Flare dancer, Arwing & Dark Link - Both go out of bounds way too easily, softlocking the player.
|
||||
// Wallmaster - Not easily visible, often makes players think they're softlocked and that there's no enemies left.
|
||||
// Club Moblin - Many issues with them falling or placing out of bounds. Maybe fixable in the future?
|
||||
bool enemiesToExcludeClearRooms = enemy.id == ACTOR_EN_FZ || enemy.id == ACTOR_EN_VM || enemy.id == ACTOR_EN_SB ||
|
||||
enemy.id == ACTOR_EN_NY || enemy.id == ACTOR_EN_CLEAR_TAG ||
|
||||
enemy.id == ACTOR_EN_WALLMAS || enemy.id == ACTOR_EN_TORCH2 ||
|
||||
enemy.id == ACTOR_EN_MB;
|
||||
bool enemiesToExcludeClearRooms =
|
||||
enemy.id == ACTOR_EN_FZ || enemy.id == ACTOR_EN_VM || enemy.id == ACTOR_EN_SB || enemy.id == ACTOR_EN_NY ||
|
||||
enemy.id == ACTOR_EN_CLEAR_TAG || enemy.id == ACTOR_EN_WALLMAS || enemy.id == ACTOR_EN_TORCH2 ||
|
||||
(enemy.id == ACTOR_EN_MB && enemy.params == 0) || enemy.id == ACTOR_EN_FD || enemy.id == ACTOR_EN_ANUBICE_TAG;
|
||||
|
||||
// Bari - Spawns 3 more enemies, potentially extremely difficult in timed rooms.
|
||||
bool enemiesToExcludeTimedRooms = enemiesToExcludeClearRooms || enemy.id == ACTOR_EN_VALI;
|
||||
|
@ -532,3 +564,53 @@ bool IsEnemyAllowedToSpawn(int16_t sceneNum, int8_t roomNum, EnemyEntry enemy) {
|
|||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
void FixClubMoblinScale(void* ptr) {
|
||||
Actor* actor = (Actor*)ptr;
|
||||
if (actor->params == -1) {
|
||||
Actor_SetScale(actor, 0.014f);
|
||||
}
|
||||
}
|
||||
|
||||
void RegisterEnemyRandomizer() {
|
||||
COND_ID_HOOK(OnActorInit, ACTOR_EN_MB, CVAR_ENEMY_RANDOMIZER_VALUE, FixClubMoblinScale);
|
||||
// prevent dark link from triggering a voidout
|
||||
COND_VB_SHOULD(VB_TRIGGER_VOIDOUT, CVAR_ENEMY_RANDOMIZER_VALUE != CVAR_ENEMY_RANDOMIZER_DEFAULT, {
|
||||
Actor* actor = va_arg(args, Actor*);
|
||||
|
||||
if (actor->category != ACTORCAT_PLAYER) {
|
||||
*should = false;
|
||||
Actor_Kill(actor);
|
||||
}
|
||||
});
|
||||
|
||||
// prevent dark link dealing fall damage to the player
|
||||
COND_VB_SHOULD(VB_RECIEVE_FALL_DAMAGE, CVAR_ENEMY_RANDOMIZER_VALUE != CVAR_ENEMY_RANDOMIZER_DEFAULT, {
|
||||
Actor* actor = va_arg(args, Actor*);
|
||||
|
||||
if (actor->category != ACTORCAT_PLAYER) {
|
||||
*should = false;
|
||||
}
|
||||
});
|
||||
|
||||
// prevent dark link from interfering with HESS/recoil/etc when at more than 100 away from him
|
||||
COND_VB_SHOULD(VB_TORCH2_HANDLE_CLANKING, CVAR_ENEMY_RANDOMIZER_VALUE != CVAR_ENEMY_RANDOMIZER_DEFAULT, {
|
||||
Actor* darkLink = va_arg(args, Actor*);
|
||||
|
||||
if (darkLink->xzDistToPlayer > 100.0f) {
|
||||
*should = false;
|
||||
}
|
||||
});
|
||||
|
||||
// prevent dark link from being grabbed by like likes and therefore grabbing the player
|
||||
COND_VB_SHOULD(VB_LIKE_LIKE_GRAB_PLAYER, CVAR_ENEMY_RANDOMIZER_VALUE != CVAR_ENEMY_RANDOMIZER_DEFAULT, {
|
||||
EnRr* likeLike = va_arg(args, EnRr*);
|
||||
|
||||
if (!(likeLike->collider1.base.oc != NULL && likeLike->collider1.base.oc->category == ACTORCAT_PLAYER) &&
|
||||
!(likeLike->collider2.base.oc != NULL && likeLike->collider2.base.oc->category == ACTORCAT_PLAYER)) {
|
||||
*should = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
static RegisterShipInitFunc initFunc(RegisterEnemyRandomizer, { CVAR_ENEMY_RANDOMIZER_NAME });
|
|
@ -1,23 +1,17 @@
|
|||
#pragma once
|
||||
|
||||
#include <libultraship/bridge.h>
|
||||
#include <libultraship/libultra/types.h>
|
||||
#include "item-tables/ItemTableTypes.h"
|
||||
|
||||
typedef struct EnemyEntry {
|
||||
int16_t id;
|
||||
int16_t params;
|
||||
} EnemyEntry;
|
||||
|
||||
#define RANDOMIZED_ENEMY_SPAWN_TABLE_SIZE 49
|
||||
|
||||
bool IsEnemyFoundToRandomize(int16_t sceneNum, int8_t roomNum, int16_t actorId, int16_t params, float posX);
|
||||
bool IsEnemyAllowedToSpawn(int16_t sceneNum, int8_t roomNum, EnemyEntry enemy);
|
||||
EnemyEntry GetRandomizedEnemyEntry(uint32_t seed);
|
||||
#define RANDOMIZED_ENEMY_SPAWN_TABLE_SIZE 52
|
||||
|
||||
extern const char* enemyCVarList[];
|
||||
extern const char* enemyNameList[];
|
||||
extern void GetSelectedEnemies();
|
||||
|
||||
#ifndef __cplusplus
|
||||
uint8_t GetRandomizedEnemy(PlayState* play, int16_t* actorId, f32* posX, f32* posY, f32* posZ, int16_t* rotX,
|
||||
struct PlayState;
|
||||
|
||||
uint8_t GetRandomizedEnemy(struct PlayState* play, int16_t* actorId, f32* posX, f32* posY, f32* posZ, int16_t* rotX,
|
||||
int16_t* rotY, int16_t* rotZ, int16_t* params);
|
||||
#endif
|
||||
|
|
|
@ -20,6 +20,14 @@ typedef enum {
|
|||
// - `int32_t` (entrance index) (promoted from `uint16_t` by va_arg)
|
||||
VB_ALLOW_ENTRANCE_CS_FOR_EITHER_AGE,
|
||||
|
||||
// #### `result`
|
||||
// ```c
|
||||
// sBgPoEventPuzzleState == 0xF
|
||||
// ```
|
||||
// #### `args`
|
||||
// - None
|
||||
VB_AMY_SOLVE,
|
||||
|
||||
// #### `result`
|
||||
// ```c
|
||||
// this->actor.textId == 0x401A
|
||||
|
@ -567,6 +575,14 @@ typedef enum {
|
|||
// - None
|
||||
VB_GANON_HEAL_BEFORE_FIGHT,
|
||||
|
||||
// #### `result`
|
||||
// ```c
|
||||
// true
|
||||
// ```
|
||||
// #### `args`
|
||||
// - `*EnGe2`
|
||||
VB_GERUDO_GUARD_SET_ACTION_AFTER_TALK,
|
||||
|
||||
// #### `result`
|
||||
// See logic in
|
||||
// ```c
|
||||
|
@ -1689,6 +1705,15 @@ typedef enum {
|
|||
// - `*ObjTsubo`
|
||||
VB_POT_SETUP_DRAW,
|
||||
|
||||
// #### `result`
|
||||
// ```c
|
||||
// dropId == ITEM00_STICK
|
||||
// ```
|
||||
// #### `args`
|
||||
// - None
|
||||
VB_PREVENT_ADULT_STICK,
|
||||
|
||||
// #### `result`
|
||||
// #### `result`
|
||||
// ```c
|
||||
// true
|
||||
|
@ -1975,6 +2000,15 @@ typedef enum {
|
|||
// - `*ShotSun`
|
||||
VB_SPAWN_SONG_FAIRY,
|
||||
|
||||
// #### `result`
|
||||
// ```c
|
||||
// varies, never set should to true
|
||||
// ```
|
||||
// #### `args`
|
||||
// - `*Actor`
|
||||
// - `*s16` - timer value
|
||||
VB_SWITCH_TIMER_TICK,
|
||||
|
||||
// #### `result`
|
||||
// ```c
|
||||
// (this->stateFlags1 & PLAYER_STATE1_CARRYING_ACTOR) && (this->heldActor != NULL) &&
|
||||
|
@ -2065,6 +2099,13 @@ typedef enum {
|
|||
VB_TRANSITION_TO_SAVE_SCREEN_ON_DEATH,
|
||||
|
||||
// #### `result`
|
||||
// ```c
|
||||
// true
|
||||
// ```
|
||||
// #### `args`
|
||||
// - `*EnWood02`
|
||||
VB_TREE_DROP_COLLECTIBLE,
|
||||
|
||||
// ```c
|
||||
// true
|
||||
// ```
|
||||
|
@ -2088,6 +2129,38 @@ typedef enum {
|
|||
// #### `args`
|
||||
// - `*EnWonderTalk2`
|
||||
VB_WONDER_TALK,
|
||||
|
||||
// #### `result`
|
||||
// ```c
|
||||
// true
|
||||
// ```
|
||||
// #### `args`
|
||||
// - `*Actor`
|
||||
VB_TRIGGER_VOIDOUT,
|
||||
|
||||
// #### `result`
|
||||
// ```c
|
||||
// true
|
||||
// ```
|
||||
// #### `args`
|
||||
// - `*Actor`
|
||||
VB_TORCH2_HANDLE_CLANKING,
|
||||
|
||||
// #### `result`
|
||||
// ```c
|
||||
// true
|
||||
// ```
|
||||
// #### `args`
|
||||
// - `*Actor`
|
||||
VB_RECIEVE_FALL_DAMAGE,
|
||||
|
||||
// #### `result`
|
||||
// ```c
|
||||
// true
|
||||
// ```
|
||||
// #### `args`
|
||||
// - `*EnRr`
|
||||
VB_LIKE_LIKE_GRAB_PLAYER,
|
||||
} GIVanillaBehavior;
|
||||
|
||||
#endif
|
||||
|
|
|
@ -98,6 +98,13 @@ void SwitchAge() {
|
|||
gSaveContext.nextTransitionType = TRANS_TYPE_FADE_BLACK_FAST;
|
||||
gPlayState->linkAgeOnLoad ^= 1;
|
||||
|
||||
// Discover adult/child spawns
|
||||
if (gPlayState->linkAgeOnLoad == LINK_AGE_ADULT) {
|
||||
Entrance_SetEntranceDiscovered(ENTR_HYRULE_FIELD_10, false);
|
||||
} else {
|
||||
Entrance_SetEntranceDiscovered(ENTR_LINKS_HOUSE_CHILD_SPAWN, false);
|
||||
}
|
||||
|
||||
static HOOK_ID hookId = 0;
|
||||
hookId = REGISTER_VB_SHOULD(VB_INFLICT_VOID_DAMAGE, {
|
||||
*should = false;
|
||||
|
|
|
@ -201,6 +201,13 @@ void ProcessExits(Region* region, GetAccessibleLocationsStruct& gals, Randomizer
|
|||
bool stopOnBeatable = false, bool addToPlaythrough = false) {
|
||||
auto ctx = Rando::Context::GetInstance();
|
||||
for (auto& exit : region->exits) {
|
||||
int16_t entranceIndex = exit.GetIndex();
|
||||
if (!logic->ACProcessUndiscoveredExits && logic->CalculatingAvailableChecks &&
|
||||
ctx->GetOption(RSK_SHUFFLE_ENTRANCES).Get() && exit.IsShuffled() && entranceIndex != -1 &&
|
||||
!Entrance_GetIsEntranceDiscovered(entranceIndex)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Region* exitRegion = exit.GetConnectedRegion();
|
||||
// Update Time of Day Access for the exit
|
||||
if (UpdateToDAccess(&exit, exitRegion)) {
|
||||
|
@ -412,18 +419,13 @@ bool AddCheckToLogic(LocationAccess& locPair, GetAccessibleLocationsStruct& gals
|
|||
Rando::ItemLocation* location = ctx->GetItemLocation(loc);
|
||||
RandomizerGet locItem = location->GetPlacedRandomizerGet();
|
||||
|
||||
if (!location->IsAddedToPool() && locPair.ConditionsMet(parentRegion, gals.calculatingAvailableChecks)) {
|
||||
if (gals.calculatingAvailableChecks) {
|
||||
gals.accessibleLocations.push_back(loc);
|
||||
StopPerformanceTimer(PT_LOCATION_LOGIC);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!location->IsAddedToPool() && locPair.ConditionsMet(parentRegion, logic->CalculatingAvailableChecks)) {
|
||||
location->AddToPool();
|
||||
|
||||
if (locItem == RG_NONE) {
|
||||
if (locItem == RG_NONE || logic->CalculatingAvailableChecks) {
|
||||
gals.accessibleLocations.push_back(loc); // Empty location, consider for placement
|
||||
} else {
|
||||
}
|
||||
if (locItem != RG_NONE) {
|
||||
// If ignore has a value, we want to check if the item location should be considered or not
|
||||
// This is necessary due to the below preprocessing for playthrough generation
|
||||
if (ignore != RG_NONE) {
|
||||
|
@ -519,11 +521,32 @@ void ProcessRegion(Region* region, GetAccessibleLocationsStruct& gals, Randomize
|
|||
// Return any of the targetLocations that are accessible in logic
|
||||
std::vector<RandomizerCheck> ReachabilitySearch(const std::vector<RandomizerCheck>& targetLocations,
|
||||
RandomizerGet ignore /* = RG_NONE*/,
|
||||
bool calculatingAvailableChecks /* = false */) {
|
||||
bool calculatingAvailableChecks /* = false */,
|
||||
RandomizerRegion startingRegion /* = RR_ROOT */) {
|
||||
auto ctx = Rando::Context::GetInstance();
|
||||
GetAccessibleLocationsStruct gals(0);
|
||||
gals.calculatingAvailableChecks = calculatingAvailableChecks;
|
||||
ResetLogic(ctx, gals, !calculatingAvailableChecks);
|
||||
if (startingRegion != RR_ROOT) {
|
||||
gals.regionPool.insert(gals.regionPool.begin(), startingRegion);
|
||||
|
||||
const auto& region = RegionTable(startingRegion);
|
||||
if (ctx->GetOption(RSK_SELECTED_STARTING_AGE).Is(RO_AGE_CHILD)) {
|
||||
region->childDay = true;
|
||||
} else {
|
||||
region->adultDay = true;
|
||||
}
|
||||
if (region->timePass) {
|
||||
if (ctx->GetOption(RSK_SELECTED_STARTING_AGE).Is(RO_AGE_CHILD)) {
|
||||
region->childNight = true;
|
||||
} else {
|
||||
region->adultNight = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (calculatingAvailableChecks) {
|
||||
logic->Reset(false);
|
||||
logic->CalculatingAvailableChecks = true;
|
||||
}
|
||||
do {
|
||||
gals.InitLoop();
|
||||
for (size_t i = 0; i < gals.regionPool.size(); i++) {
|
||||
|
@ -1015,7 +1038,8 @@ static void RandomizeOwnDungeon(const Rando::DungeonInfo* dungeon) {
|
|||
|
||||
// filter out locations that may be required to have songs placed at them
|
||||
dungeonLocations = FilterFromPool(dungeonLocations, [ctx](const auto loc) {
|
||||
if (ctx->GetOption(RSK_SHUFFLE_SONGS).Is(RO_SONG_SHUFFLE_SONG_LOCATIONS)) {
|
||||
if (ctx->GetOption(RSK_SHUFFLE_SONGS).Is(RO_SONG_SHUFFLE_SONG_LOCATIONS) ||
|
||||
ctx->GetOption(RSK_SHUFFLE_SONGS).Is(RO_SONG_SHUFFLE_OFF)) {
|
||||
return !(Rando::StaticData::GetLocation(loc)->GetRCType() == RCTYPE_SONG_LOCATION);
|
||||
}
|
||||
if (ctx->GetOption(RSK_SHUFFLE_SONGS).Is(RO_SONG_SHUFFLE_DUNGEON_REWARDS)) {
|
||||
|
@ -1344,8 +1368,8 @@ int Fill() {
|
|||
|
||||
StartPerformanceTimer(PT_LIMITED_CHECKS);
|
||||
// Then Place songs if song shuffle is set to specific locations
|
||||
if (ctx->GetOption(RSK_SHUFFLE_SONGS).IsNot(RO_SONG_SHUFFLE_ANYWHERE)) {
|
||||
|
||||
if (ctx->GetOption(RSK_SHUFFLE_SONGS).IsNot(RO_SONG_SHUFFLE_ANYWHERE) &&
|
||||
ctx->GetOption(RSK_SHUFFLE_SONGS).IsNot(RO_SONG_SHUFFLE_OFF)) {
|
||||
// Get each song
|
||||
std::vector<RandomizerGet> songs = FilterAndEraseFromPool(ItemPool, [](const auto i) {
|
||||
return Rando::StaticData::RetrieveItem(i).GetItemType() == ITEMTYPE_SONG;
|
||||
|
|
|
@ -58,6 +58,7 @@ struct GetAccessibleLocationsStruct {
|
|||
void ClearProgress();
|
||||
void VanillaFill();
|
||||
int Fill();
|
||||
void SetAreas();
|
||||
|
||||
std::vector<RandomizerCheck> GetEmptyLocations(std::vector<RandomizerCheck> allowedLocations);
|
||||
|
||||
|
@ -65,8 +66,8 @@ void ProcessRegion(Region* region, GetAccessibleLocationsStruct& gals, Randomize
|
|||
bool stopOnBeatable = false, bool addToPlaythrough = false);
|
||||
|
||||
std::vector<RandomizerCheck> ReachabilitySearch(const std::vector<RandomizerCheck>& allowedLocations,
|
||||
RandomizerGet ignore = RG_NONE,
|
||||
bool calculatingAvailableChecks = false);
|
||||
RandomizerGet ignore = RG_NONE, bool calculatingAvailableChecks = false,
|
||||
RandomizerRegion startingRegion = RR_ROOT);
|
||||
|
||||
void GeneratePlaythrough();
|
||||
|
||||
|
|
|
@ -1133,11 +1133,26 @@ void GenerateItemPool() {
|
|||
}
|
||||
}
|
||||
|
||||
// add extra songs only if song shuffle is anywhere
|
||||
AddItemsToPool(ItemPool, songList);
|
||||
if (ctx->GetOption(RSK_SHUFFLE_SONGS).Is(RO_SONG_SHUFFLE_ANYWHERE) &&
|
||||
ctx->GetOption(RSK_ITEM_POOL).Is(RO_ITEM_POOL_PLENTIFUL)) {
|
||||
AddItemsToPool(PendingJunkPool, songList);
|
||||
if (ctx->GetOption(RSK_SHUFFLE_SONGS).IsNot(RO_SONG_SHUFFLE_OFF)) {
|
||||
AddItemsToPool(ItemPool, songList);
|
||||
// add extra songs only if song shuffle is anywhere
|
||||
if (ctx->GetOption(RSK_SHUFFLE_SONGS).Is(RO_SONG_SHUFFLE_ANYWHERE) &&
|
||||
ctx->GetOption(RSK_ITEM_POOL).Is(RO_ITEM_POOL_PLENTIFUL)) {
|
||||
AddItemsToPool(PendingJunkPool, songList);
|
||||
}
|
||||
} else {
|
||||
ctx->PlaceItemInLocation(RC_SHEIK_IN_FOREST, RG_MINUET_OF_FOREST, false, true);
|
||||
ctx->PlaceItemInLocation(RC_SHEIK_IN_CRATER, RG_BOLERO_OF_FIRE, false, true);
|
||||
ctx->PlaceItemInLocation(RC_SHEIK_IN_ICE_CAVERN, RG_SERENADE_OF_WATER, false, true);
|
||||
ctx->PlaceItemInLocation(RC_SHEIK_AT_COLOSSUS, RG_REQUIEM_OF_SPIRIT, false, true);
|
||||
ctx->PlaceItemInLocation(RC_SHEIK_IN_KAKARIKO, RG_NOCTURNE_OF_SHADOW, false, true);
|
||||
ctx->PlaceItemInLocation(RC_SHEIK_AT_TEMPLE, RG_PRELUDE_OF_LIGHT, false, true);
|
||||
ctx->PlaceItemInLocation(RC_SONG_FROM_IMPA, RG_ZELDAS_LULLABY, false, true);
|
||||
ctx->PlaceItemInLocation(RC_SONG_FROM_MALON, RG_EPONAS_SONG, false, true);
|
||||
ctx->PlaceItemInLocation(RC_SONG_FROM_SARIA, RG_SARIAS_SONG, false, true);
|
||||
ctx->PlaceItemInLocation(RC_SONG_FROM_ROYAL_FAMILYS_TOMB, RG_SUNS_SONG, false, true);
|
||||
ctx->PlaceItemInLocation(RC_SONG_FROM_OCARINA_OF_TIME, RG_SONG_OF_TIME, false, true);
|
||||
ctx->PlaceItemInLocation(RC_SONG_FROM_WINDMILL, RG_SONG_OF_STORMS, false, true);
|
||||
}
|
||||
|
||||
/*For item pool generation, dungeon items are either placed in their vanilla
|
||||
|
|
|
@ -9,8 +9,6 @@ extern PlayState* gPlayState;
|
|||
#include "src/overlays/actors/ovl_En_Door/z_en_door.h"
|
||||
}
|
||||
|
||||
#define RAND_GET_OPTION(option) Rando::Context::GetInstance()->GetOption(option).Get()
|
||||
|
||||
using SceneDoorParamsPair = std::pair<int, int>;
|
||||
std::map<SceneDoorParamsPair, RandomizerInf> lookupTable = {
|
||||
// clang-format off
|
||||
|
|
|
@ -11,8 +11,7 @@ extern void EnItem00_DrawRandomizedItem(EnItem00* enItem00, PlayState* play);
|
|||
void ObjComb_RandomizerChooseItemDrop(ObjComb* objComb, PlayState* play) {
|
||||
s16 params = objComb->actor.params & 0x1F;
|
||||
|
||||
if (Rando::Context::GetInstance()->GetOption(RSK_SHUFFLE_BEEHIVES).Get() &&
|
||||
!Flags_GetRandomizerInf(objComb->beehiveIdentity.randomizerInf)) {
|
||||
if (RAND_GET_OPTION(RSK_SHUFFLE_BEEHIVES) && !Flags_GetRandomizerInf(objComb->beehiveIdentity.randomizerInf)) {
|
||||
EnItem00* item00 = (EnItem00*)Item_DropCollectible2(play, &objComb->actor.world.pos, ITEM00_SOH_DUMMY);
|
||||
item00->randoInf = objComb->beehiveIdentity.randomizerInf;
|
||||
item00->itemEntry =
|
||||
|
@ -41,8 +40,7 @@ void ObjComb_RandomizerWait(ObjComb* objComb, PlayState* play) {
|
|||
s32 dmgFlags;
|
||||
|
||||
objComb->unk_1B0 -= 50;
|
||||
if (Rando::Context::GetInstance()->GetOption(RSK_SHUFFLE_BEEHIVES).Get() &&
|
||||
!Flags_GetRandomizerInf(objComb->beehiveIdentity.randomizerInf)) {
|
||||
if (RAND_GET_OPTION(RSK_SHUFFLE_BEEHIVES) && !Flags_GetRandomizerInf(objComb->beehiveIdentity.randomizerInf)) {
|
||||
if (objComb->unk_1B0 <= -5000) {
|
||||
objComb->unk_1B0 = 1500;
|
||||
}
|
||||
|
@ -85,7 +83,7 @@ void ObjComb_RandomizerUpdate(void* actor) {
|
|||
}
|
||||
|
||||
void RegisterShuffleBeehives() {
|
||||
bool shouldRegister = IS_RANDO && Rando::Context::GetInstance()->GetOption(RSK_SHUFFLE_BEEHIVES).Get();
|
||||
bool shouldRegister = IS_RANDO && RAND_GET_OPTION(RSK_SHUFFLE_BEEHIVES);
|
||||
|
||||
COND_ID_HOOK(OnActorInit, ACTOR_OBJ_COMB, shouldRegister, ObjComb_RandomizerInit);
|
||||
COND_ID_HOOK(OnActorUpdate, ACTOR_OBJ_COMB, shouldRegister, ObjComb_RandomizerUpdate);
|
||||
|
|
|
@ -34,7 +34,7 @@ void EnCow_MoveForRandomizer(EnCow* enCow, PlayState* play) {
|
|||
}
|
||||
|
||||
void RegisterShuffleCows() {
|
||||
bool shouldRegister = IS_RANDO && Rando::Context::GetInstance()->GetOption(RSK_SHUFFLE_COWS).Get();
|
||||
bool shouldRegister = IS_RANDO && RAND_GET_OPTION(RSK_SHUFFLE_COWS);
|
||||
|
||||
COND_VB_SHOULD(VB_GIVE_ITEM_FROM_COW, shouldRegister, {
|
||||
EnCow* enCow = va_arg(args, EnCow*);
|
||||
|
|
|
@ -15,8 +15,6 @@ extern "C" {
|
|||
extern PlayState* gPlayState;
|
||||
}
|
||||
|
||||
#define RAND_GET_OPTION(option) Rando::Context::GetInstance()->GetOption(option).Get()
|
||||
|
||||
extern void EnItem00_DrawRandomizedItem(EnItem00* enItem00, PlayState* play);
|
||||
|
||||
extern "C" void ObjKibako2_RandomizerDraw(Actor* thisx, PlayState* play) {
|
||||
|
@ -158,7 +156,7 @@ extern "C" void ObjKibako_RandomizerDraw(Actor* thisx, PlayState* play) {
|
|||
uint8_t ObjKibako2_RandomizerHoldsItem(ObjKibako2* crateActor, PlayState* play) {
|
||||
RandomizerCheck rc = crateActor->crateIdentity.randomizerCheck;
|
||||
uint8_t isDungeon = Rando::StaticData::GetLocation(rc)->IsDungeon();
|
||||
uint8_t crateSetting = Rando::Context::GetInstance()->GetOption(RSK_SHUFFLE_CRATES).Get();
|
||||
uint8_t crateSetting = RAND_GET_OPTION(RSK_SHUFFLE_CRATES);
|
||||
|
||||
// Don't pull randomized item if crate isn't randomized or is already checked
|
||||
if (!IS_RANDO || (crateSetting == RO_SHUFFLE_CRATES_OVERWORLD && isDungeon) ||
|
||||
|
@ -174,7 +172,7 @@ uint8_t ObjKibako2_RandomizerHoldsItem(ObjKibako2* crateActor, PlayState* play)
|
|||
uint8_t ObjKibako_RandomizerHoldsItem(ObjKibako* smallCrateActor, PlayState* play) {
|
||||
RandomizerCheck rc = smallCrateActor->smallCrateIdentity.randomizerCheck;
|
||||
uint8_t isDungeon = Rando::StaticData::GetLocation(rc)->IsDungeon();
|
||||
uint8_t crateSetting = Rando::Context::GetInstance()->GetOption(RSK_SHUFFLE_CRATES).Get();
|
||||
uint8_t crateSetting = RAND_GET_OPTION(RSK_SHUFFLE_CRATES);
|
||||
|
||||
// Don't pull randomized item if crate isn't randomized or is already checked
|
||||
if (!IS_RANDO || (crateSetting == RO_SHUFFLE_CRATES_OVERWORLD && isDungeon) ||
|
||||
|
@ -211,7 +209,7 @@ void ObjKibako_RandomizerSpawnCollectible(ObjKibako* smallCrateActor, PlayState*
|
|||
|
||||
void ObjKibako2_RandomizerInit(void* actorRef) {
|
||||
Actor* actor = static_cast<Actor*>(actorRef);
|
||||
uint8_t logicSetting = Rando::Context::GetInstance()->GetOption(RSK_LOGIC_RULES).Get();
|
||||
uint8_t logicSetting = RAND_GET_OPTION(RSK_LOGIC_RULES);
|
||||
|
||||
// don't shuffle two OOB crates in GF and don't shuffle child GV/GF crates when not in no logic
|
||||
if (actor->id != ACTOR_OBJ_KIBAKO2 ||
|
||||
|
@ -362,7 +360,7 @@ void Rando::StaticData::RegisterCrateLocations() {
|
|||
locationTable[RC_KAK_NEAR_IMPAS_HOUSE_ADULT_CRATE_1] = Location::Crate(RC_KAK_NEAR_IMPAS_HOUSE_ADULT_CRATE_1, RCQUEST_BOTH, RCAREA_KAKARIKO_VILLAGE, SCENE_KAKARIKO_VILLAGE, TWO_ACTOR_PARAMS(-389, 1518), "Near Impas House Adult Crate 1", RHT_CRATE_KAKARIKO_VILLAGE, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_KAK_NEAR_IMPAS_HOUSE_ADULT_CRATE_1));
|
||||
locationTable[RC_KAK_NEAR_IMPAS_HOUSE_ADULT_CRATE_2] = Location::Crate(RC_KAK_NEAR_IMPAS_HOUSE_ADULT_CRATE_2, RCQUEST_BOTH, RCAREA_KAKARIKO_VILLAGE, SCENE_KAKARIKO_VILLAGE, TWO_ACTOR_PARAMS(-389, 1470), "Near Impas House Adult Crate 2", RHT_CRATE_KAKARIKO_VILLAGE, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_KAK_NEAR_IMPAS_HOUSE_ADULT_CRATE_2));
|
||||
locationTable[RC_KAK_NEAR_BAZAAR_ADULT_CRATE_1] = Location::Crate(RC_KAK_NEAR_BAZAAR_ADULT_CRATE_1, RCQUEST_BOTH, RCAREA_KAKARIKO_VILLAGE, SCENE_KAKARIKO_VILLAGE, TWO_ACTOR_PARAMS(-433, -401), "Near Bazaar Adult Crate 1", RHT_CRATE_KAKARIKO_VILLAGE, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_KAK_NEAR_BAZAAR_ADULT_CRATE_1));
|
||||
locationTable[RC_KAK_NEAR_BAZAAR_ADULT_CRATE_2] = Location::Crate(RC_KAK_NEAR_BAZAAR_ADULT_CRATE_2, RCQUEST_BOTH, RCAREA_KAKARIKO_VILLAGE, SCENE_KAKARIKO_VILLAGE, TWO_ACTOR_PARAMS(-489, -424), "Near Bazaar Adult Crate 2`", RHT_CRATE_KAKARIKO_VILLAGE, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_KAK_NEAR_BAZAAR_ADULT_CRATE_2));
|
||||
locationTable[RC_KAK_NEAR_BAZAAR_ADULT_CRATE_2] = Location::Crate(RC_KAK_NEAR_BAZAAR_ADULT_CRATE_2, RCQUEST_BOTH, RCAREA_KAKARIKO_VILLAGE, SCENE_KAKARIKO_VILLAGE, TWO_ACTOR_PARAMS(-489, -424), "Near Bazaar Adult Crate 2", RHT_CRATE_KAKARIKO_VILLAGE, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_KAK_NEAR_BAZAAR_ADULT_CRATE_2));
|
||||
locationTable[RC_KAK_BEHIND_GS_HOUSE_ADULT_CRATE] = Location::Crate(RC_KAK_BEHIND_GS_HOUSE_ADULT_CRATE, RCQUEST_BOTH, RCAREA_KAKARIKO_VILLAGE, SCENE_KAKARIKO_VILLAGE, TWO_ACTOR_PARAMS(-724, 871), "Behind GS House Adult Crate", RHT_CRATE_KAKARIKO_VILLAGE, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_KAK_BEHIND_GS_HOUSE_ADULT_CRATE));
|
||||
locationTable[RC_KAK_NEAR_GY_CHILD_CRATE] = Location::Crate(RC_KAK_NEAR_GY_CHILD_CRATE, RCQUEST_BOTH, RCAREA_KAKARIKO_VILLAGE, SCENE_KAKARIKO_VILLAGE, TWO_ACTOR_PARAMS(1732, 1366), "Near Graveyard Child Crate", RHT_CRATE_KAKARIKO_VILLAGE, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_KAK_NEAR_GY_CHILD_CRATE));
|
||||
locationTable[RC_KAK_NEAR_WINDMILL_CHILD_CRATE] = Location::Crate(RC_KAK_NEAR_WINDMILL_CHILD_CRATE, RCQUEST_BOTH, RCAREA_KAKARIKO_VILLAGE, SCENE_KAKARIKO_VILLAGE, TWO_ACTOR_PARAMS(1170, 601), "Near Windmill Child Crate", RHT_CRATE_KAKARIKO_VILLAGE, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_KAK_NEAR_WINDMILL_CHILD_CRATE));
|
||||
|
|
|
@ -23,7 +23,7 @@ void ShuffleFreestanding_OnVanillaBehaviorHandler(GIVanillaBehavior id, bool* sh
|
|||
Rando::Location* loc =
|
||||
OTRGlobals::Instance->gRandomizer->GetCheckObjectFromActor(item00->actor.id, gPlayState->sceneNum, params);
|
||||
uint8_t isDungeon = loc->IsDungeon();
|
||||
uint8_t freestandingSetting = Rando::Context::GetInstance()->GetOption(RSK_SHUFFLE_FREESTANDING).Get();
|
||||
uint8_t freestandingSetting = RAND_GET_OPTION(RSK_SHUFFLE_FREESTANDING);
|
||||
RandomizerCheck randomizerCheck = loc->GetRandomizerCheck();
|
||||
bool checkObtained = Rando::Context::GetInstance()->GetItemLocation(randomizerCheck)->HasObtained();
|
||||
|
||||
|
|
|
@ -11,8 +11,6 @@ extern "C" {
|
|||
extern PlayState* gPlayState;
|
||||
}
|
||||
|
||||
#define RAND_GET_OPTION(option) Rando::Context::GetInstance()->GetOption(option).Get()
|
||||
|
||||
extern void EnItem00_DrawRandomizedItem(EnItem00* enItem00, PlayState* play);
|
||||
|
||||
void DrawTypeOfGrass(EnKusa* grassActor, Gfx* bushDList, Gfx* grassDList, PlayState* play) {
|
||||
|
@ -96,7 +94,7 @@ uint8_t EnKusa_RandomizerHoldsItem(EnKusa* grassActor, PlayState* play) {
|
|||
RandomizerCheck rc = grassActor->grassIdentity.randomizerCheck;
|
||||
|
||||
uint8_t isDungeon = Rando::StaticData::GetLocation(rc)->IsDungeon();
|
||||
uint8_t grassSetting = Rando::Context::GetInstance()->GetOption(RSK_SHUFFLE_GRASS).Get();
|
||||
uint8_t grassSetting = RAND_GET_OPTION(RSK_SHUFFLE_GRASS);
|
||||
|
||||
// Don't pull randomized item if grass isn't randomized or is already checked
|
||||
if (!IS_RANDO || (grassSetting == RO_SHUFFLE_GRASS_OVERWORLD && isDungeon) ||
|
||||
|
|
|
@ -27,7 +27,7 @@ extern "C" void ObjTsubo_RandomizerDraw(Actor* thisx, PlayState* play) {
|
|||
uint8_t ObjTsubo_RandomizerHoldsItem(ObjTsubo* potActor, PlayState* play) {
|
||||
RandomizerCheck rc = potActor->potIdentity.randomizerCheck;
|
||||
uint8_t isDungeon = Rando::StaticData::GetLocation(rc)->IsDungeon();
|
||||
uint8_t potSetting = Rando::Context::GetInstance()->GetOption(RSK_SHUFFLE_POTS).Get();
|
||||
uint8_t potSetting = RAND_GET_OPTION(RSK_SHUFFLE_POTS);
|
||||
|
||||
// Don't pull randomized item if pot isn't randomized or is already checked
|
||||
if (!IS_RANDO || (potSetting == RO_SHUFFLE_POTS_OVERWORLD && isDungeon) ||
|
||||
|
@ -85,7 +85,7 @@ void ShufflePots_OnVanillaBehaviorHandler(GIVanillaBehavior id, bool* should, va
|
|||
// Unlock early Ganon's Boss Key doors to allow access to the pots there when pots are shuffled in dungeon
|
||||
if (id == VB_LOCK_BOSS_DOOR) {
|
||||
DoorShutter* doorActor = va_arg(args, DoorShutter*);
|
||||
uint8_t shufflePotSetting = Rando::Context::GetInstance()->GetOption(RSK_SHUFFLE_POTS).Get();
|
||||
uint8_t shufflePotSetting = RAND_GET_OPTION(RSK_SHUFFLE_POTS);
|
||||
if (gPlayState->sceneNum == SCENE_GANONS_TOWER && doorActor->dyna.actor.world.pos.y == 800 &&
|
||||
(shufflePotSetting == RO_SHUFFLE_POTS_DUNGEONS || shufflePotSetting == RO_SHUFFLE_POTS_ALL)) {
|
||||
*should = false;
|
||||
|
|
26
soh/soh/Enhancements/randomizer/ShuffleSongs.cpp
Normal file
26
soh/soh/Enhancements/randomizer/ShuffleSongs.cpp
Normal file
|
@ -0,0 +1,26 @@
|
|||
#include "soh/ShipInit.hpp"
|
||||
#include "location.h"
|
||||
#include "static_data.h"
|
||||
|
||||
void Rando::StaticData::RegisterSongLocations() {
|
||||
static bool registered = false;
|
||||
if (registered)
|
||||
return;
|
||||
registered = true;
|
||||
// clang-format off
|
||||
locationTable[RC_SHEIK_IN_FOREST] = Location::Base(RC_SHEIK_IN_FOREST, RCQUEST_BOTH, RCTYPE_SONG_LOCATION, ACTOR_ID_MAX, SCENE_SACRED_FOREST_MEADOW, 0x00, "Sheik in Forest", "Sheik in Forest", RHT_SHEIK_IN_FOREST, RG_MINUET_OF_FOREST, SpoilerCollectionCheck::EventChkInf(EVENTCHKINF_LEARNED_MINUET_OF_FOREST), true);
|
||||
locationTable[RC_SHEIK_IN_CRATER] = Location::Base(RC_SHEIK_IN_CRATER, RCQUEST_BOTH, RCTYPE_SONG_LOCATION, ACTOR_ID_MAX, SCENE_DEATH_MOUNTAIN_CRATER, 0x00, "Sheik in Crater", "Sheik in Crater", RHT_SHEIK_IN_CRATER, RG_BOLERO_OF_FIRE, SpoilerCollectionCheck::EventChkInf(EVENTCHKINF_LEARNED_BOLERO_OF_FIRE), true);
|
||||
locationTable[RC_SHEIK_IN_ICE_CAVERN] = Location::Base(RC_SHEIK_IN_ICE_CAVERN, RCQUEST_BOTH, RCTYPE_SONG_LOCATION, ACTOR_ID_MAX, SCENE_ICE_CAVERN, 0x00, "Sheik in Ice Cavern", "Sheik in Ice Cavern", RHT_SHEIK_IN_ICE_CAVERN, RG_SERENADE_OF_WATER, SpoilerCollectionCheck::EventChkInf(EVENTCHKINF_LEARNED_SERENADE_OF_WATER), true);
|
||||
locationTable[RC_SHEIK_AT_COLOSSUS] = Location::Base(RC_SHEIK_AT_COLOSSUS, RCQUEST_BOTH, RCTYPE_SONG_LOCATION, ACTOR_ID_MAX, SCENE_DESERT_COLOSSUS, 0x00, "Sheik at Colossus", "Sheik at Colossus", RHT_SHEIK_AT_COLOSSUS, RG_REQUIEM_OF_SPIRIT, SpoilerCollectionCheck::EventChkInf(EVENTCHKINF_LEARNED_REQUIEM_OF_SPIRIT), true);
|
||||
locationTable[RC_SHEIK_IN_KAKARIKO] = Location::Base(RC_SHEIK_IN_KAKARIKO, RCQUEST_BOTH, RCTYPE_SONG_LOCATION, ACTOR_ID_MAX, SCENE_KAKARIKO_VILLAGE, 0x00, "Sheik in Kakariko", "Sheik in Kakariko", RHT_SHEIK_IN_KAKARIKO, RG_NOCTURNE_OF_SHADOW, SpoilerCollectionCheck::EventChkInf(EVENTCHKINF_BONGO_BONGO_ESCAPED_FROM_WELL), true);
|
||||
locationTable[RC_SHEIK_AT_TEMPLE] = Location::Base(RC_SHEIK_AT_TEMPLE, RCQUEST_BOTH, RCTYPE_SONG_LOCATION, ACTOR_ID_MAX, SCENE_TEMPLE_OF_TIME, 0x00, "Sheik at Temple", "Sheik at Temple", RHT_SHEIK_AT_TEMPLE, RG_PRELUDE_OF_LIGHT, SpoilerCollectionCheck::EventChkInf(EVENTCHKINF_LEARNED_PRELUDE_OF_LIGHT), true);
|
||||
locationTable[RC_SONG_FROM_IMPA] = Location::Base(RC_SONG_FROM_IMPA, RCQUEST_BOTH, RCTYPE_SONG_LOCATION, RCAREA_HYRULE_CASTLE, ACTOR_ID_MAX, SCENE_ID_MAX, 0x00, "Song from Impa", "Song from Impa", RHT_SONG_FROM_IMPA, RG_ZELDAS_LULLABY, SpoilerCollectionCheck::EventChkInf(EVENTCHKINF_LEARNED_ZELDAS_LULLABY), true);
|
||||
locationTable[RC_SONG_FROM_MALON] = Location::Base(RC_SONG_FROM_MALON, RCQUEST_BOTH, RCTYPE_SONG_LOCATION, ACTOR_ID_MAX, SCENE_LON_LON_RANCH, 0x00, "Song from Malon", "Song from Malon", RHT_SONG_FROM_MALON, RG_EPONAS_SONG, SpoilerCollectionCheck::RandomizerInf(RAND_INF_LEARNED_EPONA_SONG), true);
|
||||
locationTable[RC_SONG_FROM_SARIA] = Location::Base(RC_SONG_FROM_SARIA, RCQUEST_BOTH, RCTYPE_SONG_LOCATION, ACTOR_ID_MAX, SCENE_SACRED_FOREST_MEADOW, 0x00, "Song from Saria", "Song from Saria", RHT_SONG_FROM_SARIA, RG_SARIAS_SONG, SpoilerCollectionCheck::EventChkInf(EVENTCHKINF_LEARNED_SARIAS_SONG), true);
|
||||
locationTable[RC_SONG_FROM_ROYAL_FAMILYS_TOMB] = Location::Base(RC_SONG_FROM_ROYAL_FAMILYS_TOMB, RCQUEST_BOTH, RCTYPE_SONG_LOCATION, ACTOR_ID_MAX, SCENE_ROYAL_FAMILYS_TOMB, 0x00, "Song from Royal Family's Tomb", "Song from Royal Family's Tomb", RHT_SONG_FROM_ROYAL_FAMILYS_TOMB, RG_SUNS_SONG, SpoilerCollectionCheck::EventChkInf(EVENTCHKINF_LEARNED_SUNS_SONG), true);
|
||||
locationTable[RC_SONG_FROM_OCARINA_OF_TIME] = Location::Base(RC_SONG_FROM_OCARINA_OF_TIME, RCQUEST_BOTH, RCTYPE_SONG_LOCATION, ACTOR_ID_MAX, SCENE_HYRULE_FIELD, 0x00, "Song from Ocarina of Time", "Song from Ocarina of Time", RHT_SONG_FROM_OCARINA_OF_TIME, RG_SONG_OF_TIME, SpoilerCollectionCheck::EventChkInf(EVENTCHKINF_LEARNED_SONG_OF_TIME), true);
|
||||
locationTable[RC_SONG_FROM_WINDMILL] = Location::Base(RC_SONG_FROM_WINDMILL, RCQUEST_BOTH, RCTYPE_SONG_LOCATION, RCAREA_KAKARIKO_VILLAGE, ACTOR_ID_MAX, SCENE_WINDMILL_AND_DAMPES_GRAVE, 0x00, "Song from Windmill", "Song from Windmill", RHT_SONG_FROM_WINDMILL, RG_SONG_OF_STORMS, SpoilerCollectionCheck::EventChkInf(EVENTCHKINF_LEARNED_SONG_OF_STORMS), true);
|
||||
// clang-format-on
|
||||
}
|
||||
|
||||
static RegisterShipInitFunc initSongLocations(Rando::StaticData::RegisterSongLocations);
|
|
@ -11,6 +11,7 @@
|
|||
#include "fishsanity.h"
|
||||
#include "macros.h"
|
||||
#include "3drando/hints.hpp"
|
||||
#include "soh/util.h"
|
||||
#include "../kaleido.h"
|
||||
|
||||
#include <fstream>
|
||||
|
@ -179,6 +180,7 @@ void Context::GenerateLocationPool() {
|
|||
location.GetRandomizerCheck() == RC_LW_DEKU_SCRUB_NEAR_BRIDGE ||
|
||||
location.GetRandomizerCheck() == RC_HF_DEKU_SCRUB_GROTTO)) ||
|
||||
(location.GetRCType() == RCTYPE_ADULT_TRADE && mOptions[RSK_SHUFFLE_ADULT_TRADE].Is(RO_GENERIC_OFF)) ||
|
||||
(location.GetRCType() == RCTYPE_SONG_LOCATION && mOptions[RSK_SHUFFLE_SONGS].Is(RO_SONG_SHUFFLE_OFF)) ||
|
||||
(location.GetRCType() == RCTYPE_COW && mOptions[RSK_SHUFFLE_COWS].Is(RO_GENERIC_OFF)) ||
|
||||
(location.GetRandomizerCheck() == RC_LH_HYRULE_LOACH &&
|
||||
mOptions[RSK_FISHSANITY].IsNot(RO_FISHSANITY_HYRULE_LOACH)) ||
|
||||
|
@ -369,25 +371,8 @@ GetItemEntry Context::GetFinalGIEntry(const RandomizerCheck rc, const bool check
|
|||
return giEntry;
|
||||
}
|
||||
|
||||
std::string sanitize(std::string stringValue) {
|
||||
// Add backslashes.
|
||||
for (auto i = stringValue.begin();;) {
|
||||
auto const pos =
|
||||
std::find_if(i, stringValue.end(), [](char const c) { return '\\' == c || '\'' == c || '"' == c; });
|
||||
if (pos == stringValue.end()) {
|
||||
break;
|
||||
}
|
||||
i = std::next(stringValue.insert(pos, '\\'), 2);
|
||||
}
|
||||
|
||||
// Removes others.
|
||||
std::erase_if(stringValue, [](char const c) { return '\n' == c || '\r' == c || '\0' == c || '\x1A' == c; });
|
||||
|
||||
return stringValue;
|
||||
}
|
||||
|
||||
void Context::ParseSpoiler(const char* spoilerFileName) {
|
||||
std::ifstream spoilerFileStream(sanitize(spoilerFileName));
|
||||
std::ifstream spoilerFileStream(SohUtils::Sanitize(spoilerFileName));
|
||||
if (!spoilerFileStream) {
|
||||
return;
|
||||
}
|
||||
|
@ -396,11 +381,13 @@ void Context::ParseSpoiler(const char* spoilerFileName) {
|
|||
try {
|
||||
nlohmann::json spoilerFileJson;
|
||||
spoilerFileStream >> spoilerFileJson;
|
||||
spoilerFileStream.close();
|
||||
ParseHashIconIndexesJson(spoilerFileJson);
|
||||
Rando::Settings::GetInstance()->ParseJson(spoilerFileJson);
|
||||
ParseItemLocationsJson(spoilerFileJson);
|
||||
ParseHintJson(spoilerFileJson);
|
||||
ParseTricksJson(spoilerFileJson);
|
||||
mEntranceShuffler->ParseJson(spoilerFileJson);
|
||||
ParseHintJson(spoilerFileJson);
|
||||
mDungeons->ParseJson(spoilerFileJson);
|
||||
mTrials->ParseJson(spoilerFileJson);
|
||||
mSpoilerLoaded = true;
|
||||
|
@ -468,6 +455,17 @@ void Context::ParseHintJson(nlohmann::json spoilerFileJson) {
|
|||
CreateStaticHints();
|
||||
}
|
||||
|
||||
void Context::ParseTricksJson(nlohmann::json spoilerFileJson) {
|
||||
nlohmann::json enabledTricksJson = spoilerFileJson["enabledTricks"];
|
||||
const auto& settings = Rando::Settings::GetInstance();
|
||||
for (auto it : enabledTricksJson) {
|
||||
int rt = settings->GetRandomizerTrickByName(it);
|
||||
if (rt != -1) {
|
||||
mTrickOptions[rt].Set(RO_GENERIC_ON);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::shared_ptr<EntranceShuffler> Context::GetEntranceShuffler() {
|
||||
return mEntranceShuffler;
|
||||
}
|
||||
|
@ -523,6 +521,10 @@ RandoOptionLACSCondition Context::LACSCondition() const {
|
|||
return mLACSCondition;
|
||||
}
|
||||
|
||||
void Context::LACSCondition(RandoOptionLACSCondition lacsCondition) {
|
||||
mLACSCondition = lacsCondition;
|
||||
}
|
||||
|
||||
std::shared_ptr<Kaleido> Context::GetKaleido() {
|
||||
if (mKaleido == nullptr) {
|
||||
mKaleido = std::make_shared<Kaleido>();
|
||||
|
|
|
@ -14,6 +14,8 @@
|
|||
#include <map>
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
#define RAND_GET_OPTION(option) Rando::Context::GetInstance()->GetOption(option).Get()
|
||||
|
||||
/**
|
||||
* @brief Singleton for storing and accessing dynamic Randomizer-related data
|
||||
*
|
||||
|
@ -104,12 +106,22 @@ class Context {
|
|||
* @return RandoOptionLACSCondition
|
||||
*/
|
||||
RandoOptionLACSCondition LACSCondition() const;
|
||||
|
||||
/**
|
||||
* @brief Sets the resolved Light Arrow CutScene check condition.
|
||||
* There is no direct option for this, it is inferred based on the value of a few other options.
|
||||
*
|
||||
* @param lacsCondition
|
||||
*/
|
||||
void LACSCondition(RandoOptionLACSCondition lacsCondition);
|
||||
|
||||
GetItemEntry GetFinalGIEntry(RandomizerCheck rc, bool checkObtainability = true, GetItemID ogItemId = GI_NONE);
|
||||
void ParseSpoiler(const char* spoilerFileName);
|
||||
void ParseHashIconIndexesJson(nlohmann::json spoilerFileJson);
|
||||
void ParseItemLocationsJson(nlohmann::json spoilerFileJson);
|
||||
void WriteHintJson(nlohmann::ordered_json& spoilerFileJson);
|
||||
void ParseHintJson(nlohmann::json spoilerFileJson);
|
||||
void ParseTricksJson(nlohmann::json spoilerFileJson);
|
||||
std::map<RandomizerCheck, ItemOverride> overrides = {};
|
||||
std::vector<std::vector<RandomizerCheck>> playthroughLocations = {};
|
||||
std::vector<RandomizerCheck> everyPossibleLocation = {};
|
||||
|
|
|
@ -250,7 +250,319 @@ std::string EntranceNameByRegions(RandomizerRegion parentRegion, RandomizerRegio
|
|||
return RegionTable(parentRegion)->regionName + " -> " + RegionTable(connectedRegion)->regionName;
|
||||
}
|
||||
|
||||
void SetAllEntrancesData(std::vector<EntranceInfoPair>& entranceShuffleTable) {
|
||||
std::unordered_map<int16_t, Entrance*> entranceMap;
|
||||
|
||||
void SetAllEntrancesData() {
|
||||
std::vector<EntranceInfoPair> entranceShuffleTable = {
|
||||
// clang-format off
|
||||
// Type Parent Region Connected Region Index
|
||||
{ { EntranceType::Dungeon, RR_KF_OUTSIDE_DEKU_TREE, RR_DEKU_TREE_ENTRYWAY, ENTR_DEKU_TREE_ENTRANCE },
|
||||
{ EntranceType::Dungeon, RR_DEKU_TREE_ENTRYWAY, RR_KF_OUTSIDE_DEKU_TREE, ENTR_KOKIRI_FOREST_OUTSIDE_DEKU_TREE } },
|
||||
{ { EntranceType::Dungeon, RR_DEATH_MOUNTAIN_TRAIL, RR_DODONGOS_CAVERN_ENTRYWAY, ENTR_DODONGOS_CAVERN_ENTRANCE },
|
||||
{ EntranceType::Dungeon, RR_DODONGOS_CAVERN_ENTRYWAY, RR_DEATH_MOUNTAIN_TRAIL, ENTR_DEATH_MOUNTAIN_TRAIL_OUTSIDE_DODONGOS_CAVERN } },
|
||||
{ { EntranceType::Dungeon, RR_ZORAS_FOUNTAIN, RR_JABU_JABUS_BELLY_ENTRYWAY, ENTR_JABU_JABU_ENTRANCE },
|
||||
{ EntranceType::Dungeon, RR_JABU_JABUS_BELLY_ENTRYWAY, RR_ZORAS_FOUNTAIN, ENTR_ZORAS_FOUNTAIN_OUTSIDE_JABU_JABU } },
|
||||
{ { EntranceType::Dungeon, RR_SACRED_FOREST_MEADOW, RR_FOREST_TEMPLE_ENTRYWAY, ENTR_FOREST_TEMPLE_ENTRANCE },
|
||||
{ EntranceType::Dungeon, RR_FOREST_TEMPLE_ENTRYWAY, RR_SACRED_FOREST_MEADOW, ENTR_SACRED_FOREST_MEADOW_OUTSIDE_TEMPLE } },
|
||||
{ { EntranceType::Dungeon, RR_DMC_CENTRAL_LOCAL, RR_FIRE_TEMPLE_ENTRYWAY, ENTR_FIRE_TEMPLE_ENTRANCE },
|
||||
{ EntranceType::Dungeon, RR_FIRE_TEMPLE_ENTRYWAY, RR_DMC_CENTRAL_LOCAL, ENTR_DEATH_MOUNTAIN_CRATER_OUTSIDE_TEMPLE } },
|
||||
{ { EntranceType::Dungeon, RR_LAKE_HYLIA, RR_WATER_TEMPLE_ENTRYWAY, ENTR_WATER_TEMPLE_ENTRANCE },
|
||||
{ EntranceType::Dungeon, RR_WATER_TEMPLE_ENTRYWAY, RR_LAKE_HYLIA, ENTR_LAKE_HYLIA_OUTSIDE_TEMPLE } },
|
||||
{ { EntranceType::Dungeon, RR_DESERT_COLOSSUS, RR_SPIRIT_TEMPLE_ENTRYWAY, ENTR_SPIRIT_TEMPLE_ENTRANCE },
|
||||
{ EntranceType::Dungeon, RR_SPIRIT_TEMPLE_ENTRYWAY, RR_DESERT_COLOSSUS_OUTSIDE_TEMPLE, ENTR_DESERT_COLOSSUS_OUTSIDE_TEMPLE } },
|
||||
{ { EntranceType::Dungeon, RR_GRAVEYARD_WARP_PAD_REGION, RR_SHADOW_TEMPLE_ENTRYWAY, ENTR_SHADOW_TEMPLE_ENTRANCE },
|
||||
{ EntranceType::Dungeon, RR_SHADOW_TEMPLE_ENTRYWAY, RR_GRAVEYARD_WARP_PAD_REGION, ENTR_GRAVEYARD_OUTSIDE_TEMPLE } },
|
||||
{ { EntranceType::Dungeon, RR_KAK_WELL, RR_BOTTOM_OF_THE_WELL_ENTRYWAY, ENTR_BOTTOM_OF_THE_WELL_ENTRANCE },
|
||||
{ EntranceType::Dungeon, RR_BOTTOM_OF_THE_WELL_ENTRYWAY, RR_KAK_WELL, ENTR_KAKARIKO_VILLAGE_OUTSIDE_BOTTOM_OF_THE_WELL } },
|
||||
{ { EntranceType::Dungeon, RR_ZF_LEDGE, RR_ICE_CAVERN_ENTRYWAY, ENTR_ICE_CAVERN_ENTRANCE },
|
||||
{ EntranceType::Dungeon, RR_ICE_CAVERN_ENTRYWAY, RR_ZF_LEDGE, ENTR_ZORAS_FOUNTAIN_OUTSIDE_ICE_CAVERN } },
|
||||
{ { EntranceType::Dungeon, RR_GERUDO_FORTRESS, RR_GERUDO_TRAINING_GROUND_ENTRYWAY, ENTR_GERUDO_TRAINING_GROUND_ENTRANCE },
|
||||
{ EntranceType::Dungeon, RR_GERUDO_TRAINING_GROUND_ENTRYWAY, RR_GERUDO_FORTRESS, ENTR_GERUDOS_FORTRESS_OUTSIDE_GERUDO_TRAINING_GROUND } },
|
||||
{ { EntranceType::GanonDungeon, RR_GANONS_CASTLE_LEDGE, RR_GANONS_CASTLE_ENTRYWAY, ENTR_INSIDE_GANONS_CASTLE_ENTRANCE },
|
||||
{ EntranceType::GanonDungeon, RR_GANONS_CASTLE_ENTRYWAY, RR_CASTLE_GROUNDS_FROM_GANONS_CASTLE, ENTR_CASTLE_GROUNDS_RAINBOW_BRIDGE_EXIT } },
|
||||
|
||||
{ { EntranceType::Interior, RR_KOKIRI_FOREST, RR_KF_MIDOS_HOUSE, ENTR_MIDOS_HOUSE_0 },
|
||||
{ EntranceType::Interior, RR_KF_MIDOS_HOUSE, RR_KOKIRI_FOREST, ENTR_KOKIRI_FOREST_OUTSIDE_MIDOS_HOUSE } },
|
||||
{ { EntranceType::Interior, RR_KOKIRI_FOREST, RR_KF_SARIAS_HOUSE, ENTR_SARIAS_HOUSE_0 },
|
||||
{ EntranceType::Interior, RR_KF_SARIAS_HOUSE, RR_KOKIRI_FOREST, ENTR_KOKIRI_FOREST_OUTSIDE_SARIAS_HOUSE } },
|
||||
{ { EntranceType::Interior, RR_KOKIRI_FOREST, RR_KF_HOUSE_OF_TWINS, ENTR_TWINS_HOUSE_0 },
|
||||
{ EntranceType::Interior, RR_KF_HOUSE_OF_TWINS, RR_KOKIRI_FOREST, ENTR_KOKIRI_FOREST_OUTSIDE_TWINS_HOUSE } },
|
||||
{ { EntranceType::Interior, RR_KOKIRI_FOREST, RR_KF_KNOW_IT_ALL_HOUSE, ENTR_KNOW_IT_ALL_BROS_HOUSE_0 },
|
||||
{ EntranceType::Interior, RR_KF_KNOW_IT_ALL_HOUSE, RR_KOKIRI_FOREST, ENTR_KOKIRI_FOREST_OUTSIDE_KNOW_IT_ALL_HOUSE } },
|
||||
{ { EntranceType::Interior, RR_KOKIRI_FOREST, RR_KF_KOKIRI_SHOP, ENTR_KOKIRI_SHOP_0 },
|
||||
{ EntranceType::Interior, RR_KF_KOKIRI_SHOP, RR_KOKIRI_FOREST, ENTR_KOKIRI_FOREST_OUTSIDE_SHOP } },
|
||||
{ { EntranceType::Interior, RR_LAKE_HYLIA, RR_LH_LAB, ENTR_LAKESIDE_LABORATORY_0 },
|
||||
{ EntranceType::Interior, RR_LH_LAB, RR_LAKE_HYLIA, ENTR_LAKE_HYLIA_OUTSIDE_LAB } },
|
||||
{ { EntranceType::Interior, RR_LH_FISHING_ISLAND, RR_LH_FISHING_POND, ENTR_FISHING_POND_0 },
|
||||
{ EntranceType::Interior, RR_LH_FISHING_POND, RR_LH_FISHING_ISLAND, ENTR_LAKE_HYLIA_OUTSIDE_FISHING_POND } },
|
||||
{ { EntranceType::Interior, RR_GV_FORTRESS_SIDE, RR_GV_CARPENTER_TENT, ENTR_CARPENTERS_TENT_0 },
|
||||
{ EntranceType::Interior, RR_GV_CARPENTER_TENT, RR_GV_FORTRESS_SIDE, ENTR_GERUDO_VALLEY_OUTSIDE_TENT } },
|
||||
{ { EntranceType::Interior, RR_MARKET_ENTRANCE, RR_MARKET_GUARD_HOUSE, ENTR_MARKET_GUARD_HOUSE_0 },
|
||||
{ EntranceType::Interior, RR_MARKET_GUARD_HOUSE, RR_MARKET_ENTRANCE, ENTR_MARKET_ENTRANCE_OUTSIDE_GUARD_HOUSE } },
|
||||
{ { EntranceType::Interior, RR_THE_MARKET, RR_MARKET_MASK_SHOP, ENTR_HAPPY_MASK_SHOP_0 },
|
||||
{ EntranceType::Interior, RR_MARKET_MASK_SHOP, RR_THE_MARKET, ENTR_MARKET_DAY_OUTSIDE_HAPPY_MASK_SHOP } },
|
||||
{ { EntranceType::Interior, RR_THE_MARKET, RR_MARKET_BOMBCHU_BOWLING, ENTR_BOMBCHU_BOWLING_ALLEY_0 },
|
||||
{ EntranceType::Interior, RR_MARKET_BOMBCHU_BOWLING, RR_THE_MARKET, ENTR_MARKET_DAY_OUTSIDE_BOMBCHU_BOWLING } },
|
||||
{ { EntranceType::Interior, RR_THE_MARKET, RR_MARKET_POTION_SHOP, ENTR_POTION_SHOP_MARKET_0 },
|
||||
{ EntranceType::Interior, RR_MARKET_POTION_SHOP, RR_THE_MARKET, ENTR_MARKET_DAY_OUTSIDE_POTION_SHOP } },
|
||||
{ { EntranceType::Interior, RR_THE_MARKET, RR_MARKET_TREASURE_CHEST_GAME, ENTR_TREASURE_BOX_SHOP_0 },
|
||||
{ EntranceType::Interior, RR_MARKET_TREASURE_CHEST_GAME, RR_THE_MARKET, ENTR_MARKET_DAY_OUTSIDE_TREASURE_BOX_SHOP } },
|
||||
{ { EntranceType::Interior, RR_MARKET_BACK_ALLEY, RR_MARKET_BOMBCHU_SHOP, ENTR_BOMBCHU_SHOP_1 },
|
||||
{ EntranceType::Interior, RR_MARKET_BOMBCHU_SHOP, RR_MARKET_BACK_ALLEY, ENTR_BACK_ALLEY_DAY_OUTSIDE_BOMBCHU_SHOP } },
|
||||
{ { EntranceType::Interior, RR_MARKET_BACK_ALLEY, RR_MARKET_MAN_IN_GREEN_HOUSE, ENTR_BACK_ALLEY_MAN_IN_GREEN_HOUSE },
|
||||
{ EntranceType::Interior, RR_MARKET_MAN_IN_GREEN_HOUSE, RR_MARKET_BACK_ALLEY, ENTR_BACK_ALLEY_DAY_OUTSIDE_MAN_IN_GREEN_HOUSE } },
|
||||
{ { EntranceType::Interior, RR_KAKARIKO_VILLAGE, RR_KAK_CARPENTER_BOSS_HOUSE, ENTR_KAKARIKO_CENTER_GUEST_HOUSE_0 },
|
||||
{ EntranceType::Interior, RR_KAK_CARPENTER_BOSS_HOUSE, RR_KAKARIKO_VILLAGE, ENTR_KAKARIKO_VILLAGE_OUTSIDE_CENTER_GUEST_HOUSE } },
|
||||
{ { EntranceType::Interior, RR_KAKARIKO_VILLAGE, RR_KAK_HOUSE_OF_SKULLTULA, ENTR_HOUSE_OF_SKULLTULA_0 },
|
||||
{ EntranceType::Interior, RR_KAK_HOUSE_OF_SKULLTULA, RR_KAKARIKO_VILLAGE, ENTR_KAKARIKO_VILLAGE_OUTSIDE_SKULKLTULA_HOUSE } },
|
||||
{ { EntranceType::Interior, RR_KAKARIKO_VILLAGE, RR_KAK_IMPAS_HOUSE, ENTR_IMPAS_HOUSE_FRONT },
|
||||
{ EntranceType::Interior, RR_KAK_IMPAS_HOUSE, RR_KAKARIKO_VILLAGE, ENTR_KAKARIKO_VILLAGE_OUTSIDE_IMPAS_HOUSE_FRONT } },
|
||||
{ { EntranceType::Interior, RR_KAK_IMPAS_LEDGE, RR_KAK_IMPAS_HOUSE_BACK, ENTR_IMPAS_HOUSE_BACK },
|
||||
{ EntranceType::Interior, RR_KAK_IMPAS_HOUSE_BACK, RR_KAK_IMPAS_LEDGE, ENTR_KAKARIKO_VILLAGE_OUTSIDE_IMPAS_HOUSE_BACK } },
|
||||
{ { EntranceType::Interior, RR_KAK_BACKYARD, RR_KAK_ODD_POTION_BUILDING, ENTR_POTION_SHOP_GRANNY_0 },
|
||||
{ EntranceType::Interior, RR_KAK_ODD_POTION_BUILDING, RR_KAK_BACKYARD, ENTR_KAKARIKO_VILLAGE_OUTSIDE_SHOP_GRANNY } },
|
||||
{ { EntranceType::Interior, RR_THE_GRAVEYARD, RR_GRAVEYARD_DAMPES_HOUSE, ENTR_GRAVEKEEPERS_HUT_0 },
|
||||
{ EntranceType::Interior, RR_GRAVEYARD_DAMPES_HOUSE, RR_THE_GRAVEYARD, ENTR_GRAVEYARD_OUTSIDE_DAMPES_HUT } },
|
||||
{ { EntranceType::Interior, RR_GORON_CITY, RR_GC_SHOP, ENTR_GORON_SHOP_0 },
|
||||
{ EntranceType::Interior, RR_GC_SHOP, RR_GORON_CITY, ENTR_GORON_CITY_OUTSIDE_SHOP } },
|
||||
{ { EntranceType::Interior, RR_ZORAS_DOMAIN, RR_ZD_SHOP, ENTR_ZORA_SHOP_0 },
|
||||
{ EntranceType::Interior, RR_ZD_SHOP, RR_ZORAS_DOMAIN, ENTR_ZORAS_DOMAIN_OUTSIDE_SHOP } },
|
||||
{ { EntranceType::Interior, RR_LON_LON_RANCH, RR_LLR_TALONS_HOUSE, ENTR_LON_LON_BUILDINGS_TALONS_HOUSE },
|
||||
{ EntranceType::Interior, RR_LLR_TALONS_HOUSE, RR_LON_LON_RANCH, ENTR_LON_LON_RANCH_OUTSIDE_TALONS_HOUSE } },
|
||||
{ { EntranceType::Interior, RR_LON_LON_RANCH, RR_LLR_STABLES, ENTR_STABLE_0 },
|
||||
{ EntranceType::Interior, RR_LLR_STABLES, RR_LON_LON_RANCH, ENTR_LON_LON_RANCH_OUTSIDE_STABLES } },
|
||||
{ { EntranceType::Interior, RR_LON_LON_RANCH, RR_LLR_TOWER, ENTR_LON_LON_BUILDINGS_TOWER },
|
||||
{ EntranceType::Interior, RR_LLR_TOWER, RR_LON_LON_RANCH, ENTR_LON_LON_RANCH_OUTSIDE_TOWER } },
|
||||
{ { EntranceType::Interior, RR_THE_MARKET, RR_MARKET_BAZAAR, ENTR_BAZAAR_1 },
|
||||
{ EntranceType::Interior, RR_MARKET_BAZAAR, RR_THE_MARKET, ENTR_MARKET_DAY_OUTSIDE_BAZAAR } },
|
||||
{ { EntranceType::Interior, RR_THE_MARKET, RR_MARKET_SHOOTING_GALLERY, ENTR_SHOOTING_GALLERY_1 },
|
||||
{ EntranceType::Interior, RR_MARKET_SHOOTING_GALLERY, RR_THE_MARKET, ENTR_MARKET_DAY_OUTSIDE_SHOOTING_GALLERY } },
|
||||
{ { EntranceType::Interior, RR_KAKARIKO_VILLAGE, RR_KAK_BAZAAR, ENTR_BAZAAR_0 },
|
||||
{ EntranceType::Interior, RR_KAK_BAZAAR, RR_KAKARIKO_VILLAGE, ENTR_KAKARIKO_VILLAGE_OUTSIDE_BAZAAR } },
|
||||
{ { EntranceType::Interior, RR_KAKARIKO_VILLAGE, RR_KAK_SHOOTING_GALLERY, ENTR_SHOOTING_GALLERY_0 },
|
||||
{ EntranceType::Interior, RR_KAK_SHOOTING_GALLERY, RR_KAKARIKO_VILLAGE, ENTR_KAKARIKO_VILLAGE_OUTSIDE_SHOOTING_GALLERY } },
|
||||
{ { EntranceType::Interior, RR_DESERT_COLOSSUS, RR_COLOSSUS_GREAT_FAIRY_FOUNTAIN, ENTR_GREAT_FAIRYS_FOUNTAIN_SPELLS_NAYRUS_COLOSSUS },
|
||||
{ EntranceType::Interior, RR_COLOSSUS_GREAT_FAIRY_FOUNTAIN, RR_DESERT_COLOSSUS, ENTR_DESERT_COLOSSUS_GREAT_FAIRY_EXIT } },
|
||||
{ { EntranceType::Interior, RR_HYRULE_CASTLE_GROUNDS, RR_HC_GREAT_FAIRY_FOUNTAIN, ENTR_GREAT_FAIRYS_FOUNTAIN_SPELLS_DINS_HC },
|
||||
{ EntranceType::Interior, RR_HC_GREAT_FAIRY_FOUNTAIN, RR_CASTLE_GROUNDS, ENTR_CASTLE_GROUNDS_GREAT_FAIRY_EXIT } },
|
||||
{ { EntranceType::Interior, RR_GANONS_CASTLE_GROUNDS, RR_OGC_GREAT_FAIRY_FOUNTAIN, ENTR_GREAT_FAIRYS_FOUNTAIN_MAGIC_OGC_DD },
|
||||
// 0x3E8 is an unused entrance index repruposed to differentiate between the HC and OGC fairy
|
||||
// fountain exits (normally they both use 0x340)
|
||||
{ EntranceType::Interior, RR_OGC_GREAT_FAIRY_FOUNTAIN, RR_CASTLE_GROUNDS, ENTR_POTION_SHOP_KAKARIKO_1 } },
|
||||
{ { EntranceType::Interior, RR_DMC_LOWER_NEARBY, RR_DMC_GREAT_FAIRY_FOUNTAIN, ENTR_GREAT_FAIRYS_FOUNTAIN_MAGIC_DMC },
|
||||
{ EntranceType::Interior, RR_DMC_GREAT_FAIRY_FOUNTAIN, RR_DMC_LOWER_LOCAL, ENTR_DEATH_MOUNTAIN_CRATER_GREAT_FAIRY_EXIT } },
|
||||
{ { EntranceType::Interior, RR_DEATH_MOUNTAIN_SUMMIT, RR_DMT_GREAT_FAIRY_FOUNTAIN, ENTR_GREAT_FAIRYS_FOUNTAIN_MAGIC_DMT },
|
||||
{ EntranceType::Interior, RR_DMT_GREAT_FAIRY_FOUNTAIN, RR_DEATH_MOUNTAIN_SUMMIT, ENTR_DEATH_MOUNTAIN_TRAIL_GREAT_FAIRY_EXIT } },
|
||||
{ { EntranceType::Interior, RR_ZORAS_FOUNTAIN, RR_ZF_GREAT_FAIRY_FOUNTAIN, ENTR_GREAT_FAIRYS_FOUNTAIN_SPELLS_FARORES_ZF },
|
||||
{ EntranceType::Interior, RR_ZF_GREAT_FAIRY_FOUNTAIN, RR_ZORAS_FOUNTAIN, ENTR_ZORAS_FOUNTAIN_OUTSIDE_GREAT_FAIRY } },
|
||||
|
||||
{ { EntranceType::SpecialInterior, RR_KOKIRI_FOREST, RR_KF_LINKS_HOUSE, ENTR_LINKS_HOUSE_1 },
|
||||
{ EntranceType::SpecialInterior, RR_KF_LINKS_HOUSE, RR_KOKIRI_FOREST, ENTR_KOKIRI_FOREST_OUTSIDE_LINKS_HOUSE } },
|
||||
{ { EntranceType::SpecialInterior, RR_TOT_ENTRANCE, RR_TEMPLE_OF_TIME, ENTR_TEMPLE_OF_TIME_ENTRANCE },
|
||||
{ EntranceType::SpecialInterior, RR_TEMPLE_OF_TIME, RR_TOT_ENTRANCE, ENTR_TEMPLE_OF_TIME_EXTERIOR_DAY_OUTSIDE_TEMPLE } },
|
||||
{ { EntranceType::SpecialInterior, RR_KAKARIKO_VILLAGE, RR_KAK_WINDMILL, ENTR_WINDMILL_AND_DAMPES_GRAVE_WINDMILL },
|
||||
{ EntranceType::SpecialInterior, RR_KAK_WINDMILL, RR_KAKARIKO_VILLAGE, ENTR_KAKARIKO_VILLAGE_OUTSIDE_WINDMILL } },
|
||||
{ { EntranceType::SpecialInterior, RR_KAKARIKO_VILLAGE, RR_KAK_POTION_SHOP_FRONT, ENTR_POTION_SHOP_KAKARIKO_FRONT },
|
||||
{ EntranceType::SpecialInterior, RR_KAK_POTION_SHOP_FRONT, RR_KAKARIKO_VILLAGE, ENTR_KAKARIKO_VILLAGE_OUTSIDE_POTION_SHOP_FRONT } },
|
||||
{ { EntranceType::SpecialInterior, RR_KAK_BACKYARD, RR_KAK_POTION_SHOP_BACK, ENTR_POTION_SHOP_KAKARIKO_BACK },
|
||||
{ EntranceType::SpecialInterior, RR_KAK_POTION_SHOP_BACK, RR_KAK_BACKYARD, ENTR_KAKARIKO_VILLAGE_OUTSIDE_POTION_SHOP_BACK } },
|
||||
|
||||
// Grotto Loads use an entrance index of 0x0700 + their grotto id. The id is used as index for the
|
||||
// grottoLoadTable in soh/soh/Enhancements/randomizer/randomizer_grotto.c
|
||||
// Grotto Returns use an entrance index of 0x0800 + their grotto id. The id is used as index for the
|
||||
// grottoReturnTable in soh/soh/Enhancements/randomizer/randomizer_grotto.c
|
||||
{ { EntranceType::GrottoGrave, RR_DESERT_COLOSSUS, RR_COLOSSUS_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_COLOSSUS_OFFSET) },
|
||||
{ EntranceType::GrottoGrave, RR_COLOSSUS_GROTTO, RR_DESERT_COLOSSUS, ENTRANCE_GROTTO_EXIT(GROTTO_COLOSSUS_OFFSET) } },
|
||||
{ { EntranceType::GrottoGrave, RR_LAKE_HYLIA, RR_LH_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_LH_OFFSET) },
|
||||
{ EntranceType::GrottoGrave, RR_LH_GROTTO, RR_LAKE_HYLIA, ENTRANCE_GROTTO_EXIT(GROTTO_LH_OFFSET) } },
|
||||
{ { EntranceType::GrottoGrave, RR_ZORAS_RIVER, RR_ZR_STORMS_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_ZR_STORMS_OFFSET) },
|
||||
{ EntranceType::GrottoGrave, RR_ZR_STORMS_GROTTO, RR_ZORAS_RIVER, ENTRANCE_GROTTO_EXIT(GROTTO_ZR_STORMS_OFFSET) } },
|
||||
{ { EntranceType::GrottoGrave, RR_ZORAS_RIVER, RR_ZR_FAIRY_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_ZR_FAIRY_OFFSET) },
|
||||
{ EntranceType::GrottoGrave, RR_ZR_FAIRY_GROTTO, RR_ZORAS_RIVER, ENTRANCE_GROTTO_EXIT(GROTTO_ZR_FAIRY_OFFSET) } },
|
||||
{ { EntranceType::GrottoGrave, RR_ZORAS_RIVER, RR_ZR_OPEN_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_ZR_OPEN_OFFSET) },
|
||||
{ EntranceType::GrottoGrave, RR_ZR_OPEN_GROTTO, RR_ZORAS_RIVER, ENTRANCE_GROTTO_EXIT(GROTTO_ZR_OPEN_OFFSET) } },
|
||||
{ { EntranceType::GrottoGrave, RR_DMC_LOWER_NEARBY, RR_DMC_HAMMER_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_DMC_HAMMER_OFFSET) },
|
||||
{ EntranceType::GrottoGrave, RR_DMC_HAMMER_GROTTO, RR_DMC_LOWER_LOCAL, ENTRANCE_GROTTO_EXIT(GROTTO_DMC_HAMMER_OFFSET) } },
|
||||
{ { EntranceType::GrottoGrave, RR_DMC_UPPER_NEARBY, RR_DMC_UPPER_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_DMC_UPPER_OFFSET) },
|
||||
{ EntranceType::GrottoGrave, RR_DMC_UPPER_GROTTO, RR_DMC_UPPER_LOCAL, ENTRANCE_GROTTO_EXIT(GROTTO_DMC_UPPER_OFFSET) } },
|
||||
{ { EntranceType::GrottoGrave, RR_GC_GROTTO_PLATFORM, RR_GC_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_GORON_CITY_OFFSET) },
|
||||
{ EntranceType::GrottoGrave, RR_GC_GROTTO, RR_GC_GROTTO_PLATFORM, ENTRANCE_GROTTO_EXIT(GROTTO_GORON_CITY_OFFSET) } },
|
||||
{ { EntranceType::GrottoGrave, RR_DEATH_MOUNTAIN_TRAIL, RR_DMT_STORMS_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_DMT_STORMS_OFFSET) },
|
||||
{ EntranceType::GrottoGrave, RR_DMT_STORMS_GROTTO, RR_DEATH_MOUNTAIN_TRAIL, ENTRANCE_GROTTO_EXIT(GROTTO_DMT_STORMS_OFFSET) } },
|
||||
{ { EntranceType::GrottoGrave, RR_DEATH_MOUNTAIN_SUMMIT, RR_DMT_COW_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_DMT_COW_OFFSET) },
|
||||
{ EntranceType::GrottoGrave, RR_DMT_COW_GROTTO, RR_DEATH_MOUNTAIN_SUMMIT, ENTRANCE_GROTTO_EXIT(GROTTO_DMT_COW_OFFSET) } },
|
||||
{ { EntranceType::GrottoGrave, RR_KAK_BACKYARD, RR_KAK_OPEN_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_KAK_OPEN_OFFSET) },
|
||||
{ EntranceType::GrottoGrave, RR_KAK_OPEN_GROTTO, RR_KAK_BACKYARD, ENTRANCE_GROTTO_EXIT(GROTTO_KAK_OPEN_OFFSET) } },
|
||||
{ { EntranceType::GrottoGrave, RR_KAKARIKO_VILLAGE, RR_KAK_REDEAD_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_KAK_REDEAD_OFFSET) },
|
||||
{ EntranceType::GrottoGrave, RR_KAK_REDEAD_GROTTO, RR_KAKARIKO_VILLAGE, ENTRANCE_GROTTO_EXIT(GROTTO_KAK_REDEAD_OFFSET) } },
|
||||
{ { EntranceType::GrottoGrave, RR_HYRULE_CASTLE_GROUNDS, RR_HC_STORMS_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_HC_STORMS_OFFSET) },
|
||||
{ EntranceType::GrottoGrave, RR_HC_STORMS_GROTTO, RR_CASTLE_GROUNDS, ENTRANCE_GROTTO_EXIT(GROTTO_HC_STORMS_OFFSET) } },
|
||||
{ { EntranceType::GrottoGrave, RR_HYRULE_FIELD, RR_HF_TEKTITE_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_HF_TEKTITE_OFFSET) },
|
||||
{ EntranceType::GrottoGrave, RR_HF_TEKTITE_GROTTO, RR_HYRULE_FIELD, ENTRANCE_GROTTO_EXIT(GROTTO_HF_TEKTITE_OFFSET) } },
|
||||
{ { EntranceType::GrottoGrave, RR_HYRULE_FIELD, RR_HF_NEAR_KAK_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_HF_NEAR_KAK_OFFSET) },
|
||||
{ EntranceType::GrottoGrave, RR_HF_NEAR_KAK_GROTTO, RR_HYRULE_FIELD, ENTRANCE_GROTTO_EXIT(GROTTO_HF_NEAR_KAK_OFFSET) } },
|
||||
{ { EntranceType::GrottoGrave, RR_HYRULE_FIELD, RR_HF_FAIRY_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_HF_FAIRY_OFFSET) },
|
||||
{ EntranceType::GrottoGrave, RR_HF_FAIRY_GROTTO, RR_HYRULE_FIELD, ENTRANCE_GROTTO_EXIT(GROTTO_HF_FAIRY_OFFSET) } },
|
||||
{ { EntranceType::GrottoGrave, RR_HYRULE_FIELD, RR_HF_NEAR_MARKET_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_HF_NEAR_MARKET_OFFSET) },
|
||||
{ EntranceType::GrottoGrave, RR_HF_NEAR_MARKET_GROTTO, RR_HYRULE_FIELD, ENTRANCE_GROTTO_EXIT(GROTTO_HF_NEAR_MARKET_OFFSET) } },
|
||||
{ { EntranceType::GrottoGrave, RR_HYRULE_FIELD, RR_HF_COW_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_HF_COW_OFFSET) },
|
||||
{ EntranceType::GrottoGrave, RR_HF_COW_GROTTO, RR_HYRULE_FIELD, ENTRANCE_GROTTO_EXIT(GROTTO_HF_COW_OFFSET) } },
|
||||
{ { EntranceType::GrottoGrave, RR_HYRULE_FIELD, RR_HF_INSIDE_FENCE_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_HF_INSIDE_FENCE_OFFSET) },
|
||||
{ EntranceType::GrottoGrave, RR_HF_INSIDE_FENCE_GROTTO, RR_HYRULE_FIELD, ENTRANCE_GROTTO_EXIT(GROTTO_HF_INSIDE_FENCE_OFFSET) } },
|
||||
{ { EntranceType::GrottoGrave, RR_HYRULE_FIELD, RR_HF_OPEN_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_HF_OPEN_OFFSET) },
|
||||
{ EntranceType::GrottoGrave, RR_HF_OPEN_GROTTO, RR_HYRULE_FIELD, ENTRANCE_GROTTO_EXIT(GROTTO_HF_OPEN_OFFSET) } },
|
||||
{ { EntranceType::GrottoGrave, RR_HYRULE_FIELD, RR_HF_SOUTHEAST_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_HF_SOUTHEAST_OFFSET) },
|
||||
{ EntranceType::GrottoGrave, RR_HF_SOUTHEAST_GROTTO, RR_HYRULE_FIELD, ENTRANCE_GROTTO_EXIT(GROTTO_HF_SOUTHEAST_OFFSET) } },
|
||||
{ { EntranceType::GrottoGrave, RR_LON_LON_RANCH, RR_LLR_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_LLR_OFFSET) },
|
||||
{ EntranceType::GrottoGrave, RR_LLR_GROTTO, RR_LON_LON_RANCH, ENTRANCE_GROTTO_EXIT(GROTTO_LLR_OFFSET) } },
|
||||
{ { EntranceType::GrottoGrave, RR_SFM_ENTRYWAY, RR_SFM_WOLFOS_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_SFM_WOLFOS_OFFSET) },
|
||||
{ EntranceType::GrottoGrave, RR_SFM_WOLFOS_GROTTO, RR_SFM_ENTRYWAY, ENTRANCE_GROTTO_EXIT(GROTTO_SFM_WOLFOS_OFFSET) } },
|
||||
{ { EntranceType::GrottoGrave, RR_SACRED_FOREST_MEADOW, RR_SFM_STORMS_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_SFM_STORMS_OFFSET) },
|
||||
{ EntranceType::GrottoGrave, RR_SFM_STORMS_GROTTO, RR_SACRED_FOREST_MEADOW, ENTRANCE_GROTTO_EXIT(GROTTO_SFM_STORMS_OFFSET) } },
|
||||
{ { EntranceType::GrottoGrave, RR_SACRED_FOREST_MEADOW, RR_SFM_FAIRY_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_SFM_FAIRY_OFFSET) },
|
||||
{ EntranceType::GrottoGrave, RR_SFM_FAIRY_GROTTO, RR_SACRED_FOREST_MEADOW, ENTRANCE_GROTTO_EXIT(GROTTO_SFM_FAIRY_OFFSET) } },
|
||||
{ { EntranceType::GrottoGrave, RR_LW_BEYOND_MIDO, RR_LW_SCRUBS_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_LW_SCRUBS_OFFSET) },
|
||||
{ EntranceType::GrottoGrave, RR_LW_SCRUBS_GROTTO, RR_LW_BEYOND_MIDO, ENTRANCE_GROTTO_EXIT(GROTTO_LW_SCRUBS_OFFSET) } },
|
||||
{ { EntranceType::GrottoGrave, RR_THE_LOST_WOODS, RR_LW_NEAR_SHORTCUTS_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_LW_NEAR_SHORTCUTS_OFFSET) },
|
||||
{ EntranceType::GrottoGrave, RR_LW_NEAR_SHORTCUTS_GROTTO, RR_THE_LOST_WOODS, ENTRANCE_GROTTO_EXIT(GROTTO_LW_NEAR_SHORTCUTS_OFFSET) } },
|
||||
{ { EntranceType::GrottoGrave, RR_KOKIRI_FOREST, RR_KF_STORMS_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_KF_STORMS_OFFSET) },
|
||||
{ EntranceType::GrottoGrave, RR_KF_STORMS_GROTTO, RR_KOKIRI_FOREST, ENTRANCE_GROTTO_EXIT(GROTTO_KF_STORMS_OFFSET) } },
|
||||
{ { EntranceType::GrottoGrave, RR_ZORAS_DOMAIN_ISLAND, RR_ZD_STORMS_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_ZD_STORMS_OFFSET) },
|
||||
{ EntranceType::GrottoGrave, RR_ZD_STORMS_GROTTO, RR_ZORAS_DOMAIN_ISLAND, ENTRANCE_GROTTO_EXIT(GROTTO_ZD_STORMS_OFFSET) } },
|
||||
{ { EntranceType::GrottoGrave, RR_GERUDO_FORTRESS, RR_GF_STORMS_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_GF_STORMS_OFFSET) },
|
||||
{ EntranceType::GrottoGrave, RR_GF_STORMS_GROTTO, RR_GERUDO_FORTRESS, ENTRANCE_GROTTO_EXIT(GROTTO_GF_STORMS_OFFSET) } },
|
||||
{ { EntranceType::GrottoGrave, RR_GV_FORTRESS_SIDE, RR_GV_STORMS_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_GV_STORMS_OFFSET) },
|
||||
{ EntranceType::GrottoGrave, RR_GV_STORMS_GROTTO, RR_GV_FORTRESS_SIDE, ENTRANCE_GROTTO_EXIT(GROTTO_GV_STORMS_OFFSET) } },
|
||||
{ { EntranceType::GrottoGrave, RR_GV_GROTTO_LEDGE, RR_GV_OCTOROK_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_GV_OCTOROK_OFFSET) },
|
||||
{ EntranceType::GrottoGrave, RR_GV_OCTOROK_GROTTO, RR_GV_GROTTO_LEDGE, ENTRANCE_GROTTO_EXIT(GROTTO_GV_OCTOROK_OFFSET) } },
|
||||
{ { EntranceType::GrottoGrave, RR_LW_BEYOND_MIDO, RR_DEKU_THEATER, ENTRANCE_GROTTO_LOAD(GROTTO_LW_DEKU_THEATRE_OFFSET) },
|
||||
{ EntranceType::GrottoGrave, RR_DEKU_THEATER, RR_LW_BEYOND_MIDO, ENTRANCE_GROTTO_EXIT(GROTTO_LW_DEKU_THEATRE_OFFSET) } },
|
||||
|
||||
// Graves have their own specified entrance indices
|
||||
{ { EntranceType::GrottoGrave, RR_THE_GRAVEYARD, RR_GRAVEYARD_SHIELD_GRAVE, ENTR_GRAVE_WITH_FAIRYS_FOUNTAIN_0 },
|
||||
{ EntranceType::GrottoGrave, RR_GRAVEYARD_SHIELD_GRAVE, RR_THE_GRAVEYARD, ENTR_GRAVEYARD_SHIELD_GRAVE_EXIT } },
|
||||
{ { EntranceType::GrottoGrave, RR_THE_GRAVEYARD, RR_GRAVEYARD_HEART_PIECE_GRAVE, ENTR_REDEAD_GRAVE_0 },
|
||||
{ EntranceType::GrottoGrave, RR_GRAVEYARD_HEART_PIECE_GRAVE, RR_THE_GRAVEYARD, ENTR_GRAVEYARD_HEART_PIECE_GRAVE_EXIT } },
|
||||
{ { EntranceType::GrottoGrave, RR_THE_GRAVEYARD, RR_GRAVEYARD_COMPOSERS_GRAVE, ENTR_ROYAL_FAMILYS_TOMB_0 },
|
||||
{ EntranceType::GrottoGrave, RR_GRAVEYARD_COMPOSERS_GRAVE, RR_THE_GRAVEYARD, ENTR_GRAVEYARD_ROYAL_TOMB_EXIT } },
|
||||
{ { EntranceType::GrottoGrave, RR_THE_GRAVEYARD, RR_GRAVEYARD_DAMPES_GRAVE, ENTR_WINDMILL_AND_DAMPES_GRAVE_GRAVE },
|
||||
{ EntranceType::GrottoGrave, RR_GRAVEYARD_DAMPES_GRAVE, RR_THE_GRAVEYARD, ENTR_GRAVEYARD_DAMPES_GRAVE_EXIT } },
|
||||
|
||||
{ { EntranceType::Overworld, RR_KOKIRI_FOREST, RR_LW_BRIDGE_FROM_FOREST, ENTR_LOST_WOODS_BRIDGE_EAST_EXIT },
|
||||
{ EntranceType::Overworld, RR_LW_BRIDGE, RR_KOKIRI_FOREST, ENTR_KOKIRI_FOREST_LOWER_EXIT } },
|
||||
{ { EntranceType::Overworld, RR_KOKIRI_FOREST, RR_THE_LOST_WOODS, ENTR_LOST_WOODS_SOUTH_EXIT },
|
||||
{ EntranceType::Overworld, RR_LW_FOREST_EXIT, RR_KOKIRI_FOREST, ENTR_KOKIRI_FOREST_UPPER_EXIT } },
|
||||
{ { EntranceType::Overworld, RR_THE_LOST_WOODS, RR_GC_WOODS_WARP, ENTR_GORON_CITY_TUNNEL_SHORTCUT },
|
||||
{ EntranceType::Overworld, RR_GC_WOODS_WARP, RR_THE_LOST_WOODS, ENTR_LOST_WOODS_TUNNEL_SHORTCUT } },
|
||||
{ { EntranceType::Overworld, RR_THE_LOST_WOODS, RR_ZORAS_RIVER, ENTR_ZORAS_RIVER_UNDERWATER_SHORTCUT },
|
||||
{ EntranceType::Overworld, RR_ZORAS_RIVER, RR_THE_LOST_WOODS, ENTR_LOST_WOODS_UNDERWATER_SHORTCUT } },
|
||||
{ { EntranceType::Overworld, RR_LW_BEYOND_MIDO, RR_SFM_ENTRYWAY, ENTR_SACRED_FOREST_MEADOW_SOUTH_EXIT },
|
||||
{ EntranceType::Overworld, RR_SFM_ENTRYWAY, RR_LW_BEYOND_MIDO, ENTR_LOST_WOODS_NORTH_EXIT } },
|
||||
{ { EntranceType::Overworld, RR_LW_BRIDGE, RR_HYRULE_FIELD, ENTR_HYRULE_FIELD_WOODED_EXIT },
|
||||
{ EntranceType::Overworld, RR_HYRULE_FIELD, RR_LW_BRIDGE, ENTR_LOST_WOODS_BRIDGE_WEST_EXIT } },
|
||||
{ { EntranceType::Overworld, RR_HYRULE_FIELD, RR_LAKE_HYLIA, ENTR_LAKE_HYLIA_NORTH_EXIT },
|
||||
{ EntranceType::Overworld, RR_LAKE_HYLIA, RR_HYRULE_FIELD, ENTR_HYRULE_FIELD_FENCE_EXIT } },
|
||||
{ { EntranceType::Overworld, RR_HYRULE_FIELD, RR_GERUDO_VALLEY, ENTR_GERUDO_VALLEY_EAST_EXIT },
|
||||
{ EntranceType::Overworld, RR_GERUDO_VALLEY, RR_HYRULE_FIELD, ENTR_HYRULE_FIELD_ROCKY_PATH } },
|
||||
{ { EntranceType::Overworld, RR_HYRULE_FIELD, RR_MARKET_ENTRANCE, ENTR_MARKET_ENTRANCE_NEAR_GUARD_EXIT },
|
||||
{ EntranceType::Overworld, RR_MARKET_ENTRANCE, RR_HYRULE_FIELD, ENTR_HYRULE_FIELD_ON_BRIDGE_SPAWN } },
|
||||
{ { EntranceType::Overworld, RR_HYRULE_FIELD, RR_KAKARIKO_VILLAGE, ENTR_KAKARIKO_VILLAGE_FRONT_GATE },
|
||||
{ EntranceType::Overworld, RR_KAKARIKO_VILLAGE, RR_HYRULE_FIELD, ENTR_HYRULE_FIELD_STAIRS_EXIT } },
|
||||
{ { EntranceType::Overworld, RR_HYRULE_FIELD, RR_ZR_FRONT, ENTR_ZORAS_RIVER_WEST_EXIT },
|
||||
{ EntranceType::Overworld, RR_ZR_FRONT, RR_HYRULE_FIELD, ENTR_HYRULE_FIELD_RIVER_EXIT } },
|
||||
{ { EntranceType::Overworld, RR_HYRULE_FIELD, RR_LON_LON_RANCH, ENTR_LON_LON_RANCH_ENTRANCE },
|
||||
{ EntranceType::Overworld, RR_LON_LON_RANCH, RR_HYRULE_FIELD, ENTR_HYRULE_FIELD_CENTER_EXIT } },
|
||||
{ { EntranceType::Overworld, RR_LAKE_HYLIA, RR_ZORAS_DOMAIN, ENTR_ZORAS_DOMAIN_UNDERWATER_SHORTCUT },
|
||||
{ EntranceType::Overworld, RR_ZORAS_DOMAIN, RR_LAKE_HYLIA, ENTR_LAKE_HYLIA_UNDERWATER_SHORTCUT } },
|
||||
{ { EntranceType::Overworld, RR_GV_FORTRESS_SIDE, RR_GERUDO_FORTRESS, ENTR_GERUDOS_FORTRESS_EAST_EXIT },
|
||||
{ EntranceType::Overworld, RR_GERUDO_FORTRESS, RR_GV_FORTRESS_SIDE, ENTR_GERUDO_VALLEY_WEST_EXIT } },
|
||||
{ { EntranceType::Overworld, RR_GF_OUTSIDE_GATE, RR_WASTELAND_NEAR_FORTRESS, ENTR_HAUNTED_WASTELAND_EAST_EXIT },
|
||||
{ EntranceType::Overworld, RR_WASTELAND_NEAR_FORTRESS, RR_GF_OUTSIDE_GATE, ENTR_GERUDOS_FORTRESS_GATE_EXIT } },
|
||||
{ { EntranceType::Overworld, RR_WASTELAND_NEAR_COLOSSUS, RR_DESERT_COLOSSUS, ENTR_DESERT_COLOSSUS_EAST_EXIT },
|
||||
{ EntranceType::Overworld, RR_DESERT_COLOSSUS, RR_WASTELAND_NEAR_COLOSSUS, ENTR_HAUNTED_WASTELAND_WEST_EXIT } },
|
||||
{ { EntranceType::Overworld, RR_MARKET_ENTRANCE, RR_THE_MARKET, ENTR_MARKET_SOUTH_EXIT },
|
||||
{ EntranceType::Overworld, RR_THE_MARKET, RR_MARKET_ENTRANCE, ENTR_MARKET_ENTRANCE_NORTH_EXIT } },
|
||||
{ { EntranceType::Overworld, RR_THE_MARKET, RR_CASTLE_GROUNDS, ENTR_CASTLE_GROUNDS_SOUTH_EXIT },
|
||||
{ EntranceType::Overworld, RR_CASTLE_GROUNDS, RR_THE_MARKET, ENTR_MARKET_DAY_CASTLE_EXIT } },
|
||||
{ { EntranceType::Overworld, RR_THE_MARKET, RR_TOT_ENTRANCE, ENTR_TEMPLE_OF_TIME_EXTERIOR_DAY_GOSSIP_STONE_EXIT },
|
||||
{ EntranceType::Overworld, RR_TOT_ENTRANCE, RR_THE_MARKET, ENTR_MARKET_DAY_TEMPLE_EXIT } },
|
||||
{ { EntranceType::Overworld, RR_KAKARIKO_VILLAGE, RR_THE_GRAVEYARD, ENTR_GRAVEYARD_ENTRANCE },
|
||||
{ EntranceType::Overworld, RR_THE_GRAVEYARD, RR_KAKARIKO_VILLAGE, ENTR_KAKARIKO_VILLAGE_SOUTHEAST_EXIT } },
|
||||
{ { EntranceType::Overworld, RR_KAK_BEHIND_GATE, RR_DEATH_MOUNTAIN_TRAIL, ENTR_DEATH_MOUNTAIN_TRAIL_BOTTOM_EXIT },
|
||||
{ EntranceType::Overworld, RR_DEATH_MOUNTAIN_TRAIL, RR_KAK_BEHIND_GATE, ENTR_KAKARIKO_VILLAGE_GUARD_GATE } },
|
||||
{ { EntranceType::Overworld, RR_DEATH_MOUNTAIN_TRAIL, RR_GORON_CITY, ENTR_GORON_CITY_UPPER_EXIT },
|
||||
{ EntranceType::Overworld, RR_GORON_CITY, RR_DEATH_MOUNTAIN_TRAIL, ENTR_DEATH_MOUNTAIN_TRAIL_GC_EXIT } },
|
||||
{ { EntranceType::Overworld, RR_GC_DARUNIAS_CHAMBER, RR_DMC_LOWER_LOCAL, ENTR_DEATH_MOUNTAIN_CRATER_GC_EXIT },
|
||||
{ EntranceType::Overworld, RR_DMC_LOWER_NEARBY, RR_GC_DARUNIAS_CHAMBER, ENTR_GORON_CITY_DARUNIA_ROOM_EXIT } },
|
||||
{ { EntranceType::Overworld, RR_DEATH_MOUNTAIN_SUMMIT, RR_DMC_UPPER_LOCAL, ENTR_DEATH_MOUNTAIN_CRATER_UPPER_EXIT },
|
||||
{ EntranceType::Overworld, RR_DMC_UPPER_NEARBY, RR_DEATH_MOUNTAIN_SUMMIT, ENTR_DEATH_MOUNTAIN_TRAIL_SUMMIT_EXIT } },
|
||||
{ { EntranceType::Overworld, RR_ZR_BEHIND_WATERFALL, RR_ZORAS_DOMAIN, ENTR_ZORAS_DOMAIN_ENTRANCE },
|
||||
{ EntranceType::Overworld, RR_ZORAS_DOMAIN, RR_ZR_BEHIND_WATERFALL, ENTR_ZORAS_RIVER_WATERFALL_EXIT } },
|
||||
{ { EntranceType::Overworld, RR_ZD_BEHIND_KING_ZORA, RR_ZORAS_FOUNTAIN, ENTR_ZORAS_FOUNTAIN_TUNNEL_EXIT },
|
||||
{ EntranceType::Overworld, RR_ZORAS_FOUNTAIN, RR_ZD_BEHIND_KING_ZORA, ENTR_ZORAS_DOMAIN_KING_ZORA_EXIT } },
|
||||
|
||||
{ { EntranceType::Overworld, RR_GV_LOWER_STREAM, RR_LAKE_HYLIA, ENTR_LAKE_HYLIA_RIVER_EXIT },
|
||||
NO_RETURN_ENTRANCE },
|
||||
|
||||
{ { EntranceType::OwlDrop, RR_LH_OWL_FLIGHT, RR_HYRULE_FIELD, ENTR_HYRULE_FIELD_OWL_DROP },
|
||||
NO_RETURN_ENTRANCE },
|
||||
{ { EntranceType::OwlDrop, RR_DMT_OWL_FLIGHT, RR_KAK_IMPAS_ROOFTOP, ENTR_KAKARIKO_VILLAGE_OWL_DROP },
|
||||
NO_RETURN_ENTRANCE },
|
||||
|
||||
{ { EntranceType::Spawn, RR_CHILD_SPAWN, RR_KF_LINKS_HOUSE, ENTR_LINKS_HOUSE_CHILD_SPAWN },
|
||||
NO_RETURN_ENTRANCE },
|
||||
{ { EntranceType::Spawn, RR_ADULT_SPAWN, RR_TEMPLE_OF_TIME, ENTR_HYRULE_FIELD_10 },
|
||||
NO_RETURN_ENTRANCE }, // 0x282 is an unused entrance index repurposed to differentiate between
|
||||
// Adult Spawn and prelude of light (normally they both use 0x5F4)
|
||||
{ { EntranceType::WarpSong, RR_MINUET_OF_FOREST_WARP, RR_SACRED_FOREST_MEADOW, ENTR_SACRED_FOREST_MEADOW_WARP_PAD },
|
||||
NO_RETURN_ENTRANCE },
|
||||
{ { EntranceType::WarpSong, RR_BOLERO_OF_FIRE_WARP, RR_DMC_CENTRAL_LOCAL, ENTR_DEATH_MOUNTAIN_CRATER_WARP_PAD },
|
||||
NO_RETURN_ENTRANCE },
|
||||
{ { EntranceType::WarpSong, RR_SERENADE_OF_WATER_WARP, RR_LAKE_HYLIA, ENTR_LAKE_HYLIA_WARP_PAD },
|
||||
NO_RETURN_ENTRANCE },
|
||||
{ { EntranceType::WarpSong, RR_REQUIEM_OF_SPIRIT_WARP, RR_DESERT_COLOSSUS, ENTR_DESERT_COLOSSUS_WARP_PAD },
|
||||
NO_RETURN_ENTRANCE },
|
||||
{ { EntranceType::WarpSong, RR_NOCTURNE_OF_SHADOW_WARP, RR_GRAVEYARD_WARP_PAD_REGION, ENTR_GRAVEYARD_WARP_PAD },
|
||||
NO_RETURN_ENTRANCE },
|
||||
{ { EntranceType::WarpSong, RR_PRELUDE_OF_LIGHT_WARP, RR_TEMPLE_OF_TIME, ENTR_TEMPLE_OF_TIME_WARP_PAD },
|
||||
NO_RETURN_ENTRANCE },
|
||||
|
||||
{ { EntranceType::ChildBoss, RR_DEKU_TREE_BOSS_ENTRYWAY, RR_DEKU_TREE_BOSS_ROOM, ENTR_DEKU_TREE_BOSS_ENTRANCE },
|
||||
{ EntranceType::ChildBoss, RR_DEKU_TREE_BOSS_ROOM, RR_DEKU_TREE_BOSS_EXIT, ENTR_DEKU_TREE_BOSS_DOOR } },
|
||||
{ { EntranceType::ChildBoss, RR_DODONGOS_CAVERN_BOSS_ENTRYWAY, RR_DODONGOS_CAVERN_BOSS_ROOM, ENTR_DODONGOS_CAVERN_BOSS_ENTRANCE },
|
||||
{ EntranceType::ChildBoss, RR_DODONGOS_CAVERN_BOSS_ROOM, RR_DODONGOS_CAVERN_BOSS_EXIT, ENTR_DODONGOS_CAVERN_BOSS_DOOR } },
|
||||
{ { EntranceType::ChildBoss, RR_JABU_JABUS_BELLY_BOSS_ENTRYWAY, RR_JABU_JABUS_BELLY_BOSS_ROOM, ENTR_JABU_JABU_BOSS_ENTRANCE },
|
||||
{ EntranceType::ChildBoss, RR_JABU_JABUS_BELLY_BOSS_ROOM, RR_JABU_JABUS_BELLY_BOSS_EXIT, ENTR_JABU_JABU_BOSS_DOOR } },
|
||||
{ { EntranceType::AdultBoss, RR_FOREST_TEMPLE_BOSS_ENTRYWAY, RR_FOREST_TEMPLE_BOSS_ROOM, ENTR_FOREST_TEMPLE_BOSS_ENTRANCE },
|
||||
{ EntranceType::AdultBoss, RR_FOREST_TEMPLE_BOSS_ROOM, RR_FOREST_TEMPLE_BOSS_ENTRYWAY, ENTR_FOREST_TEMPLE_BOSS_DOOR } },
|
||||
{ { EntranceType::AdultBoss, RR_FIRE_TEMPLE_BOSS_ENTRYWAY, RR_FIRE_TEMPLE_BOSS_ROOM, ENTR_FIRE_TEMPLE_BOSS_ENTRANCE },
|
||||
{ EntranceType::AdultBoss, RR_FIRE_TEMPLE_BOSS_ROOM, RR_FIRE_TEMPLE_BOSS_ENTRYWAY, ENTR_FIRE_TEMPLE_BOSS_DOOR } },
|
||||
{ { EntranceType::AdultBoss, RR_WATER_TEMPLE_BOSS_ENTRYWAY, RR_WATER_TEMPLE_BOSS_ROOM, ENTR_WATER_TEMPLE_BOSS_ENTRANCE },
|
||||
{ EntranceType::AdultBoss, RR_WATER_TEMPLE_BOSS_ROOM, RR_WATER_TEMPLE_BOSS_ENTRYWAY, ENTR_WATER_TEMPLE_BOSS_DOOR } },
|
||||
{ { EntranceType::AdultBoss, RR_SPIRIT_TEMPLE_BOSS_ENTRYWAY, RR_SPIRIT_TEMPLE_BOSS_ROOM, ENTR_SPIRIT_TEMPLE_BOSS_ENTRANCE },
|
||||
{ EntranceType::AdultBoss, RR_SPIRIT_TEMPLE_BOSS_ROOM, RR_SPIRIT_TEMPLE_BOSS_ENTRYWAY, ENTR_SPIRIT_TEMPLE_BOSS_DOOR } },
|
||||
{ { EntranceType::AdultBoss, RR_SHADOW_TEMPLE_BOSS_ENTRYWAY, RR_SHADOW_TEMPLE_BOSS_ROOM, ENTR_SHADOW_TEMPLE_BOSS_ENTRANCE },
|
||||
{ EntranceType::AdultBoss, RR_SHADOW_TEMPLE_BOSS_ROOM, RR_SHADOW_TEMPLE_BOSS_ENTRYWAY, ENTR_SHADOW_TEMPLE_BOSS_DOOR } },
|
||||
|
||||
{ { EntranceType::BlueWarp, RR_DEKU_TREE_BOSS_ROOM, RR_KF_OUTSIDE_DEKU_TREE, ENTR_KOKIRI_FOREST_DEKU_TREE_BLUE_WARP },
|
||||
NO_RETURN_ENTRANCE },
|
||||
{ { EntranceType::BlueWarp, RR_DODONGOS_CAVERN_BOSS_ROOM, RR_DEATH_MOUNTAIN_TRAIL, ENTR_DEATH_MOUNTAIN_TRAIL_DODONGO_BLUE_WARP },
|
||||
NO_RETURN_ENTRANCE },
|
||||
{ { EntranceType::BlueWarp, RR_JABU_JABUS_BELLY_BOSS_ROOM, RR_ZORAS_FOUNTAIN, ENTR_ZORAS_FOUNTAIN_JABU_JABU_BLUE_WARP },
|
||||
NO_RETURN_ENTRANCE },
|
||||
{ { EntranceType::BlueWarp, RR_FOREST_TEMPLE_BOSS_ROOM, RR_SACRED_FOREST_MEADOW, ENTR_SACRED_FOREST_MEADOW_FOREST_TEMPLE_BLUE_WARP },
|
||||
NO_RETURN_ENTRANCE },
|
||||
{ { EntranceType::BlueWarp, RR_FIRE_TEMPLE_BOSS_ROOM, RR_DMC_CENTRAL_LOCAL, ENTR_DEATH_MOUNTAIN_CRATER_FIRE_TEMPLE_BLUE_WARP },
|
||||
NO_RETURN_ENTRANCE },
|
||||
{ { EntranceType::BlueWarp, RR_WATER_TEMPLE_BOSS_ROOM, RR_LAKE_HYLIA, ENTR_LAKE_HYLIA_WATER_TEMPLE_BLUE_WARP },
|
||||
NO_RETURN_ENTRANCE },
|
||||
{ { EntranceType::BlueWarp, RR_SPIRIT_TEMPLE_BOSS_ROOM, RR_DESERT_COLOSSUS, ENTR_DESERT_COLOSSUS_SPIRIT_TEMPLE_BLUE_WARP },
|
||||
NO_RETURN_ENTRANCE },
|
||||
{ { EntranceType::BlueWarp, RR_SHADOW_TEMPLE_BOSS_ROOM, RR_GRAVEYARD_WARP_PAD_REGION, ENTR_GRAVEYARD_SHADOW_TEMPLE_BLUE_WARP },
|
||||
NO_RETURN_ENTRANCE },
|
||||
// clang-format on
|
||||
};
|
||||
|
||||
auto ctx = Rando::Context::GetInstance();
|
||||
for (auto& entrancePair : entranceShuffleTable) {
|
||||
|
||||
|
@ -262,6 +574,7 @@ void SetAllEntrancesData(std::vector<EntranceInfoPair>& entranceShuffleTable) {
|
|||
forwardEntrance->SetIndex(forwardEntry.index);
|
||||
forwardEntrance->SetType(forwardEntry.type);
|
||||
forwardEntrance->SetAsPrimary();
|
||||
entranceMap[forwardEntry.index] = forwardEntrance;
|
||||
|
||||
// When decouple entrances is on, mark the forward entrance
|
||||
if (ctx->GetOption(RSK_DECOUPLED_ENTRANCES)) {
|
||||
|
@ -273,6 +586,7 @@ void SetAllEntrancesData(std::vector<EntranceInfoPair>& entranceShuffleTable) {
|
|||
returnEntrance->SetIndex(returnEntry.index);
|
||||
returnEntrance->SetType(returnEntry.type);
|
||||
forwardEntrance->BindTwoWay(returnEntrance);
|
||||
entranceMap[returnEntry.index] = returnEntrance;
|
||||
|
||||
// Mark reverse entrance as decoupled
|
||||
if (ctx->GetOption(RSK_DECOUPLED_ENTRANCES)) {
|
||||
|
@ -855,316 +1169,6 @@ int EntranceShuffler::ShuffleAllEntrances() {
|
|||
mTotalRandomizableEntrances = 0;
|
||||
mCurNumRandomizedEntrances = 0;
|
||||
|
||||
std::vector<EntranceInfoPair> entranceShuffleTable = {
|
||||
// clang-format off
|
||||
// Type Parent Region Connected Region Index
|
||||
{ { EntranceType::Dungeon, RR_KF_OUTSIDE_DEKU_TREE, RR_DEKU_TREE_ENTRYWAY, ENTR_DEKU_TREE_ENTRANCE },
|
||||
{ EntranceType::Dungeon, RR_DEKU_TREE_ENTRYWAY, RR_KF_OUTSIDE_DEKU_TREE, ENTR_KOKIRI_FOREST_OUTSIDE_DEKU_TREE } },
|
||||
{ { EntranceType::Dungeon, RR_DEATH_MOUNTAIN_TRAIL, RR_DODONGOS_CAVERN_ENTRYWAY, ENTR_DODONGOS_CAVERN_ENTRANCE },
|
||||
{ EntranceType::Dungeon, RR_DODONGOS_CAVERN_ENTRYWAY, RR_DEATH_MOUNTAIN_TRAIL, ENTR_DEATH_MOUNTAIN_TRAIL_OUTSIDE_DODONGOS_CAVERN } },
|
||||
{ { EntranceType::Dungeon, RR_ZORAS_FOUNTAIN, RR_JABU_JABUS_BELLY_ENTRYWAY, ENTR_JABU_JABU_ENTRANCE },
|
||||
{ EntranceType::Dungeon, RR_JABU_JABUS_BELLY_ENTRYWAY, RR_ZORAS_FOUNTAIN, ENTR_ZORAS_FOUNTAIN_OUTSIDE_JABU_JABU } },
|
||||
{ { EntranceType::Dungeon, RR_SACRED_FOREST_MEADOW, RR_FOREST_TEMPLE_ENTRYWAY, ENTR_FOREST_TEMPLE_ENTRANCE },
|
||||
{ EntranceType::Dungeon, RR_FOREST_TEMPLE_ENTRYWAY, RR_SACRED_FOREST_MEADOW, ENTR_SACRED_FOREST_MEADOW_OUTSIDE_TEMPLE } },
|
||||
{ { EntranceType::Dungeon, RR_DMC_CENTRAL_LOCAL, RR_FIRE_TEMPLE_ENTRYWAY, ENTR_FIRE_TEMPLE_ENTRANCE },
|
||||
{ EntranceType::Dungeon, RR_FIRE_TEMPLE_ENTRYWAY, RR_DMC_CENTRAL_LOCAL, ENTR_DEATH_MOUNTAIN_CRATER_OUTSIDE_TEMPLE } },
|
||||
{ { EntranceType::Dungeon, RR_LAKE_HYLIA, RR_WATER_TEMPLE_ENTRYWAY, ENTR_WATER_TEMPLE_ENTRANCE },
|
||||
{ EntranceType::Dungeon, RR_WATER_TEMPLE_ENTRYWAY, RR_LAKE_HYLIA, ENTR_LAKE_HYLIA_OUTSIDE_TEMPLE } },
|
||||
{ { EntranceType::Dungeon, RR_DESERT_COLOSSUS, RR_SPIRIT_TEMPLE_ENTRYWAY, ENTR_SPIRIT_TEMPLE_ENTRANCE },
|
||||
{ EntranceType::Dungeon, RR_SPIRIT_TEMPLE_ENTRYWAY, RR_DESERT_COLOSSUS_OUTSIDE_TEMPLE, ENTR_DESERT_COLOSSUS_OUTSIDE_TEMPLE } },
|
||||
{ { EntranceType::Dungeon, RR_GRAVEYARD_WARP_PAD_REGION, RR_SHADOW_TEMPLE_ENTRYWAY, ENTR_SHADOW_TEMPLE_ENTRANCE },
|
||||
{ EntranceType::Dungeon, RR_SHADOW_TEMPLE_ENTRYWAY, RR_GRAVEYARD_WARP_PAD_REGION, ENTR_GRAVEYARD_OUTSIDE_TEMPLE } },
|
||||
{ { EntranceType::Dungeon, RR_KAK_WELL, RR_BOTTOM_OF_THE_WELL_ENTRYWAY, ENTR_BOTTOM_OF_THE_WELL_ENTRANCE },
|
||||
{ EntranceType::Dungeon, RR_BOTTOM_OF_THE_WELL_ENTRYWAY, RR_KAK_WELL, ENTR_KAKARIKO_VILLAGE_OUTSIDE_BOTTOM_OF_THE_WELL } },
|
||||
{ { EntranceType::Dungeon, RR_ZF_LEDGE, RR_ICE_CAVERN_ENTRYWAY, ENTR_ICE_CAVERN_ENTRANCE },
|
||||
{ EntranceType::Dungeon, RR_ICE_CAVERN_ENTRYWAY, RR_ZF_LEDGE, ENTR_ZORAS_FOUNTAIN_OUTSIDE_ICE_CAVERN } },
|
||||
{ { EntranceType::Dungeon, RR_GERUDO_FORTRESS, RR_GERUDO_TRAINING_GROUND_ENTRYWAY, ENTR_GERUDO_TRAINING_GROUND_ENTRANCE },
|
||||
{ EntranceType::Dungeon, RR_GERUDO_TRAINING_GROUND_ENTRYWAY, RR_GERUDO_FORTRESS, ENTR_GERUDOS_FORTRESS_OUTSIDE_GERUDO_TRAINING_GROUND } },
|
||||
{ { EntranceType::GanonDungeon, RR_GANONS_CASTLE_LEDGE, RR_GANONS_CASTLE_ENTRYWAY, ENTR_INSIDE_GANONS_CASTLE_ENTRANCE },
|
||||
{ EntranceType::GanonDungeon, RR_GANONS_CASTLE_ENTRYWAY, RR_CASTLE_GROUNDS_FROM_GANONS_CASTLE, ENTR_CASTLE_GROUNDS_RAINBOW_BRIDGE_EXIT } },
|
||||
|
||||
{ { EntranceType::Interior, RR_KOKIRI_FOREST, RR_KF_MIDOS_HOUSE, ENTR_MIDOS_HOUSE_0 },
|
||||
{ EntranceType::Interior, RR_KF_MIDOS_HOUSE, RR_KOKIRI_FOREST, ENTR_KOKIRI_FOREST_OUTSIDE_MIDOS_HOUSE } },
|
||||
{ { EntranceType::Interior, RR_KOKIRI_FOREST, RR_KF_SARIAS_HOUSE, ENTR_SARIAS_HOUSE_0 },
|
||||
{ EntranceType::Interior, RR_KF_SARIAS_HOUSE, RR_KOKIRI_FOREST, ENTR_KOKIRI_FOREST_OUTSIDE_SARIAS_HOUSE } },
|
||||
{ { EntranceType::Interior, RR_KOKIRI_FOREST, RR_KF_HOUSE_OF_TWINS, ENTR_TWINS_HOUSE_0 },
|
||||
{ EntranceType::Interior, RR_KF_HOUSE_OF_TWINS, RR_KOKIRI_FOREST, ENTR_KOKIRI_FOREST_OUTSIDE_TWINS_HOUSE } },
|
||||
{ { EntranceType::Interior, RR_KOKIRI_FOREST, RR_KF_KNOW_IT_ALL_HOUSE, ENTR_KNOW_IT_ALL_BROS_HOUSE_0 },
|
||||
{ EntranceType::Interior, RR_KF_KNOW_IT_ALL_HOUSE, RR_KOKIRI_FOREST, ENTR_KOKIRI_FOREST_OUTSIDE_KNOW_IT_ALL_HOUSE } },
|
||||
{ { EntranceType::Interior, RR_KOKIRI_FOREST, RR_KF_KOKIRI_SHOP, ENTR_KOKIRI_SHOP_0 },
|
||||
{ EntranceType::Interior, RR_KF_KOKIRI_SHOP, RR_KOKIRI_FOREST, ENTR_KOKIRI_FOREST_OUTSIDE_SHOP } },
|
||||
{ { EntranceType::Interior, RR_LAKE_HYLIA, RR_LH_LAB, ENTR_LAKESIDE_LABORATORY_0 },
|
||||
{ EntranceType::Interior, RR_LH_LAB, RR_LAKE_HYLIA, ENTR_LAKE_HYLIA_OUTSIDE_LAB } },
|
||||
{ { EntranceType::Interior, RR_LH_FISHING_ISLAND, RR_LH_FISHING_POND, ENTR_FISHING_POND_0 },
|
||||
{ EntranceType::Interior, RR_LH_FISHING_POND, RR_LH_FISHING_ISLAND, ENTR_LAKE_HYLIA_OUTSIDE_FISHING_POND } },
|
||||
{ { EntranceType::Interior, RR_GV_FORTRESS_SIDE, RR_GV_CARPENTER_TENT, ENTR_CARPENTERS_TENT_0 },
|
||||
{ EntranceType::Interior, RR_GV_CARPENTER_TENT, RR_GV_FORTRESS_SIDE, ENTR_GERUDO_VALLEY_OUTSIDE_TENT } },
|
||||
{ { EntranceType::Interior, RR_MARKET_ENTRANCE, RR_MARKET_GUARD_HOUSE, ENTR_MARKET_GUARD_HOUSE_0 },
|
||||
{ EntranceType::Interior, RR_MARKET_GUARD_HOUSE, RR_MARKET_ENTRANCE, ENTR_MARKET_ENTRANCE_OUTSIDE_GUARD_HOUSE } },
|
||||
{ { EntranceType::Interior, RR_THE_MARKET, RR_MARKET_MASK_SHOP, ENTR_HAPPY_MASK_SHOP_0 },
|
||||
{ EntranceType::Interior, RR_MARKET_MASK_SHOP, RR_THE_MARKET, ENTR_MARKET_DAY_OUTSIDE_HAPPY_MASK_SHOP } },
|
||||
{ { EntranceType::Interior, RR_THE_MARKET, RR_MARKET_BOMBCHU_BOWLING, ENTR_BOMBCHU_BOWLING_ALLEY_0 },
|
||||
{ EntranceType::Interior, RR_MARKET_BOMBCHU_BOWLING, RR_THE_MARKET, ENTR_MARKET_DAY_OUTSIDE_BOMBCHU_BOWLING } },
|
||||
{ { EntranceType::Interior, RR_THE_MARKET, RR_MARKET_POTION_SHOP, ENTR_POTION_SHOP_MARKET_0 },
|
||||
{ EntranceType::Interior, RR_MARKET_POTION_SHOP, RR_THE_MARKET, ENTR_MARKET_DAY_OUTSIDE_POTION_SHOP } },
|
||||
{ { EntranceType::Interior, RR_THE_MARKET, RR_MARKET_TREASURE_CHEST_GAME, ENTR_TREASURE_BOX_SHOP_0 },
|
||||
{ EntranceType::Interior, RR_MARKET_TREASURE_CHEST_GAME, RR_THE_MARKET, ENTR_MARKET_DAY_OUTSIDE_TREASURE_BOX_SHOP } },
|
||||
{ { EntranceType::Interior, RR_MARKET_BACK_ALLEY, RR_MARKET_BOMBCHU_SHOP, ENTR_BOMBCHU_SHOP_1 },
|
||||
{ EntranceType::Interior, RR_MARKET_BOMBCHU_SHOP, RR_MARKET_BACK_ALLEY, ENTR_BACK_ALLEY_DAY_OUTSIDE_BOMBCHU_SHOP } },
|
||||
{ { EntranceType::Interior, RR_MARKET_BACK_ALLEY, RR_MARKET_MAN_IN_GREEN_HOUSE, ENTR_BACK_ALLEY_MAN_IN_GREEN_HOUSE },
|
||||
{ EntranceType::Interior, RR_MARKET_MAN_IN_GREEN_HOUSE, RR_MARKET_BACK_ALLEY, ENTR_BACK_ALLEY_DAY_OUTSIDE_MAN_IN_GREEN_HOUSE } },
|
||||
{ { EntranceType::Interior, RR_KAKARIKO_VILLAGE, RR_KAK_CARPENTER_BOSS_HOUSE, ENTR_KAKARIKO_CENTER_GUEST_HOUSE_0 },
|
||||
{ EntranceType::Interior, RR_KAK_CARPENTER_BOSS_HOUSE, RR_KAKARIKO_VILLAGE, ENTR_KAKARIKO_VILLAGE_OUTSIDE_CENTER_GUEST_HOUSE } },
|
||||
{ { EntranceType::Interior, RR_KAKARIKO_VILLAGE, RR_KAK_HOUSE_OF_SKULLTULA, ENTR_HOUSE_OF_SKULLTULA_0 },
|
||||
{ EntranceType::Interior, RR_KAK_HOUSE_OF_SKULLTULA, RR_KAKARIKO_VILLAGE, ENTR_KAKARIKO_VILLAGE_OUTSIDE_SKULKLTULA_HOUSE } },
|
||||
{ { EntranceType::Interior, RR_KAKARIKO_VILLAGE, RR_KAK_IMPAS_HOUSE, ENTR_IMPAS_HOUSE_FRONT },
|
||||
{ EntranceType::Interior, RR_KAK_IMPAS_HOUSE, RR_KAKARIKO_VILLAGE, ENTR_KAKARIKO_VILLAGE_OUTSIDE_IMPAS_HOUSE_FRONT } },
|
||||
{ { EntranceType::Interior, RR_KAK_IMPAS_LEDGE, RR_KAK_IMPAS_HOUSE_BACK, ENTR_IMPAS_HOUSE_BACK },
|
||||
{ EntranceType::Interior, RR_KAK_IMPAS_HOUSE_BACK, RR_KAK_IMPAS_LEDGE, ENTR_KAKARIKO_VILLAGE_OUTSIDE_IMPAS_HOUSE_BACK } },
|
||||
{ { EntranceType::Interior, RR_KAK_BACKYARD, RR_KAK_ODD_POTION_BUILDING, ENTR_POTION_SHOP_GRANNY_0 },
|
||||
{ EntranceType::Interior, RR_KAK_ODD_POTION_BUILDING, RR_KAK_BACKYARD, ENTR_KAKARIKO_VILLAGE_OUTSIDE_SHOP_GRANNY } },
|
||||
{ { EntranceType::Interior, RR_THE_GRAVEYARD, RR_GRAVEYARD_DAMPES_HOUSE, ENTR_GRAVEKEEPERS_HUT_0 },
|
||||
{ EntranceType::Interior, RR_GRAVEYARD_DAMPES_HOUSE, RR_THE_GRAVEYARD, ENTR_GRAVEYARD_OUTSIDE_DAMPES_HUT } },
|
||||
{ { EntranceType::Interior, RR_GORON_CITY, RR_GC_SHOP, ENTR_GORON_SHOP_0 },
|
||||
{ EntranceType::Interior, RR_GC_SHOP, RR_GORON_CITY, ENTR_GORON_CITY_OUTSIDE_SHOP } },
|
||||
{ { EntranceType::Interior, RR_ZORAS_DOMAIN, RR_ZD_SHOP, ENTR_ZORA_SHOP_0 },
|
||||
{ EntranceType::Interior, RR_ZD_SHOP, RR_ZORAS_DOMAIN, ENTR_ZORAS_DOMAIN_OUTSIDE_SHOP } },
|
||||
{ { EntranceType::Interior, RR_LON_LON_RANCH, RR_LLR_TALONS_HOUSE, ENTR_LON_LON_BUILDINGS_TALONS_HOUSE },
|
||||
{ EntranceType::Interior, RR_LLR_TALONS_HOUSE, RR_LON_LON_RANCH, ENTR_LON_LON_RANCH_OUTSIDE_TALONS_HOUSE } },
|
||||
{ { EntranceType::Interior, RR_LON_LON_RANCH, RR_LLR_STABLES, ENTR_STABLE_0 },
|
||||
{ EntranceType::Interior, RR_LLR_STABLES, RR_LON_LON_RANCH, ENTR_LON_LON_RANCH_OUTSIDE_STABLES } },
|
||||
{ { EntranceType::Interior, RR_LON_LON_RANCH, RR_LLR_TOWER, ENTR_LON_LON_BUILDINGS_TOWER },
|
||||
{ EntranceType::Interior, RR_LLR_TOWER, RR_LON_LON_RANCH, ENTR_LON_LON_RANCH_OUTSIDE_TOWER } },
|
||||
{ { EntranceType::Interior, RR_THE_MARKET, RR_MARKET_BAZAAR, ENTR_BAZAAR_1 },
|
||||
{ EntranceType::Interior, RR_MARKET_BAZAAR, RR_THE_MARKET, ENTR_MARKET_DAY_OUTSIDE_BAZAAR } },
|
||||
{ { EntranceType::Interior, RR_THE_MARKET, RR_MARKET_SHOOTING_GALLERY, ENTR_SHOOTING_GALLERY_1 },
|
||||
{ EntranceType::Interior, RR_MARKET_SHOOTING_GALLERY, RR_THE_MARKET, ENTR_MARKET_DAY_OUTSIDE_SHOOTING_GALLERY } },
|
||||
{ { EntranceType::Interior, RR_KAKARIKO_VILLAGE, RR_KAK_BAZAAR, ENTR_BAZAAR_0 },
|
||||
{ EntranceType::Interior, RR_KAK_BAZAAR, RR_KAKARIKO_VILLAGE, ENTR_KAKARIKO_VILLAGE_OUTSIDE_BAZAAR } },
|
||||
{ { EntranceType::Interior, RR_KAKARIKO_VILLAGE, RR_KAK_SHOOTING_GALLERY, ENTR_SHOOTING_GALLERY_0 },
|
||||
{ EntranceType::Interior, RR_KAK_SHOOTING_GALLERY, RR_KAKARIKO_VILLAGE, ENTR_KAKARIKO_VILLAGE_OUTSIDE_SHOOTING_GALLERY } },
|
||||
{ { EntranceType::Interior, RR_DESERT_COLOSSUS, RR_COLOSSUS_GREAT_FAIRY_FOUNTAIN, ENTR_GREAT_FAIRYS_FOUNTAIN_SPELLS_NAYRUS_COLOSSUS },
|
||||
{ EntranceType::Interior, RR_COLOSSUS_GREAT_FAIRY_FOUNTAIN, RR_DESERT_COLOSSUS, ENTR_DESERT_COLOSSUS_GREAT_FAIRY_EXIT } },
|
||||
{ { EntranceType::Interior, RR_HYRULE_CASTLE_GROUNDS, RR_HC_GREAT_FAIRY_FOUNTAIN, ENTR_GREAT_FAIRYS_FOUNTAIN_SPELLS_DINS_HC },
|
||||
{ EntranceType::Interior, RR_HC_GREAT_FAIRY_FOUNTAIN, RR_CASTLE_GROUNDS, ENTR_CASTLE_GROUNDS_GREAT_FAIRY_EXIT } },
|
||||
{ { EntranceType::Interior, RR_GANONS_CASTLE_GROUNDS, RR_OGC_GREAT_FAIRY_FOUNTAIN, ENTR_GREAT_FAIRYS_FOUNTAIN_MAGIC_OGC_DD },
|
||||
// 0x3E8 is an unused entrance index repruposed to differentiate between the HC and OGC fairy
|
||||
// fountain exits (normally they both use 0x340)
|
||||
{ EntranceType::Interior, RR_OGC_GREAT_FAIRY_FOUNTAIN, RR_CASTLE_GROUNDS, ENTR_POTION_SHOP_KAKARIKO_1 } },
|
||||
{ { EntranceType::Interior, RR_DMC_LOWER_NEARBY, RR_DMC_GREAT_FAIRY_FOUNTAIN, ENTR_GREAT_FAIRYS_FOUNTAIN_MAGIC_DMC },
|
||||
{ EntranceType::Interior, RR_DMC_GREAT_FAIRY_FOUNTAIN, RR_DMC_LOWER_LOCAL, ENTR_DEATH_MOUNTAIN_CRATER_GREAT_FAIRY_EXIT } },
|
||||
{ { EntranceType::Interior, RR_DEATH_MOUNTAIN_SUMMIT, RR_DMT_GREAT_FAIRY_FOUNTAIN, ENTR_GREAT_FAIRYS_FOUNTAIN_MAGIC_DMT },
|
||||
{ EntranceType::Interior, RR_DMT_GREAT_FAIRY_FOUNTAIN, RR_DEATH_MOUNTAIN_SUMMIT, ENTR_DEATH_MOUNTAIN_TRAIL_GREAT_FAIRY_EXIT } },
|
||||
{ { EntranceType::Interior, RR_ZORAS_FOUNTAIN, RR_ZF_GREAT_FAIRY_FOUNTAIN, ENTR_GREAT_FAIRYS_FOUNTAIN_SPELLS_FARORES_ZF },
|
||||
{ EntranceType::Interior, RR_ZF_GREAT_FAIRY_FOUNTAIN, RR_ZORAS_FOUNTAIN, ENTR_ZORAS_FOUNTAIN_OUTSIDE_GREAT_FAIRY } },
|
||||
|
||||
{ { EntranceType::SpecialInterior, RR_KOKIRI_FOREST, RR_KF_LINKS_HOUSE, ENTR_LINKS_HOUSE_1 },
|
||||
{ EntranceType::SpecialInterior, RR_KF_LINKS_HOUSE, RR_KOKIRI_FOREST, ENTR_KOKIRI_FOREST_OUTSIDE_LINKS_HOUSE } },
|
||||
{ { EntranceType::SpecialInterior, RR_TOT_ENTRANCE, RR_TEMPLE_OF_TIME, ENTR_TEMPLE_OF_TIME_ENTRANCE },
|
||||
{ EntranceType::SpecialInterior, RR_TEMPLE_OF_TIME, RR_TOT_ENTRANCE, ENTR_TEMPLE_OF_TIME_EXTERIOR_DAY_OUTSIDE_TEMPLE } },
|
||||
{ { EntranceType::SpecialInterior, RR_KAKARIKO_VILLAGE, RR_KAK_WINDMILL, ENTR_WINDMILL_AND_DAMPES_GRAVE_WINDMILL },
|
||||
{ EntranceType::SpecialInterior, RR_KAK_WINDMILL, RR_KAKARIKO_VILLAGE, ENTR_KAKARIKO_VILLAGE_OUTSIDE_WINDMILL } },
|
||||
{ { EntranceType::SpecialInterior, RR_KAKARIKO_VILLAGE, RR_KAK_POTION_SHOP_FRONT, ENTR_POTION_SHOP_KAKARIKO_FRONT },
|
||||
{ EntranceType::SpecialInterior, RR_KAK_POTION_SHOP_FRONT, RR_KAKARIKO_VILLAGE, ENTR_KAKARIKO_VILLAGE_OUTSIDE_POTION_SHOP_FRONT } },
|
||||
{ { EntranceType::SpecialInterior, RR_KAK_BACKYARD, RR_KAK_POTION_SHOP_BACK, ENTR_POTION_SHOP_KAKARIKO_BACK },
|
||||
{ EntranceType::SpecialInterior, RR_KAK_POTION_SHOP_BACK, RR_KAK_BACKYARD, ENTR_KAKARIKO_VILLAGE_OUTSIDE_POTION_SHOP_BACK } },
|
||||
|
||||
// Grotto Loads use an entrance index of 0x0700 + their grotto id. The id is used as index for the
|
||||
// grottoLoadTable in soh/soh/Enhancements/randomizer/randomizer_grotto.c
|
||||
// Grotto Returns use an entrance index of 0x0800 + their grotto id. The id is used as index for the
|
||||
// grottoReturnTable in soh/soh/Enhancements/randomizer/randomizer_grotto.c
|
||||
{ { EntranceType::GrottoGrave, RR_DESERT_COLOSSUS, RR_COLOSSUS_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_COLOSSUS_OFFSET) },
|
||||
{ EntranceType::GrottoGrave, RR_COLOSSUS_GROTTO, RR_DESERT_COLOSSUS, ENTRANCE_GROTTO_EXIT(GROTTO_COLOSSUS_OFFSET) } },
|
||||
{ { EntranceType::GrottoGrave, RR_LAKE_HYLIA, RR_LH_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_LH_OFFSET) },
|
||||
{ EntranceType::GrottoGrave, RR_LH_GROTTO, RR_LAKE_HYLIA, ENTRANCE_GROTTO_EXIT(GROTTO_LH_OFFSET) } },
|
||||
{ { EntranceType::GrottoGrave, RR_ZORAS_RIVER, RR_ZR_STORMS_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_ZR_STORMS_OFFSET) },
|
||||
{ EntranceType::GrottoGrave, RR_ZR_STORMS_GROTTO, RR_ZORAS_RIVER, ENTRANCE_GROTTO_EXIT(GROTTO_ZR_STORMS_OFFSET) } },
|
||||
{ { EntranceType::GrottoGrave, RR_ZORAS_RIVER, RR_ZR_FAIRY_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_ZR_FAIRY_OFFSET) },
|
||||
{ EntranceType::GrottoGrave, RR_ZR_FAIRY_GROTTO, RR_ZORAS_RIVER, ENTRANCE_GROTTO_EXIT(GROTTO_ZR_FAIRY_OFFSET) } },
|
||||
{ { EntranceType::GrottoGrave, RR_ZORAS_RIVER, RR_ZR_OPEN_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_ZR_OPEN_OFFSET) },
|
||||
{ EntranceType::GrottoGrave, RR_ZR_OPEN_GROTTO, RR_ZORAS_RIVER, ENTRANCE_GROTTO_EXIT(GROTTO_ZR_OPEN_OFFSET) } },
|
||||
{ { EntranceType::GrottoGrave, RR_DMC_LOWER_NEARBY, RR_DMC_HAMMER_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_DMC_HAMMER_OFFSET) },
|
||||
{ EntranceType::GrottoGrave, RR_DMC_HAMMER_GROTTO, RR_DMC_LOWER_LOCAL, ENTRANCE_GROTTO_EXIT(GROTTO_DMC_HAMMER_OFFSET) } },
|
||||
{ { EntranceType::GrottoGrave, RR_DMC_UPPER_NEARBY, RR_DMC_UPPER_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_DMC_UPPER_OFFSET) },
|
||||
{ EntranceType::GrottoGrave, RR_DMC_UPPER_GROTTO, RR_DMC_UPPER_LOCAL, ENTRANCE_GROTTO_EXIT(GROTTO_DMC_UPPER_OFFSET) } },
|
||||
{ { EntranceType::GrottoGrave, RR_GC_GROTTO_PLATFORM, RR_GC_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_GORON_CITY_OFFSET) },
|
||||
{ EntranceType::GrottoGrave, RR_GC_GROTTO, RR_GC_GROTTO_PLATFORM, ENTRANCE_GROTTO_EXIT(GROTTO_GORON_CITY_OFFSET) } },
|
||||
{ { EntranceType::GrottoGrave, RR_DEATH_MOUNTAIN_TRAIL, RR_DMT_STORMS_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_DMT_STORMS_OFFSET) },
|
||||
{ EntranceType::GrottoGrave, RR_DMT_STORMS_GROTTO, RR_DEATH_MOUNTAIN_TRAIL, ENTRANCE_GROTTO_EXIT(GROTTO_DMT_STORMS_OFFSET) } },
|
||||
{ { EntranceType::GrottoGrave, RR_DEATH_MOUNTAIN_SUMMIT, RR_DMT_COW_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_DMT_COW_OFFSET) },
|
||||
{ EntranceType::GrottoGrave, RR_DMT_COW_GROTTO, RR_DEATH_MOUNTAIN_SUMMIT, ENTRANCE_GROTTO_EXIT(GROTTO_DMT_COW_OFFSET) } },
|
||||
{ { EntranceType::GrottoGrave, RR_KAK_BACKYARD, RR_KAK_OPEN_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_KAK_OPEN_OFFSET) },
|
||||
{ EntranceType::GrottoGrave, RR_KAK_OPEN_GROTTO, RR_KAK_BACKYARD, ENTRANCE_GROTTO_EXIT(GROTTO_KAK_OPEN_OFFSET) } },
|
||||
{ { EntranceType::GrottoGrave, RR_KAKARIKO_VILLAGE, RR_KAK_REDEAD_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_KAK_REDEAD_OFFSET) },
|
||||
{ EntranceType::GrottoGrave, RR_KAK_REDEAD_GROTTO, RR_KAKARIKO_VILLAGE, ENTRANCE_GROTTO_EXIT(GROTTO_KAK_REDEAD_OFFSET) } },
|
||||
{ { EntranceType::GrottoGrave, RR_HYRULE_CASTLE_GROUNDS, RR_HC_STORMS_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_HC_STORMS_OFFSET) },
|
||||
{ EntranceType::GrottoGrave, RR_HC_STORMS_GROTTO, RR_CASTLE_GROUNDS, ENTRANCE_GROTTO_EXIT(GROTTO_HC_STORMS_OFFSET) } },
|
||||
{ { EntranceType::GrottoGrave, RR_HYRULE_FIELD, RR_HF_TEKTITE_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_HF_TEKTITE_OFFSET) },
|
||||
{ EntranceType::GrottoGrave, RR_HF_TEKTITE_GROTTO, RR_HYRULE_FIELD, ENTRANCE_GROTTO_EXIT(GROTTO_HF_TEKTITE_OFFSET) } },
|
||||
{ { EntranceType::GrottoGrave, RR_HYRULE_FIELD, RR_HF_NEAR_KAK_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_HF_NEAR_KAK_OFFSET) },
|
||||
{ EntranceType::GrottoGrave, RR_HF_NEAR_KAK_GROTTO, RR_HYRULE_FIELD, ENTRANCE_GROTTO_EXIT(GROTTO_HF_NEAR_KAK_OFFSET) } },
|
||||
{ { EntranceType::GrottoGrave, RR_HYRULE_FIELD, RR_HF_FAIRY_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_HF_FAIRY_OFFSET) },
|
||||
{ EntranceType::GrottoGrave, RR_HF_FAIRY_GROTTO, RR_HYRULE_FIELD, ENTRANCE_GROTTO_EXIT(GROTTO_HF_FAIRY_OFFSET) } },
|
||||
{ { EntranceType::GrottoGrave, RR_HYRULE_FIELD, RR_HF_NEAR_MARKET_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_HF_NEAR_MARKET_OFFSET) },
|
||||
{ EntranceType::GrottoGrave, RR_HF_NEAR_MARKET_GROTTO, RR_HYRULE_FIELD, ENTRANCE_GROTTO_EXIT(GROTTO_HF_NEAR_MARKET_OFFSET) } },
|
||||
{ { EntranceType::GrottoGrave, RR_HYRULE_FIELD, RR_HF_COW_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_HF_COW_OFFSET) },
|
||||
{ EntranceType::GrottoGrave, RR_HF_COW_GROTTO, RR_HYRULE_FIELD, ENTRANCE_GROTTO_EXIT(GROTTO_HF_COW_OFFSET) } },
|
||||
{ { EntranceType::GrottoGrave, RR_HYRULE_FIELD, RR_HF_INSIDE_FENCE_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_HF_INSIDE_FENCE_OFFSET) },
|
||||
{ EntranceType::GrottoGrave, RR_HF_INSIDE_FENCE_GROTTO, RR_HYRULE_FIELD, ENTRANCE_GROTTO_EXIT(GROTTO_HF_INSIDE_FENCE_OFFSET) } },
|
||||
{ { EntranceType::GrottoGrave, RR_HYRULE_FIELD, RR_HF_OPEN_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_HF_OPEN_OFFSET) },
|
||||
{ EntranceType::GrottoGrave, RR_HF_OPEN_GROTTO, RR_HYRULE_FIELD, ENTRANCE_GROTTO_EXIT(GROTTO_HF_OPEN_OFFSET) } },
|
||||
{ { EntranceType::GrottoGrave, RR_HYRULE_FIELD, RR_HF_SOUTHEAST_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_HF_SOUTHEAST_OFFSET) },
|
||||
{ EntranceType::GrottoGrave, RR_HF_SOUTHEAST_GROTTO, RR_HYRULE_FIELD, ENTRANCE_GROTTO_EXIT(GROTTO_HF_SOUTHEAST_OFFSET) } },
|
||||
{ { EntranceType::GrottoGrave, RR_LON_LON_RANCH, RR_LLR_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_LLR_OFFSET) },
|
||||
{ EntranceType::GrottoGrave, RR_LLR_GROTTO, RR_LON_LON_RANCH, ENTRANCE_GROTTO_EXIT(GROTTO_LLR_OFFSET) } },
|
||||
{ { EntranceType::GrottoGrave, RR_SFM_ENTRYWAY, RR_SFM_WOLFOS_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_SFM_WOLFOS_OFFSET) },
|
||||
{ EntranceType::GrottoGrave, RR_SFM_WOLFOS_GROTTO, RR_SFM_ENTRYWAY, ENTRANCE_GROTTO_EXIT(GROTTO_SFM_WOLFOS_OFFSET) } },
|
||||
{ { EntranceType::GrottoGrave, RR_SACRED_FOREST_MEADOW, RR_SFM_STORMS_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_SFM_STORMS_OFFSET) },
|
||||
{ EntranceType::GrottoGrave, RR_SFM_STORMS_GROTTO, RR_SACRED_FOREST_MEADOW, ENTRANCE_GROTTO_EXIT(GROTTO_SFM_STORMS_OFFSET) } },
|
||||
{ { EntranceType::GrottoGrave, RR_SACRED_FOREST_MEADOW, RR_SFM_FAIRY_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_SFM_FAIRY_OFFSET) },
|
||||
{ EntranceType::GrottoGrave, RR_SFM_FAIRY_GROTTO, RR_SACRED_FOREST_MEADOW, ENTRANCE_GROTTO_EXIT(GROTTO_SFM_FAIRY_OFFSET) } },
|
||||
{ { EntranceType::GrottoGrave, RR_LW_BEYOND_MIDO, RR_LW_SCRUBS_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_LW_SCRUBS_OFFSET) },
|
||||
{ EntranceType::GrottoGrave, RR_LW_SCRUBS_GROTTO, RR_LW_BEYOND_MIDO, ENTRANCE_GROTTO_EXIT(GROTTO_LW_SCRUBS_OFFSET) } },
|
||||
{ { EntranceType::GrottoGrave, RR_THE_LOST_WOODS, RR_LW_NEAR_SHORTCUTS_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_LW_NEAR_SHORTCUTS_OFFSET) },
|
||||
{ EntranceType::GrottoGrave, RR_LW_NEAR_SHORTCUTS_GROTTO, RR_THE_LOST_WOODS, ENTRANCE_GROTTO_EXIT(GROTTO_LW_NEAR_SHORTCUTS_OFFSET) } },
|
||||
{ { EntranceType::GrottoGrave, RR_KOKIRI_FOREST, RR_KF_STORMS_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_KF_STORMS_OFFSET) },
|
||||
{ EntranceType::GrottoGrave, RR_KF_STORMS_GROTTO, RR_KOKIRI_FOREST, ENTRANCE_GROTTO_EXIT(GROTTO_KF_STORMS_OFFSET) } },
|
||||
{ { EntranceType::GrottoGrave, RR_ZORAS_DOMAIN_ISLAND, RR_ZD_STORMS_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_ZD_STORMS_OFFSET) },
|
||||
{ EntranceType::GrottoGrave, RR_ZD_STORMS_GROTTO, RR_ZORAS_DOMAIN_ISLAND, ENTRANCE_GROTTO_EXIT(GROTTO_ZD_STORMS_OFFSET) } },
|
||||
{ { EntranceType::GrottoGrave, RR_GERUDO_FORTRESS, RR_GF_STORMS_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_GF_STORMS_OFFSET) },
|
||||
{ EntranceType::GrottoGrave, RR_GF_STORMS_GROTTO, RR_GERUDO_FORTRESS, ENTRANCE_GROTTO_EXIT(GROTTO_GF_STORMS_OFFSET) } },
|
||||
{ { EntranceType::GrottoGrave, RR_GV_FORTRESS_SIDE, RR_GV_STORMS_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_GV_STORMS_OFFSET) },
|
||||
{ EntranceType::GrottoGrave, RR_GV_STORMS_GROTTO, RR_GV_FORTRESS_SIDE, ENTRANCE_GROTTO_EXIT(GROTTO_GV_STORMS_OFFSET) } },
|
||||
{ { EntranceType::GrottoGrave, RR_GV_GROTTO_LEDGE, RR_GV_OCTOROK_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_GV_OCTOROK_OFFSET) },
|
||||
{ EntranceType::GrottoGrave, RR_GV_OCTOROK_GROTTO, RR_GV_GROTTO_LEDGE, ENTRANCE_GROTTO_EXIT(GROTTO_GV_OCTOROK_OFFSET) } },
|
||||
{ { EntranceType::GrottoGrave, RR_LW_BEYOND_MIDO, RR_DEKU_THEATER, ENTRANCE_GROTTO_LOAD(GROTTO_LW_DEKU_THEATRE_OFFSET) },
|
||||
{ EntranceType::GrottoGrave, RR_DEKU_THEATER, RR_LW_BEYOND_MIDO, ENTRANCE_GROTTO_EXIT(GROTTO_LW_DEKU_THEATRE_OFFSET) } },
|
||||
|
||||
// Graves have their own specified entrance indices
|
||||
{ { EntranceType::GrottoGrave, RR_THE_GRAVEYARD, RR_GRAVEYARD_SHIELD_GRAVE, ENTR_GRAVE_WITH_FAIRYS_FOUNTAIN_0 },
|
||||
{ EntranceType::GrottoGrave, RR_GRAVEYARD_SHIELD_GRAVE, RR_THE_GRAVEYARD, ENTR_GRAVEYARD_SHIELD_GRAVE_EXIT } },
|
||||
{ { EntranceType::GrottoGrave, RR_THE_GRAVEYARD, RR_GRAVEYARD_HEART_PIECE_GRAVE, ENTR_REDEAD_GRAVE_0 },
|
||||
{ EntranceType::GrottoGrave, RR_GRAVEYARD_HEART_PIECE_GRAVE, RR_THE_GRAVEYARD, ENTR_GRAVEYARD_HEART_PIECE_GRAVE_EXIT } },
|
||||
{ { EntranceType::GrottoGrave, RR_THE_GRAVEYARD, RR_GRAVEYARD_COMPOSERS_GRAVE, ENTR_ROYAL_FAMILYS_TOMB_0 },
|
||||
{ EntranceType::GrottoGrave, RR_GRAVEYARD_COMPOSERS_GRAVE, RR_THE_GRAVEYARD, ENTR_GRAVEYARD_ROYAL_TOMB_EXIT } },
|
||||
{ { EntranceType::GrottoGrave, RR_THE_GRAVEYARD, RR_GRAVEYARD_DAMPES_GRAVE, ENTR_WINDMILL_AND_DAMPES_GRAVE_GRAVE },
|
||||
{ EntranceType::GrottoGrave, RR_GRAVEYARD_DAMPES_GRAVE, RR_THE_GRAVEYARD, ENTR_GRAVEYARD_DAMPES_GRAVE_EXIT } },
|
||||
|
||||
{ { EntranceType::Overworld, RR_KOKIRI_FOREST, RR_LW_BRIDGE_FROM_FOREST, ENTR_LOST_WOODS_BRIDGE_EAST_EXIT },
|
||||
{ EntranceType::Overworld, RR_LW_BRIDGE, RR_KOKIRI_FOREST, ENTR_KOKIRI_FOREST_LOWER_EXIT } },
|
||||
{ { EntranceType::Overworld, RR_KOKIRI_FOREST, RR_THE_LOST_WOODS, ENTR_LOST_WOODS_SOUTH_EXIT },
|
||||
{ EntranceType::Overworld, RR_LW_FOREST_EXIT, RR_KOKIRI_FOREST, ENTR_KOKIRI_FOREST_UPPER_EXIT } },
|
||||
{ { EntranceType::Overworld, RR_THE_LOST_WOODS, RR_GC_WOODS_WARP, ENTR_GORON_CITY_TUNNEL_SHORTCUT },
|
||||
{ EntranceType::Overworld, RR_GC_WOODS_WARP, RR_THE_LOST_WOODS, ENTR_LOST_WOODS_TUNNEL_SHORTCUT } },
|
||||
{ { EntranceType::Overworld, RR_THE_LOST_WOODS, RR_ZORAS_RIVER, ENTR_ZORAS_RIVER_UNDERWATER_SHORTCUT },
|
||||
{ EntranceType::Overworld, RR_ZORAS_RIVER, RR_THE_LOST_WOODS, ENTR_LOST_WOODS_UNDERWATER_SHORTCUT } },
|
||||
{ { EntranceType::Overworld, RR_LW_BEYOND_MIDO, RR_SFM_ENTRYWAY, ENTR_SACRED_FOREST_MEADOW_SOUTH_EXIT },
|
||||
{ EntranceType::Overworld, RR_SFM_ENTRYWAY, RR_LW_BEYOND_MIDO, ENTR_LOST_WOODS_NORTH_EXIT } },
|
||||
{ { EntranceType::Overworld, RR_LW_BRIDGE, RR_HYRULE_FIELD, ENTR_HYRULE_FIELD_WOODED_EXIT },
|
||||
{ EntranceType::Overworld, RR_HYRULE_FIELD, RR_LW_BRIDGE, ENTR_LOST_WOODS_BRIDGE_WEST_EXIT } },
|
||||
{ { EntranceType::Overworld, RR_HYRULE_FIELD, RR_LAKE_HYLIA, ENTR_LAKE_HYLIA_NORTH_EXIT },
|
||||
{ EntranceType::Overworld, RR_LAKE_HYLIA, RR_HYRULE_FIELD, ENTR_HYRULE_FIELD_FENCE_EXIT } },
|
||||
{ { EntranceType::Overworld, RR_HYRULE_FIELD, RR_GERUDO_VALLEY, ENTR_GERUDO_VALLEY_EAST_EXIT },
|
||||
{ EntranceType::Overworld, RR_GERUDO_VALLEY, RR_HYRULE_FIELD, ENTR_HYRULE_FIELD_ROCKY_PATH } },
|
||||
{ { EntranceType::Overworld, RR_HYRULE_FIELD, RR_MARKET_ENTRANCE, ENTR_MARKET_ENTRANCE_NEAR_GUARD_EXIT },
|
||||
{ EntranceType::Overworld, RR_MARKET_ENTRANCE, RR_HYRULE_FIELD, ENTR_HYRULE_FIELD_ON_BRIDGE_SPAWN } },
|
||||
{ { EntranceType::Overworld, RR_HYRULE_FIELD, RR_KAKARIKO_VILLAGE, ENTR_KAKARIKO_VILLAGE_FRONT_GATE },
|
||||
{ EntranceType::Overworld, RR_KAKARIKO_VILLAGE, RR_HYRULE_FIELD, ENTR_HYRULE_FIELD_STAIRS_EXIT } },
|
||||
{ { EntranceType::Overworld, RR_HYRULE_FIELD, RR_ZR_FRONT, ENTR_ZORAS_RIVER_WEST_EXIT },
|
||||
{ EntranceType::Overworld, RR_ZR_FRONT, RR_HYRULE_FIELD, ENTR_HYRULE_FIELD_RIVER_EXIT } },
|
||||
{ { EntranceType::Overworld, RR_HYRULE_FIELD, RR_LON_LON_RANCH, ENTR_LON_LON_RANCH_ENTRANCE },
|
||||
{ EntranceType::Overworld, RR_LON_LON_RANCH, RR_HYRULE_FIELD, ENTR_HYRULE_FIELD_CENTER_EXIT } },
|
||||
{ { EntranceType::Overworld, RR_LAKE_HYLIA, RR_ZORAS_DOMAIN, ENTR_ZORAS_DOMAIN_UNDERWATER_SHORTCUT },
|
||||
{ EntranceType::Overworld, RR_ZORAS_DOMAIN, RR_LAKE_HYLIA, ENTR_LAKE_HYLIA_UNDERWATER_SHORTCUT } },
|
||||
{ { EntranceType::Overworld, RR_GV_FORTRESS_SIDE, RR_GERUDO_FORTRESS, ENTR_GERUDOS_FORTRESS_EAST_EXIT },
|
||||
{ EntranceType::Overworld, RR_GERUDO_FORTRESS, RR_GV_FORTRESS_SIDE, ENTR_GERUDO_VALLEY_WEST_EXIT } },
|
||||
{ { EntranceType::Overworld, RR_GF_OUTSIDE_GATE, RR_WASTELAND_NEAR_FORTRESS, ENTR_HAUNTED_WASTELAND_EAST_EXIT },
|
||||
{ EntranceType::Overworld, RR_WASTELAND_NEAR_FORTRESS, RR_GF_OUTSIDE_GATE, ENTR_GERUDOS_FORTRESS_GATE_EXIT } },
|
||||
{ { EntranceType::Overworld, RR_WASTELAND_NEAR_COLOSSUS, RR_DESERT_COLOSSUS, ENTR_DESERT_COLOSSUS_EAST_EXIT },
|
||||
{ EntranceType::Overworld, RR_DESERT_COLOSSUS, RR_WASTELAND_NEAR_COLOSSUS, ENTR_HAUNTED_WASTELAND_WEST_EXIT } },
|
||||
{ { EntranceType::Overworld, RR_MARKET_ENTRANCE, RR_THE_MARKET, ENTR_MARKET_SOUTH_EXIT },
|
||||
{ EntranceType::Overworld, RR_THE_MARKET, RR_MARKET_ENTRANCE, ENTR_MARKET_ENTRANCE_NORTH_EXIT } },
|
||||
{ { EntranceType::Overworld, RR_THE_MARKET, RR_CASTLE_GROUNDS, ENTR_CASTLE_GROUNDS_SOUTH_EXIT },
|
||||
{ EntranceType::Overworld, RR_CASTLE_GROUNDS, RR_THE_MARKET, ENTR_MARKET_DAY_CASTLE_EXIT } },
|
||||
{ { EntranceType::Overworld, RR_THE_MARKET, RR_TOT_ENTRANCE, ENTR_TEMPLE_OF_TIME_EXTERIOR_DAY_GOSSIP_STONE_EXIT },
|
||||
{ EntranceType::Overworld, RR_TOT_ENTRANCE, RR_THE_MARKET, ENTR_MARKET_DAY_TEMPLE_EXIT } },
|
||||
{ { EntranceType::Overworld, RR_KAKARIKO_VILLAGE, RR_THE_GRAVEYARD, ENTR_GRAVEYARD_ENTRANCE },
|
||||
{ EntranceType::Overworld, RR_THE_GRAVEYARD, RR_KAKARIKO_VILLAGE, ENTR_KAKARIKO_VILLAGE_SOUTHEAST_EXIT } },
|
||||
{ { EntranceType::Overworld, RR_KAK_BEHIND_GATE, RR_DEATH_MOUNTAIN_TRAIL, ENTR_DEATH_MOUNTAIN_TRAIL_BOTTOM_EXIT },
|
||||
{ EntranceType::Overworld, RR_DEATH_MOUNTAIN_TRAIL, RR_KAK_BEHIND_GATE, ENTR_KAKARIKO_VILLAGE_GUARD_GATE } },
|
||||
{ { EntranceType::Overworld, RR_DEATH_MOUNTAIN_TRAIL, RR_GORON_CITY, ENTR_GORON_CITY_UPPER_EXIT },
|
||||
{ EntranceType::Overworld, RR_GORON_CITY, RR_DEATH_MOUNTAIN_TRAIL, ENTR_DEATH_MOUNTAIN_TRAIL_GC_EXIT } },
|
||||
{ { EntranceType::Overworld, RR_GC_DARUNIAS_CHAMBER, RR_DMC_LOWER_LOCAL, ENTR_DEATH_MOUNTAIN_CRATER_GC_EXIT },
|
||||
{ EntranceType::Overworld, RR_DMC_LOWER_NEARBY, RR_GC_DARUNIAS_CHAMBER, ENTR_GORON_CITY_DARUNIA_ROOM_EXIT } },
|
||||
{ { EntranceType::Overworld, RR_DEATH_MOUNTAIN_SUMMIT, RR_DMC_UPPER_LOCAL, ENTR_DEATH_MOUNTAIN_CRATER_UPPER_EXIT },
|
||||
{ EntranceType::Overworld, RR_DMC_UPPER_NEARBY, RR_DEATH_MOUNTAIN_SUMMIT, ENTR_DEATH_MOUNTAIN_TRAIL_SUMMIT_EXIT } },
|
||||
{ { EntranceType::Overworld, RR_ZR_BEHIND_WATERFALL, RR_ZORAS_DOMAIN, ENTR_ZORAS_DOMAIN_ENTRANCE },
|
||||
{ EntranceType::Overworld, RR_ZORAS_DOMAIN, RR_ZR_BEHIND_WATERFALL, ENTR_ZORAS_RIVER_WATERFALL_EXIT } },
|
||||
{ { EntranceType::Overworld, RR_ZD_BEHIND_KING_ZORA, RR_ZORAS_FOUNTAIN, ENTR_ZORAS_FOUNTAIN_TUNNEL_EXIT },
|
||||
{ EntranceType::Overworld, RR_ZORAS_FOUNTAIN, RR_ZD_BEHIND_KING_ZORA, ENTR_ZORAS_DOMAIN_KING_ZORA_EXIT } },
|
||||
|
||||
{ { EntranceType::Overworld, RR_GV_LOWER_STREAM, RR_LAKE_HYLIA, ENTR_LAKE_HYLIA_RIVER_EXIT },
|
||||
NO_RETURN_ENTRANCE },
|
||||
|
||||
{ { EntranceType::OwlDrop, RR_LH_OWL_FLIGHT, RR_HYRULE_FIELD, ENTR_HYRULE_FIELD_OWL_DROP },
|
||||
NO_RETURN_ENTRANCE },
|
||||
{ { EntranceType::OwlDrop, RR_DMT_OWL_FLIGHT, RR_KAK_IMPAS_ROOFTOP, ENTR_KAKARIKO_VILLAGE_OWL_DROP },
|
||||
NO_RETURN_ENTRANCE },
|
||||
|
||||
{ { EntranceType::Spawn, RR_CHILD_SPAWN, RR_KF_LINKS_HOUSE, ENTR_LINKS_HOUSE_CHILD_SPAWN },
|
||||
NO_RETURN_ENTRANCE },
|
||||
{ { EntranceType::Spawn, RR_ADULT_SPAWN, RR_TEMPLE_OF_TIME, ENTR_HYRULE_FIELD_10 },
|
||||
NO_RETURN_ENTRANCE }, // 0x282 is an unused entrance index repurposed to differentiate between
|
||||
// Adult Spawn and prelude of light (normally they both use 0x5F4)
|
||||
{ { EntranceType::WarpSong, RR_MINUET_OF_FOREST_WARP, RR_SACRED_FOREST_MEADOW, ENTR_SACRED_FOREST_MEADOW_WARP_PAD },
|
||||
NO_RETURN_ENTRANCE },
|
||||
{ { EntranceType::WarpSong, RR_BOLERO_OF_FIRE_WARP, RR_DMC_CENTRAL_LOCAL, ENTR_DEATH_MOUNTAIN_CRATER_WARP_PAD },
|
||||
NO_RETURN_ENTRANCE },
|
||||
{ { EntranceType::WarpSong, RR_SERENADE_OF_WATER_WARP, RR_LAKE_HYLIA, ENTR_LAKE_HYLIA_WARP_PAD },
|
||||
NO_RETURN_ENTRANCE },
|
||||
{ { EntranceType::WarpSong, RR_REQUIEM_OF_SPIRIT_WARP, RR_DESERT_COLOSSUS, ENTR_DESERT_COLOSSUS_WARP_PAD },
|
||||
NO_RETURN_ENTRANCE },
|
||||
{ { EntranceType::WarpSong, RR_NOCTURNE_OF_SHADOW_WARP, RR_GRAVEYARD_WARP_PAD_REGION, ENTR_GRAVEYARD_WARP_PAD },
|
||||
NO_RETURN_ENTRANCE },
|
||||
{ { EntranceType::WarpSong, RR_PRELUDE_OF_LIGHT_WARP, RR_TEMPLE_OF_TIME, ENTR_TEMPLE_OF_TIME_WARP_PAD },
|
||||
NO_RETURN_ENTRANCE },
|
||||
|
||||
{ { EntranceType::ChildBoss, RR_DEKU_TREE_BOSS_ENTRYWAY, RR_DEKU_TREE_BOSS_ROOM, ENTR_DEKU_TREE_BOSS_ENTRANCE },
|
||||
{ EntranceType::ChildBoss, RR_DEKU_TREE_BOSS_ROOM, RR_DEKU_TREE_BOSS_ENTRYWAY, ENTR_DEKU_TREE_BOSS_DOOR } },
|
||||
{ { EntranceType::ChildBoss, RR_DODONGOS_CAVERN_BOSS_ENTRYWAY, RR_DODONGOS_CAVERN_BOSS_ROOM, ENTR_DODONGOS_CAVERN_BOSS_ENTRANCE },
|
||||
{ EntranceType::ChildBoss, RR_DODONGOS_CAVERN_BOSS_ROOM, RR_DODONGOS_CAVERN_BOSS_ENTRYWAY, ENTR_DODONGOS_CAVERN_BOSS_DOOR } },
|
||||
{ { EntranceType::ChildBoss, RR_JABU_JABUS_BELLY_BOSS_ENTRYWAY, RR_JABU_JABUS_BELLY_BOSS_ROOM, ENTR_JABU_JABU_BOSS_ENTRANCE },
|
||||
{ EntranceType::ChildBoss, RR_JABU_JABUS_BELLY_BOSS_ROOM, RR_JABU_JABUS_BELLY_BOSS_ENTRYWAY, ENTR_JABU_JABU_BOSS_DOOR } },
|
||||
{ { EntranceType::AdultBoss, RR_FOREST_TEMPLE_BOSS_ENTRYWAY, RR_FOREST_TEMPLE_BOSS_ROOM, ENTR_FOREST_TEMPLE_BOSS_ENTRANCE },
|
||||
{ EntranceType::AdultBoss, RR_FOREST_TEMPLE_BOSS_ROOM, RR_FOREST_TEMPLE_BOSS_ENTRYWAY, ENTR_FOREST_TEMPLE_BOSS_DOOR } },
|
||||
{ { EntranceType::AdultBoss, RR_FIRE_TEMPLE_BOSS_ENTRYWAY, RR_FIRE_TEMPLE_BOSS_ROOM, ENTR_FIRE_TEMPLE_BOSS_ENTRANCE },
|
||||
{ EntranceType::AdultBoss, RR_FIRE_TEMPLE_BOSS_ROOM, RR_FIRE_TEMPLE_BOSS_ENTRYWAY, ENTR_FIRE_TEMPLE_BOSS_DOOR } },
|
||||
{ { EntranceType::AdultBoss, RR_WATER_TEMPLE_BOSS_ENTRYWAY, RR_WATER_TEMPLE_BOSS_ROOM, ENTR_WATER_TEMPLE_BOSS_ENTRANCE },
|
||||
{ EntranceType::AdultBoss, RR_WATER_TEMPLE_BOSS_ROOM, RR_WATER_TEMPLE_BOSS_ENTRYWAY, ENTR_WATER_TEMPLE_BOSS_DOOR } },
|
||||
{ { EntranceType::AdultBoss, RR_SPIRIT_TEMPLE_BOSS_ENTRYWAY, RR_SPIRIT_TEMPLE_BOSS_ROOM, ENTR_SPIRIT_TEMPLE_BOSS_ENTRANCE },
|
||||
{ EntranceType::AdultBoss, RR_SPIRIT_TEMPLE_BOSS_ROOM, RR_SPIRIT_TEMPLE_BOSS_ENTRYWAY, ENTR_SPIRIT_TEMPLE_BOSS_DOOR } },
|
||||
{ { EntranceType::AdultBoss, RR_SHADOW_TEMPLE_BOSS_ENTRYWAY, RR_SHADOW_TEMPLE_BOSS_ROOM, ENTR_SHADOW_TEMPLE_BOSS_ENTRANCE },
|
||||
{ EntranceType::AdultBoss, RR_SHADOW_TEMPLE_BOSS_ROOM, RR_SHADOW_TEMPLE_BOSS_ENTRYWAY, ENTR_SHADOW_TEMPLE_BOSS_DOOR } },
|
||||
|
||||
{ { EntranceType::BlueWarp, RR_DEKU_TREE_BOSS_ROOM, RR_KF_OUTSIDE_DEKU_TREE, ENTR_KOKIRI_FOREST_DEKU_TREE_BLUE_WARP },
|
||||
NO_RETURN_ENTRANCE },
|
||||
{ { EntranceType::BlueWarp, RR_DODONGOS_CAVERN_BOSS_ROOM, RR_DEATH_MOUNTAIN_TRAIL, ENTR_DEATH_MOUNTAIN_TRAIL_DODONGO_BLUE_WARP },
|
||||
NO_RETURN_ENTRANCE },
|
||||
{ { EntranceType::BlueWarp, RR_JABU_JABUS_BELLY_BOSS_ROOM, RR_ZORAS_FOUNTAIN, ENTR_ZORAS_FOUNTAIN_JABU_JABU_BLUE_WARP },
|
||||
NO_RETURN_ENTRANCE },
|
||||
{ { EntranceType::BlueWarp, RR_FOREST_TEMPLE_BOSS_ROOM, RR_SACRED_FOREST_MEADOW, ENTR_SACRED_FOREST_MEADOW_FOREST_TEMPLE_BLUE_WARP },
|
||||
NO_RETURN_ENTRANCE },
|
||||
{ { EntranceType::BlueWarp, RR_FIRE_TEMPLE_BOSS_ROOM, RR_DMC_CENTRAL_LOCAL, ENTR_DEATH_MOUNTAIN_CRATER_FIRE_TEMPLE_BLUE_WARP },
|
||||
NO_RETURN_ENTRANCE },
|
||||
{ { EntranceType::BlueWarp, RR_WATER_TEMPLE_BOSS_ROOM, RR_LAKE_HYLIA, ENTR_LAKE_HYLIA_WATER_TEMPLE_BLUE_WARP },
|
||||
NO_RETURN_ENTRANCE },
|
||||
{ { EntranceType::BlueWarp, RR_SPIRIT_TEMPLE_BOSS_ROOM, RR_DESERT_COLOSSUS, ENTR_DESERT_COLOSSUS_SPIRIT_TEMPLE_BLUE_WARP },
|
||||
NO_RETURN_ENTRANCE },
|
||||
{ { EntranceType::BlueWarp, RR_SHADOW_TEMPLE_BOSS_ROOM, RR_GRAVEYARD_WARP_PAD_REGION, ENTR_GRAVEYARD_SHADOW_TEMPLE_BLUE_WARP },
|
||||
NO_RETURN_ENTRANCE },
|
||||
// clang-format on
|
||||
};
|
||||
|
||||
std::map<std::string, PriorityEntrance> priorityEntranceTable = {
|
||||
{ "Bolero", { { RR_DMC_CENTRAL_LOCAL }, { EntranceType::OwlDrop, EntranceType::WarpSong } } },
|
||||
{ "Nocturne",
|
||||
|
@ -1176,7 +1180,7 @@ int EntranceShuffler::ShuffleAllEntrances() {
|
|||
};
|
||||
|
||||
mEntranceShuffleFailure = false;
|
||||
SetAllEntrancesData(entranceShuffleTable);
|
||||
SetAllEntrancesData();
|
||||
|
||||
EntrancePools oneWayEntrancePools = {};
|
||||
EntrancePools entrancePools = {};
|
||||
|
@ -1486,11 +1490,11 @@ int EntranceShuffler::ShuffleAllEntrances() {
|
|||
if (true /* ctx->GetOption(RSK_SHUFFLE_BLUEWARP_ENTRANCES).Is(RO_BLUEWARP_ENTRANCE_SHUFFLE_DUNGEON) */) {
|
||||
// If a boss room is inside a boss door, make the blue warp go outside the dungeon's entrance
|
||||
std::map<std::string, Entrance*> bossExits = {
|
||||
{ EntranceNameByRegions(RR_DEKU_TREE_BOSS_ROOM, RR_DEKU_TREE_BOSS_ENTRYWAY),
|
||||
{ EntranceNameByRegions(RR_DEKU_TREE_BOSS_ROOM, RR_DEKU_TREE_BOSS_EXIT),
|
||||
GetEntrance(RR_DEKU_TREE_ENTRYWAY, RR_KF_OUTSIDE_DEKU_TREE) },
|
||||
{ EntranceNameByRegions(RR_DODONGOS_CAVERN_BOSS_ROOM, RR_DODONGOS_CAVERN_BOSS_ENTRYWAY),
|
||||
{ EntranceNameByRegions(RR_DODONGOS_CAVERN_BOSS_ROOM, RR_DODONGOS_CAVERN_BOSS_EXIT),
|
||||
GetEntrance(RR_DODONGOS_CAVERN_ENTRYWAY, RR_DEATH_MOUNTAIN_TRAIL) },
|
||||
{ EntranceNameByRegions(RR_JABU_JABUS_BELLY_BOSS_ROOM, RR_JABU_JABUS_BELLY_BOSS_ENTRYWAY),
|
||||
{ EntranceNameByRegions(RR_JABU_JABUS_BELLY_BOSS_ROOM, RR_JABU_JABUS_BELLY_BOSS_EXIT),
|
||||
GetEntrance(RR_JABU_JABUS_BELLY_ENTRYWAY, RR_ZORAS_FOUNTAIN) },
|
||||
{ EntranceNameByRegions(RR_FOREST_TEMPLE_BOSS_ROOM, RR_FOREST_TEMPLE_BOSS_ENTRYWAY),
|
||||
GetEntrance(RR_FOREST_TEMPLE_ENTRYWAY, RR_SACRED_FOREST_MEADOW) },
|
||||
|
@ -1528,11 +1532,11 @@ int EntranceShuffler::ShuffleAllEntrances() {
|
|||
// Pair <BlueWarp exit, BossRoom reverse exit>
|
||||
std::vector<EntrancePair> bossRoomExitPairs = {
|
||||
{ GetEntrance(RR_DEKU_TREE_BOSS_ROOM, RR_KF_OUTSIDE_DEKU_TREE),
|
||||
GetEntrance(RR_DEKU_TREE_BOSS_ROOM, RR_DEKU_TREE_BOSS_ENTRYWAY) },
|
||||
GetEntrance(RR_DEKU_TREE_BOSS_ROOM, RR_DEKU_TREE_BOSS_EXIT) },
|
||||
{ GetEntrance(RR_DODONGOS_CAVERN_BOSS_ROOM, RR_DEATH_MOUNTAIN_TRAIL),
|
||||
GetEntrance(RR_DODONGOS_CAVERN_BOSS_ROOM, RR_DODONGOS_CAVERN_BOSS_ENTRYWAY) },
|
||||
GetEntrance(RR_DODONGOS_CAVERN_BOSS_ROOM, RR_DODONGOS_CAVERN_BOSS_EXIT) },
|
||||
{ GetEntrance(RR_JABU_JABUS_BELLY_BOSS_ROOM, RR_ZORAS_FOUNTAIN),
|
||||
GetEntrance(RR_JABU_JABUS_BELLY_BOSS_ROOM, RR_JABU_JABUS_BELLY_BOSS_ENTRYWAY) },
|
||||
GetEntrance(RR_JABU_JABUS_BELLY_BOSS_ROOM, RR_JABU_JABUS_BELLY_BOSS_EXIT) },
|
||||
{ GetEntrance(RR_FOREST_TEMPLE_BOSS_ROOM, RR_SACRED_FOREST_MEADOW),
|
||||
GetEntrance(RR_FOREST_TEMPLE_BOSS_ROOM, RR_FOREST_TEMPLE_BOSS_ENTRYWAY) },
|
||||
{ GetEntrance(RR_FIRE_TEMPLE_BOSS_ROOM, RR_DMC_CENTRAL_LOCAL),
|
||||
|
@ -1660,6 +1664,30 @@ void EntranceShuffler::ParseJson(nlohmann::json spoilerFileJson) {
|
|||
}
|
||||
}
|
||||
} catch (const std::exception& e) { throw e; }
|
||||
// We may need to reset more things here or elsewhere in spoiler loading
|
||||
RegionTable_Init();
|
||||
ApplyEntranceOverrides();
|
||||
SetAreas();
|
||||
}
|
||||
|
||||
void EntranceShuffler::ApplyEntranceOverrides() {
|
||||
SetAllEntrancesData();
|
||||
|
||||
for (size_t i = 0; i < entranceOverrides.size(); i++) {
|
||||
EntranceOverride entranceOverride = entranceOverrides[i];
|
||||
|
||||
if (entranceOverride.index == 0 && entranceOverride.destination == 0 && entranceOverride.override == 0 &&
|
||||
entranceOverride.overrideDestination == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Entrance* entrance = entranceMap[entranceOverride.index];
|
||||
Entrance* overrideEntrance = entranceMap[entranceOverride.override];
|
||||
|
||||
entrance->Disconnect();
|
||||
entrance->Connect(overrideEntrance->GetOriginalConnectedRegionKey());
|
||||
entrance->SetAsShuffled();
|
||||
}
|
||||
}
|
||||
} // namespace Rando
|
||||
|
||||
|
|
|
@ -128,6 +128,7 @@ class EntranceShuffler {
|
|||
void CreateEntranceOverrides();
|
||||
void UnshuffleAllEntrances();
|
||||
void ParseJson(nlohmann::json spoilerFileJson);
|
||||
void ApplyEntranceOverrides();
|
||||
|
||||
private:
|
||||
std::vector<Entrance*> AssumeEntrancePool(std::vector<Entrance*>& entrancePool);
|
||||
|
|
|
@ -15,10 +15,6 @@ extern SaveContext gSaveContext;
|
|||
extern PlayState* gPlayState;
|
||||
}
|
||||
|
||||
#define FSi OTRGlobals::Instance->gRandoContext->GetFishsanity()
|
||||
|
||||
#define RAND_GET_OPTION(option) Rando::Context::GetInstance()->GetOption(option).Get()
|
||||
|
||||
/**
|
||||
* @brief Parallel list of pond fish checks for both ages
|
||||
*/
|
||||
|
@ -488,15 +484,15 @@ void Fishsanity::OnItemReceiveHandler(GetItemEntry itemEntry) {
|
|||
// C interface
|
||||
extern "C" {
|
||||
bool Randomizer_GetPondFishShuffled() {
|
||||
return FSi->GetPondFishShuffled();
|
||||
return Rando::Context::GetInstance()->GetFishsanity()->GetPondFishShuffled();
|
||||
}
|
||||
|
||||
bool Randomizer_GetOverworldFishShuffled() {
|
||||
return FSi->GetOverworldFishShuffled();
|
||||
return Rando::Context::GetInstance()->GetFishsanity()->GetOverworldFishShuffled();
|
||||
}
|
||||
|
||||
bool Randomizer_IsAdultPond() {
|
||||
return FSi->IsAdultPond();
|
||||
return Rando::Context::GetInstance()->GetFishsanity()->IsAdultPond();
|
||||
}
|
||||
|
||||
void Fishsanity_DrawEffShadow(Actor* actor, Lights* lights, PlayState* play) {
|
||||
|
|
|
@ -46,6 +46,7 @@ extern "C" {
|
|||
#include "src/overlays/actors/ovl_En_Hy/z_en_hy.h"
|
||||
#include "src/overlays/actors/ovl_En_Bom_Bowl_Pit/z_en_bom_bowl_pit.h"
|
||||
#include "src/overlays/actors/ovl_En_Ge1/z_en_ge1.h"
|
||||
#include "src/overlays/actors/ovl_En_Ge2/z_en_ge2.h"
|
||||
#include "src/overlays/actors/ovl_En_Ds/z_en_ds.h"
|
||||
#include "src/overlays/actors/ovl_En_Gm/z_en_gm.h"
|
||||
#include "src/overlays/actors/ovl_En_Js/z_en_js.h"
|
||||
|
@ -55,7 +56,6 @@ extern "C" {
|
|||
#include "src/overlays/actors/ovl_En_Xc/z_en_xc.h"
|
||||
#include "src/overlays/actors/ovl_Fishing/z_fishing.h"
|
||||
#include "src/overlays/actors/ovl_En_Mk/z_en_mk.h"
|
||||
#include "src/overlays/actors/ovl_En_Ge1/z_en_ge1.h"
|
||||
#include "draw.h"
|
||||
|
||||
extern SaveContext gSaveContext;
|
||||
|
@ -69,10 +69,10 @@ extern void EnMk_Wait(EnMk* enMk, PlayState* play);
|
|||
extern void func_80ABA778(EnNiwLady* enNiwLady, PlayState* play);
|
||||
extern void EnGe1_Wait_Archery(EnGe1* enGe1, PlayState* play);
|
||||
extern void EnGe1_SetAnimationIdle(EnGe1* enGe1);
|
||||
extern void EnGe1_SetAnimationIdle(EnGe1* enGe1);
|
||||
extern void EnGe2_SetupCapturePlayer(EnGe2* enGe2, PlayState* play);
|
||||
}
|
||||
|
||||
#define RAND_GET_OPTION(option) Rando::Context::GetInstance()->GetOption(option).Get()
|
||||
|
||||
bool LocMatchesQuest(Rando::Location loc) {
|
||||
if (loc.GetQuest() == RCQUEST_BOTH) {
|
||||
return true;
|
||||
|
@ -927,13 +927,6 @@ void RandomizerOnVanillaBehaviorHandler(GIVanillaBehavior id, bool* should, va_l
|
|||
*should = Flags_GetRandomizerInf(RAND_INF_LEARNED_EPONA_SONG);
|
||||
break;
|
||||
}
|
||||
case VB_SET_CUCCO_COUNT: {
|
||||
EnNiwLady* enNiwLady = va_arg(args, EnNiwLady*);
|
||||
// Override starting Cucco count using setting value
|
||||
enNiwLady->cuccosInPen = 7 - RAND_GET_OPTION(RSK_CUCCO_COUNT);
|
||||
*should = false;
|
||||
break;
|
||||
}
|
||||
case VB_KING_ZORA_THANK_CHILD: {
|
||||
// Allow turning in Ruto's letter even if you have already rescued her
|
||||
if (!Flags_GetEventChkInf(EVENTCHKINF_KING_ZORA_MOVED)) {
|
||||
|
@ -1444,6 +1437,13 @@ void RandomizerOnVanillaBehaviorHandler(GIVanillaBehavior id, bool* should, va_l
|
|||
}
|
||||
break;
|
||||
}
|
||||
case VB_GERUDO_GUARD_SET_ACTION_AFTER_TALK:
|
||||
if (gPlayState->msgCtx.choiceIndex == 0) {
|
||||
EnGe2* enGe2 = va_arg(args, EnGe2*);
|
||||
EnGe2_SetupCapturePlayer(enGe2, gPlayState);
|
||||
*should = false;
|
||||
}
|
||||
break;
|
||||
case VB_GERUDOS_BE_FRIENDLY: {
|
||||
*should = CHECK_QUEST_ITEM(QUEST_GERUDO_CARD);
|
||||
break;
|
||||
|
@ -2144,7 +2144,7 @@ void RandomizerOnActorUpdateHandler(void* refActor) {
|
|||
shutterDoor->unk_16E = 0;
|
||||
}
|
||||
} else if (actor->id == ACTOR_DOOR_GERUDO) {
|
||||
DoorGerudo* gerudoDoor = (DoorGerudo*)actor;
|
||||
DoorGerudo* gerudoDoor = reinterpret_cast<DoorGerudo*>(actor);
|
||||
gerudoDoor->actionFunc = func_8099485C;
|
||||
gerudoDoor->dyna.actor.world.pos.y = gerudoDoor->dyna.actor.home.pos.y + 200.0f;
|
||||
}
|
||||
|
|
|
@ -46,14 +46,20 @@ Item::~Item() = default;
|
|||
|
||||
void Item::ApplyEffect() const {
|
||||
auto ctx = Rando::Context::GetInstance();
|
||||
ctx->GetLogic()->ApplyItemEffect(StaticData::RetrieveItem(randomizerGet), true);
|
||||
ctx->GetLogic()->SetInLogic(logicVal, true);
|
||||
auto logic = ctx->GetLogic();
|
||||
if (!logic->CalculatingAvailableChecks) {
|
||||
logic->ApplyItemEffect(StaticData::RetrieveItem(randomizerGet), true);
|
||||
}
|
||||
logic->SetInLogic(logicVal, true);
|
||||
}
|
||||
|
||||
void Item::UndoEffect() const {
|
||||
auto ctx = Rando::Context::GetInstance();
|
||||
ctx->GetLogic()->ApplyItemEffect(StaticData::RetrieveItem(randomizerGet), false);
|
||||
ctx->GetLogic()->SetInLogic(logicVal, false);
|
||||
auto logic = ctx->GetLogic();
|
||||
if (!logic->CalculatingAvailableChecks) {
|
||||
logic->ApplyItemEffect(StaticData::RetrieveItem(randomizerGet), false);
|
||||
}
|
||||
logic->SetInLogic(logicVal, false);
|
||||
}
|
||||
|
||||
const Text& Item::GetName() const {
|
||||
|
|
|
@ -437,10 +437,14 @@ void RegionTable_Init_DekuTree() {
|
|||
|
||||
// Boss Room
|
||||
areaTable[RR_DEKU_TREE_BOSS_ENTRYWAY] = Region("Deku Tree Boss Entryway", SCENE_DEKU_TREE, {}, {}, {
|
||||
// Exits
|
||||
Entrance(RR_DEKU_TREE_BOSS_ROOM, []{return true;}),
|
||||
});
|
||||
|
||||
areaTable[RR_DEKU_TREE_BOSS_EXIT] = Region("Deku Tree Boss Exit", "Deku Tree", {RA_DEKU_TREE}, NO_DAY_NIGHT_CYCLE, {}, {}, {
|
||||
// Exits
|
||||
Entrance(RR_DEKU_TREE_OUTSIDE_BOSS_ROOM, []{return ctx->GetDungeon(DEKU_TREE)->IsVanilla();}),
|
||||
Entrance(RR_DEKU_TREE_MQ_OUTSIDE_BOSS_ROOM, []{return ctx->GetDungeon(DEKU_TREE)->IsMQ();}),
|
||||
Entrance(RR_DEKU_TREE_BOSS_ROOM, []{return true;}),
|
||||
});
|
||||
|
||||
areaTable[RR_DEKU_TREE_BOSS_ROOM] = Region("Deku Tree Boss Room", SCENE_DEKU_TREE_BOSS, {
|
||||
|
@ -460,8 +464,8 @@ void RegionTable_Init_DekuTree() {
|
|||
LOCATION(RC_DEKU_TREE_QUEEN_GOHMA_GRASS_8, logic->CanCutShrubs()),
|
||||
}, {
|
||||
// Exits
|
||||
Entrance(RR_DEKU_TREE_BOSS_ENTRYWAY, []{return true;}),
|
||||
Entrance(RR_KF_OUTSIDE_DEKU_TREE, []{return logic->DekuTreeClear;}, false),
|
||||
Entrance(RR_DEKU_TREE_BOSS_EXIT, []{return true;}),
|
||||
Entrance(RR_KF_OUTSIDE_DEKU_TREE, []{return logic->DekuTreeClear;}, false),
|
||||
});
|
||||
|
||||
// clang-format on
|
||||
|
|
|
@ -268,6 +268,7 @@ void RegionTable_Init_DodongosCavern() {
|
|||
LOCATION(RC_DODONGOS_CAVERN_MQ_DEKU_SCRUB_LOBBY_FRONT, logic->CanStunDeku()),
|
||||
}, {
|
||||
//Exits
|
||||
Entrance(RR_DODONGOS_CAVERN_MQ_BEGINNING, []{return true;}),
|
||||
Entrance(RR_DODONGOS_CAVERN_MQ_GOSSIP_STONE, []{return Here(RR_DODONGOS_CAVERN_MQ_LOBBY, []{return logic->CanBreakMudWalls() || logic->HasItem(RG_GORONS_BRACELET);});}),
|
||||
Entrance(RR_DODONGOS_CAVERN_MQ_MOUTH_SIDE_BRIDGE, []{return Here(RR_DODONGOS_CAVERN_MQ_LOBBY, []{return logic->BlastOrSmash() || logic->HasItem(RG_GORONS_BRACELET);});}),
|
||||
Entrance(RR_DODONGOS_CAVERN_MQ_STAIRS_LOWER, []{return Here(RR_DODONGOS_CAVERN_MQ_LOBBY, []{return logic->BlastOrSmash() || logic->HasItem(RG_GORONS_BRACELET);});}),
|
||||
|
@ -559,10 +560,14 @@ void RegionTable_Init_DodongosCavern() {
|
|||
|
||||
// Boss Room
|
||||
areaTable[RR_DODONGOS_CAVERN_BOSS_ENTRYWAY] = Region("Dodongos Cavern Boss Entryway", SCENE_DODONGOS_CAVERN, {}, {}, {
|
||||
// Exits
|
||||
Entrance(RR_DODONGOS_CAVERN_BOSS_ROOM, []{return true;}),
|
||||
});
|
||||
|
||||
areaTable[RR_DODONGOS_CAVERN_BOSS_EXIT] = Region("Dodongos Cavern Boss Exit", SCENE_DODONGOS_CAVERN, {}, {}, {
|
||||
// Exits
|
||||
Entrance(RR_DODONGOS_CAVERN_BOSS_AREA, []{return ctx->GetDungeon(DODONGOS_CAVERN)->IsVanilla();}),
|
||||
Entrance(RR_DODONGOS_CAVERN_MQ_BEHIND_MOUTH, []{return ctx->GetDungeon(DODONGOS_CAVERN)->IsMQ();}),
|
||||
Entrance(RR_DODONGOS_CAVERN_BOSS_ROOM, []{return true;}),
|
||||
});
|
||||
|
||||
areaTable[RR_DODONGOS_CAVERN_BOSS_ROOM] = Region("Dodongos Cavern Boss Room", SCENE_DODONGOS_CAVERN_BOSS, {
|
||||
|
@ -575,8 +580,8 @@ void RegionTable_Init_DodongosCavern() {
|
|||
LOCATION(RC_KING_DODONGO, logic->DodongosCavernClear),
|
||||
}, {
|
||||
// Exits
|
||||
Entrance(RR_DODONGOS_CAVERN_BOSS_ENTRYWAY, []{return true;}),
|
||||
Entrance(RR_DEATH_MOUNTAIN_TRAIL, []{return logic->DodongosCavernClear;}, false),
|
||||
Entrance(RR_DODONGOS_CAVERN_BOSS_EXIT, []{return true;}),
|
||||
Entrance(RR_DEATH_MOUNTAIN_TRAIL, []{return logic->DodongosCavernClear;}, false),
|
||||
});
|
||||
|
||||
// clang-format on
|
||||
|
|
|
@ -38,7 +38,7 @@ void RegionTable_Init_FireTemple() {
|
|||
}, {
|
||||
//Exits
|
||||
Entrance(RR_FIRE_TEMPLE_FIRST_ROOM, []{return true;}),
|
||||
Entrance(RR_FIRE_TEMPLE_BOSS_ENTRYWAY, []{return logic->HasItem(RG_FIRE_TEMPLE_BOSS_KEY) && ((logic->IsAdult && (ctx->GetTrickOption(RT_FIRE_BOSS_DOOR_JUMP) || Here(RR_FIRE_TEMPLE_FIRE_MAZE_UPPER, []{return logic->CanUse(RG_MEGATON_HAMMER);}))) || logic->CanUse(RG_HOVER_BOOTS));}),
|
||||
Entrance(RR_FIRE_TEMPLE_BOSS_ENTRYWAY, []{return logic->IsAdult && (ctx->GetTrickOption(RT_FIRE_BOSS_DOOR_JUMP) || Here(RR_FIRE_TEMPLE_FIRE_MAZE_UPPER, []{return logic->CanUse(RG_MEGATON_HAMMER);}) || logic->CanUse(RG_HOVER_BOOTS));}),
|
||||
});
|
||||
|
||||
areaTable[RR_FIRE_TEMPLE_LOOP_ENEMIES] = Region("Fire Temple Loop Enemies", SCENE_FIRE_TEMPLE, {}, {}, {
|
||||
|
@ -444,7 +444,7 @@ void RegionTable_Init_FireTemple() {
|
|||
Entrance(RR_FIRE_TEMPLE_MQ_FIRST_ROOM_UPPER, []{return true;}),
|
||||
//Child cannot make it to the north side torches without a hook without specifically bunny hood speed + hover boots
|
||||
Entrance(RR_FIRE_TEMPLE_MQ_NEAR_BOSS_ROOM_NORTH, []{return logic->FireTimer() > 32 && (logic->CanUse(RG_HOOKSHOT) || (logic->IsAdult && logic->CanUse(RG_HOVER_BOOTS)));}),
|
||||
Entrance(RR_FIRE_TEMPLE_BOSS_ENTRYWAY, []{return logic->HasItem(RG_FIRE_TEMPLE_BOSS_KEY) && logic->FireTimer() >= 15 && ((logic->IsAdult && (ctx->GetTrickOption(RT_FIRE_BOSS_DOOR_JUMP) || logic->CanUse(RG_HOVER_BOOTS))) || (logic->IsAdult && logic->HitFireTemplePlatform) || (logic->HitFireTemplePlatform && logic->CanUse(RG_HOVER_BOOTS)));}),
|
||||
Entrance(RR_FIRE_TEMPLE_BOSS_ENTRYWAY, []{return logic->FireTimer() >= 15 && ((logic->IsAdult && (ctx->GetTrickOption(RT_FIRE_BOSS_DOOR_JUMP) || logic->CanUse(RG_HOVER_BOOTS))) || (logic->IsAdult && logic->HitFireTemplePlatform) || (logic->HitFireTemplePlatform && logic->CanUse(RG_HOVER_BOOTS)));}),
|
||||
});
|
||||
|
||||
//This room assumes tunic logic is handled on entry.
|
||||
|
@ -739,7 +739,7 @@ void RegionTable_Init_FireTemple() {
|
|||
// Exits
|
||||
Entrance(RR_FIRE_TEMPLE_NEAR_BOSS_ROOM, []{return ctx->GetDungeon(FIRE_TEMPLE)->IsVanilla() && false;}),
|
||||
Entrance(RR_FIRE_TEMPLE_MQ_NEAR_BOSS_ROOM, []{return ctx->GetDungeon(FIRE_TEMPLE)->IsMQ() && false;}),
|
||||
Entrance(RR_FIRE_TEMPLE_BOSS_ROOM, []{return true;}),
|
||||
Entrance(RR_FIRE_TEMPLE_BOSS_ROOM, []{return logic->HasItem(RG_FIRE_TEMPLE_BOSS_KEY);}),
|
||||
});
|
||||
|
||||
areaTable[RR_FIRE_TEMPLE_BOSS_ROOM] = Region("Fire Temple Boss Room", SCENE_FIRE_TEMPLE_BOSS, {
|
||||
|
|
|
@ -298,7 +298,7 @@ void RegionTable_Init_ForestTemple() {
|
|||
}, {
|
||||
//Exits
|
||||
Entrance(RR_FOREST_TEMPLE_LOBBY, []{return true;}),
|
||||
Entrance(RR_FOREST_TEMPLE_BOSS_ENTRYWAY, []{return logic->HasItem(RG_FOREST_TEMPLE_BOSS_KEY);}),
|
||||
Entrance(RR_FOREST_TEMPLE_BOSS_ENTRYWAY, []{return true;}),
|
||||
});
|
||||
|
||||
#pragma endregion
|
||||
|
@ -591,7 +591,7 @@ void RegionTable_Init_ForestTemple() {
|
|||
areaTable[RR_FOREST_TEMPLE_MQ_BOSS_REGION] = Region("Forest Temple MQ Boss Region", SCENE_FOREST_TEMPLE, {}, {}, {
|
||||
//Exits
|
||||
Entrance(RR_FOREST_TEMPLE_MQ_BASEMENT, []{return logic->ForestOpenBossCorridor;}),
|
||||
Entrance(RR_FOREST_TEMPLE_BOSS_ENTRYWAY, []{return logic->HasItem(RG_FOREST_TEMPLE_BOSS_KEY);}),
|
||||
Entrance(RR_FOREST_TEMPLE_BOSS_ENTRYWAY, []{return true;}),
|
||||
});
|
||||
|
||||
#pragma endregion
|
||||
|
@ -601,7 +601,7 @@ void RegionTable_Init_ForestTemple() {
|
|||
// Exits
|
||||
Entrance(RR_FOREST_TEMPLE_BOSS_REGION, []{return ctx->GetDungeon(FOREST_TEMPLE)->IsVanilla() && false;}),
|
||||
Entrance(RR_FOREST_TEMPLE_MQ_BOSS_REGION, []{return ctx->GetDungeon(FOREST_TEMPLE)->IsMQ() && false;}),
|
||||
Entrance(RR_FOREST_TEMPLE_BOSS_ROOM, []{return true;}),
|
||||
Entrance(RR_FOREST_TEMPLE_BOSS_ROOM, []{return logic->HasItem(RG_FOREST_TEMPLE_BOSS_KEY);}),
|
||||
});
|
||||
|
||||
areaTable[RR_FOREST_TEMPLE_BOSS_ROOM] = Region("Forest Temple Boss Room", SCENE_FOREST_TEMPLE_BOSS, {
|
||||
|
|
|
@ -29,12 +29,7 @@ void RegionTable_Init_GanonsCastle() {
|
|||
Entrance(RR_GANONS_CASTLE_SHADOW_TRIAL, []{return true;}),
|
||||
Entrance(RR_GANONS_CASTLE_SPIRIT_TRIAL, []{return true;}),
|
||||
Entrance(RR_GANONS_CASTLE_LIGHT_TRIAL, []{return logic->CanUse(RG_GOLDEN_GAUNTLETS);}),
|
||||
Entrance(RR_GANONS_TOWER_ENTRYWAY, []{return (logic->ForestTrialClear || ctx->GetTrial(TK_FOREST_TRIAL)->IsSkipped()) &&
|
||||
(logic->FireTrialClear || ctx->GetTrial(TK_FIRE_TRIAL)->IsSkipped()) &&
|
||||
(logic->WaterTrialClear || ctx->GetTrial(TK_WATER_TRIAL)->IsSkipped()) &&
|
||||
(logic->ShadowTrialClear || ctx->GetTrial(TK_SHADOW_TRIAL)->IsSkipped()) &&
|
||||
(logic->SpiritTrialClear || ctx->GetTrial(TK_SPIRIT_TRIAL)->IsSkipped()) &&
|
||||
(logic->LightTrialClear || ctx->GetTrial(TK_LIGHT_TRIAL)->IsSkipped());}),
|
||||
Entrance(RR_GANONS_TOWER_ENTRYWAY, []{return true;}),
|
||||
Entrance(RR_GANONS_CASTLE_DEKU_SCRUBS, []{return ctx->GetTrickOption(RT_LENS_GANON) || logic->CanUse(RG_LENS_OF_TRUTH);}),
|
||||
});
|
||||
|
||||
|
@ -162,13 +157,7 @@ void RegionTable_Init_GanonsCastle() {
|
|||
Entrance(RR_GANONS_CASTLE_MQ_SHADOW_TRIAL_STARTING_LEDGE, []{return true;}),
|
||||
Entrance(RR_GANONS_CASTLE_MQ_SPIRIT_TRIAL_CHAIRS_ROOM, []{return true;}),
|
||||
Entrance(RR_GANONS_CASTLE_MQ_LIGHT_TRIAL_DINOLFOS_ROOM, []{return Here(RR_GANONS_CASTLE_MQ_MAIN, []{return logic->CanUse(RG_GOLDEN_GAUNTLETS);});}),
|
||||
//RANDOTODO could we just set these events automatically based on the setting?
|
||||
Entrance(RR_GANONS_TOWER_ENTRYWAY, []{return (logic->ForestTrialClear || ctx->GetTrial(TK_FOREST_TRIAL)->IsSkipped()) &&
|
||||
(logic->FireTrialClear || ctx->GetTrial(TK_FIRE_TRIAL)->IsSkipped()) &&
|
||||
(logic->WaterTrialClear || ctx->GetTrial(TK_WATER_TRIAL)->IsSkipped()) &&
|
||||
(logic->ShadowTrialClear || ctx->GetTrial(TK_SHADOW_TRIAL)->IsSkipped()) &&
|
||||
(logic->SpiritTrialClear || ctx->GetTrial(TK_SPIRIT_TRIAL)->IsSkipped()) &&
|
||||
(logic->LightTrialClear || ctx->GetTrial(TK_LIGHT_TRIAL)->IsSkipped());}),
|
||||
Entrance(RR_GANONS_TOWER_ENTRYWAY, []{return true;}),
|
||||
Entrance(RR_GANONS_CASTLE_MQ_DEKU_SCRUBS, []{return ctx->GetTrickOption(RT_LENS_GANON_MQ) || logic->CanUse(RG_LENS_OF_TRUTH);}),
|
||||
});
|
||||
|
||||
|
@ -436,7 +425,13 @@ void RegionTable_Init_GanonsCastle() {
|
|||
//Exits
|
||||
Entrance(RR_GANONS_CASTLE_LOBBY, []{return ctx->GetDungeon(GANONS_CASTLE)->IsVanilla();}),
|
||||
Entrance(RR_GANONS_CASTLE_MQ_MAIN, []{return ctx->GetDungeon(GANONS_CASTLE)->IsMQ();}),
|
||||
Entrance(RR_GANONS_TOWER_FLOOR_1, []{return true;}),
|
||||
//RANDOTODO could we just set these events automatically based on the setting?
|
||||
Entrance(RR_GANONS_TOWER_FLOOR_1, []{return (logic->ForestTrialClear || ctx->GetTrial(TK_FOREST_TRIAL)->IsSkipped()) &&
|
||||
(logic->FireTrialClear || ctx->GetTrial(TK_FIRE_TRIAL)->IsSkipped()) &&
|
||||
(logic->WaterTrialClear || ctx->GetTrial(TK_WATER_TRIAL)->IsSkipped()) &&
|
||||
(logic->ShadowTrialClear || ctx->GetTrial(TK_SHADOW_TRIAL)->IsSkipped()) &&
|
||||
(logic->SpiritTrialClear || ctx->GetTrial(TK_SPIRIT_TRIAL)->IsSkipped()) &&
|
||||
(logic->LightTrialClear || ctx->GetTrial(TK_LIGHT_TRIAL)->IsSkipped());}),
|
||||
});
|
||||
|
||||
areaTable[RR_GANONS_TOWER_FLOOR_1] = Region("Ganon's Tower Floor 1", SCENE_GANONS_TOWER, {}, {}, {
|
||||
|
|
|
@ -84,7 +84,7 @@ void RegionTable_Init_IceCavern() {
|
|||
//Exits
|
||||
//the switch for the glass blocking the entrance is linked to the switch that controls the glass around the skulltulla in RR_ICE_CAVERN_MQ_SCARECROW_ROOM
|
||||
//if you clear the ice, you can hit it with a pot from here.
|
||||
Entrance(RR_ICE_CAVERN_BEGINNING, []{return logic->BlueFire();}),
|
||||
Entrance(RR_ICE_CAVERN_MQ_BEGINNING, []{return logic->BlueFire();}),
|
||||
Entrance(RR_ICE_CAVERN_MQ_MAP_ROOM, []{return Here(RR_ICE_CAVERN_MQ_BEGINNING, []{return logic->CanKillEnemy(RE_WHITE_WOLFOS) && logic->CanKillEnemy(RE_FREEZARD);});}),
|
||||
Entrance(RR_ICE_CAVERN_MQ_COMPASS_ROOM, []{return logic->IsAdult && logic->BlueFire();}),
|
||||
Entrance(RR_ICE_CAVERN_MQ_SCARECROW_ROOM, []{return logic->BlueFire();}),
|
||||
|
|
|
@ -353,10 +353,14 @@ void RegionTable_Init_JabuJabusBelly() {
|
|||
|
||||
// Boss Room
|
||||
areaTable[RR_JABU_JABUS_BELLY_BOSS_ENTRYWAY] = Region("Jabu Jabus Belly Boss Entryway", SCENE_JABU_JABU, {}, {}, {
|
||||
// Exits
|
||||
Entrance(RR_JABU_JABUS_BELLY_BOSS_ROOM, []{return true;}),
|
||||
});
|
||||
|
||||
areaTable[RR_JABU_JABUS_BELLY_BOSS_EXIT] = Region("Jabu Jabus Belly Boss Exit", "Jabu Jabus Belly", {RA_JABU_JABUS_BELLY}, NO_DAY_NIGHT_CYCLE, {}, {}, {
|
||||
// Exits
|
||||
Entrance(RR_JABU_JABUS_BELLY_NEAR_BOSS_ROOM, []{return ctx->GetDungeon(JABU_JABUS_BELLY)->IsVanilla();}),
|
||||
Entrance(RR_JABU_JABUS_BELLY_MQ_EAST_ROOM, []{return ctx->GetDungeon(JABU_JABUS_BELLY)->IsMQ();}),
|
||||
Entrance(RR_JABU_JABUS_BELLY_BOSS_ROOM, []{return true;}),
|
||||
});
|
||||
|
||||
areaTable[RR_JABU_JABUS_BELLY_BOSS_ROOM] = Region("Jabu Jabus Belly Boss Room", SCENE_JABU_JABU_BOSS, {
|
||||
|
@ -374,8 +378,8 @@ void RegionTable_Init_JabuJabusBelly() {
|
|||
LOCATION(RC_BARINADE, logic->JabuJabusBellyClear),
|
||||
}, {
|
||||
// Exits
|
||||
Entrance(RR_JABU_JABUS_BELLY_BOSS_ENTRYWAY, []{return false;}),
|
||||
Entrance(RR_ZORAS_FOUNTAIN, []{return logic->JabuJabusBellyClear;}, false),
|
||||
Entrance(RR_JABU_JABUS_BELLY_BOSS_EXIT, []{return false;}),
|
||||
Entrance(RR_ZORAS_FOUNTAIN, []{return logic->JabuJabusBellyClear;}, false),
|
||||
});
|
||||
|
||||
// clang-format on
|
||||
|
|
|
@ -112,7 +112,7 @@ void RegionTable_Init_ShadowTemple() {
|
|||
LOCATION(RC_SHADOW_TEMPLE_AFTER_SHIP_LOWER_HEART, (logic->CanUse(RG_FAIRY_BOW) || logic->CanUse(RG_DISTANT_SCARECROW) || (ctx->GetTrickOption(RT_SHADOW_STATUE) && logic->CanUse(RG_BOMBCHU_5))) && logic->CanUse(RG_SONG_OF_TIME) || (logic->CanUse(RG_DISTANT_SCARECROW) && logic->CanUse(RG_HOVER_BOOTS))),
|
||||
}, {
|
||||
//Exits
|
||||
Entrance(RR_SHADOW_TEMPLE_BOSS_ENTRYWAY, []{return (logic->CanUse(RG_FAIRY_BOW) || logic->CanUse(RG_DISTANT_SCARECROW) || (ctx->GetTrickOption(RT_SHADOW_STATUE) && logic->CanUse(RG_BOMBCHU_5))) && logic->SmallKeys(RR_SHADOW_TEMPLE, 5) && logic->CanUse(RG_HOVER_BOOTS) && logic->HasItem(RG_SHADOW_TEMPLE_BOSS_KEY);})
|
||||
Entrance(RR_SHADOW_TEMPLE_BOSS_ENTRYWAY, []{return (logic->CanUse(RG_FAIRY_BOW) || logic->CanUse(RG_DISTANT_SCARECROW) || (ctx->GetTrickOption(RT_SHADOW_STATUE) && logic->CanUse(RG_BOMBCHU_5))) && logic->SmallKeys(RR_SHADOW_TEMPLE, 5) && logic->CanUse(RG_HOVER_BOOTS);})
|
||||
});
|
||||
|
||||
#pragma endregion
|
||||
|
@ -370,7 +370,7 @@ void RegionTable_Init_ShadowTemple() {
|
|||
}, {
|
||||
//Exits
|
||||
Entrance(RR_SHADOW_TEMPLE_MQ_ACROSS_CHASM, []{return logic->CanUse(RG_HOVER_BOOTS) && (ctx->GetTrickOption(RT_LENS_SHADOW_MQ) || logic->CanUse(RG_LENS_OF_TRUTH));}),
|
||||
Entrance(RR_SHADOW_TEMPLE_BOSS_ENTRYWAY, []{return logic->HasItem(RG_SHADOW_TEMPLE_BOSS_KEY);}),
|
||||
Entrance(RR_SHADOW_TEMPLE_BOSS_ENTRYWAY, []{return true;}),
|
||||
});
|
||||
|
||||
//Assumes lens is checked on entry
|
||||
|
@ -403,7 +403,7 @@ void RegionTable_Init_ShadowTemple() {
|
|||
// Exits
|
||||
Entrance(RR_SHADOW_TEMPLE_BEYOND_BOAT, []{return ctx->GetDungeon(SHADOW_TEMPLE)->IsVanilla() && false;}),
|
||||
Entrance(RR_SHADOW_TEMPLE_MQ_BEYOND_BOAT, []{return ctx->GetDungeon(SHADOW_TEMPLE)->IsMQ() && false;}),
|
||||
Entrance(RR_SHADOW_TEMPLE_BOSS_ROOM, []{return true;}),
|
||||
Entrance(RR_SHADOW_TEMPLE_BOSS_ROOM, []{return logic->HasItem(RG_SHADOW_TEMPLE_BOSS_KEY);}),
|
||||
});
|
||||
|
||||
areaTable[RR_SHADOW_TEMPLE_BOSS_ROOM] = Region("Shadow Temple Boss Room", SCENE_SHADOW_TEMPLE_BOSS, {
|
||||
|
|
|
@ -145,7 +145,7 @@ void RegionTable_Init_SpiritTemple() {
|
|||
areaTable[RR_SPIRIT_TEMPLE_INSIDE_STATUE_HEAD] = Region("Spirit Temple Inside Statue Head", SCENE_SPIRIT_TEMPLE, {}, {}, {
|
||||
// Exits
|
||||
Entrance(RR_SPIRIT_TEMPLE_CENTRAL_CHAMBER, []{return true;}),
|
||||
Entrance(RR_SPIRIT_TEMPLE_BOSS_ENTRYWAY, []{return logic->HasItem(RG_SPIRIT_TEMPLE_BOSS_KEY);}),
|
||||
Entrance(RR_SPIRIT_TEMPLE_BOSS_ENTRYWAY, []{return true;}),
|
||||
});
|
||||
|
||||
#pragma endregion
|
||||
|
@ -541,7 +541,7 @@ void RegionTable_Init_SpiritTemple() {
|
|||
areaTable[RR_SPIRIT_TEMPLE_MQ_INSIDE_STATUE_HEAD] = Region("Spirit Temple MQ Inside Statue Head", SCENE_SPIRIT_TEMPLE, {}, {}, {
|
||||
// Exits
|
||||
Entrance(RR_SPIRIT_TEMPLE_MQ_LOBBY, []{return true;}),
|
||||
Entrance(RR_SPIRIT_TEMPLE_BOSS_ENTRYWAY, []{return logic->HasItem(RG_SPIRIT_TEMPLE_BOSS_KEY);}),
|
||||
Entrance(RR_SPIRIT_TEMPLE_BOSS_ENTRYWAY, []{return true;}),
|
||||
});
|
||||
|
||||
#pragma endregion
|
||||
|
@ -551,7 +551,7 @@ void RegionTable_Init_SpiritTemple() {
|
|||
// Exits
|
||||
Entrance(RR_SPIRIT_TEMPLE_INSIDE_STATUE_HEAD, []{return ctx->GetDungeon(SPIRIT_TEMPLE)->IsVanilla() && false;}),
|
||||
Entrance(RR_SPIRIT_TEMPLE_MQ_INSIDE_STATUE_HEAD, []{return ctx->GetDungeon(SPIRIT_TEMPLE)->IsMQ() && false;}),
|
||||
Entrance(RR_SPIRIT_TEMPLE_BOSS_ROOM, []{return true;}),
|
||||
Entrance(RR_SPIRIT_TEMPLE_BOSS_ROOM, []{return logic->HasItem(RG_SPIRIT_TEMPLE_BOSS_KEY);}),
|
||||
});
|
||||
|
||||
areaTable[RR_SPIRIT_TEMPLE_BOSS_ROOM] = Region("Spirit Temple Boss Room", SCENE_SPIRIT_TEMPLE_BOSS, {
|
||||
|
|
|
@ -277,7 +277,7 @@ void RegionTable_Init_WaterTemple() {
|
|||
}, {
|
||||
//Exits
|
||||
Entrance(RR_WATER_TEMPLE_LOBBY, []{return true;}),
|
||||
Entrance(RR_WATER_TEMPLE_BOSS_ENTRYWAY, []{return logic->HasItem(RG_WATER_TEMPLE_BOSS_KEY);}),
|
||||
Entrance(RR_WATER_TEMPLE_BOSS_ENTRYWAY, []{return true;}),
|
||||
});
|
||||
|
||||
#pragma endregion
|
||||
|
@ -365,7 +365,7 @@ void RegionTable_Init_WaterTemple() {
|
|||
areaTable[RR_WATER_TEMPLE_MQ_BOSS_DOOR] = Region("Water Temple MQ Main", SCENE_WATER_TEMPLE, {}, {}, {
|
||||
//Exits
|
||||
Entrance(RR_WATER_TEMPLE_MQ_3F_NORTH_LEDGE, []{return logic->CanUse(RG_ICE_ARROWS) || logic->TakeDamage();}),
|
||||
Entrance(RR_WATER_TEMPLE_BOSS_ENTRYWAY, []{return logic->HasItem(RG_WATER_TEMPLE_BOSS_KEY);}),
|
||||
Entrance(RR_WATER_TEMPLE_BOSS_ENTRYWAY, []{return true;}),
|
||||
});
|
||||
|
||||
areaTable[RR_WATER_TEMPLE_MQ_EAST_TOWER] = Region("Water Temple MQ East Tower", SCENE_WATER_TEMPLE, {
|
||||
|
@ -843,7 +843,7 @@ void RegionTable_Init_WaterTemple() {
|
|||
// Exits
|
||||
Entrance(RR_WATER_TEMPLE_PRE_BOSS_ROOM, []{return ctx->GetDungeon(WATER_TEMPLE)->IsVanilla() && false;}),
|
||||
Entrance(RR_WATER_TEMPLE_MQ_BOSS_DOOR, []{return ctx->GetDungeon(WATER_TEMPLE)->IsMQ() && false;}),
|
||||
Entrance(RR_WATER_TEMPLE_BOSS_ROOM, []{return true;}),
|
||||
Entrance(RR_WATER_TEMPLE_BOSS_ROOM, []{return logic->HasItem(RG_WATER_TEMPLE_BOSS_KEY);}),
|
||||
});
|
||||
|
||||
areaTable[RR_WATER_TEMPLE_BOSS_ROOM] = Region("Water Temple Boss Room", SCENE_WATER_TEMPLE_BOSS, {
|
||||
|
|
|
@ -117,7 +117,7 @@ void RegionTable_Init_LakeHylia() {
|
|||
|
||||
areaTable[RR_LH_LAB] = Region("LH Lab", SCENE_LAKESIDE_LABORATORY, {}, {
|
||||
//Locations
|
||||
LOCATION(RC_LH_LAB_DIVE, logic->HasItem(RG_GOLDEN_SCALE) || (ctx->GetTrickOption(RT_LH_LAB_DIVING) && logic->CanUse(RG_IRON_BOOTS) && logic->CanUse(RG_HOOKSHOT))),
|
||||
LOCATION(RC_LH_LAB_DIVE, logic->HasItem(RG_GOLDEN_SCALE) || (ctx->GetTrickOption(RT_LH_LAB_DIVING) && logic->CanUse(RG_IRON_BOOTS) && logic->CanUse(RG_HOOKSHOT) && logic->HasItem(RG_BRONZE_SCALE))),
|
||||
LOCATION(RC_LH_TRADE_FROG, logic->IsAdult && logic->CanUse(RG_EYEBALL_FROG)),
|
||||
LOCATION(RC_LH_GS_LAB_CRATE, logic->CanUse(RG_IRON_BOOTS) && logic->CanUse(RG_HOOKSHOT) && logic->CanBreakCrates()),
|
||||
LOCATION(RC_LH_LAB_FRONT_RUPEE, logic->CanUse(RG_IRON_BOOTS) || logic->HasItem(RG_GOLDEN_SCALE)),
|
||||
|
|
|
@ -828,20 +828,6 @@ void Rando::StaticData::InitLocationTable() {
|
|||
locationTable[RC_DMC_GREAT_FAIRY_REWARD] = Location::Base(RC_DMC_GREAT_FAIRY_REWARD, RCQUEST_BOTH, RCTYPE_STANDARD, RCAREA_DEATH_MOUNTAIN_CRATER, ACTOR_BG_DY_YOSEIZO, SCENE_GREAT_FAIRYS_FOUNTAIN_MAGIC, 2, "Great Fairy Reward", RHT_DMC_GREAT_FAIRY_REWARD, RG_PROGRESSIVE_MAGIC_METER, SpoilerCollectionCheck::RandomizerInf(RAND_INF_DMC_GREAT_FAIRY_REWARD), true);
|
||||
locationTable[RC_OGC_GREAT_FAIRY_REWARD] = Location::Base(RC_OGC_GREAT_FAIRY_REWARD, RCQUEST_BOTH, RCTYPE_STANDARD, RCAREA_HYRULE_CASTLE, ACTOR_BG_DY_YOSEIZO, SCENE_GREAT_FAIRYS_FOUNTAIN_MAGIC, 3, "OGC Great Fairy Reward", "OGC Great Fairy Reward", RHT_OGC_GREAT_FAIRY_REWARD, RG_DOUBLE_DEFENSE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_OGC_GREAT_FAIRY_REWARD), true);
|
||||
|
||||
// Songs
|
||||
locationTable[RC_SHEIK_IN_FOREST] = Location::Base(RC_SHEIK_IN_FOREST, RCQUEST_BOTH, RCTYPE_SONG_LOCATION, ACTOR_ID_MAX, SCENE_SACRED_FOREST_MEADOW, 0x00, "Sheik in Forest", "Sheik in Forest", RHT_SHEIK_IN_FOREST, RG_MINUET_OF_FOREST, SpoilerCollectionCheck::EventChkInf(EVENTCHKINF_LEARNED_MINUET_OF_FOREST), true);
|
||||
locationTable[RC_SHEIK_IN_CRATER] = Location::Base(RC_SHEIK_IN_CRATER, RCQUEST_BOTH, RCTYPE_SONG_LOCATION, ACTOR_ID_MAX, SCENE_DEATH_MOUNTAIN_CRATER, 0x00, "Sheik in Crater", "Sheik in Crater", RHT_SHEIK_IN_CRATER, RG_BOLERO_OF_FIRE, SpoilerCollectionCheck::EventChkInf(EVENTCHKINF_LEARNED_BOLERO_OF_FIRE), true);
|
||||
locationTable[RC_SHEIK_IN_ICE_CAVERN] = Location::Base(RC_SHEIK_IN_ICE_CAVERN, RCQUEST_BOTH, RCTYPE_SONG_LOCATION, ACTOR_ID_MAX, SCENE_ICE_CAVERN, 0x00, "Sheik in Ice Cavern", "Sheik in Ice Cavern", RHT_SHEIK_IN_ICE_CAVERN, RG_SERENADE_OF_WATER, SpoilerCollectionCheck::EventChkInf(EVENTCHKINF_LEARNED_SERENADE_OF_WATER), true);
|
||||
locationTable[RC_SHEIK_AT_COLOSSUS] = Location::Base(RC_SHEIK_AT_COLOSSUS, RCQUEST_BOTH, RCTYPE_SONG_LOCATION, ACTOR_ID_MAX, SCENE_DESERT_COLOSSUS, 0x00, "Sheik at Colossus", "Sheik at Colossus", RHT_SHEIK_AT_COLOSSUS, RG_REQUIEM_OF_SPIRIT, SpoilerCollectionCheck::EventChkInf(EVENTCHKINF_LEARNED_REQUIEM_OF_SPIRIT), true);
|
||||
locationTable[RC_SHEIK_IN_KAKARIKO] = Location::Base(RC_SHEIK_IN_KAKARIKO, RCQUEST_BOTH, RCTYPE_SONG_LOCATION, ACTOR_ID_MAX, SCENE_KAKARIKO_VILLAGE, 0x00, "Sheik in Kakariko", "Sheik in Kakariko", RHT_SHEIK_IN_KAKARIKO, RG_NOCTURNE_OF_SHADOW, SpoilerCollectionCheck::EventChkInf(EVENTCHKINF_BONGO_BONGO_ESCAPED_FROM_WELL), true);
|
||||
locationTable[RC_SHEIK_AT_TEMPLE] = Location::Base(RC_SHEIK_AT_TEMPLE, RCQUEST_BOTH, RCTYPE_SONG_LOCATION, ACTOR_ID_MAX, SCENE_TEMPLE_OF_TIME, 0x00, "Sheik at Temple", "Sheik at Temple", RHT_SHEIK_AT_TEMPLE, RG_PRELUDE_OF_LIGHT, SpoilerCollectionCheck::EventChkInf(EVENTCHKINF_LEARNED_PRELUDE_OF_LIGHT), true);
|
||||
locationTable[RC_SONG_FROM_IMPA] = Location::Base(RC_SONG_FROM_IMPA, RCQUEST_BOTH, RCTYPE_SONG_LOCATION, RCAREA_HYRULE_CASTLE, ACTOR_ID_MAX, SCENE_ID_MAX, 0x00, "Song from Impa", "Song from Impa", RHT_SONG_FROM_IMPA, RG_ZELDAS_LULLABY, SpoilerCollectionCheck::EventChkInf(EVENTCHKINF_LEARNED_ZELDAS_LULLABY), true);
|
||||
locationTable[RC_SONG_FROM_MALON] = Location::Base(RC_SONG_FROM_MALON, RCQUEST_BOTH, RCTYPE_SONG_LOCATION, ACTOR_ID_MAX, SCENE_LON_LON_RANCH, 0x00, "Song from Malon", "Song from Malon", RHT_SONG_FROM_MALON, RG_EPONAS_SONG, SpoilerCollectionCheck::RandomizerInf(RAND_INF_LEARNED_EPONA_SONG), true);
|
||||
locationTable[RC_SONG_FROM_SARIA] = Location::Base(RC_SONG_FROM_SARIA, RCQUEST_BOTH, RCTYPE_SONG_LOCATION, ACTOR_ID_MAX, SCENE_SACRED_FOREST_MEADOW, 0x00, "Song from Saria", "Song from Saria", RHT_SONG_FROM_SARIA, RG_SARIAS_SONG, SpoilerCollectionCheck::EventChkInf(EVENTCHKINF_LEARNED_SARIAS_SONG), true);
|
||||
locationTable[RC_SONG_FROM_ROYAL_FAMILYS_TOMB] = Location::Base(RC_SONG_FROM_ROYAL_FAMILYS_TOMB, RCQUEST_BOTH, RCTYPE_SONG_LOCATION, ACTOR_ID_MAX, SCENE_ROYAL_FAMILYS_TOMB, 0x00, "Song from Royal Family's Tomb", "Song from Royal Family's Tomb", RHT_SONG_FROM_ROYAL_FAMILYS_TOMB, RG_SUNS_SONG, SpoilerCollectionCheck::EventChkInf(EVENTCHKINF_LEARNED_SUNS_SONG), true);
|
||||
locationTable[RC_SONG_FROM_OCARINA_OF_TIME] = Location::Base(RC_SONG_FROM_OCARINA_OF_TIME, RCQUEST_BOTH, RCTYPE_SONG_LOCATION, ACTOR_ID_MAX, SCENE_HYRULE_FIELD, 0x00, "Song from Ocarina of Time", "Song from Ocarina of Time", RHT_SONG_FROM_OCARINA_OF_TIME, RG_SONG_OF_TIME, SpoilerCollectionCheck::EventChkInf(EVENTCHKINF_LEARNED_SONG_OF_TIME), true);
|
||||
locationTable[RC_SONG_FROM_WINDMILL] = Location::Base(RC_SONG_FROM_WINDMILL, RCQUEST_BOTH, RCTYPE_SONG_LOCATION, RCAREA_KAKARIKO_VILLAGE, ACTOR_ID_MAX, SCENE_WINDMILL_AND_DAMPES_GRAVE, 0x00, "Song from Windmill", "Song from Windmill", RHT_SONG_FROM_WINDMILL, RG_SONG_OF_STORMS, SpoilerCollectionCheck::EventChkInf(EVENTCHKINF_LEARNED_SONG_OF_STORMS), true);
|
||||
|
||||
/*-------------------------------
|
||||
--- SHOPS ---
|
||||
8 6 2 4
|
||||
|
|
|
@ -2328,8 +2328,10 @@ void Logic::SetInLogic(LogicVal logicVal, bool value) {
|
|||
inLogic[logicVal] = value;
|
||||
}
|
||||
|
||||
void Logic::Reset() {
|
||||
NewSaveContext();
|
||||
void Logic::Reset(bool resetSaveContext /*= true*/) {
|
||||
if (resetSaveContext) {
|
||||
NewSaveContext();
|
||||
}
|
||||
StartPerformanceTimer(PT_LOGIC_RESET);
|
||||
memset(inLogic, false, sizeof(inLogic));
|
||||
// Settings-dependent variables
|
||||
|
@ -2368,37 +2370,39 @@ void Logic::Reset() {
|
|||
ShadowTrialClear = false;
|
||||
LightTrialClear = false;
|
||||
|
||||
// Ocarina C Buttons
|
||||
bool ocBtnShuffle = ctx->GetOption(RSK_SHUFFLE_OCARINA_BUTTONS).Is(true);
|
||||
SetRandoInf(RAND_INF_HAS_OCARINA_A, !ocBtnShuffle);
|
||||
SetRandoInf(RAND_INF_HAS_OCARINA_C_UP, !ocBtnShuffle);
|
||||
SetRandoInf(RAND_INF_HAS_OCARINA_C_DOWN, !ocBtnShuffle);
|
||||
SetRandoInf(RAND_INF_HAS_OCARINA_C_LEFT, !ocBtnShuffle);
|
||||
SetRandoInf(RAND_INF_HAS_OCARINA_C_RIGHT, !ocBtnShuffle);
|
||||
if (resetSaveContext) {
|
||||
// Ocarina C Buttons
|
||||
bool ocBtnShuffle = ctx->GetOption(RSK_SHUFFLE_OCARINA_BUTTONS).Is(true);
|
||||
SetRandoInf(RAND_INF_HAS_OCARINA_A, !ocBtnShuffle);
|
||||
SetRandoInf(RAND_INF_HAS_OCARINA_C_UP, !ocBtnShuffle);
|
||||
SetRandoInf(RAND_INF_HAS_OCARINA_C_DOWN, !ocBtnShuffle);
|
||||
SetRandoInf(RAND_INF_HAS_OCARINA_C_LEFT, !ocBtnShuffle);
|
||||
SetRandoInf(RAND_INF_HAS_OCARINA_C_RIGHT, !ocBtnShuffle);
|
||||
|
||||
// Progressive Items
|
||||
SetUpgrade(UPG_STICKS, ctx->GetOption(RSK_SHUFFLE_DEKU_STICK_BAG).Is(true) ? 0 : 1);
|
||||
SetUpgrade(UPG_NUTS, ctx->GetOption(RSK_SHUFFLE_DEKU_NUT_BAG).Is(true) ? 0 : 1);
|
||||
// Progressive Items
|
||||
SetUpgrade(UPG_STICKS, ctx->GetOption(RSK_SHUFFLE_DEKU_STICK_BAG).Is(true) ? 0 : 1);
|
||||
SetUpgrade(UPG_NUTS, ctx->GetOption(RSK_SHUFFLE_DEKU_NUT_BAG).Is(true) ? 0 : 1);
|
||||
|
||||
// If we're not shuffling swim, we start with it
|
||||
if (ctx->GetOption(RSK_SHUFFLE_SWIM).Is(false)) {
|
||||
SetRandoInf(RAND_INF_CAN_SWIM, true);
|
||||
}
|
||||
// If we're not shuffling swim, we start with it
|
||||
if (ctx->GetOption(RSK_SHUFFLE_SWIM).Is(false)) {
|
||||
SetRandoInf(RAND_INF_CAN_SWIM, true);
|
||||
}
|
||||
|
||||
// If we're not shuffling child's wallet, we start with it
|
||||
if (ctx->GetOption(RSK_SHUFFLE_CHILD_WALLET).Is(false)) {
|
||||
SetRandoInf(RAND_INF_HAS_WALLET, true);
|
||||
}
|
||||
// If we're not shuffling child's wallet, we start with it
|
||||
if (ctx->GetOption(RSK_SHUFFLE_CHILD_WALLET).Is(false)) {
|
||||
SetRandoInf(RAND_INF_HAS_WALLET, true);
|
||||
}
|
||||
|
||||
// If we're not shuffling fishing pole, we start with it
|
||||
if (ctx->GetOption(RSK_SHUFFLE_FISHING_POLE).Is(false)) {
|
||||
SetRandoInf(RAND_INF_FISHING_POLE_FOUND, true);
|
||||
}
|
||||
// If we're not shuffling fishing pole, we start with it
|
||||
if (ctx->GetOption(RSK_SHUFFLE_FISHING_POLE).Is(false)) {
|
||||
SetRandoInf(RAND_INF_FISHING_POLE_FOUND, true);
|
||||
}
|
||||
|
||||
// If not keysanity, start with 1 logical key to account for automatically unlocking the basement door in vanilla
|
||||
// FiT
|
||||
if (!IsFireLoopLocked && ctx->GetDungeon(Rando::FIRE_TEMPLE)->IsVanilla()) {
|
||||
SetSmallKeyCount(SCENE_FIRE_TEMPLE, 1);
|
||||
// If not keysanity, start with 1 logical key to account for automatically unlocking the basement door in
|
||||
// vanilla FiT
|
||||
if (!IsFireLoopLocked && ctx->GetDungeon(Rando::FIRE_TEMPLE)->IsVanilla()) {
|
||||
SetSmallKeyCount(SCENE_FIRE_TEMPLE, 1);
|
||||
}
|
||||
}
|
||||
|
||||
// Bottle Count
|
||||
|
@ -2450,7 +2454,9 @@ void Logic::Reset() {
|
|||
// Other
|
||||
AtDay = false;
|
||||
AtNight = false;
|
||||
GetSaveContext()->linkAge = !ctx->GetOption(RSK_SELECTED_STARTING_AGE).Get();
|
||||
if (resetSaveContext) {
|
||||
GetSaveContext()->linkAge = !ctx->GetOption(RSK_SELECTED_STARTING_AGE).Get();
|
||||
}
|
||||
|
||||
// Events
|
||||
ShowedMidoSwordAndShield = false;
|
||||
|
@ -2515,6 +2521,8 @@ void Logic::Reset() {
|
|||
Spirit1FSilverRupees = false;
|
||||
JabuRutoIn1F = false;
|
||||
|
||||
CalculatingAvailableChecks = false;
|
||||
|
||||
StopPerformanceTimer(PT_LOGIC_RESET);
|
||||
}
|
||||
} // namespace Rando
|
||||
|
|
|
@ -177,6 +177,9 @@ class Logic {
|
|||
|
||||
/* --- END OF HELPERS AND LOCATION ACCESS --- */
|
||||
|
||||
bool CalculatingAvailableChecks = false;
|
||||
bool ACProcessUndiscoveredExits = false;
|
||||
|
||||
SaveContext* mSaveContext = nullptr;
|
||||
Logic();
|
||||
bool CanUse(RandomizerGet itemName);
|
||||
|
@ -248,7 +251,7 @@ class Logic {
|
|||
bool CanUseProjectile();
|
||||
bool CanBuildRainbowBridge();
|
||||
bool CanTriggerLACS();
|
||||
void Reset();
|
||||
void Reset(bool resetSaveContext = true);
|
||||
void SetContext(std::shared_ptr<Context> _ctx);
|
||||
bool GetInLogic(LogicVal logicVal);
|
||||
void SetInLogic(LogicVal logicVal, bool remove);
|
||||
|
|
|
@ -196,6 +196,8 @@ void Settings::CreateOptionDescriptions() {
|
|||
mOptionDescriptions[RSK_MIX_INTERIOR_ENTRANCES] = "Interior entrances will be part of the mixed pool.";
|
||||
mOptionDescriptions[RSK_MIX_GROTTO_ENTRANCES] = "Grotto entrances will be part of the mixed pool.";
|
||||
mOptionDescriptions[RSK_SHUFFLE_SONGS] =
|
||||
"Off - Songs will appear at their vanilla locations.\n"
|
||||
"\n"
|
||||
"Song locations - Songs will only appear at locations that normally teach songs.\n"
|
||||
"\n"
|
||||
"Dungeon rewards - Songs appear after beating a major dungeon boss.\n"
|
||||
|
@ -593,7 +595,6 @@ void Settings::CreateOptionDescriptions() {
|
|||
"\n"
|
||||
"Greg as Wildcard - Greg does not change logic, Greg helps obtain GBK, max number of "
|
||||
"rewards on slider does not change.";
|
||||
mOptionDescriptions[RSK_CUCCO_COUNT] = "The amount of cuccos needed to claim the reward from Anju the Cucco Lady.";
|
||||
mOptionDescriptions[RSK_BIG_POE_COUNT] = "The Poe collector will give a reward for turning in this many Big Poes.";
|
||||
mOptionDescriptions[RSK_SKIP_CHILD_STEALTH] =
|
||||
"The crawlspace into Hyrule Castle goes straight to Zelda, skipping the guards.";
|
||||
|
|
|
@ -365,10 +365,20 @@ std::unordered_map<s16, s16> getItemIdToItemId = {
|
|||
bool Randomizer::SpoilerFileExists(const char* spoilerFileName) {
|
||||
if (strcmp(spoilerFileName, "") != 0) {
|
||||
std::ifstream spoilerFileStream(SohUtils::Sanitize(spoilerFileName));
|
||||
if (!spoilerFileStream) {
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
if (spoilerFileStream) {
|
||||
nlohmann::json contents;
|
||||
spoilerFileStream >> contents;
|
||||
spoilerFileStream.close();
|
||||
if (contents.contains("version") &&
|
||||
strcmp(std::string(contents["version"]).c_str(), (char*)gBuildVersion) == 0) {
|
||||
return true;
|
||||
} else {
|
||||
SohGui::RegisterPopup(
|
||||
"Old Spoiler Version",
|
||||
"The spoiler file located at\n" + std::string(spoilerFileName) +
|
||||
"\nwas made by a version that doesn't match the currently running version.\n" +
|
||||
"Loading for this file has been cancelled.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -4393,7 +4403,7 @@ CustomMessage Randomizer::GetFishingPondOwnerMessage(u16 originalTextId) {
|
|||
"fischen!",
|
||||
"Désolé, mais l'étang est fermé.&J'ai perdu ma bonne %rCanne à Pêche%w...&Impossible de pêcher sans elle!");
|
||||
|
||||
if (Rando::Context::GetInstance()->GetOption(RSK_FISHING_POLE_HINT)) {
|
||||
if (GetRandoSettingValue(RSK_FISHING_POLE_HINT)) {
|
||||
messageEntry = messageEntry + CustomMessage(ctx->GetHint(RH_FISHING_POLE)->GetHintMessage());
|
||||
}
|
||||
|
||||
|
|
|
@ -577,6 +577,7 @@ typedef enum {
|
|||
RR_DEKU_TREE_MQ_OUTSIDE_BOSS_ROOM,
|
||||
|
||||
RR_DEKU_TREE_BOSS_ENTRYWAY,
|
||||
RR_DEKU_TREE_BOSS_EXIT,
|
||||
RR_DEKU_TREE_BOSS_ROOM,
|
||||
|
||||
RR_DODONGOS_CAVERN_BEGINNING,
|
||||
|
@ -624,7 +625,9 @@ typedef enum {
|
|||
RR_DODONGOS_CAVERN_MQ_BEHIND_MOUTH,
|
||||
RR_DODONGOS_CAVERN_MQ_BACK_BEHIND_FIRE,
|
||||
RR_DODONGOS_CAVERN_MQ_BACK_SWITCH_GRAVE,
|
||||
|
||||
RR_DODONGOS_CAVERN_BOSS_ENTRYWAY,
|
||||
RR_DODONGOS_CAVERN_BOSS_EXIT,
|
||||
RR_DODONGOS_CAVERN_BOSS_ROOM,
|
||||
|
||||
RR_JABU_JABUS_BELLY_BEGINNING,
|
||||
|
@ -653,6 +656,7 @@ typedef enum {
|
|||
RR_JABU_JABUS_BELLY_MQ_EAST_ROOM,
|
||||
|
||||
RR_JABU_JABUS_BELLY_BOSS_ENTRYWAY,
|
||||
RR_JABU_JABUS_BELLY_BOSS_EXIT,
|
||||
RR_JABU_JABUS_BELLY_BOSS_ROOM,
|
||||
|
||||
RR_FOREST_TEMPLE_FIRST_ROOM,
|
||||
|
@ -5808,7 +5812,6 @@ typedef enum {
|
|||
RSK_STARTING_NUTS,
|
||||
RSK_FULL_WALLETS,
|
||||
RSK_SHUFFLE_CHEST_MINIGAME,
|
||||
RSK_CUCCO_COUNT,
|
||||
RSK_BIG_POE_COUNT,
|
||||
RSK_SKIP_EPONA_RACE,
|
||||
RSK_COMPLETE_MASK_QUEST,
|
||||
|
@ -6152,6 +6155,7 @@ typedef enum {
|
|||
|
||||
// Song shuffle Settings (Song locations, Dungeon rewards, anywhere)
|
||||
typedef enum {
|
||||
RO_SONG_SHUFFLE_OFF,
|
||||
RO_SONG_SHUFFLE_SONG_LOCATIONS,
|
||||
RO_SONG_SHUFFLE_DUNGEON_REWARDS,
|
||||
RO_SONG_SHUFFLE_ANYWHERE,
|
||||
|
|
|
@ -137,8 +137,10 @@ void RandomizerCheckObjects::UpdateImGuiVisibility() {
|
|||
CVarGetInteger(CVAR_RANDOMIZER_SETTING("ShuffleMerchants"), RO_SHUFFLE_MERCHANTS_OFF) !=
|
||||
RO_SHUFFLE_MERCHANTS_OFF) &&
|
||||
(location.GetRCType() != RCTYPE_SONG_LOCATION ||
|
||||
CVarGetInteger(CVAR_RANDOMIZER_SETTING("ShuffleSongs"), RO_SONG_SHUFFLE_SONG_LOCATIONS) !=
|
||||
RO_SONG_SHUFFLE_SONG_LOCATIONS) && // song locations
|
||||
(CVarGetInteger(CVAR_RANDOMIZER_SETTING("ShuffleSongs"), RO_SONG_SHUFFLE_SONG_LOCATIONS) !=
|
||||
RO_SONG_SHUFFLE_SONG_LOCATIONS &&
|
||||
CVarGetInteger(CVAR_RANDOMIZER_SETTING("ShuffleSongs"), RO_SONG_SHUFFLE_SONG_LOCATIONS) !=
|
||||
RO_SONG_SHUFFLE_OFF)) && // song locations
|
||||
((location.GetRCType() != RCTYPE_BOSS_HEART_OR_OTHER_REWARD &&
|
||||
location.GetRandomizerCheck() != RC_SONG_FROM_IMPA &&
|
||||
location.GetRandomizerCheck() != RC_SHEIK_IN_ICE_CAVERN) ||
|
||||
|
|
|
@ -51,6 +51,7 @@ bool showBeans;
|
|||
bool showScrubs;
|
||||
bool showMajorScrubs;
|
||||
bool showMerchants;
|
||||
bool showSongs;
|
||||
bool showBeehives;
|
||||
bool showCows;
|
||||
bool showOverworldFreestanding;
|
||||
|
@ -584,6 +585,13 @@ void CheckTrackerLoadGame(int32_t fileNum) {
|
|||
UpdateAllOrdering();
|
||||
UpdateInventoryChecks();
|
||||
UpdateFilters();
|
||||
|
||||
RegionTable_Init();
|
||||
|
||||
if (Rando::Context::GetInstance()->GetOption(RSK_SHUFFLE_ENTRANCES).Get()) {
|
||||
Rando::Context::GetInstance()->GetEntranceShuffler()->ApplyEntranceOverrides();
|
||||
}
|
||||
|
||||
RecalculateAvailableChecks();
|
||||
}
|
||||
|
||||
|
@ -902,7 +910,6 @@ void LoadFile() {
|
|||
SaveManager::Instance->LoadData("areasSpoiled", areasSpoiled, (uint32_t)0);
|
||||
UpdateAllOrdering();
|
||||
UpdateAllAreas();
|
||||
RegionTable_Init();
|
||||
}
|
||||
|
||||
void Teardown() {
|
||||
|
@ -1306,6 +1313,9 @@ void LoadSettings() {
|
|||
OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_SHUFFLE_MERCHANTS) ==
|
||||
RO_SHUFFLE_MERCHANTS_ALL
|
||||
: true;
|
||||
showSongs = IS_RANDO
|
||||
? OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_SHUFFLE_SONGS) != RO_SONG_SHUFFLE_OFF
|
||||
: false;
|
||||
showBeehives = IS_RANDO
|
||||
? OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_SHUFFLE_BEEHIVES) == RO_GENERIC_YES
|
||||
: false;
|
||||
|
@ -1491,6 +1501,27 @@ void LoadSettings() {
|
|||
showOverworldFreestanding = false;
|
||||
showDungeonFreestanding = true;
|
||||
}
|
||||
|
||||
switch (OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_GANONS_BOSS_KEY)) {
|
||||
case RO_GANON_BOSS_KEY_LACS_STONES:
|
||||
Rando::Context::GetInstance()->LACSCondition(RO_LACS_STONES);
|
||||
break;
|
||||
case RO_GANON_BOSS_KEY_LACS_MEDALLIONS:
|
||||
Rando::Context::GetInstance()->LACSCondition(RO_LACS_MEDALLIONS);
|
||||
break;
|
||||
case RO_GANON_BOSS_KEY_LACS_REWARDS:
|
||||
Rando::Context::GetInstance()->LACSCondition(RO_LACS_REWARDS);
|
||||
break;
|
||||
case RO_GANON_BOSS_KEY_LACS_DUNGEONS:
|
||||
Rando::Context::GetInstance()->LACSCondition(RO_LACS_DUNGEONS);
|
||||
break;
|
||||
case RO_GANON_BOSS_KEY_LACS_TOKENS:
|
||||
Rando::Context::GetInstance()->LACSCondition(RO_LACS_TOKENS);
|
||||
break;
|
||||
default:
|
||||
Rando::Context::GetInstance()->LACSCondition(RO_LACS_VANILLA);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool IsCheckShuffled(RandomizerCheck rc) {
|
||||
|
@ -1516,6 +1547,7 @@ bool IsCheckShuffled(RandomizerCheck rc) {
|
|||
(showMajorScrubs && (rc == RC_LW_DEKU_SCRUB_NEAR_BRIDGE || // The 3 scrubs that are always randomized
|
||||
rc == RC_HF_DEKU_SCRUB_GROTTO || rc == RC_LW_DEKU_SCRUB_GROTTO_FRONT))) &&
|
||||
(loc->GetRCType() != RCTYPE_MERCHANT || showMerchants) &&
|
||||
(loc->GetRCType() != RCTYPE_SONG_LOCATION || showSongs) &&
|
||||
(loc->GetRCType() != RCTYPE_BEEHIVE || showBeehives) &&
|
||||
(loc->GetRCType() != RCTYPE_OCARINA || showOcarinas) &&
|
||||
(loc->GetRCType() != RCTYPE_SKULL_TOKEN || alwaysShowGS ||
|
||||
|
@ -1830,7 +1862,7 @@ void DrawLocation(RandomizerCheck rc) {
|
|||
case RCSHOW_IDENTIFIED:
|
||||
case RCSHOW_SEEN:
|
||||
if (IS_RANDO) {
|
||||
if (itemLoc->GetPlacedRandomizerGet() == RG_ICE_TRAP && !mystery && !itemLoc->IsAddedToPool()) {
|
||||
if (itemLoc->GetPlacedRandomizerGet() == RG_ICE_TRAP && !mystery) {
|
||||
if (status == RCSHOW_IDENTIFIED) {
|
||||
txt = OTRGlobals::Instance->gRandoContext->overrides[rc].GetTrickName().GetForLanguage(
|
||||
gSaveContext.language);
|
||||
|
@ -1840,11 +1872,10 @@ void DrawLocation(RandomizerCheck rc) {
|
|||
.GetName()
|
||||
.GetForLanguage(gSaveContext.language);
|
||||
}
|
||||
} else if (!mystery && !itemLoc->IsAddedToPool()) {
|
||||
} else if (!mystery) {
|
||||
txt = itemLoc->GetPlacedItem().GetName().GetForLanguage(gSaveContext.language);
|
||||
}
|
||||
if (IsVisibleInCheckTracker(rc) && status == RCSHOW_IDENTIFIED && !mystery &&
|
||||
!itemLoc->IsAddedToPool()) {
|
||||
if (IsVisibleInCheckTracker(rc) && status == RCSHOW_IDENTIFIED && !mystery) {
|
||||
auto price = OTRGlobals::Instance->gRandoContext->GetItemLocation(rc)->GetPrice();
|
||||
if (price) {
|
||||
txt += fmt::format(" - {}", price);
|
||||
|
@ -1968,7 +1999,7 @@ void ImGuiDrawTwoColorPickerSection(const char* text, const char* cvarMainName,
|
|||
UIWidgets::PopStyleCombobox();
|
||||
}
|
||||
|
||||
void RecalculateAvailableChecks() {
|
||||
void RecalculateAvailableChecks(RandomizerRegion startingRegion /* = RR_ROOT */) {
|
||||
if (!enableAvailableChecks) {
|
||||
return;
|
||||
}
|
||||
|
@ -1976,20 +2007,22 @@ void RecalculateAvailableChecks() {
|
|||
ResetPerformanceTimer(PT_RECALCULATE_AVAILABLE_CHECKS);
|
||||
StartPerformanceTimer(PT_RECALCULATE_AVAILABLE_CHECKS);
|
||||
|
||||
const auto& ctx = Rando::Context::GetInstance();
|
||||
|
||||
std::vector<RandomizerCheck> targetLocations;
|
||||
targetLocations.reserve(RC_MAX);
|
||||
for (auto& location : Rando::StaticData::GetLocationTable()) {
|
||||
RandomizerCheck rc = location.GetRandomizerCheck();
|
||||
Rando::ItemLocation* itemLocation = OTRGlobals::Instance->gRandoContext->GetItemLocation(rc);
|
||||
Rando::ItemLocation* itemLocation = ctx->GetItemLocation(rc);
|
||||
itemLocation->SetAvailable(false);
|
||||
if (!itemLocation->HasObtained()) {
|
||||
targetLocations.emplace_back(rc);
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<RandomizerCheck> availableChecks = ReachabilitySearch(targetLocations, RG_NONE, true);
|
||||
std::vector<RandomizerCheck> availableChecks = ReachabilitySearch(targetLocations, RG_NONE, true, startingRegion);
|
||||
for (auto& rc : availableChecks) {
|
||||
const auto& itemLocation = OTRGlobals::Instance->gRandoContext->GetItemLocation(rc);
|
||||
const auto& itemLocation = ctx->GetItemLocation(rc);
|
||||
itemLocation->SetAvailable(true);
|
||||
}
|
||||
|
||||
|
@ -1997,7 +2030,7 @@ void RecalculateAvailableChecks() {
|
|||
for (auto& [rcArea, vec] : checksByArea) {
|
||||
areaChecksAvailable[rcArea] = 0;
|
||||
for (auto& rc : vec) {
|
||||
Rando::ItemLocation* itemLocation = OTRGlobals::Instance->gRandoContext->GetItemLocation(rc);
|
||||
Rando::ItemLocation* itemLocation = ctx->GetItemLocation(rc);
|
||||
if (itemLocation->IsAvailable() && IsVisibleInCheckTracker(rc) && !IsCheckHidden(rc)) {
|
||||
areaChecksAvailable[rcArea]++;
|
||||
}
|
||||
|
|
|
@ -61,6 +61,6 @@ void UpdateAllOrdering();
|
|||
void UpdateAllAreas();
|
||||
void RecalculateAllAreaTotals();
|
||||
void SpoilAreaFromCheck(RandomizerCheck rc);
|
||||
void RecalculateAvailableChecks();
|
||||
void RecalculateAvailableChecks(RandomizerRegion startingRegion = RR_ROOT);
|
||||
void CheckTracker_LoadFromPreset(nlohmann::json info);
|
||||
} // namespace CheckTracker
|
||||
|
|
|
@ -253,7 +253,9 @@ void Entrance_Init(void) {
|
|||
for (s16 i = 0; i < 4; i++) {
|
||||
// Zero out the bit in the field which tells the game to keep playing
|
||||
// background music for all four scene setups at each index
|
||||
gEntranceTable[override + i].field &= ~ENTRANCE_INFO_CONTINUE_BGM_FLAG;
|
||||
if (override + i < ENTRANCE_TABLE_SIZE) {
|
||||
gEntranceTable[override + i].field &= ~ENTRANCE_INFO_CONTINUE_BGM_FLAG;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -809,6 +811,7 @@ void Entrance_SetEntranceDiscovered(u16 entranceIndex, u8 isReversedEntrance) {
|
|||
if (idx < SAVEFILE_ENTRANCES_DISCOVERED_IDX_COUNT) {
|
||||
u32 entranceBit = 1 << (entranceIndex - (idx * bitsPerIndex));
|
||||
gSaveContext.ship.stats.entrancesDiscovered[idx] |= entranceBit;
|
||||
CheckTracker_RecalculateAvailableChecks();
|
||||
|
||||
// Set reverse entrance when not decoupled
|
||||
if (!Randomizer_GetSettingValue(RSK_DECOUPLED_ENTRANCES) && !isReversedEntrance) {
|
||||
|
|
|
@ -232,6 +232,8 @@ extern "C" void Randomizer_InitSaveFile() {
|
|||
// Reset triforce pieces collected.
|
||||
gSaveContext.ship.quest.data.randomizer.triforcePiecesCollected = 0;
|
||||
|
||||
SetStartingItems();
|
||||
|
||||
// Set Cutscene flags and texts to skip them.
|
||||
Flags_SetEventChkInf(EVENTCHKINF_FIRST_SPOKE_TO_MIDO);
|
||||
Flags_SetInfTable(INFTABLE_SPOKE_TO_KAEPORA_IN_LAKE_HYLIA);
|
||||
|
@ -430,6 +432,4 @@ extern "C" void Randomizer_InitSaveFile() {
|
|||
gSaveContext.itemGetInf[3] |= 0x800; // Bunny Hood related
|
||||
gSaveContext.itemGetInf[3] |= 0x8000; // Obtained Mask of Truth
|
||||
}
|
||||
|
||||
SetStartingItems();
|
||||
}
|
||||
|
|
|
@ -177,7 +177,7 @@ void Settings::CreateOptions() {
|
|||
OPT_U8(RSK_MQ_GANONS_CASTLE, "Ganon's Castle Quest", {"Vanilla", "Master Quest", "Random"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("MQDungeonsGanonsCastle"), "", WidgetType::Combobox, RO_MQ_SET_VANILLA);
|
||||
OPT_U8(RSK_SHUFFLE_DUNGEON_REWARDS, "Shuffle Dungeon Rewards", {"Vanilla", "End of Dungeons", "Any Dungeon", "Overworld", "Anywhere"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("ShuffleDungeonReward"), mOptionDescriptions[RSK_SHUFFLE_DUNGEON_REWARDS], WidgetType::Combobox, RO_DUNGEON_REWARDS_END_OF_DUNGEON);
|
||||
OPT_U8(RSK_LINKS_POCKET, "Link's Pocket", {"Dungeon Reward", "Advancement", "Anything", "Nothing"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("LinksPocket"), "", WidgetType::Combobox, RO_LINKS_POCKET_DUNGEON_REWARD);
|
||||
OPT_U8(RSK_SHUFFLE_SONGS, "Shuffle Songs", {"Song Locations", "Dungeon Rewards", "Anywhere"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("ShuffleSongs"), mOptionDescriptions[RSK_SHUFFLE_SONGS], WidgetType::Combobox, RO_SONG_SHUFFLE_SONG_LOCATIONS);
|
||||
OPT_U8(RSK_SHUFFLE_SONGS, "Shuffle Songs", {"Off", "Song Locations", "Dungeon Rewards", "Anywhere"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("ShuffleSongs"), mOptionDescriptions[RSK_SHUFFLE_SONGS], WidgetType::Combobox, RO_SONG_SHUFFLE_SONG_LOCATIONS);
|
||||
OPT_U8(RSK_SHOPSANITY, "Shop Shuffle", {"Off", "Specific Count", "Random"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("Shopsanity"), mOptionDescriptions[RSK_SHOPSANITY], WidgetType::Combobox, RO_SHOPSANITY_OFF);
|
||||
OPT_U8(RSK_SHOPSANITY_COUNT, "Shops Item Count", {NumOpts(0, 7/*8*/)}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("ShopsanityCount"), mOptionDescriptions[RSK_SHOPSANITY_COUNT], WidgetType::Slider, 0, false, IMFLAG_NONE);
|
||||
OPT_U8(RSK_SHOPSANITY_PRICES, "Shops Prices", {"Vanilla", "Cheap Balanced", "Balanced", "Fixed", "Range", "Set By Wallet"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("ShopsanityPrices"), mOptionDescriptions[RSK_SHOPSANITY_PRICES], WidgetType::Combobox, RO_PRICE_VANILLA, false, IMFLAG_NONE);
|
||||
|
@ -269,7 +269,6 @@ void Settings::CreateOptions() {
|
|||
OPT_BOOL(RSK_SKIP_EPONA_RACE, "Skip Epona Race", {"Don't Skip", "Skip"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("SkipEponaRace"), mOptionDescriptions[RSK_SKIP_EPONA_RACE], WidgetType::Checkbox, RO_GENERIC_DONT_SKIP);
|
||||
OPT_BOOL(RSK_SKIP_SCARECROWS_SONG, "Skip Scarecrow's Song", CVAR_RANDOMIZER_SETTING("SkipScarecrowsSong"), mOptionDescriptions[RSK_SKIP_SCARECROWS_SONG]);
|
||||
OPT_U8(RSK_BIG_POE_COUNT, "Big Poe Target Count", {NumOpts(0, 10)}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("BigPoeTargetCount"), mOptionDescriptions[RSK_BIG_POE_COUNT], WidgetType::Slider, 10);
|
||||
OPT_U8(RSK_CUCCO_COUNT, "Cuccos to return", {NumOpts(0, 7)}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("CuccosToReturn"), mOptionDescriptions[RSK_CUCCO_COUNT], WidgetType::Slider, 7);
|
||||
OPT_BOOL(RSK_COMPLETE_MASK_QUEST, "Complete Mask Quest", CVAR_RANDOMIZER_SETTING("CompleteMaskQuest"), mOptionDescriptions[RSK_COMPLETE_MASK_QUEST]);
|
||||
OPT_U8(RSK_GOSSIP_STONE_HINTS, "Gossip Stone Hints", {"No Hints", "Need Nothing", "Mask of Truth", "Stone of Agony"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("GossipStoneHints"), mOptionDescriptions[RSK_GOSSIP_STONE_HINTS], WidgetType::Combobox, RO_GOSSIP_STONES_NEED_NOTHING, false, IMFLAG_NONE);
|
||||
OPT_U8(RSK_HINT_CLARITY, "Hint Clarity", {"Obscure", "Ambiguous", "Clear"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("HintClarity"), mOptionDescriptions[RSK_HINT_CLARITY], WidgetType::Combobox, RO_HINT_CLARITY_CLEAR, true, IMFLAG_INDENT);
|
||||
|
@ -1316,8 +1315,8 @@ void Settings::CreateOptions() {
|
|||
WidgetContainerType::TABLE);
|
||||
mOptionGroups[RSG_TIMESAVERS_IMGUI] = OptionGroup::SubGroup(
|
||||
"Timesavers",
|
||||
{ &mOptions[RSK_CUCCO_COUNT], &mOptions[RSK_BIG_POE_COUNT], &mOptions[RSK_SKIP_CHILD_ZELDA],
|
||||
&mOptions[RSK_SKIP_EPONA_RACE], &mOptions[RSK_COMPLETE_MASK_QUEST], &mOptions[RSK_SKIP_SCARECROWS_SONG] },
|
||||
{ &mOptions[RSK_BIG_POE_COUNT], &mOptions[RSK_SKIP_CHILD_ZELDA], &mOptions[RSK_SKIP_EPONA_RACE],
|
||||
&mOptions[RSK_COMPLETE_MASK_QUEST], &mOptions[RSK_SKIP_SCARECROWS_SONG] },
|
||||
WidgetContainerType::COLUMN);
|
||||
mOptionGroups[RSG_ITEM_POOL_HINTS_IMGUI] = OptionGroup::SubGroup("",
|
||||
{
|
||||
|
@ -1582,7 +1581,6 @@ void Settings::CreateOptions() {
|
|||
&mOptions[RSK_SKIP_EPONA_RACE],
|
||||
&mOptions[RSK_SKIP_SCARECROWS_SONG],
|
||||
&mOptions[RSK_BIG_POE_COUNT],
|
||||
&mOptions[RSK_CUCCO_COUNT],
|
||||
&mOptions[RSK_COMPLETE_MASK_QUEST],
|
||||
});
|
||||
mOptionGroups[RSG_MISC] = OptionGroup("Miscellaneous Settings",
|
||||
|
@ -1744,6 +1742,14 @@ TrickOption& Settings::GetTrickOption(const RandomizerTrick key) {
|
|||
return mTrickOptions[key];
|
||||
}
|
||||
|
||||
int Settings::GetRandomizerTrickByName(const std::string& name) {
|
||||
const auto& it = mTrickNameToEnum.find(name);
|
||||
if (it == mTrickNameToEnum.end()) {
|
||||
return -1;
|
||||
}
|
||||
return it->second;
|
||||
}
|
||||
|
||||
void Context::ResetTrickOptions() {
|
||||
for (int count = 0; count < RT_MAX; count++) {
|
||||
mTrickOptions[count].Set(0); // RANDOTODO this can probably be done better
|
||||
|
@ -2878,11 +2884,8 @@ void Context::FinalizeSettings(const std::set<RandomizerCheck>& excludedLocation
|
|||
if (mOptions[RSK_FISHSANITY].IsNot(RO_FISHSANITY_HYRULE_LOACH)) {
|
||||
mOptions[RSK_LOACH_HINT].Set(RO_GENERIC_OFF);
|
||||
}
|
||||
|
||||
if (mOptions[RSK_CUCCO_COUNT].Is(0)) {
|
||||
mOptions[RSK_CHICKENS_HINT].Set(RO_GENERIC_OFF);
|
||||
}
|
||||
}
|
||||
|
||||
void Settings::ParseJson(nlohmann::json spoilerFileJson) {
|
||||
mContext->SetSeedString(spoilerFileJson["seed"].get<std::string>());
|
||||
mContext->SetSeed(spoilerFileJson["finalSeed"].get<uint32_t>());
|
||||
|
|
|
@ -50,6 +50,14 @@ class Settings {
|
|||
*/
|
||||
TrickOption& GetTrickOption(RandomizerTrick key);
|
||||
|
||||
/**
|
||||
* @brief Get the RandomizerTrick corresponding to the provided name.
|
||||
*
|
||||
* @param name
|
||||
* @return int RandomizerTrick index or -1 if not found
|
||||
*/
|
||||
int GetRandomizerTrickByName(const std::string& name);
|
||||
|
||||
/**
|
||||
* @brief Returns a reference to the entire array of options.
|
||||
*
|
||||
|
|
|
@ -49,6 +49,7 @@ class StaticData {
|
|||
static std::vector<RandomizerCheck> GetPondFishLocations();
|
||||
static std::vector<RandomizerCheck> GetOverworldFishLocations();
|
||||
static std::vector<RandomizerCheck> GetOverworldFairyLocations();
|
||||
static void RegisterSongLocations();
|
||||
static void RegisterBeehiveLocations();
|
||||
static void RegisterCowLocations();
|
||||
static void RegisterFishLocations();
|
||||
|
|
|
@ -28,6 +28,7 @@ extern "C" {
|
|||
#include "src/overlays/actors/ovl_En_Daiku/z_en_daiku.h"
|
||||
#include "src/overlays/actors/ovl_Bg_Spot02_Objects/z_bg_spot02_objects.h"
|
||||
#include "src/overlays/actors/ovl_Bg_Spot03_Taki/z_bg_spot03_taki.h"
|
||||
#include "src/overlays/actors/ovl_Bg_Spot06_Objects/z_bg_spot06_objects.h"
|
||||
#include "src/overlays/actors/ovl_Bg_Hidan_Kousi/z_bg_hidan_kousi.h"
|
||||
#include "src/overlays/actors/ovl_Bg_Dy_Yoseizo/z_bg_dy_yoseizo.h"
|
||||
#include "src/overlays/actors/ovl_En_Dnt_Demo/z_en_dnt_demo.h"
|
||||
|
@ -49,8 +50,6 @@ extern void EnRu2_SetEncounterSwitchFlag(EnRu2* enRu2, PlayState* play);
|
|||
extern void EnDaiku_EscapeSuccess(EnDaiku* enDaiku, PlayState* play);
|
||||
}
|
||||
|
||||
#define RAND_GET_OPTION(option) Rando::Context::GetInstance()->GetOption(option).Get()
|
||||
|
||||
void EnMa1_EndTeachSong(EnMa1* enMa1, PlayState* play) {
|
||||
if (Message_GetState(&gPlayState->msgCtx) == TEXT_STATE_CLOSING) {
|
||||
Flags_SetRandomizerInf(RAND_INF_LEARNED_EPONA_SONG);
|
||||
|
@ -1386,3 +1385,15 @@ void TimeSaverRegisterHooks() {
|
|||
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnItemReceive>(TimeSaverOnItemReceiveHandler);
|
||||
});
|
||||
}
|
||||
|
||||
void RegisterSkipWaterTempleGateDelay() {
|
||||
COND_ID_HOOK(OnActorUpdate, ACTOR_BG_SPOT06_OBJECTS,
|
||||
CVarGetInteger(CVAR_ENHANCEMENT("TimeSavers.SkipMiscInteractions"), IS_RANDO), [](void* actor) {
|
||||
BgSpot06Objects* spot06 = static_cast<BgSpot06Objects*>(actor);
|
||||
if (spot06->dyna.actor.params == 0) {
|
||||
spot06->timer = 0;
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
static RegisterShipInitFunc skipWaterTempleGateDelay(RegisterSkipWaterTempleGateDelay);
|
||||
|
|
|
@ -147,28 +147,25 @@ void RegisterOnInterfaceUpdateHook() {
|
|||
timer = gSaveContext.subTimerSeconds;
|
||||
}
|
||||
|
||||
if (timer > 0) {
|
||||
if (timer > prevTimer || (timer % 30 == 0 && prevTimer != timer)) {
|
||||
uint32_t minutes = timer / 60;
|
||||
uint32_t seconds = timer % 60;
|
||||
char* announceBuf = ttsAnnounceBuf;
|
||||
char arg[8]; // at least big enough where no s8 string will overflow
|
||||
if (minutes > 0) {
|
||||
snprintf(arg, sizeof(arg), "%d", minutes);
|
||||
auto translation = GetParameritizedText((minutes > 1) ? "minutes_plural" : "minutes_singular",
|
||||
TEXT_BANK_MISC, arg);
|
||||
announceBuf += snprintf(announceBuf, sizeof(ttsAnnounceBuf), "%s ", translation.c_str());
|
||||
}
|
||||
if (seconds > 0) {
|
||||
snprintf(arg, sizeof(arg), "%d", seconds);
|
||||
auto translation = GetParameritizedText((seconds > 1) ? "seconds_plural" : "seconds_singular",
|
||||
TEXT_BANK_MISC, arg);
|
||||
announceBuf += snprintf(announceBuf, sizeof(ttsAnnounceBuf), "%s", translation.c_str());
|
||||
}
|
||||
assert(announceBuf < ttsAnnounceBuf + sizeof(ttsAnnounceBuf));
|
||||
SpeechSynthesizer::Instance->Speak(ttsAnnounceBuf, GetLanguageCode());
|
||||
prevTimer = timer;
|
||||
if (timer > 0 && timer % (timer < 60 ? 10 : 30) == 0 && timer != prevTimer) {
|
||||
uint32_t minutes = timer / 60;
|
||||
uint32_t seconds = timer % 60;
|
||||
char* announceBuf = ttsAnnounceBuf;
|
||||
char arg[8]; // at least big enough where no s8 string will overflow
|
||||
if (minutes > 0) {
|
||||
snprintf(arg, sizeof(arg), "%d", minutes);
|
||||
auto translation =
|
||||
GetParameritizedText((minutes > 1) ? "minutes_plural" : "minutes_singular", TEXT_BANK_MISC, arg);
|
||||
announceBuf += snprintf(announceBuf, sizeof(ttsAnnounceBuf), "%s ", translation.c_str());
|
||||
}
|
||||
if (seconds > 0) {
|
||||
snprintf(arg, sizeof(arg), "%d", seconds);
|
||||
auto translation =
|
||||
GetParameritizedText((seconds > 1) ? "seconds_plural" : "seconds_singular", TEXT_BANK_MISC, arg);
|
||||
announceBuf += snprintf(announceBuf, sizeof(ttsAnnounceBuf), "%s", translation.c_str());
|
||||
}
|
||||
assert(announceBuf < ttsAnnounceBuf + sizeof(ttsAnnounceBuf));
|
||||
SpeechSynthesizer::Instance->Speak(ttsAnnounceBuf, GetLanguageCode());
|
||||
}
|
||||
|
||||
prevTimer = timer;
|
||||
|
|
|
@ -1151,6 +1151,13 @@ extern "C" void InitOTR() {
|
|||
"Error", "SoH does not have proper file permissions. Please move it to a folder that does and run again.");
|
||||
exit(1);
|
||||
}
|
||||
if (ownPath.string().find("OneDrive") != std::string::npos) {
|
||||
Extractor::ShowErrorBox(
|
||||
"Error",
|
||||
"SoH appears to be in a OneDrive folder, which will cause issues. "
|
||||
"Please move it to a folder outside of OneDrive, like the root of a drive (e.g. \"C:\\Games\\SoH\").");
|
||||
exit(1);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if not defined(__SWITCH__) && not defined(__WIIU__)
|
||||
|
@ -2236,7 +2243,7 @@ extern "C" int CustomMessage_RetrieveIfExists(PlayState* play) {
|
|||
(Randomizer_GetSettingValue(RSK_GOSSIP_STONE_HINTS) == RO_GOSSIP_STONES_NEED_STONE &&
|
||||
CHECK_QUEST_ITEM(QUEST_STONE_OF_AGONY)))) {
|
||||
|
||||
Actor* stone = GET_PLAYER(play)->talkActor;
|
||||
Actor* stone = player->talkActor;
|
||||
RandomizerHint stoneHint = RH_NONE;
|
||||
s16 hintParams = stone->params & 0xFF;
|
||||
|
||||
|
@ -2294,7 +2301,7 @@ extern "C" int CustomMessage_RetrieveIfExists(PlayState* play) {
|
|||
(RandomizerInf)((textId - TEXT_SHOP_ITEM_RANDOM_CONFIRM) + RAND_INF_SHOP_ITEMS_KF_SHOP_ITEM_1));
|
||||
messageEntry = OTRGlobals::Instance->gRandomizer->GetMerchantMessage(rc, TEXT_SHOP_ITEM_RANDOM_CONFIRM);
|
||||
} else if (textId == TEXT_SCRUB_RANDOM) {
|
||||
EnDns* enDns = (EnDns*)GET_PLAYER(play)->talkActor;
|
||||
EnDns* enDns = (EnDns*)player->talkActor;
|
||||
RandomizerCheck rc = OTRGlobals::Instance->gRandomizer->GetCheckFromRandomizerInf(
|
||||
(RandomizerInf)enDns->sohScrubIdentity.randomizerInf);
|
||||
messageEntry = OTRGlobals::Instance->gRandomizer->GetMerchantMessage(
|
||||
|
@ -2344,7 +2351,7 @@ extern "C" int CustomMessage_RetrieveIfExists(PlayState* play) {
|
|||
messageEntry = CustomMessageManager::Instance->RetrieveMessage(Randomizer::merchantMessageTableID, textId,
|
||||
MF_AUTO_FORMAT);
|
||||
} else if (textId == TEXT_SKULLTULA_PEOPLE_IM_CURSED) {
|
||||
actorParams = GET_PLAYER(play)->talkActor->params;
|
||||
actorParams = player->talkActor->params;
|
||||
if (actorParams == 1 && ctx->GetOption(RSK_KAK_10_SKULLS_HINT)) {
|
||||
messageEntry = ctx->GetHint(RH_KAK_10_SKULLS_HINT)->GetHintMessage(MF_AUTO_FORMAT);
|
||||
} else if (actorParams == 2 && ctx->GetOption(RSK_KAK_20_SKULLS_HINT)) {
|
||||
|
@ -2443,6 +2450,10 @@ extern "C" int CustomMessage_RetrieveIfExists(PlayState* play) {
|
|||
} else if (textId == TEXT_BIG_POE_COLLECTED_RANDO) {
|
||||
messageEntry =
|
||||
CustomMessageManager::Instance->RetrieveMessage(customMessageTableID, textId, MF_AUTO_FORMAT);
|
||||
} else if (textId == TEXT_GERUDO_GUARD_FRIENDLY && player->talkActor->id == ACTOR_EN_GE2) {
|
||||
// TODO_TRANSLATE Translate into french and german
|
||||
messageEntry = CustomMessage("Want me to throw you in jail?&\x1B#Yes please&No thanks#", { QM_GREEN });
|
||||
messageEntry.AutoFormat();
|
||||
}
|
||||
}
|
||||
if (textId == TEXT_GS_NO_FREEZE || textId == TEXT_GS_FREEZE) {
|
||||
|
@ -2643,4 +2654,7 @@ void SoH_ProcessDroppedFiles(std::string filePath) {
|
|||
return;
|
||||
}
|
||||
}
|
||||
// #endregion
|
||||
|
||||
extern "C" void CheckTracker_RecalculateAvailableChecks() {
|
||||
CheckTracker::RecalculateAvailableChecks();
|
||||
}
|
||||
|
|
|
@ -168,6 +168,7 @@ void Gfx_UnregisterBlendedTexture(const char* name);
|
|||
void Gfx_TextureCacheDelete(const uint8_t* addr);
|
||||
void SaveManager_ThreadPoolWait();
|
||||
void CheckTracker_OnMessageClose();
|
||||
void CheckTracker_RecalculateAvailableChecks();
|
||||
|
||||
GetItemID RetrieveGetItemIDFromItemID(ItemID itemID);
|
||||
RandomizerGet RetrieveRandomizerGetFromItemID(ItemID itemID);
|
||||
|
|
29
soh/soh/ObjectExtension/ActorMaximumHealth.cpp
Normal file
29
soh/soh/ObjectExtension/ActorMaximumHealth.cpp
Normal file
|
@ -0,0 +1,29 @@
|
|||
#include "ActorMaximumHealth.h"
|
||||
#include "soh/ObjectExtension/ObjectExtension.h"
|
||||
#include "soh/ShipInit.hpp"
|
||||
#include "soh/Enhancements/game-interactor/GameInteractor.h"
|
||||
|
||||
struct ActorMaximumHealth {
|
||||
u8 maximumHealth = 0;
|
||||
};
|
||||
static ObjectExtension::Register<ActorMaximumHealth> ActorMaximumHealthRegister;
|
||||
|
||||
u8 GetActorMaximumHealth(const Actor* actor) {
|
||||
const ActorMaximumHealth* maxHealth = ObjectExtension::GetInstance().Get<ActorMaximumHealth>(actor);
|
||||
return maxHealth != nullptr ? maxHealth->maximumHealth : ActorMaximumHealth{}.maximumHealth;
|
||||
}
|
||||
|
||||
void SetActorMaximumHealth(const Actor* actor, u8 maximumHealth) {
|
||||
ObjectExtension::GetInstance().Set<ActorMaximumHealth>(actor, ActorMaximumHealth{ maximumHealth });
|
||||
}
|
||||
|
||||
static void ActorMaximumHealth_Register() {
|
||||
COND_HOOK(OnActorInit, true, [](void* ptr) {
|
||||
Actor* actor = static_cast<Actor*>(ptr);
|
||||
if (actor->category == ACTORCAT_ENEMY) {
|
||||
SetActorMaximumHealth(actor, actor->colChkInfo.health);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
RegisterShipInitFunc actorMaximumHealthInit(ActorMaximumHealth_Register);
|
17
soh/soh/ObjectExtension/ActorMaximumHealth.h
Normal file
17
soh/soh/ObjectExtension/ActorMaximumHealth.h
Normal file
|
@ -0,0 +1,17 @@
|
|||
#ifndef ACTOR_MAXIMUM_HEALTH_H
|
||||
#define ACTOR_MAXIMUM_HEALTH_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#include "z64actor.h"
|
||||
#endif
|
||||
|
||||
// Max health value for use with health bars, set on actor init
|
||||
u8 GetActorMaximumHealth(const Actor* actor);
|
||||
void SetActorMaximumHealth(const Actor* actor, u8 maximumHealth);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // ACTOR_MAXIMUM_HEALTH_H
|
|
@ -119,10 +119,8 @@ SaveManager::SaveManager() {
|
|||
AddLoadFunction("base", 4, LoadBaseVersion4);
|
||||
AddSaveFunction("base", 4, SaveBase, true, SECTION_PARENT_NONE);
|
||||
|
||||
AddLoadFunction("randomizer", 1, LoadRandomizerVersion1);
|
||||
AddLoadFunction("randomizer", 2, LoadRandomizerVersion2);
|
||||
AddLoadFunction("randomizer", 3, LoadRandomizerVersion3);
|
||||
AddSaveFunction("randomizer", 3, SaveRandomizer, true, SECTION_PARENT_NONE);
|
||||
AddLoadFunction("randomizer", 1, LoadRandomizer);
|
||||
AddSaveFunction("randomizer", 1, SaveRandomizer, true, SECTION_PARENT_NONE);
|
||||
|
||||
AddInitFunction(InitFileImpl);
|
||||
|
||||
|
@ -157,234 +155,7 @@ SaveManager::SaveManager() {
|
|||
}
|
||||
}
|
||||
|
||||
// RANDOTODO should we just have dummy functions that raise warnings instead if these aren't supported?
|
||||
void SaveManager::LoadRandomizerVersion1() {
|
||||
auto randoContext = Rando::Context::GetInstance();
|
||||
RandomizerCheck location = RC_UNKNOWN_CHECK;
|
||||
for (int i = 0; i < RC_MAX; i++) {
|
||||
SaveManager::Instance->LoadData("check" + std::to_string(i), location);
|
||||
SaveManager::Instance->LoadStruct("get" + std::to_string(i), [&]() {
|
||||
SaveManager::Instance->LoadData("rgID", randoContext->GetItemLocation(location)->RefPlacedItem());
|
||||
if (randoContext->GetItemLocation(location)->GetPlacedRandomizerGet() == RG_ICE_TRAP) {
|
||||
randoContext->overrides[location].SetLocation(location);
|
||||
SaveManager::Instance->LoadData("fakeRgID", randoContext->overrides[location].RefLooksLike());
|
||||
SaveManager::Instance->LoadData("trickName", randoContext->overrides[location].GetTrickName().english);
|
||||
SaveManager::Instance->LoadData("trickName", randoContext->overrides[location].GetTrickName().french);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < randoContext->hashIconIndexes.size(); i++) {
|
||||
SaveManager::Instance->LoadData("seed" + std::to_string(i), randoContext->hashIconIndexes[i]);
|
||||
}
|
||||
|
||||
for (int i = 0; i < RSK_MAX; i++) {
|
||||
int key, value;
|
||||
SaveManager::Instance->LoadData("sk" + std::to_string(i), key);
|
||||
SaveManager::Instance->LoadData("sv" + std::to_string(i), value);
|
||||
randoContext->GetOption(RandomizerSettingKey(key)).Set(value);
|
||||
}
|
||||
|
||||
for (int i = 0; i < 50; i++) {
|
||||
RandomizerCheck check;
|
||||
char hintText[200];
|
||||
SaveManager::Instance->LoadData("hc" + std::to_string(i), check);
|
||||
for (int j = 0; j < ARRAY_COUNT(hintText); j++) {
|
||||
SaveManager::Instance->LoadData("ht" + std::to_string(i) + "-" + std::to_string(j), hintText[j]);
|
||||
}
|
||||
RandomizerHint stoneHint = Rando::StaticData::oldVerHintOrder[i - Rando::StaticData::oldVerGossipStoneStart];
|
||||
randoContext->AddHint(stoneHint, Rando::Hint(stoneHint, { CustomMessage(hintText) }));
|
||||
}
|
||||
|
||||
char childAltarText[250];
|
||||
for (int i = 0; i < ARRAY_COUNT(childAltarText); i++) {
|
||||
SaveManager::Instance->LoadData("cat" + std::to_string(i), childAltarText[i]);
|
||||
}
|
||||
randoContext->AddHint(RH_ALTAR_CHILD, Rando::Hint(RH_ALTAR_CHILD, { CustomMessage(childAltarText) }));
|
||||
|
||||
char adultAltarText[750];
|
||||
for (int i = 0; i < ARRAY_COUNT(adultAltarText); i++) {
|
||||
SaveManager::Instance->LoadData("aat" + std::to_string(i), adultAltarText[i]);
|
||||
}
|
||||
randoContext->AddHint(RH_ALTAR_ADULT, Rando::Hint(RH_ALTAR_ADULT, { CustomMessage(adultAltarText) }));
|
||||
|
||||
char ganonHintText[150];
|
||||
for (int i = 0; i < ARRAY_COUNT(ganonHintText); i++) {
|
||||
SaveManager::Instance->LoadData("ght" + std::to_string(i), ganonHintText[i]);
|
||||
}
|
||||
randoContext->AddHint(RH_GANONDORF_HINT, Rando::Hint(RH_GANONDORF_HINT, { CustomMessage(ganonHintText) }));
|
||||
|
||||
char ganonText[250];
|
||||
for (int i = 0; i < ARRAY_COUNT(ganonText); i++) {
|
||||
SaveManager::Instance->LoadData("gt" + std::to_string(i), ganonText[i]);
|
||||
}
|
||||
randoContext->AddHint(RH_GANONDORF_JOKE, Rando::Hint(RH_GANONDORF_JOKE, { CustomMessage(ganonText) }));
|
||||
|
||||
SaveManager::Instance->LoadData("triforcePiecesCollected",
|
||||
gSaveContext.ship.quest.data.randomizer.triforcePiecesCollected);
|
||||
|
||||
SaveManager::Instance->LoadData("pendingIceTrapCount", gSaveContext.ship.pendingIceTrapCount);
|
||||
|
||||
size_t merchantPricesSize = 0;
|
||||
if (randoContext->GetOption(RSK_SHUFFLE_SCRUBS).Is(RO_SCRUBS_OFF)) {
|
||||
merchantPricesSize += NUM_SCRUBS;
|
||||
}
|
||||
if (randoContext->GetOption(RSK_SHOPSANITY).Is(RO_SHOPSANITY_OFF)) {
|
||||
merchantPricesSize += NUM_SHOP_ITEMS;
|
||||
}
|
||||
|
||||
SaveManager::Instance->LoadArray("merchantPrices", merchantPricesSize, [&](size_t i) {
|
||||
SaveManager::Instance->LoadStruct("", [&]() {
|
||||
RandomizerCheck rc;
|
||||
SaveManager::Instance->LoadData("check", rc);
|
||||
uint32_t price;
|
||||
SaveManager::Instance->LoadData("price", price);
|
||||
randoContext->GetItemLocation(rc)->SetCustomPrice(price);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// RANDOTODO if we actually support this, be less lazy
|
||||
void SaveManager::LoadRandomizerVersion2() {
|
||||
auto randoContext = Rando::Context::GetInstance();
|
||||
SaveManager::Instance->LoadArray("itemLocations", RC_MAX, [&](size_t i) {
|
||||
SaveManager::Instance->LoadStruct("", [&]() {
|
||||
SaveManager::Instance->LoadData("rgID", randoContext->GetItemLocation(i)->RefPlacedItem());
|
||||
RandomizerGet rg = RG_NONE;
|
||||
SaveManager::Instance->LoadData("fakeRgID", rg, RG_NONE);
|
||||
if (rg != RG_NONE) {
|
||||
randoContext->overrides[static_cast<RandomizerCheck>(i)] =
|
||||
Rando::ItemOverride(static_cast<RandomizerCheck>(i), rg);
|
||||
SaveManager::Instance->LoadData(
|
||||
"trickName", randoContext->overrides[static_cast<RandomizerCheck>(i)].GetTrickName().english);
|
||||
SaveManager::Instance->LoadData(
|
||||
"trickName", randoContext->overrides[static_cast<RandomizerCheck>(i)].GetTrickName().french);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
auto entranceCtx = randoContext->GetEntranceShuffler();
|
||||
SaveManager::Instance->LoadArray("entrances", ARRAY_COUNT(entranceCtx->entranceOverrides), [&](size_t i) {
|
||||
SaveManager::Instance->LoadStruct("", [&]() {
|
||||
SaveManager::Instance->LoadData("type", entranceCtx->entranceOverrides[i].type);
|
||||
SaveManager::Instance->LoadData("index", entranceCtx->entranceOverrides[i].index);
|
||||
SaveManager::Instance->LoadData("destination", entranceCtx->entranceOverrides[i].destination);
|
||||
SaveManager::Instance->LoadData("override", entranceCtx->entranceOverrides[i].override);
|
||||
SaveManager::Instance->LoadData("overrideDestination",
|
||||
entranceCtx->entranceOverrides[i].overrideDestination);
|
||||
});
|
||||
});
|
||||
|
||||
SaveManager::Instance->LoadArray("seed", randoContext->hashIconIndexes.size(), [&](size_t i) {
|
||||
SaveManager::Instance->LoadData("", randoContext->hashIconIndexes[i]);
|
||||
});
|
||||
|
||||
std::string inputSeed;
|
||||
SaveManager::Instance->LoadData("inputSeed", inputSeed);
|
||||
randoContext->SetSeedString(inputSeed);
|
||||
|
||||
uint32_t finalSeed;
|
||||
SaveManager::Instance->LoadData("finalSeed", finalSeed);
|
||||
randoContext->SetSeed(finalSeed);
|
||||
|
||||
SaveManager::Instance->LoadArray("randoSettings", RSK_MAX, [&](size_t i) {
|
||||
int value = 0;
|
||||
SaveManager::Instance->LoadData("", value);
|
||||
randoContext->GetOption(RandomizerSettingKey(i)).Set(value);
|
||||
});
|
||||
|
||||
SaveManager::Instance->LoadArray("hintLocations", RH_ZR_OPEN_GROTTO_GOSSIP_STONE + 1, [&](size_t i) {
|
||||
SaveManager::Instance->LoadStruct("", [&]() {
|
||||
RandomizerCheck rc = RC_UNKNOWN_CHECK;
|
||||
SaveManager::Instance->LoadData("check", rc);
|
||||
if (rc != RC_UNKNOWN_CHECK) {
|
||||
std::string hintText;
|
||||
SaveManager::Instance->LoadData("hintText", hintText);
|
||||
RandomizerHint stoneHint =
|
||||
Rando::StaticData::oldVerHintOrder[rc - Rando::StaticData::oldVerGossipStoneStart];
|
||||
randoContext->AddHint(stoneHint, Rando::Hint(stoneHint, { CustomMessage(hintText) }));
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
std::string childAltarText;
|
||||
SaveManager::Instance->LoadData("childAltarText", childAltarText);
|
||||
randoContext->AddHint(RH_ALTAR_CHILD, Rando::Hint(RH_ALTAR_CHILD, { CustomMessage(childAltarText) }));
|
||||
std::string adultAltarText;
|
||||
SaveManager::Instance->LoadData("adultAltarText", adultAltarText);
|
||||
randoContext->AddHint(RH_ALTAR_ADULT, Rando::Hint(RH_ALTAR_ADULT, { CustomMessage(adultAltarText) }));
|
||||
std::string ganonHintText;
|
||||
SaveManager::Instance->LoadData("ganonHintText", ganonHintText);
|
||||
randoContext->AddHint(RH_GANONDORF_HINT, Rando::Hint(RH_GANONDORF_HINT, { CustomMessage(ganonHintText) }));
|
||||
std::string ganonText;
|
||||
SaveManager::Instance->LoadData("ganonText", ganonText);
|
||||
randoContext->AddHint(RH_GANONDORF_JOKE, Rando::Hint(RH_GANONDORF_JOKE, { CustomMessage(ganonText) }));
|
||||
std::string dampeText;
|
||||
SaveManager::Instance->LoadData("dampeText", dampeText);
|
||||
randoContext->AddHint(RH_DAMPES_DIARY, Rando::Hint(RH_DAMPES_DIARY, { CustomMessage(dampeText) }));
|
||||
std::string gregHintText;
|
||||
SaveManager::Instance->LoadData("gregHintText", gregHintText);
|
||||
randoContext->AddHint(RH_GREG_RUPEE, Rando::Hint(RH_GREG_RUPEE, { CustomMessage(gregHintText) }));
|
||||
std::string sheikText;
|
||||
SaveManager::Instance->LoadData("sheikText", sheikText);
|
||||
randoContext->AddHint(RH_SHEIK_HINT, Rando::Hint(RH_SHEIK_HINT, { CustomMessage(sheikText) }));
|
||||
std::string sariaText;
|
||||
SaveManager::Instance->LoadData("sariaText", sariaText);
|
||||
randoContext->AddHint(RH_SARIA_HINT, Rando::Hint(RH_SARIA_HINT, { CustomMessage(sariaText) }));
|
||||
std::string fishingPoleText;
|
||||
SaveManager::Instance->LoadData("fishingPoleText", fishingPoleText);
|
||||
randoContext->AddHint(RH_FISHING_POLE, Rando::Hint(RH_FISHING_POLE, { CustomMessage(fishingPoleText) }));
|
||||
std::string warpMinuetText;
|
||||
SaveManager::Instance->LoadData("warpMinuetText", warpMinuetText);
|
||||
randoContext->AddHint(RH_MINUET_WARP_LOC, Rando::Hint(RH_MINUET_WARP_LOC, { CustomMessage(warpMinuetText) }));
|
||||
std::string warpBoleroText;
|
||||
SaveManager::Instance->LoadData("warpBoleroText", warpBoleroText);
|
||||
randoContext->AddHint(RH_BOLERO_WARP_LOC, Rando::Hint(RH_BOLERO_WARP_LOC, { CustomMessage(warpBoleroText) }));
|
||||
std::string warpSerenadeText;
|
||||
SaveManager::Instance->LoadData("warpSerenadeText", warpSerenadeText);
|
||||
randoContext->AddHint(RH_SERENADE_WARP_LOC, Rando::Hint(RH_SERENADE_WARP_LOC, { CustomMessage(warpSerenadeText) }));
|
||||
std::string warpRequiemText;
|
||||
SaveManager::Instance->LoadData("warpRequiemText", warpRequiemText);
|
||||
randoContext->AddHint(RH_REQUIEM_WARP_LOC, Rando::Hint(RH_REQUIEM_WARP_LOC, { CustomMessage(warpRequiemText) }));
|
||||
std::string warpNocturneText;
|
||||
SaveManager::Instance->LoadData("warpNocturneText", warpNocturneText);
|
||||
randoContext->AddHint(RH_NOCTURNE_WARP_LOC, Rando::Hint(RH_NOCTURNE_WARP_LOC, { CustomMessage(warpNocturneText) }));
|
||||
std::string warpPreludeText;
|
||||
SaveManager::Instance->LoadData("warpPreludeText", warpPreludeText);
|
||||
randoContext->AddHint(RH_PRELUDE_WARP_LOC, Rando::Hint(RH_PRELUDE_WARP_LOC, { CustomMessage(warpPreludeText) }));
|
||||
|
||||
SaveManager::Instance->LoadData("triforcePiecesCollected",
|
||||
gSaveContext.ship.quest.data.randomizer.triforcePiecesCollected);
|
||||
|
||||
SaveManager::Instance->LoadData("pendingIceTrapCount", gSaveContext.ship.pendingIceTrapCount);
|
||||
|
||||
std::shared_ptr<Randomizer> randomizer = OTRGlobals::Instance->gRandomizer;
|
||||
|
||||
size_t merchantPricesSize = 0;
|
||||
SaveManager::Instance->LoadData("merchantPricesSize", merchantPricesSize);
|
||||
|
||||
SaveManager::Instance->LoadArray("merchantPrices", merchantPricesSize, [&](size_t i) {
|
||||
SaveManager::Instance->LoadStruct("", [&]() {
|
||||
RandomizerCheck rc;
|
||||
SaveManager::Instance->LoadData("check", rc);
|
||||
uint32_t price;
|
||||
SaveManager::Instance->LoadData("price", price);
|
||||
randoContext->GetItemLocation(rc)->SetCustomPrice(price);
|
||||
});
|
||||
});
|
||||
|
||||
size_t mqDungeonCount;
|
||||
SaveManager::Instance->LoadData("masterQuestDungeonCount", mqDungeonCount, (size_t)0);
|
||||
|
||||
randoContext->GetDungeons()->ClearAllMQ();
|
||||
SaveManager::Instance->LoadArray("masterQuestDungeons", mqDungeonCount, [&](size_t i) {
|
||||
uint16_t scene;
|
||||
SaveManager::Instance->LoadData("", scene);
|
||||
randoContext->GetDungeons()->GetDungeonFromScene(SceneID(scene))->SetMQ();
|
||||
});
|
||||
}
|
||||
|
||||
void SaveManager::LoadRandomizerVersion3() {
|
||||
void SaveManager::LoadRandomizer() {
|
||||
auto randoContext = Rando::Context::GetInstance();
|
||||
SaveManager::Instance->LoadArray("itemLocations", RC_MAX, [&](size_t i) {
|
||||
SaveManager::Instance->LoadStruct("", [&]() {
|
||||
|
@ -1355,8 +1126,57 @@ void SaveManager::LoadFile(int fileNum) {
|
|||
switch (saveBlock["version"].get<int>()) {
|
||||
case 1:
|
||||
for (auto& block : saveBlock["sections"].items()) {
|
||||
int sectionVersion = block.value()["version"];
|
||||
std::string sectionName = block.key();
|
||||
if (sectionName == "randomizer") {
|
||||
bool hasStats = saveBlock["sections"].contains("sohStats");
|
||||
if (block.value()["data"].contains("aat0") || !hasStats) { // Rachael rando data
|
||||
SohGui::RegisterPopup(
|
||||
"Loading old file",
|
||||
"The file in slot " + std::to_string(fileNum + 1) +
|
||||
" appears to contain randomizer data, but is a very old format.\n" +
|
||||
"The randomizer data has been removed, and this file will be treated as a vanilla "
|
||||
"file.\n" +
|
||||
"If this was a randomizer file, the file will not work, and should be deleted.");
|
||||
input.close();
|
||||
saveMtx.unlock();
|
||||
SaveFile(fileNum);
|
||||
return;
|
||||
}
|
||||
s16 major = saveBlock["sections"]["sohStats"]["data"]["buildVersionMajor"];
|
||||
s16 minor = saveBlock["sections"]["sohStats"]["data"]["buildVersionMinor"];
|
||||
s16 patch = saveBlock["sections"]["sohStats"]["data"]["buildVersionPatch"];
|
||||
// block loading outdated rando save
|
||||
if (!(major == gBuildVersionMajor && minor == gBuildVersionMinor &&
|
||||
patch == gBuildVersionPatch)) {
|
||||
input.close();
|
||||
std::string newFileName = Ship::Context::GetPathRelativeToAppDirectory("Save") +
|
||||
("/file" + std::to_string(fileNum + 1) + "-" +
|
||||
std::to_string(GetUnixTimestamp()) + ".bak");
|
||||
std::filesystem::path newFile(newFileName);
|
||||
|
||||
#if defined(__SWITCH__) || defined(__WIIU__)
|
||||
copy_file(fileName.c_str(), newFile.c_str());
|
||||
#else
|
||||
std::filesystem::copy_file(fileName, newFile);
|
||||
#endif
|
||||
|
||||
std::filesystem::remove(fileName);
|
||||
SohGui::RegisterPopup(
|
||||
"Outdated Randomizer Save",
|
||||
"The SoH version in the file in slot " + std::to_string(fileNum + 1) +
|
||||
" does not match the currently running version.\n" +
|
||||
"Non-matching rando saves are unsupported, and the file has been renamed to\n" +
|
||||
" " + newFileName + "\n" +
|
||||
"If this was not in error, the file should be deleted.");
|
||||
saveMtx.unlock();
|
||||
SaveFile(fileNum);
|
||||
return;
|
||||
}
|
||||
}
|
||||
int sectionVersion = block.value()["version"];
|
||||
if (sectionName == "randomizer" && sectionVersion != 1) {
|
||||
sectionVersion = 1;
|
||||
}
|
||||
if (!sectionLoadHandlers.contains(sectionName)) {
|
||||
// Unloadable sections aren't necessarily errors, they are probably mods that were unloaded
|
||||
// TODO report in a more noticeable manner
|
||||
|
|
|
@ -166,11 +166,7 @@ class SaveManager {
|
|||
static void InitFileDebug();
|
||||
static void InitFileMaxed();
|
||||
|
||||
static void LoadRandomizerVersion1();
|
||||
static void LoadRandomizerVersion2();
|
||||
static void LoadRandomizerVersion3();
|
||||
static void LoadTrackerData();
|
||||
static void SaveTrackerData(SaveContext* saveContext, int sectionID, bool fullSave);
|
||||
static void LoadRandomizer();
|
||||
static void SaveRandomizer(SaveContext* saveContext, int sectionID, bool fullSave);
|
||||
|
||||
static void LoadBaseVersion1();
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
#ifdef __cplusplus
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <set>
|
||||
#include <unordered_map>
|
||||
|
|
|
@ -185,33 +185,35 @@ bool ModernMenuHeaderEntry(std::string label) {
|
|||
}
|
||||
|
||||
uint32_t Menu::DrawSearchResults(std::string& menuSearchText) {
|
||||
ImGui::BeginChild("Search Results");
|
||||
int searchCount = 0;
|
||||
for (auto& menuLabel : menuOrder) {
|
||||
auto& menuEntry = menuEntries.at(menuLabel);
|
||||
for (auto& sidebarLabel : menuEntry.sidebarOrder) {
|
||||
auto& sidebar = menuEntry.sidebars[sidebarLabel];
|
||||
for (int i = 0; i < sidebar.columnWidgets.size(); i++) {
|
||||
auto& column = sidebar.columnWidgets.at(i);
|
||||
for (auto& info : column) {
|
||||
if (info.type == WIDGET_SEARCH || info.type == WIDGET_SEPARATOR ||
|
||||
info.type == WIDGET_SEPARATOR_TEXT || info.isHidden) {
|
||||
continue;
|
||||
}
|
||||
const char* tooltip = info.options->tooltip;
|
||||
std::string widgetStr = std::string(info.name) + std::string(tooltip != NULL ? tooltip : "");
|
||||
std::transform(menuSearchText.begin(), menuSearchText.end(), menuSearchText.begin(), ::tolower);
|
||||
menuSearchText.erase(std::remove(menuSearchText.begin(), menuSearchText.end(), ' '),
|
||||
menuSearchText.end());
|
||||
std::transform(widgetStr.begin(), widgetStr.end(), widgetStr.begin(), ::tolower);
|
||||
widgetStr.erase(std::remove(widgetStr.begin(), widgetStr.end(), ' '), widgetStr.end());
|
||||
if (widgetStr.find(menuSearchText) != std::string::npos) {
|
||||
MenuDrawItem(info, 90 / sidebar.columnCount, menuThemeIndex);
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, UIWidgets::ColorValues.at(UIWidgets::Colors::Gray));
|
||||
std::string origin = fmt::format(" ({} -> {}, Col {})", menuEntry.label, sidebarLabel, i + 1);
|
||||
ImGui::Text("%s", origin.c_str());
|
||||
ImGui::PopStyleColor();
|
||||
searchCount++;
|
||||
if (ImGui::BeginChild("Search Results")) {
|
||||
for (auto& menuLabel : menuOrder) {
|
||||
auto& menuEntry = menuEntries.at(menuLabel);
|
||||
for (auto& sidebarLabel : menuEntry.sidebarOrder) {
|
||||
auto& sidebar = menuEntry.sidebars[sidebarLabel];
|
||||
for (int i = 0; i < sidebar.columnWidgets.size(); i++) {
|
||||
auto& column = sidebar.columnWidgets.at(i);
|
||||
for (auto& info : column) {
|
||||
if (info.type == WIDGET_SEARCH || info.type == WIDGET_SEPARATOR ||
|
||||
info.type == WIDGET_SEPARATOR_TEXT || info.isHidden) {
|
||||
continue;
|
||||
}
|
||||
const char* tooltip = info.options->tooltip;
|
||||
std::string widgetStr = std::string(info.name) + std::string(tooltip != NULL ? tooltip : "");
|
||||
std::transform(menuSearchText.begin(), menuSearchText.end(), menuSearchText.begin(), ::tolower);
|
||||
menuSearchText.erase(std::remove(menuSearchText.begin(), menuSearchText.end(), ' '),
|
||||
menuSearchText.end());
|
||||
std::transform(widgetStr.begin(), widgetStr.end(), widgetStr.begin(), ::tolower);
|
||||
widgetStr.erase(std::remove(widgetStr.begin(), widgetStr.end(), ' '), widgetStr.end());
|
||||
if (widgetStr.find(menuSearchText) != std::string::npos) {
|
||||
MenuDrawItem(info, 90 / sidebar.columnCount, menuThemeIndex);
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, UIWidgets::ColorValues.at(UIWidgets::Colors::Gray));
|
||||
std::string origin =
|
||||
fmt::format(" ({} -> {}, Col {})", menuEntry.label, sidebarLabel, i + 1);
|
||||
ImGui::Text("%s", origin.c_str());
|
||||
ImGui::PopStyleColor();
|
||||
searchCount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1123,6 +1123,11 @@ void SohMenu::AddMenuEnhancements() {
|
|||
.Options(CheckboxOptions().Tooltip("Dying will delete your file.\n\n" ICON_FA_EXCLAMATION_TRIANGLE
|
||||
" WARNING " ICON_FA_EXCLAMATION_TRIANGLE
|
||||
"\nTHIS IS NOT REVERSIBLE!\nUSE AT YOUR OWN RISK!"));
|
||||
AddWidget(path, "Switch Timer Multiplier", WIDGET_CVAR_SLIDER_INT)
|
||||
.CVar(CVAR_ENHANCEMENT("SwitchTimerMultiplier"))
|
||||
.Options(IntSliderOptions().Min(-5).Max(5).DefaultValue(0).Format("%+d").Tooltip(
|
||||
"-5 will be half as much time, +5 will be 6x as much time. Affects timed switches, torches, GTG statue "
|
||||
"eyes, & doors in race with Dampe."));
|
||||
AddWidget(path, "Always Win Goron Pot", WIDGET_CVAR_CHECKBOX)
|
||||
.CVar(CVAR_ENHANCEMENT("GoronPot"))
|
||||
.Options(CheckboxOptions().Tooltip("Always get the Heart Piece/Purple Rupee from the Spinning Goron Pot."));
|
||||
|
@ -1138,6 +1143,10 @@ void SohMenu::AddMenuEnhancements() {
|
|||
.CVar(CVAR_ENHANCEMENT("CuccoStayDurationMult"))
|
||||
.Options(IntSliderOptions().Min(1).Max(5).DefaultValue(1).Format("%dx").Tooltip(
|
||||
"Cuccos will stay in place longer after putting them down, by a multiple of the value of the slider."));
|
||||
AddWidget(path, "Cuccos Needed By Anju: %d", WIDGET_CVAR_SLIDER_INT)
|
||||
.CVar(CVAR_ENHANCEMENT("CuccosToReturn"))
|
||||
.Options(IntSliderOptions().Min(0).Max(7).DefaultValue(7).Format("%d").Tooltip(
|
||||
"The amount of cuccos needed to receive bottle from Anju the Cucco Lady."));
|
||||
|
||||
path.column = SECTION_COLUMN_3;
|
||||
AddWidget(path, "Enemies", WIDGET_SEPARATOR_TEXT);
|
||||
|
@ -1312,6 +1321,11 @@ void SohMenu::AddMenuEnhancements() {
|
|||
.Format("%d notes")
|
||||
.Tooltip("Adjust the number of notes you need to play to end the third round."));
|
||||
|
||||
AddWidget(path, "Forest Temple", WIDGET_SEPARATOR_TEXT);
|
||||
AddWidget(path, "Solve Amy's Puzzle", WIDGET_CVAR_CHECKBOX)
|
||||
.CVar(CVAR_ENHANCEMENT("SkipAmyPuzzle"))
|
||||
.Options(CheckboxOptions().Tooltip("Amy's block pushing puzzle instantly solved."));
|
||||
|
||||
path.column = SECTION_COLUMN_3;
|
||||
AddWidget(path, "Fishing", WIDGET_SEPARATOR_TEXT);
|
||||
AddWidget(path, "Customize Behavior##Fishing", WIDGET_CVAR_CHECKBOX)
|
||||
|
@ -1581,9 +1595,6 @@ void SohMenu::AddMenuEnhancements() {
|
|||
.Options(CheckboxOptions().Tooltip(
|
||||
"Keese and Guay no longer target you and simply ignore you as if you were wearing the "
|
||||
"Skull Mask."));
|
||||
AddWidget(path, "No Dampe Fire", WIDGET_CVAR_CHECKBOX)
|
||||
.CVar(CVAR_CHEAT("NoDampeFire"))
|
||||
.Options(CheckboxOptions().Tooltip("Dampe won't drop fireballs during race."));
|
||||
|
||||
AddWidget(path, "Glitch Aids", WIDGET_SEPARATOR_TEXT);
|
||||
AddWidget(path, "Easy Frame Advancing with Pause", WIDGET_CVAR_CHECKBOX)
|
||||
|
|
|
@ -371,17 +371,6 @@ void SohUtils::CopyStringToCharArray(char* destination, std::string source, size
|
|||
}
|
||||
|
||||
std::string SohUtils::Sanitize(std::string stringValue) {
|
||||
// Add backslashes.
|
||||
for (auto i = stringValue.begin();;) {
|
||||
auto const pos =
|
||||
std::find_if(i, stringValue.end(), [](char const c) { return '\\' == c || '\'' == c || '"' == c; });
|
||||
if (pos == stringValue.end()) {
|
||||
break;
|
||||
}
|
||||
i = std::next(stringValue.insert(pos, '\\'), 2);
|
||||
}
|
||||
|
||||
// Removes others.
|
||||
stringValue.erase(std::remove_if(stringValue.begin(), stringValue.end(),
|
||||
[](char const c) { return '\n' == c || '\r' == c || '\0' == c || '\x1A' == c; }),
|
||||
stringValue.end());
|
||||
|
|
|
@ -1260,11 +1260,6 @@ void Actor_Init(Actor* actor, PlayState* play) {
|
|||
actor->init = NULL;
|
||||
|
||||
GameInteractor_ExecuteOnActorInit(actor);
|
||||
|
||||
// For enemy health bar we need to know the max health during init
|
||||
if (actor->category == ACTORCAT_ENEMY) {
|
||||
actor->maximumHealth = actor->colChkInfo.health;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2625,11 +2620,6 @@ void Actor_UpdateAll(PlayState* play, ActorContext* actorCtx) {
|
|||
actor->init = NULL;
|
||||
|
||||
GameInteractor_ExecuteOnActorInit(actor);
|
||||
|
||||
// For enemy health bar we need to know the max health during init
|
||||
if (actor->category == ACTORCAT_ENEMY) {
|
||||
actor->maximumHealth = actor->colChkInfo.health;
|
||||
}
|
||||
}
|
||||
actor = actor->next;
|
||||
} else if (!Object_IsLoaded(&play->objectCtx, actor->objBankIndex)) {
|
||||
|
|
|
@ -4,8 +4,8 @@
|
|||
#include "overlays/effects/ovl_Effect_Ss_Dead_Sound/z_eff_ss_dead_sound.h"
|
||||
#include "textures/icon_item_static/icon_item_static.h"
|
||||
#include "soh/Enhancements/game-interactor/GameInteractor.h"
|
||||
#include "soh/OTRGlobals.h"
|
||||
#include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h"
|
||||
#include "soh/OTRGlobals.h"
|
||||
|
||||
#define FLAGS 0
|
||||
|
||||
|
@ -1536,7 +1536,7 @@ s16 func_8001F404(s16 dropId) {
|
|||
if (LINK_IS_ADULT) {
|
||||
if (dropId == ITEM00_SEEDS) {
|
||||
dropId = ITEM00_ARROWS_SMALL;
|
||||
} else if ((dropId == ITEM00_STICK) && !(CVarGetInteger(CVAR_ENHANCEMENT("TreesDropSticks"), 0))) {
|
||||
} else if (GameInteractor_Should(VB_PREVENT_ADULT_STICK, dropId == ITEM00_STICK)) {
|
||||
dropId = ITEM00_RUPEE_GREEN;
|
||||
}
|
||||
} else {
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
#include "soh/OTRGlobals.h"
|
||||
#include "soh/ResourceManagerHelpers.h"
|
||||
#include "soh/Enhancements/gameplaystats.h"
|
||||
#include "soh/ObjectExtension/ActorMaximumHealth.h"
|
||||
|
||||
#include "message_data_static.h"
|
||||
extern MessageTableEntry* sNesMessageEntryTablePtr;
|
||||
|
@ -3643,7 +3644,7 @@ void Interface_DrawEnemyHealthBar(TargetContext* targetCtx, PlayState* play) {
|
|||
f32 scaleY = -0.75f;
|
||||
f32 scaledHeight = -texHeight * scaleY;
|
||||
f32 halfBarWidth = endTexWidth + ((f32)healthbar_fillWidth / 2);
|
||||
s16 healthBarFill = ((f32)actor->colChkInfo.health / actor->maximumHealth) * healthbar_fillWidth;
|
||||
s16 healthBarFill = ((f32)actor->colChkInfo.health / GetActorMaximumHealth(actor)) * healthbar_fillWidth;
|
||||
|
||||
if (anchorType == ENEMYHEALTH_ANCHOR_ACTOR) {
|
||||
// Get actor projected position
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
|
||||
#include "z_bg_gnd_darkmeiro.h"
|
||||
#include "objects/object_demo_kekkai/object_demo_kekkai.h"
|
||||
#include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h"
|
||||
|
||||
#define FLAGS (ACTOR_FLAG_UPDATE_CULLING_DISABLED | ACTOR_FLAG_DRAW_CULLING_DISABLED)
|
||||
|
||||
|
@ -115,7 +116,9 @@ void BgGndDarkmeiro_UpdateBlockTimer(BgGndDarkmeiro* this, PlayState* play) {
|
|||
if (Flags_GetSwitch(play, ((this->dyna.actor.params >> 8) & 0x3F) + 1)) {
|
||||
if (this->actionFlags & 4) {
|
||||
if (this->timer1 > 0) {
|
||||
this->timer1--;
|
||||
if (GameInteractor_Should(VB_SWITCH_TIMER_TICK, true, this, &this->timer1)) {
|
||||
this->timer1--;
|
||||
}
|
||||
} else {
|
||||
Flags_UnsetSwitch(play, ((this->dyna.actor.params >> 8) & 0x3F) + 1);
|
||||
this->actionFlags &= ~4;
|
||||
|
@ -131,7 +134,9 @@ void BgGndDarkmeiro_UpdateBlockTimer(BgGndDarkmeiro* this, PlayState* play) {
|
|||
if (Flags_GetSwitch(play, ((this->dyna.actor.params >> 8) & 0x3F) + 2)) {
|
||||
if (this->actionFlags & 8) {
|
||||
if (this->timer2 > 0) {
|
||||
this->timer2--;
|
||||
if (GameInteractor_Should(VB_SWITCH_TIMER_TICK, true, &this->timer2)) {
|
||||
this->timer2--;
|
||||
}
|
||||
} else {
|
||||
Flags_UnsetSwitch(play, ((this->dyna.actor.params >> 8) & 0x3F) + 2);
|
||||
this->actionFlags &= ~8;
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
|
||||
#include "z_bg_hidan_curtain.h"
|
||||
#include "objects/gameplay_keep/gameplay_keep.h"
|
||||
#include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h"
|
||||
|
||||
#define FLAGS ACTOR_FLAG_UPDATE_CULLING_DISABLED
|
||||
|
||||
|
@ -191,7 +192,10 @@ void BgHidanCurtain_TurnOff(BgHidanCurtain* this, PlayState* play) {
|
|||
}
|
||||
|
||||
void BgHidanCurtain_WaitForTimer(BgHidanCurtain* this, PlayState* play) {
|
||||
DECR(this->timer);
|
||||
if (GameInteractor_Should(VB_SWITCH_TIMER_TICK, true, this, &this->timer)) {
|
||||
DECR(this->timer);
|
||||
}
|
||||
|
||||
if (this->timer == 0) {
|
||||
this->actionFunc = BgHidanCurtain_TurnOn;
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
#include "z_bg_hidan_fwbig.h"
|
||||
#include "objects/gameplay_keep/gameplay_keep.h"
|
||||
#include "objects/object_hidan_objects/object_hidan_objects.h"
|
||||
#include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h"
|
||||
|
||||
#define FLAGS ACTOR_FLAG_UPDATE_CULLING_DISABLED
|
||||
|
||||
|
@ -165,9 +166,10 @@ void BgHidanFwbig_Lower(BgHidanFwbig* this, PlayState* play) {
|
|||
}
|
||||
|
||||
void BgHidanFwbig_WaitForTimer(BgHidanFwbig* this, PlayState* play) {
|
||||
if (this->timer != 0) {
|
||||
if (GameInteractor_Should(VB_SWITCH_TIMER_TICK, this->timer != 0, this, &this->timer)) {
|
||||
this->timer--;
|
||||
}
|
||||
|
||||
if (this->timer == 0) {
|
||||
this->actionFunc = BgHidanFwbig_Rise;
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
|
||||
#include "z_bg_menkuri_eye.h"
|
||||
#include "objects/object_menkuri_objects/object_menkuri_objects.h"
|
||||
#include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h"
|
||||
|
||||
#define FLAGS ACTOR_FLAG_DRAW_CULLING_DISABLED
|
||||
|
||||
|
@ -90,7 +91,8 @@ void BgMenkuriEye_Update(Actor* thisx, PlayState* play) {
|
|||
|
||||
if (!Flags_GetSwitch(play, this->actor.params)) {
|
||||
if (this->framesUntilDisable != -1) {
|
||||
if (this->framesUntilDisable != 0) {
|
||||
if (GameInteractor_Should(VB_SWITCH_TIMER_TICK, this->framesUntilDisable != 0, this,
|
||||
&this->framesUntilDisable)) {
|
||||
this->framesUntilDisable -= 1;
|
||||
}
|
||||
if (this->framesUntilDisable == 0) {
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
#include "z_bg_mizu_shutter.h"
|
||||
#include "objects/object_mizu_objects/object_mizu_objects.h"
|
||||
#include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h"
|
||||
|
||||
#define FLAGS ACTOR_FLAG_UPDATE_CULLING_DISABLED
|
||||
|
||||
|
@ -137,7 +138,9 @@ void BgMizuShutter_Move(BgMizuShutter* this, PlayState* play) {
|
|||
|
||||
void BgMizuShutter_WaitForTimer(BgMizuShutter* this, PlayState* play) {
|
||||
if (this->timerMax != 0x3F * 20) {
|
||||
this->timer--;
|
||||
if (GameInteractor_Should(VB_SWITCH_TIMER_TICK, true, this, &this->timer)) {
|
||||
this->timer--;
|
||||
}
|
||||
func_8002F994(&this->dyna.actor, this->timer);
|
||||
if (this->timer == 0) {
|
||||
Audio_PlayActorSound2(&this->dyna.actor, NA_SE_EV_METALDOOR_CLOSE);
|
||||
|
|
|
@ -6,6 +6,8 @@
|
|||
|
||||
#include "z_bg_po_event.h"
|
||||
#include "objects/object_po_sisters/object_po_sisters.h"
|
||||
#include "soh/Enhancements/game-interactor/GameInteractor.h"
|
||||
#include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h"
|
||||
|
||||
#define FLAGS 0
|
||||
|
||||
|
@ -333,7 +335,7 @@ void BgPoEvent_BlockIdle(BgPoEvent* this, PlayState* play) {
|
|||
Player* player = GET_PLAYER(play);
|
||||
Actor* amy;
|
||||
|
||||
if (sBgPoEventPuzzleState == 0xF) {
|
||||
if (GameInteractor_Should(VB_AMY_SOLVE, sBgPoEventPuzzleState == 0xF)) {
|
||||
this->actionFunc = BgPoEvent_BlockSolved;
|
||||
if ((this->type == 0) && (this->index == 0)) {
|
||||
amy = Actor_Spawn(&play->actorCtx, play, ACTOR_EN_PO_SISTERS, this->dyna.actor.world.pos.x + 30.0f,
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
|
||||
#include "z_bg_relay_objects.h"
|
||||
#include "objects/object_relay_objects/object_relay_objects.h"
|
||||
#include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h"
|
||||
|
||||
#define FLAGS ACTOR_FLAG_UPDATE_CULLING_DISABLED
|
||||
|
||||
|
@ -133,7 +134,7 @@ void func_808A90F4(BgRelayObjects* this, PlayState* play) {
|
|||
|
||||
void func_808A91AC(BgRelayObjects* this, PlayState* play) {
|
||||
if (this->unk_169 != 5) {
|
||||
if (this->timer != 0) {
|
||||
if (GameInteractor_Should(VB_SWITCH_TIMER_TICK, this->timer != 0, this, &this->timer)) {
|
||||
this->timer--;
|
||||
}
|
||||
func_8002F994(&this->dyna.actor, this->timer);
|
||||
|
@ -168,7 +169,7 @@ void BgRelayObjects_DoNothing(BgRelayObjects* this, PlayState* play) {
|
|||
}
|
||||
|
||||
void func_808A932C(BgRelayObjects* this, PlayState* play) {
|
||||
if (this->timer != 0) {
|
||||
if (GameInteractor_Should(VB_SWITCH_TIMER_TICK, this->timer != 0, &this->timer)) {
|
||||
this->timer--;
|
||||
}
|
||||
if (this->timer == 0) {
|
||||
|
|
|
@ -131,8 +131,14 @@ void func_808BAF40(BgTokiSwd* this, PlayState* play) {
|
|||
Item_Give(play, ITEM_SWORD_MASTER);
|
||||
}
|
||||
play->csCtx.segment = D_808BB2F0;
|
||||
|
||||
// Discover adult spawn
|
||||
Entrance_SetEntranceDiscovered(ENTR_HYRULE_FIELD_10, false);
|
||||
} else {
|
||||
play->csCtx.segment = D_808BB7A0;
|
||||
|
||||
// Discover child spawn
|
||||
Entrance_SetEntranceDiscovered(ENTR_LINKS_HOUSE_CHILD_SPAWN, false);
|
||||
}
|
||||
Audio_QueueSeqCmd(SEQ_PLAYER_BGM_MAIN << 24 | NA_BGM_STOP);
|
||||
Audio_QueueSeqCmd(SEQ_PLAYER_BGM_MAIN << 24 | NA_BGM_MASTER_SWORD);
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
|
||||
#include "z_bg_ydan_hasi.h"
|
||||
#include "objects/object_ydan_objects/object_ydan_objects.h"
|
||||
#include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h"
|
||||
|
||||
#define FLAGS (ACTOR_FLAG_UPDATE_CULLING_DISABLED | ACTOR_FLAG_DRAW_CULLING_DISABLED)
|
||||
|
||||
|
@ -126,9 +127,10 @@ void BgYdanHasi_MoveWater(BgYdanHasi* this, PlayState* play) {
|
|||
}
|
||||
|
||||
void BgYdanHasi_DecWaterTimer(BgYdanHasi* this, PlayState* play) {
|
||||
if (this->timer != 0) {
|
||||
if (GameInteractor_Should(VB_SWITCH_TIMER_TICK, this->timer != 0, this, &this->timer)) {
|
||||
this->timer--;
|
||||
}
|
||||
|
||||
func_8002F994(&this->dyna.actor, this->timer);
|
||||
if (this->timer == 0) {
|
||||
this->actionFunc = BgYdanHasi_MoveWater;
|
||||
|
@ -145,9 +147,10 @@ void BgYdanHasi_SetupThreeBlocks(BgYdanHasi* this, PlayState* play) {
|
|||
}
|
||||
|
||||
void BgYdanHasi_UpdateThreeBlocks(BgYdanHasi* this, PlayState* play) {
|
||||
if (this->timer != 0) {
|
||||
if (GameInteractor_Should(VB_SWITCH_TIMER_TICK, this->timer != 0, this, &this->timer)) {
|
||||
this->timer--;
|
||||
}
|
||||
|
||||
if (this->timer == 0) {
|
||||
if (Math_StepToF(&this->dyna.actor.world.pos.y, this->dyna.actor.home.pos.y, 3.0f) != 0) {
|
||||
Flags_UnsetSwitch(play, this->type);
|
||||
|
|
|
@ -154,17 +154,6 @@ void EnFu_WaitChild(EnFu* this, PlayState* play) {
|
|||
}
|
||||
}
|
||||
|
||||
void GivePlayerRandoRewardSongOfStorms(EnFu* windmillGuy, PlayState* play, RandomizerCheck check) {
|
||||
if (windmillGuy->actor.parent != NULL && windmillGuy->actor.parent->id == GET_PLAYER(play)->actor.id &&
|
||||
!Flags_GetTreasure(play, 0x1F)) {
|
||||
Flags_SetTreasure(play, 0x1F);
|
||||
windmillGuy->actionFunc = func_80A1DBD4;
|
||||
} else if (!Flags_GetTreasure(play, 0x1F)) {
|
||||
GetItemEntry getItemEntry = Randomizer_GetItemFromKnownCheck(check, RG_SONG_OF_STORMS);
|
||||
GiveItemEntryFromActor(&windmillGuy->actor, play, getItemEntry, 10000.0f, 100.0f);
|
||||
}
|
||||
}
|
||||
|
||||
void func_80A1DB60(EnFu* this, PlayState* play) {
|
||||
if (play->csCtx.state == CS_STATE_IDLE) {
|
||||
this->actionFunc = EnFu_WaitAdult;
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
#include "objects/object_fz/object_fz.h"
|
||||
#include "soh/frame_interpolation.h"
|
||||
#include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h"
|
||||
#include "soh/ObjectExtension/ActorMaximumHealth.h"
|
||||
|
||||
#define FLAGS \
|
||||
(ACTOR_FLAG_ATTENTION_ENABLED | ACTOR_FLAG_HOSTILE | ACTOR_FLAG_UPDATE_CULLING_DISABLED | \
|
||||
|
@ -725,7 +726,7 @@ void EnFz_Draw(Actor* thisx, PlayState* play) {
|
|||
// displayLists, so we need to recompute the index based on the scaled health (using the maximum health value) and
|
||||
// clamp the final result for safety.
|
||||
if (CVarGetInteger(CVAR_ENHANCEMENT("EnemySizeScalesHealth"), 0)) {
|
||||
u8 scaledHealth = (u8)(((f32)this->actor.colChkInfo.health / this->actor.maximumHealth) * 6);
|
||||
u8 scaledHealth = (u8)(((f32)this->actor.colChkInfo.health / GetActorMaximumHealth(this)) * 6);
|
||||
index = (6 - scaledHealth) >> 1;
|
||||
index = CLAMP(index, 0, 2);
|
||||
}
|
||||
|
|
|
@ -439,20 +439,21 @@ void EnGe2_LookAtPlayer(EnGe2* this, PlayState* play) {
|
|||
|
||||
void EnGe2_SetActionAfterTalk(EnGe2* this, PlayState* play) {
|
||||
if (Actor_TextboxIsClosing(&this->actor, play)) {
|
||||
|
||||
switch (this->actor.params & 0xFF) {
|
||||
case GE2_TYPE_PATROLLING:
|
||||
EnGe2_ChangeAction(this, GE2_ACTION_ABOUTTURN);
|
||||
break;
|
||||
case GE2_TYPE_STATIONARY:
|
||||
EnGe2_ChangeAction(this, GE2_ACTION_STAND);
|
||||
break;
|
||||
case GE2_TYPE_GERUDO_CARD_GIVER:
|
||||
this->actionFunc = EnGe2_WaitLookAtPlayer;
|
||||
break;
|
||||
if (GameInteractor_Should(VB_GERUDO_GUARD_SET_ACTION_AFTER_TALK, true, this)) {
|
||||
switch (this->actor.params & 0xFF) {
|
||||
case GE2_TYPE_PATROLLING:
|
||||
EnGe2_ChangeAction(this, GE2_ACTION_ABOUTTURN);
|
||||
break;
|
||||
case GE2_TYPE_STATIONARY:
|
||||
EnGe2_ChangeAction(this, GE2_ACTION_STAND);
|
||||
break;
|
||||
case GE2_TYPE_GERUDO_CARD_GIVER:
|
||||
this->actionFunc = EnGe2_WaitLookAtPlayer;
|
||||
break;
|
||||
}
|
||||
this->actor.update = EnGe2_UpdateFriendly;
|
||||
this->actor.flags &= ~ACTOR_FLAG_TALK_OFFER_AUTO_ACCEPTED;
|
||||
}
|
||||
this->actor.update = EnGe2_UpdateFriendly;
|
||||
this->actor.flags &= ~ACTOR_FLAG_TALK_OFFER_AUTO_ACCEPTED;
|
||||
}
|
||||
EnGe2_TurnToFacePlayer(this, play);
|
||||
}
|
||||
|
|
|
@ -303,7 +303,7 @@ void EnMb_Init(Actor* thisx, PlayState* play) {
|
|||
|
||||
relYawFromPlayer =
|
||||
this->actor.world.rot.y - Math_Vec3f_Yaw(&this->actor.world.pos, &player->actor.world.pos);
|
||||
if (ABS(relYawFromPlayer) > 0x4000) {
|
||||
if (ABS(relYawFromPlayer) > 0x4000 && !CVarGetInteger(CVAR_ENHANCEMENT("RandomizedEnemies"), 0)) {
|
||||
this->actor.world.rot.y = thisx->world.rot.y + 0x8000;
|
||||
this->actor.shape.rot.y = thisx->world.rot.y;
|
||||
this->actor.world.pos.z = thisx->world.pos.z + 600.0f;
|
||||
|
|
|
@ -507,7 +507,7 @@ void EnRr_CollisionCheck(EnRr* this, PlayState* play) {
|
|||
this->collider2.base.ocFlags1 &= ~OC1_HIT;
|
||||
// "catch"
|
||||
osSyncPrintf(VT_FGCOL(GREEN) "キャッチ(%d)!!" VT_RST "\n", this->frameCount);
|
||||
if (play->grabPlayer(play, player)) {
|
||||
if (GameInteractor_Should(VB_LIKE_LIKE_GRAB_PLAYER, true, this) && play->grabPlayer(play, player)) {
|
||||
player->actor.parent = &this->actor;
|
||||
this->stopScroll = false;
|
||||
EnRr_SetupGrabPlayer(this, player);
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
|
||||
#include "z_en_siofuki.h"
|
||||
#include "objects/object_siofuki/object_siofuki.h"
|
||||
#include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h"
|
||||
|
||||
#define FLAGS (ACTOR_FLAG_UPDATE_CULLING_DISABLED | ACTOR_FLAG_DRAW_CULLING_DISABLED)
|
||||
|
||||
|
@ -188,7 +189,10 @@ void func_80AFC218(EnSiofuki* this, PlayState* play) {
|
|||
func_80AFBE8C(this, play);
|
||||
func_80AFC1D0(this, play);
|
||||
|
||||
this->timer--;
|
||||
if (GameInteractor_Should(VB_SWITCH_TIMER_TICK, true, this, &this->timer)) {
|
||||
this->timer--;
|
||||
}
|
||||
|
||||
if (this->timer < 0) {
|
||||
Flags_UnsetSwitch(play, ((u16)this->dyna.actor.params >> 6) & 0x3F);
|
||||
switch (((u16)this->dyna.actor.params >> 0xC) & 0xF) {
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#include "z_en_skj.h"
|
||||
#include "overlays/actors/ovl_En_Skjneedle/z_en_skjneedle.h"
|
||||
#include "objects/object_skj/object_skj.h"
|
||||
#include "soh/Enhancements/enhancementTypes.h"
|
||||
#include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h"
|
||||
#include "soh/OTRGlobals.h"
|
||||
#include "soh/ResourceManagerHelpers.h"
|
||||
|
@ -404,7 +405,9 @@ void EnSkj_Init(Actor* thisx, PlayState* play2) {
|
|||
default:
|
||||
this->actor.params = type;
|
||||
if (((this->actor.params != 0) && (this->actor.params != 1)) && (this->actor.params != 2)) {
|
||||
if (INV_CONTENT(ITEM_TRADE_ADULT) < ITEM_SAW) {
|
||||
if (INV_CONTENT(ITEM_TRADE_ADULT) < ITEM_SAW &&
|
||||
CVarGetInteger(CVAR_ENHANCEMENT("RandomizedEnemies"), ENEMY_RANDOMIZER_OFF) ==
|
||||
ENEMY_RANDOMIZER_OFF) {
|
||||
Actor_Kill(&this->actor);
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -706,7 +706,7 @@ void EnTorch2_Update(Actor* thisx, PlayState* play2) {
|
|||
sStaggerCount = 0;
|
||||
}
|
||||
}
|
||||
if (player->linearVelocity == -18.0f) {
|
||||
if (GameInteractor_Should(VB_TORCH2_HANDLE_CLANKING, player->linearVelocity == -18.0f, this)) {
|
||||
if (this->actor.xzDistToPlayer > 80.0f) {
|
||||
player->linearVelocity = 1.2f;
|
||||
} else if (this->actor.xzDistToPlayer < 70.0f) {
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
|
||||
#include "z_en_wood02.h"
|
||||
#include "objects/object_wood02/object_wood02.h"
|
||||
#include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h"
|
||||
|
||||
#define FLAGS 0
|
||||
|
||||
|
@ -327,7 +328,6 @@ void EnWood02_Update(Actor* thisx, PlayState* play2) {
|
|||
Vec3f dropsSpawnPt;
|
||||
s32 i;
|
||||
s32 leavesParams;
|
||||
s32 numDrops;
|
||||
|
||||
// Despawn extra trees in a group if out of range
|
||||
if ((this->spawnType == WOOD_SPAWN_SPAWNED) && (this->actor.parent != NULL)) {
|
||||
|
@ -358,12 +358,7 @@ void EnWood02_Update(Actor* thisx, PlayState* play2) {
|
|||
dropsSpawnPt.y += 200.0f;
|
||||
|
||||
if ((this->unk_14C >= 0) && (this->unk_14C < 0x64)) {
|
||||
if (CVarGetInteger(CVAR_ENHANCEMENT("TreesDropSticks"), 0) && INV_CONTENT(ITEM_STICK) != ITEM_NONE) {
|
||||
numDrops = Rand_ZeroOne() * 4;
|
||||
for (i = 0; i < numDrops; ++i) {
|
||||
Item_DropCollectible(play, &dropsSpawnPt, ITEM00_STICK);
|
||||
}
|
||||
} else {
|
||||
if (GameInteractor_Should(VB_TREE_DROP_COLLECTIBLE, true, this)) {
|
||||
Item_DropCollectibleRandom(play, &this->actor, &dropsSpawnPt, this->unk_14C << 4);
|
||||
}
|
||||
} else if (this->actor.home.rot.z != 0) {
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
#include "overlays/actors/ovl_En_Arrow/z_en_arrow.h"
|
||||
#include "objects/gameplay_keep/gameplay_keep.h"
|
||||
#include "objects/object_syokudai/object_syokudai.h"
|
||||
#include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h"
|
||||
|
||||
#define FLAGS (ACTOR_FLAG_UPDATE_CULLING_DISABLED | ACTOR_FLAG_HOOKSHOT_PULLS_PLAYER)
|
||||
|
||||
|
@ -239,7 +240,7 @@ void ObjSyokudai_Update(Actor* thisx, PlayState* play2) {
|
|||
Collider_UpdateCylinder(&this->actor, &this->colliderFlame);
|
||||
CollisionCheck_SetAC(play, &play->colChkCtx, &this->colliderFlame.base);
|
||||
|
||||
if (this->litTimer > 0) {
|
||||
if (GameInteractor_Should(VB_SWITCH_TIMER_TICK, this->litTimer > 0, this, &this->litTimer)) {
|
||||
this->litTimer--;
|
||||
if ((this->litTimer == 0) && (torchType != 0)) {
|
||||
sLitTorchCount--;
|
||||
|
|
|
@ -4764,7 +4764,9 @@ s32 func_808382DC(Player* this, PlayState* play) {
|
|||
gSaveContext.respawn[RESPAWN_MODE_DOWN].yaw = respawnInfo->yaw;
|
||||
}
|
||||
|
||||
Play_TriggerVoidOut(play);
|
||||
if (GameInteractor_Should(VB_TRIGGER_VOIDOUT, true, this)) {
|
||||
Play_TriggerVoidOut(play);
|
||||
}
|
||||
}
|
||||
|
||||
Player_PlayVoiceSfx(this, NA_SE_VO_LI_TAKEN_AWAY);
|
||||
|
@ -5129,7 +5131,9 @@ s32 Player_HandleExitsAndVoids(PlayState* play, Player* this, CollisionPoly* pol
|
|||
}
|
||||
|
||||
if (exitIndex == 0) {
|
||||
Play_TriggerVoidOut(play);
|
||||
if (GameInteractor_Should(VB_TRIGGER_VOIDOUT, true, this)) {
|
||||
Play_TriggerVoidOut(play);
|
||||
}
|
||||
Scene_SetTransitionForNextEntrance(play);
|
||||
} else {
|
||||
play->nextEntranceIndex = play->setupExitList[exitIndex - 1];
|
||||
|
@ -5163,7 +5167,9 @@ s32 Player_HandleExitsAndVoids(PlayState* play, Player* this, CollisionPoly* pol
|
|||
SurfaceType_GetSlope(&play->colCtx, poly, bgId) == 2,
|
||||
play->setupExitList[exitIndex - 1])) {
|
||||
gSaveContext.respawn[RESPAWN_MODE_DOWN].entranceIndex = play->nextEntranceIndex;
|
||||
Play_TriggerVoidOut(play);
|
||||
if (GameInteractor_Should(VB_TRIGGER_VOIDOUT, true, this)) {
|
||||
Play_TriggerVoidOut(play);
|
||||
}
|
||||
gSaveContext.respawnFlag = -2;
|
||||
}
|
||||
gSaveContext.retainWeatherMode = 1;
|
||||
|
@ -5226,7 +5232,7 @@ s32 Player_HandleExitsAndVoids(PlayState* play, Player* this, CollisionPoly* pol
|
|||
if (this->actor.bgCheckFlags & 1) {
|
||||
if (this->floorProperty == 5) {
|
||||
Play_TriggerRespawn(play);
|
||||
} else {
|
||||
} else if (GameInteractor_Should(VB_TRIGGER_VOIDOUT, true, this)) {
|
||||
Play_TriggerVoidOut(play);
|
||||
}
|
||||
play->transitionType = TRANS_TYPE_FADE_BLACK_FAST;
|
||||
|
@ -9584,6 +9590,10 @@ static FallImpactInfo D_80854600[] = {
|
|||
s32 func_80843E64(PlayState* play, Player* this) {
|
||||
s32 sp34;
|
||||
|
||||
if (!GameInteractor_Should(VB_RECIEVE_FALL_DAMAGE, true, this)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if ((sFloorType == 6) || (sFloorType == 9)) {
|
||||
sp34 = 0;
|
||||
} else {
|
||||
|
@ -14977,7 +14987,7 @@ void Player_Action_8084F88C(Player* this, PlayState* play) {
|
|||
if (IS_RANDO && Randomizer_GetSettingValue(RSK_SHUFFLE_ENTRANCES)) {
|
||||
Grotto_ForceRegularVoidOut();
|
||||
}
|
||||
} else {
|
||||
} else if (GameInteractor_Should(VB_TRIGGER_VOIDOUT, true, this)) {
|
||||
Play_TriggerVoidOut(play);
|
||||
}
|
||||
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
#include "soh/SaveManager.h"
|
||||
#include "soh/OTRGlobals.h"
|
||||
#include "soh/ResourceManagerHelpers.h"
|
||||
#include "soh/ShipUtils.h"
|
||||
|
||||
typedef struct {
|
||||
s16 left;
|
||||
|
@ -1053,20 +1054,27 @@ void FileChoose_UpdateRandomizer() {
|
|||
if (!SpoilerFileExists(CVarGetString(CVAR_GENERAL("SpoilerLog"), "")) &&
|
||||
!CVarGetInteger(CVAR_RANDOMIZER_SETTING("DontGenerateSpoiler"), 0)) {
|
||||
CVarSetString(CVAR_GENERAL("SpoilerLog"), "");
|
||||
Randomizer_SetSpoilerLoaded(false);
|
||||
}
|
||||
|
||||
if (CVarGetInteger(CVAR_GENERAL("RandomizerNewFileDropped"), 0) != 0 ||
|
||||
!(Randomizer_IsSeedGenerated() || Randomizer_IsSpoilerLoaded()) &&
|
||||
SpoilerFileExists(CVarGetString(CVAR_GENERAL("SpoilerLog"), "")) && !fileSelectSpoilerFileLoaded) {
|
||||
if (CVarGetInteger(CVAR_GENERAL("RandomizerNewFileDropped"), 0) != 0) {
|
||||
CVarSetString(CVAR_GENERAL("SpoilerLog"), CVarGetString(CVAR_GENERAL("RandomizerDroppedFile"), ""));
|
||||
Audio_PlayFanfare(NA_BGM_HORSE_GOAL);
|
||||
if (SpoilerFileExists(CVarGetString(CVAR_GENERAL("RandomizerDroppedFile"), ""))) {
|
||||
CVarSetString(CVAR_GENERAL("SpoilerLog"), CVarGetString(CVAR_GENERAL("RandomizerDroppedFile"), ""));
|
||||
Sfx_PlaySfxCentered(NA_SE_SY_CORRECT_CHIME);
|
||||
} else {
|
||||
Sfx_PlaySfxCentered(NA_SE_SY_ERROR);
|
||||
}
|
||||
}
|
||||
const char* fileLoc = CVarGetString(CVAR_GENERAL("SpoilerLog"), "");
|
||||
CVarSetInteger(CVAR_GENERAL("RandomizerNewFileDropped"), 0);
|
||||
CVarSetString(CVAR_GENERAL("RandomizerDroppedFile"), "");
|
||||
Randomizer_ParseSpoiler(fileLoc);
|
||||
fileSelectSpoilerFileLoaded = true;
|
||||
if (!Ship_IsCStringEmpty(fileLoc)) {
|
||||
Randomizer_ParseSpoiler(fileLoc);
|
||||
fileSelectSpoilerFileLoaded = true;
|
||||
}
|
||||
|
||||
if (SpoilerFileExists(CVarGetString(CVAR_GENERAL("SpoilerLog"), "")) &&
|
||||
CVarGetInteger(CVAR_RANDOMIZER_SETTING("DontGenerateSpoiler"), 0)) {
|
||||
|
@ -1374,7 +1382,8 @@ void FileChoose_UpdateQuestMenu(GameState* thisx) {
|
|||
} else {
|
||||
defaultName = &emptyNameNES;
|
||||
}
|
||||
} else { // GAME_REGION_NTSC
|
||||
this->charPage = FS_CHAR_PAGE_HIRA; // Default to Hiragana Keyboard
|
||||
} else { // GAME_REGION_NTSC
|
||||
defaultName = CVarGetInteger(CVAR_ENHANCEMENT("LinkDefaultName"), 0) ? &linkNameNES : &emptyNameNES;
|
||||
}
|
||||
memcpy(Save_GetSaveMetaInfo(this->buttonIndex)->playerName, defaultName, 8);
|
||||
|
@ -1575,7 +1584,8 @@ void FileChoose_UpdateRandomizerMenu(GameState* thisx) {
|
|||
} else {
|
||||
defaultName = &emptyNameNES;
|
||||
}
|
||||
} else { // GAME_REGION_NTSC
|
||||
this->charPage = FS_CHAR_PAGE_HIRA; // Default to Hiragana Keyboard
|
||||
} else { // GAME_REGION_NTSC
|
||||
defaultName = CVarGetInteger(CVAR_ENHANCEMENT("LinkDefaultName"), 0) ? &linkNameNES : &emptyNameNES;
|
||||
}
|
||||
memcpy(Save_GetSaveMetaInfo(this->buttonIndex)->playerName, defaultName, 8);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue