Merge remote-tracking branch 'upstream/rando-next' into bombchu-logic

This commit is contained in:
lil David 2022-09-01 07:59:09 -05:00
commit ea9db56914
47 changed files with 1443 additions and 342 deletions

View File

@ -87,6 +87,7 @@ set(Source_Files__Controller
"KeyboardController.cpp"
"KeyboardController.h"
"UltraController.h"
"DummyController.cpp"
"DummyController.h"
)

View File

@ -389,4 +389,20 @@ namespace Ship {
Commands[command] = entry;
}
}
std::string Console::GetCurrentChannel() {
return currentChannel;
}
bool Console::IsOpened() {
return opened;
}
void Console::Close() {
opened = false;
}
void Console::Open() {
opened = true;
}
}

View File

@ -93,10 +93,9 @@ namespace Ship {
void Append(const std::string& channel, spdlog::level::level_enum priority, const char* fmt, ...);
bool HasCommand(const std::string& command);
void AddCommand(const std::string& command, CommandEntry entry);
std::string GetCurrentChannel() { return currentChannel; }
bool IsOpened() { return opened; }
void Close() { opened = false; }
void Open() { opened = true; }
std::string GetCurrentChannel();
bool IsOpened();
void Close();
void Open();
};
}

View File

@ -21,7 +21,7 @@ namespace Ship {
size_t GetNumVirtualDevices();
uint8_t* GetControllerBits();
private:
std::vector<int> virtualDevices = {};
std::vector<int32_t> virtualDevices = {};
std::vector<std::shared_ptr<Controller>> physicalDevices = {};
uint8_t* controllerBits = nullptr;
};

View File

@ -112,4 +112,16 @@ namespace Ship {
std::shared_ptr<DeviceProfile> Controller::getProfile(int32_t virtualSlot) {
return profiles[virtualSlot];
}
std::shared_ptr<ControllerAttachment> Controller::GetAttachment() {
return Attachment;
}
bool Controller::IsRumbling() {
return isRumbling;
}
std::string Controller::GetGuid() {
return GUID;
}
}

View File

@ -40,7 +40,6 @@ namespace Ship {
public:
virtual ~Controller() = default;
Controller();
void Read(OSContPad* pad, int32_t virtualSlot);
virtual void ReadFromSource(int32_t virtualSlot) = 0;
virtual void WriteToSource(int32_t virtualSlot, ControllerCallback* controller) = 0;
virtual bool Connected() const = 0;
@ -49,8 +48,12 @@ namespace Ship {
virtual void CreateDefaultBinding(int32_t virtualSlot) = 0;
virtual void ClearRawPress() = 0;
virtual int32_t ReadRawPress() = 0;
virtual const std::string GetButtonName(int32_t virtualSlot, int32_t n64Button) = 0;
virtual const std::string GetControllerName() = 0;
void Read(OSContPad* pad, int32_t virtualSlot);
void SetButtonMapping(int32_t virtualSlot, int32_t n64Button, int32_t dwScancode);
std::shared_ptr<ControllerAttachment> GetAttachment() { return Attachment; }
std::shared_ptr<ControllerAttachment> GetAttachment();
std::shared_ptr<DeviceProfile> getProfile(int32_t virtualSlot);
int8_t& getLeftStickX(int32_t virtualSlot);
int8_t& getLeftStickY(int32_t virtualSlot);
int8_t& getRightStickX(int32_t virtualSlot);
@ -58,11 +61,8 @@ namespace Ship {
int32_t& getPressedButtons(int32_t virtualSlot);
float& getGyroX(int32_t virtualSlot);
float& getGyroY(int32_t virtualSlot);
std::shared_ptr<DeviceProfile> getProfile(int32_t virtualSlot);
bool IsRumbling() { return isRumbling; }
std::string GetGuid() { return GUID; }
virtual const std::string GetButtonName(int32_t virtualSlot, int32_t n64Button) = 0;
virtual const std::string GetControllerName() = 0;
bool IsRumbling();
std::string GetGuid();
protected:
std::shared_ptr<ControllerAttachment> Attachment;

View File

@ -0,0 +1,61 @@
#include "DummyController.h"
namespace Ship {
DummyController::DummyController(const std::string& CUID, const std::string& KeyName, bool Connected) {
GUID = CUID;
isConnected = Connected;
ButtonName = KeyName;
}
void DummyController::ReadFromSource(int32_t virtualSlot) {
}
const std::string DummyController::GetControllerName() {
return GUID;
}
const std::string DummyController::GetButtonName(int32_t virtualSlot, int32_t n64Button) {
return ButtonName;
}
void DummyController::WriteToSource(int32_t virtualSlot, ControllerCallback* controller){
}
bool DummyController::Connected() const {
return isConnected;
}
bool DummyController::CanRumble() const {
return false;
}
bool DummyController::CanGyro() const {
return false;
}
void DummyController::CreateDefaultBinding(int32_t slot) {
}
std::string DummyController::GetControllerType() {
return "Unk";
}
std::string DummyController::GetConfSection() {
return "Unk";
}
std::string DummyController::GetBindingConfSection() {
return "Unk";
}
void DummyController::ClearRawPress() {
}
int32_t DummyController::ReadRawPress() {
return -1;
}
}

View File

@ -7,31 +7,25 @@
namespace Ship {
class DummyController final : public Controller {
public:
DummyController(const std::string& CUID, const std::string& KeyName, bool Connected) {
GUID = CUID;
isConnected = Connected;
ButtonName = KeyName;
}
DummyController(const std::string& CUID, const std::string& KeyName, bool Connected);
std::map<std::vector<std::string>, int32_t> ReadButtonPress();
void ReadFromSource(int32_t slot) override {}
const std::string GetControllerName() override { return GUID; }
const std::string GetButtonName(int slot, int n64Button) override { return ButtonName; }
void WriteToSource(int32_t slot, ControllerCallback* controller) override { }
bool Connected() const override { return isConnected; }
bool CanRumble() const override { return false; }
bool CanGyro() const override { return false; }
void ClearRawPress() override {}
int32_t ReadRawPress() override { return -1; }
bool HasPadConf() const { return true; }
std::optional<std::string> GetPadConfSection() { return "Unk"; }
void CreateDefaultBinding(int32_t slot) override {}
void ReadFromSource(int32_t virtualSlot) override;
const std::string GetControllerName() override;
const std::string GetButtonName(int32_t virtualSlot, int32_t n64Button) override;
void WriteToSource(int32_t slot, ControllerCallback* controller) override;
bool Connected() const override;
bool CanRumble() const override;
bool CanGyro() const override;
void ClearRawPress() override;
int32_t ReadRawPress() override;
bool HasPadConf() const;
std::optional<std::string> GetPadConfSection();
void CreateDefaultBinding(int32_t virtualSlot) override;
protected:
std::string ButtonName;
bool isConnected = false;
std::string GetControllerType() { return "Unk"; }
std::string GetConfSection() { return "Unk"; }
std::string GetBindingConfSection() { return "Unk"; }
std::string GetControllerType();
std::string GetConfSection();
std::string GetBindingConfSection();
};
}

View File

@ -0,0 +1,5 @@
#include "Hooks.h"
namespace Ship {
}

View File

@ -2011,7 +2011,9 @@ namespace SohImGui {
);
PaddedEnhancementCheckbox("Key Colors Match Dungeon", "gRandoMatchKeyColors", true, false);
Tooltip(
"Matches the color of small keys and boss keys to the dungeon they belong to. This helps identify keys from afar and adds a little bit of flair.");
"Matches the color of small keys and boss keys to the dungeon they belong to. "
"This helps identify keys from afar and adds a little bit of flair.\n\nThis only "
"applies to seeds with keys and boss keys shuffled to Any Dungeon, Overworld, or Anywhere.");
PaddedEnhancementCheckbox("Quest Item Fanfares", "gRandoQuestItemFanfares", true, false);
Tooltip(
"Play unique fanfares when obtaining quest items "

View File

@ -7,7 +7,7 @@
#endif
#include "Hooks.h"
#include "Window.h"
namespace Ship {
@ -100,4 +100,28 @@ namespace Ship {
const std::string KeyboardController::GetControllerName() {
return "Keyboard";
}
bool KeyboardController::Connected() const {
return true;
}
bool KeyboardController::CanRumble() const {
return false;
}
bool KeyboardController::CanGyro() const {
return false;
}
void KeyboardController::ClearRawPress() {
lastKey = -1;
}
void KeyboardController::SetLastScancode(int32_t key) {
lastScancode = key;
}
int32_t KeyboardController::GetLastScancode() {
return lastScancode;
}
}

View File

@ -9,26 +9,18 @@ namespace Ship {
void ReadFromSource(int32_t virtualSlot) override;
void WriteToSource(int32_t virtualSlot, ControllerCallback* controller) override;
bool Connected() const override { return true; }
bool CanRumble() const override { return false; }
bool CanGyro() const override { return false; }
const std::string GetControllerName() override;
const std::string GetButtonName(int32_t virtualSlot, int32_t n64Button) override;
bool PressButton(int32_t dwScancode);
bool ReleaseButton(int32_t dwScancode);
void ClearRawPress() override {
lastKey = -1;
}
bool Connected() const override;
bool CanRumble() const override;
bool CanGyro() const override;
void ClearRawPress() override;
int32_t ReadRawPress() override;
void ReleaseAllButtons();
void SetLastScancode(int32_t key) {
lastScancode = key;
}
int32_t GetLastScancode() { return lastScancode; }
void SetLastScancode(int32_t key);
int32_t GetLastScancode();
void CreateDefaultBinding(int32_t virtualSlot) override;
protected:

View File

@ -333,4 +333,17 @@ namespace Ship {
const std::string* ResourceMgr::HashToString(uint64_t Hash) const {
return OTR->HashToString(Hash);
}
std::shared_ptr<Archive> ResourceMgr::GetArchive() {
return OTR;
}
std::shared_ptr<Window> ResourceMgr::GetContext() {
return Context;
}
std::shared_ptr<Resource> ResourceMgr::LoadResource(const std::string& FilePath) {
return LoadResource(FilePath.c_str());
}
}

View File

@ -23,20 +23,17 @@ namespace Ship {
bool IsRunning();
bool DidLoadSuccessfully();
std::shared_ptr<Archive> GetArchive() { return OTR; }
std::shared_ptr<Window> GetContext() { return Context; }
std::shared_ptr<Archive> GetArchive();
std::shared_ptr<Window> GetContext();
const std::string* HashToString(uint64_t Hash) const;
void InvalidateResourceCache();
uint32_t GetGameVersion();
void SetGameVersion(uint32_t newGameVersion);
std::shared_ptr<File> LoadFileAsync(const std::string& FilePath);
std::shared_ptr<File> LoadFile(const std::string& FilePath);
std::shared_ptr<Resource> GetCachedFile(const char* FilePath) const;
std::shared_ptr<Resource> LoadResource(const char* FilePath);
std::shared_ptr<Resource> LoadResource(const std::string& FilePath) { return LoadResource(FilePath.c_str()); }
std::shared_ptr<Resource> LoadResource(const std::string& FilePath);
std::variant<std::shared_ptr<Resource>, std::shared_ptr<ResourcePromise>> LoadResourceAsync(const char* FilePath);
std::shared_ptr<std::vector<std::shared_ptr<Resource>>> CacheDirectory(const std::string& SearchMask);
std::shared_ptr<std::vector<std::shared_ptr<ResourcePromise>>> CacheDirectoryAsync(const std::string& SearchMask);

View File

@ -12,6 +12,10 @@ extern "C" uint8_t __osMaxControllers;
namespace Ship {
SDLController::SDLController(int32_t physicalSlot) : Controller(), Cont(nullptr), physicalSlot(physicalSlot) {
}
bool SDLController::Open() {
const auto NewCont = SDL_GameControllerOpen(physicalSlot);
@ -450,4 +454,23 @@ namespace Ship {
profile->GyroData[DRIFT_Y] = 0.0f;
profile->GyroData[GYRO_SENSITIVITY] = 1.0f;
}
bool SDLController::Connected() const {
return Cont != nullptr;
}
bool SDLController::CanGyro() const {
return supportsGyro;
}
bool SDLController::CanRumble() const {
#if SDL_COMPILEDVERSION >= SDL_VERSIONNUM(2,0,18)
return SDL_GameControllerHasRumble(Cont);
#endif
return false;
}
void SDLController::ClearRawPress() {
}
}

View File

@ -5,6 +5,19 @@
namespace Ship {
class SDLController : public Controller {
public:
SDLController(int32_t physicalSlot);
void ReadFromSource(int32_t virtualSlot) override;
const std::string GetControllerName() override;
const std::string GetButtonName(int32_t virtualSlot, int32_t n64Button) override;
void WriteToSource(int32_t virtualSlot, ControllerCallback* controller) override;
bool Connected() const override;
bool CanGyro() const override;
bool CanRumble() const override;
bool Open();
void ClearRawPress() override;
int32_t ReadRawPress() override;
protected:
inline static const char* AxisNames[] = {
"Left Stick X",
"Left Stick Y",
@ -15,25 +28,6 @@ namespace Ship {
"Start Button"
};
SDLController(int32_t physicalSlot) : Controller(), Cont(nullptr), physicalSlot(physicalSlot) { }
void ReadFromSource(int32_t virtualSlot) override;
const std::string GetControllerName() override;
const std::string GetButtonName(int32_t virtualSlot, int32_t n64Button) override;
void WriteToSource(int32_t virtualSlot, ControllerCallback* controller) override;
bool Connected() const override { return Cont != nullptr; }
bool CanGyro() const override { return supportsGyro; }
bool CanRumble() const override {
#if SDL_COMPILEDVERSION >= SDL_VERSIONNUM(2,0,18)
return SDL_GameControllerHasRumble(Cont);
#endif
return false;
}
bool Open();
void ClearRawPress() override {}
int32_t ReadRawPress() override;
protected:
void CreateDefaultBinding(int32_t virtualSlot) override;
private:

View File

@ -581,4 +581,52 @@ namespace Ship {
saveFile.close();
}
bool Window::IsFullscreen() {
return bIsFullscreen;
}
uint32_t Window::GetMenuBar() {
return dwMenubar;
}
void Window::SetMenuBar(uint32_t dwMenuBar) {
this->dwMenubar = dwMenuBar;
}
std::string Window::GetName() {
return Name;
}
std::shared_ptr<ControlDeck> Window::GetControlDeck() {
return ControllerApi;
}
std::shared_ptr<AudioPlayer> Window::GetAudioPlayer() {
return APlayer;
}
std::shared_ptr<ResourceMgr> Window::GetResourceManager() {
return ResMan;
}
std::shared_ptr<Mercury> Window::GetConfig() {
return Config;
}
std::shared_ptr<spdlog::logger> Window::GetLogger() {
return Logger;
}
const char* Window::GetKeyName(int32_t scancode) {
return WmApi->get_key_name(scancode);
}
int32_t Window::GetLastScancode() {
return lastScancode;
}
void Window::SetLastScancode(int32_t scanCode) {
lastScancode = scanCode;
}
}

View File

@ -36,20 +36,20 @@ namespace Ship {
void ToggleFullscreen();
void SetFullscreen(bool bIsFullscreen);
void ShowCursor(bool hide);
bool IsFullscreen() { return bIsFullscreen; }
uint32_t GetCurrentWidth();
uint32_t GetCurrentHeight();
uint32_t GetMenuBar() { return dwMenubar; }
void SetMenuBar(uint32_t dwMenuBar) { this->dwMenubar = dwMenuBar; }
std::string GetName() { return Name; }
std::shared_ptr<ControlDeck> GetControlDeck() { return ControllerApi; };
std::shared_ptr<AudioPlayer> GetAudioPlayer() { return APlayer; }
std::shared_ptr<ResourceMgr> GetResourceManager() { return ResMan; }
std::shared_ptr<Mercury> GetConfig() { return Config; }
std::shared_ptr<spdlog::logger> GetLogger() { return Logger; }
const char* GetKeyName(int32_t scancode) { return WmApi->get_key_name(scancode); }
int32_t GetLastScancode() { return lastScancode; }
void SetLastScancode(int32_t scanCode) { lastScancode = scanCode; }
bool IsFullscreen();
uint32_t GetMenuBar();
void SetMenuBar(uint32_t dwMenuBar);
std::string GetName();
std::shared_ptr<ControlDeck> GetControlDeck();
std::shared_ptr<AudioPlayer> GetAudioPlayer();
std::shared_ptr<ResourceMgr> GetResourceManager();
std::shared_ptr<Mercury> GetConfig();
std::shared_ptr<spdlog::logger> GetLogger();
const char* GetKeyName(int32_t scancode);
int32_t GetLastScancode();
void SetLastScancode(int32_t scanCode);
protected:
Window() = default;

View File

@ -260,5 +260,6 @@ extern GraphicsContext* __gfxCtx;
#define SEG_ADDR(seg, addr) (addr | (seg << 24) | 1)
#define NUM_TRIALS 6
#endif

View File

@ -183,7 +183,7 @@ typedef struct {
char ganonHintText[150];
char ganonText[250];
u8 seedIcons[5];
u16 randomizerInf[2];
u16 randomizerInf[4];
u8 temporaryWeapon;
u16 adultTradeItems;
} SaveContext; // size = 0x1428

View File

@ -17,7 +17,8 @@ typedef enum {
TEXT_BLUE_RUPEE = 0xCC,
TEXT_RED_RUPEE = 0xF0,
TEXT_PURPLE_RUPEE = 0xF1,
TEXT_HUGE_RUPEE = 0xF2
TEXT_HUGE_RUPEE = 0xF2,
TEXT_BEAN_SALESMAN = 0x405E
} TextIDs;
#ifdef __cplusplus

View File

@ -1039,6 +1039,19 @@ int Fill() {
//Fast fill for the rest of the pool
std::vector<uint32_t> remainingPool = FilterAndEraseFromPool(ItemPool, [](const auto i) { return true; });
FastFill(remainingPool, GetAllEmptyLocations(), false);
//Add prices for scrubsanity, this is unique to SoH because we write/read scrub prices to/from the spoilerfile.
if (Scrubsanity.Is(SCRUBSANITY_AFFORDABLE)) {
for (size_t i = 0; i < ScrubLocations.size(); i++) {
Location(ScrubLocations[i])->SetScrubsanityPrice(10);
}
} else if (Scrubsanity.Is(SCRUBSANITY_RANDOM_PRICES)) {
for (size_t i = 0; i < ScrubLocations.size(); i++) {
int randomPrice = GetRandomScrubPrice();
Location(ScrubLocations[i])->SetScrubsanityPrice(randomPrice);
}
}
GeneratePlaythrough();
//Successful placement, produced beatable result
if(playthroughBeatable && !placementFailure) {

View File

@ -1011,6 +1011,56 @@ std::vector<std::vector<uint32_t>> ShopLocationLists = {
GC_ShopLocations,
};
//List of scrubs, used for pricing the scrubs
std::vector<uint32_t> ScrubLocations = {
LW_DEKU_SCRUB_NEAR_DEKU_THEATER_RIGHT,
LW_DEKU_SCRUB_NEAR_DEKU_THEATER_LEFT,
LW_DEKU_SCRUB_NEAR_BRIDGE,
LW_DEKU_SCRUB_GROTTO_REAR,
LW_DEKU_SCRUB_GROTTO_FRONT,
SFM_DEKU_SCRUB_GROTTO_REAR,
SFM_DEKU_SCRUB_GROTTO_FRONT,
HF_DEKU_SCRUB_GROTTO,
LH_DEKU_SCRUB_GROTTO_LEFT,
LH_DEKU_SCRUB_GROTTO_RIGHT,
LH_DEKU_SCRUB_GROTTO_CENTER,
GV_DEKU_SCRUB_GROTTO_REAR,
GV_DEKU_SCRUB_GROTTO_FRONT,
COLOSSUS_DEKU_SCRUB_GROTTO_REAR,
COLOSSUS_DEKU_SCRUB_GROTTO_FRONT,
GC_DEKU_SCRUB_GROTTO_LEFT,
GC_DEKU_SCRUB_GROTTO_RIGHT,
GC_DEKU_SCRUB_GROTTO_CENTER,
DMC_DEKU_SCRUB,
DMC_DEKU_SCRUB_GROTTO_LEFT,
DMC_DEKU_SCRUB_GROTTO_RIGHT,
DMC_DEKU_SCRUB_GROTTO_CENTER,
ZR_DEKU_SCRUB_GROTTO_REAR,
ZR_DEKU_SCRUB_GROTTO_FRONT,
LLR_DEKU_SCRUB_GROTTO_LEFT,
LLR_DEKU_SCRUB_GROTTO_RIGHT,
LLR_DEKU_SCRUB_GROTTO_CENTER,
DEKU_TREE_MQ_DEKU_SCRUB,
DODONGOS_CAVERN_DEKU_SCRUB_NEAR_BOMB_BAG_LEFT,
DODONGOS_CAVERN_DEKU_SCRUB_SIDE_ROOM_NEAR_DODONGOS,
DODONGOS_CAVERN_DEKU_SCRUB_NEAR_BOMB_BAG_RIGHT,
DODONGOS_CAVERN_DEKU_SCRUB_LOBBY,
DODONGOS_CAVERN_MQ_DEKU_SCRUB_LOBBY_REAR,
DODONGOS_CAVERN_MQ_DEKU_SCRUB_LOBBY_FRONT,
DODONGOS_CAVERN_MQ_DEKU_SCRUB_STAIRCASE,
DODONGOS_CAVERN_MQ_DEKU_SCRUB_SIDE_ROOM_NEAR_LOWER_LIZALFOS,
JABU_JABUS_BELLY_DEKU_SCRUB,
GANONS_CASTLE_DEKU_SCRUB_CENTER_LEFT,
GANONS_CASTLE_DEKU_SCRUB_CENTER_RIGHT,
GANONS_CASTLE_DEKU_SCRUB_RIGHT,
GANONS_CASTLE_DEKU_SCRUB_LEFT,
GANONS_CASTLE_MQ_DEKU_SCRUB_RIGHT,
GANONS_CASTLE_MQ_DEKU_SCRUB_CENTER_LEFT,
GANONS_CASTLE_MQ_DEKU_SCRUB_CENTER,
GANONS_CASTLE_MQ_DEKU_SCRUB_CENTER_RIGHT,
GANONS_CASTLE_MQ_DEKU_SCRUB_LEFT,
};
//List of gossip stone locations for hints
std::vector<uint32_t> gossipStoneLocations = {
DMC_GOSSIP_STONE,

View File

@ -257,8 +257,8 @@ public:
}
void SetPrice(uint16_t price_) {
//don't override price if the price was set for shopsanity
if (hasShopsanityPrice) {
//don't override price if the price was set for shopsanity/scrubsanity
if (hasShopsanityPrice || hasScrubsanityPrice) {
return;
}
price = price_;
@ -269,10 +269,19 @@ public:
hasShopsanityPrice = true;
}
void SetScrubsanityPrice(uint16_t price_) {
price = price_;
hasScrubsanityPrice = true;
}
bool HasShopsanityPrice() const {
return hasShopsanityPrice;
}
bool HasScrubsanityPrice() const {
return hasScrubsanityPrice;
}
bool IsExcluded() const {
return excludedOption.Value<bool>();
}
@ -426,6 +435,7 @@ public:
isHintable = false;
price = 0;
hasShopsanityPrice = false;
hasScrubsanityPrice = false;
hidden = false;
}
@ -451,6 +461,7 @@ private:
bool isHintable = false;
uint32_t parentRegion = NONE;
bool hasShopsanityPrice = false;
bool hasScrubsanityPrice = false;
bool hidden = false;
};
@ -467,6 +478,8 @@ ItemLocation* Location(uint32_t locKey);
extern std::vector<std::vector<uint32_t>> ShopLocationLists;
extern std::vector<uint32_t> ScrubLocations;
extern std::vector<uint32_t> gossipStoneLocations;
extern std::vector<uint32_t> dungeonRewardLocations;

View File

@ -2517,21 +2517,20 @@ namespace Settings {
BridgeRewardCount.SetSelectedIndex(cvarSettings[RSK_RAINBOW_BRIDGE_REWARD_COUNT]);
BridgeDungeonCount.SetSelectedIndex(cvarSettings[RSK_RAINBOW_BRIDGE_DUNGEON_COUNT]);
BridgeTokenCount.SetSelectedIndex(cvarSettings[RSK_RAINBOW_BRIDGE_TOKEN_COUNT]);
RandomGanonsTrials.SetSelectedIndex(cvarSettings[RSK_RANDOM_TRIALS]);
// RANDTODO: Switch this back once Ganon's Trials Count is properly implemented.
//GanonsTrialsCount.SetSelectedIndex(cvarSettings[RSK_TRIAL_COUNT]);
switch (cvarSettings[RSK_TRIAL_COUNT]) {
case 0:
GanonsTrialsCount.SetSelectedIndex(6);
break;
case 1:
GanonsTrialsCount.SetSelectedIndex(0);
break;
if (cvarSettings[RSK_RANDOM_TRIALS] == 2) {
RandomGanonsTrials.SetSelectedIndex(1);
} else {
RandomGanonsTrials.SetSelectedIndex(0);
}
if (cvarSettings[RSK_RANDOM_TRIALS] == 0) {
GanonsTrialsCount.SetSelectedIndex(0);
} else {
GanonsTrialsCount.SetSelectedIndex(cvarSettings[RSK_TRIAL_COUNT]);
}
ShuffleRewards.SetSelectedIndex(cvarSettings[RSK_SHUFFLE_DUNGEON_REWARDS]);
ShuffleSongs.SetSelectedIndex(cvarSettings[RSK_SHUFFLE_SONGS]);
Tokensanity.SetSelectedIndex(cvarSettings[RSK_SHUFFLE_TOKENS]);
Scrubsanity.SetSelectedIndex(cvarSettings[RSK_SHUFFLE_SCRUBS]);
ShuffleCows.SetSelectedIndex(cvarSettings[RSK_SHUFFLE_COWS]);
ShuffleKokiriSword.SetSelectedIndex(cvarSettings[RSK_SHUFFLE_KOKIRI_SWORD]);
ShuffleOcarinas.SetSelectedIndex(cvarSettings[RSK_SHUFFLE_OCARINA]);
@ -2551,6 +2550,7 @@ namespace Settings {
ShuffleFrogSongRupees.SetSelectedIndex(cvarSettings[RSK_SHUFFLE_FROG_SONG_RUPEES]);
ShuffleAdultTradeQuest.SetSelectedIndex(cvarSettings[RSK_SHUFFLE_ADULT_TRADE]);
ShuffleMagicBeans.SetSelectedIndex(cvarSettings[RSK_SHUFFLE_MAGIC_BEANS]);
// the checkbox works because 0 is "Off" and 1 is "Fairy Ocarina"
StartingOcarina.SetSelectedIndex(cvarSettings[RSK_STARTING_OCARINA]);

View File

@ -501,25 +501,23 @@ static void WriteMasterQuestDungeons(tinyxml2::XMLDocument& spoilerLog) {
}
}
// Writes the required trails to the spoiler log, if there are any.
static void WriteRequiredTrials(tinyxml2::XMLDocument& spoilerLog) {
auto parentNode = spoilerLog.NewElement("required-trials");
for (const auto* trial : Trial::trialList) {
if (trial->IsSkipped()) {
continue;
// Writes the required trials to the spoiler log, if there are any.
static void WriteRequiredTrials() {
for (const auto& trial : Trial::trialList) {
if (trial->IsRequired()) {
std::string trialName;
switch (gSaveContext.language) {
case LANGUAGE_FRA:
trialName = trial->GetName().GetFrench();
break;
case LANGUAGE_ENG:
default:
trialName = trial->GetName().GetEnglish();
break;
}
jsonData["requiredTrials"].push_back(RemoveLineBreaks(trialName));
}
}
auto node = parentNode->InsertNewChildElement("trial");
// PURPLE TODO: LOCALIZATION
std::string name = trial->GetName().GetEnglish();
name[0] = toupper(name[0]); // Capitalize T in "The"
node->SetAttribute("name", name.c_str());
}
if (!parentNode->NoChildren()) {
spoilerLog.RootElement()->InsertEndChild(parentNode);
}
}
// Writes the intended playthrough to the spoiler log, separated into spheres.
@ -673,15 +671,24 @@ static void WriteHints(int language) {
static void WriteAllLocations(int language) {
for (const uint32_t key : allLocations) {
ItemLocation* location = Location(key);
std::string placedItemName;
switch (language) {
case 0:
default:
jsonData["locations"][location->GetName()] = location->GetPlacedItemName().english;
break;
case 2:
jsonData["locations"][location->GetName()] = location->GetPlacedItemName().french;
break;
case 0:
default:
placedItemName = location->GetPlacedItemName().english;
break;
case 2:
placedItemName = location->GetPlacedItemName().french;
break;
}
// Eventually check for other things here like fake name
if (location->HasScrubsanityPrice() || location->HasShopsanityPrice()) {
jsonData["locations"][location->GetName()]["item"] = placedItemName;
jsonData["locations"][location->GetName()]["price"] = location->GetPrice();
} else {
jsonData["locations"][location->GetName()] = placedItemName;
}
}
}
@ -714,7 +721,7 @@ const char* SpoilerLog_Write(int language) {
// WriteEnabledGlitches(spoilerLog);
//}
//WriteMasterQuestDungeons(spoilerLog);
//WriteRequiredTrials(spoilerLog);
WriteRequiredTrials();
WritePlaythrough();
//WriteWayOfTheHeroLocation(spoilerLog);
@ -764,7 +771,7 @@ bool PlacementLog_Write() {
WriteEnabledTricks(placementLog);
WriteEnabledGlitches(placementLog);
WriteMasterQuestDungeons(placementLog);
WriteRequiredTrials(placementLog);
//WriteRequiredTrials(placementLog);
placementtxt = "\n" + placementtxt;

View File

@ -7,12 +7,12 @@ TrialInfo::TrialInfo(Text name_)
TrialInfo::~TrialInfo() = default;
TrialInfo ForestTrial = TrialInfo(Text{"the Forest Trial", /*french*/"l'épreuve de la forêt", /*spanish*/"la prueba del bosque"});
TrialInfo FireTrial = TrialInfo(Text{"the Fire Trial", /*french*/"l'épreuve du feu", /*spanish*/"la prueba del fuego"});
TrialInfo WaterTrial = TrialInfo(Text{"the Water Trial", /*french*/"l'épreuve de l'eau", /*spanish*/"la prueba del agua"});
TrialInfo SpiritTrial = TrialInfo(Text{"the Spirit Trial", /*french*/"l'épreuve de l'esprit", /*spanish*/"la prueba del espíritu"});
TrialInfo ShadowTrial = TrialInfo(Text{"the Shadow Trial", /*french*/"l'épreuve de l'ombre", /*spanish*/"la prueba de las sombras"});
TrialInfo LightTrial = TrialInfo(Text{"the Light Trial", /*french*/"l'épreuve de la lumière", /*spanish*/"la prueba de la luz"});
TrialInfo ForestTrial = TrialInfo(Text{"the Forest Trial", /*french*/"l'épreuve de la Forêt", /*spanish*/"la prueba del bosque"});
TrialInfo FireTrial = TrialInfo(Text{"the Fire Trial", /*french*/"l'épreuve du Feu", /*spanish*/"la prueba del fuego"});
TrialInfo WaterTrial = TrialInfo(Text{"the Water Trial", /*french*/"l'épreuve de l'Eau", /*spanish*/"la prueba del agua"});
TrialInfo SpiritTrial = TrialInfo(Text{"the Spirit Trial", /*french*/"l'épreuve de l'Esprit", /*spanish*/"la prueba del espíritu"});
TrialInfo ShadowTrial = TrialInfo(Text{"the Shadow Trial", /*french*/"l'épreuve de l'Ombre", /*spanish*/"la prueba de las sombras"});
TrialInfo LightTrial = TrialInfo(Text{"the Light Trial", /*french*/"l'épreuve de la Lumière", /*spanish*/"la prueba de la luz"});
const TrialArray trialList = {
&ForestTrial,

View File

@ -32,7 +32,7 @@ u8 generated;
const std::string Randomizer::getItemMessageTableID = "Randomizer";
const std::string Randomizer::hintMessageTableID = "RandomizerHints";
const std::string Randomizer::scrubMessageTableID = "RandomizerScrubs";
const std::string Randomizer::merchantMessageTableID = "RandomizerMerchants";
const std::string Randomizer::rupeeMessageTableID = "RandomizerRupees";
const std::string Randomizer::NaviRandoMessageTableID = "RandomizerNavi";
@ -104,8 +104,24 @@ Sprite* Randomizer::GetSeedTexture(uint8_t index) {
Randomizer::~Randomizer() {
this->randoSettings.clear();
this->itemLocations.clear();
this->randomizerMerchantPrices.clear();
}
std::unordered_map<std::string, RandomizerInf> spoilerFileTrialToEnum = {
{ "the Forest Trial", RAND_INF_TRIALS_DONE_FOREST_TRIAL },
{ "l'épreuve de la Forêt", RAND_INF_TRIALS_DONE_FOREST_TRIAL },
{ "the Fire Trial", RAND_INF_TRIALS_DONE_FIRE_TRIAL },
{ "l'épreuve du Feu", RAND_INF_TRIALS_DONE_FIRE_TRIAL },
{ "the Water Trial", RAND_INF_TRIALS_DONE_WATER_TRIAL },
{ "l'épreuve de l'Eau", RAND_INF_TRIALS_DONE_WATER_TRIAL },
{ "the Spirit Trial", RAND_INF_TRIALS_DONE_SPIRIT_TRIAL },
{ "l'épreuve de l'Esprit", RAND_INF_TRIALS_DONE_SPIRIT_TRIAL },
{ "the Shadow Trial", RAND_INF_TRIALS_DONE_SHADOW_TRIAL },
{ "l'épreuve de l'Ombre", RAND_INF_TRIALS_DONE_SHADOW_TRIAL },
{ "the Light Trial", RAND_INF_TRIALS_DONE_LIGHT_TRIAL },
{ "l'épreuve de la Lumière", RAND_INF_TRIALS_DONE_LIGHT_TRIAL }
};
std::unordered_map<s16, s16> getItemIdToItemId = {
{ GI_BOW, ITEM_BOW },
{ GI_ARROW_FIRE, ITEM_ARROW_FIRE },
@ -546,9 +562,11 @@ std::unordered_map<std::string, RandomizerSettingKey> SpoilerfileSettingNameToEn
{ "Open Settings:Random Ganon's Trials", RSK_RANDOM_TRIALS },
{ "Open Settings:Trial Count", RSK_TRIAL_COUNT },
{ "Shuffle Settings:Shuffle Gerudo Card", RSK_SHUFFLE_GERUDO_MEMBERSHIP_CARD },
{ "Shuffle Settings:Scrub Shuffle", RSK_SHUFFLE_SCRUBS },
{ "Shuffle Settings:Shuffle Cows", RSK_SHUFFLE_COWS },
{ "Shuffle Settings:Tokensanity", RSK_SHUFFLE_TOKENS },
{ "Shuffle Settings:Shuffle Adult Trade", RSK_SHUFFLE_ADULT_TRADE },
{ "Shuffle Settings:Shuffle Magic Beans", RSK_SHUFFLE_MAGIC_BEANS},
{ "Start with Deku Shield", RSK_STARTING_DEKU_SHIELD },
{ "Start with Kokiri Sword", RSK_STARTING_KOKIRI_SWORD },
{ "Start with Fairy Ocarina", RSK_STARTING_OCARINA },
@ -671,6 +689,12 @@ void Randomizer::LoadItemLocations(const char* spoilerFileName, bool silent) {
itemLocations[RC_UNKNOWN_CHECK] = RG_NONE;
}
void Randomizer::LoadRequiredTrials(const char* spoilerFileName) {
if (strcmp(spoilerFileName, "") != 0) {
ParseRequiredTrialsFile(spoilerFileName);
}
}
void Randomizer::ParseRandomizerSettingsFile(const char* spoilerFileName) {
std::ifstream spoilerFileStream(sanitize(spoilerFileName));
if (!spoilerFileStream)
@ -773,9 +797,21 @@ void Randomizer::ParseRandomizerSettingsFile(const char* spoilerFileName) {
numericValueString = it.value();
gSaveContext.randoSettings[index].value = std::stoi(numericValueString);
break;
case RSK_SHUFFLE_SCRUBS:
if(it.value() == "Off") {
gSaveContext.randoSettings[index].value = 0;
} else if(it.value() == "Affordable") {
gSaveContext.randoSettings[index].value = 1;
} else if(it.value() == "Expensive") {
gSaveContext.randoSettings[index].value = 2;
} else if(it.value() == "Random Prices") {
gSaveContext.randoSettings[index].value = 3;
}
break;
case RSK_SHUFFLE_GERUDO_MEMBERSHIP_CARD:
case RSK_SHUFFLE_COWS:
case RSK_SHUFFLE_ADULT_TRADE:
case RSK_SHUFFLE_MAGIC_BEANS:
case RSK_RANDOM_TRIALS:
case RSK_STARTING_DEKU_SHIELD:
case RSK_STARTING_KOKIRI_SWORD:
@ -841,6 +877,16 @@ void Randomizer::ParseRandomizerSettingsFile(const char* spoilerFileName) {
gSaveContext.randoSettings[index].value = 3;
}
break;
case RSK_GERUDO_KEYS:
if (it.value() == "Vanilla") {
gSaveContext.randoSettings[index].value = 0;
} else if (it.value() == "Any Dungeon") {
gSaveContext.randoSettings[index].value = 1;
} else if (it.value() == "Overworld") {
gSaveContext.randoSettings[index].value = 2;
} else if (it.value() == "Anywhere") {
gSaveContext.randoSettings[index].value = 3;
}
case RSK_KEYSANITY:
if(it.value() == "Start With") {
gSaveContext.randoSettings[index].value = 0;
@ -1078,6 +1124,25 @@ void Randomizer::ParseHintLocationsFile(const char* spoilerFileName) {
}
}
void Randomizer::ParseRequiredTrialsFile(const char* spoilerFileName) {
std::ifstream spoilerFileStream(sanitize(spoilerFileName));
if (!spoilerFileStream) {
return;
}
try {
json spoilerFileJson;
spoilerFileStream >> spoilerFileJson;
json trialsJson = spoilerFileJson["requiredTrials"];
for (auto it = trialsJson.begin(); it != trialsJson.end(); it++) {
this->trialsRequired[spoilerFileTrialToEnum[it.value()]] = true;
}
} catch (const std::exception& e) {
return;
}
}
void Randomizer::ParseItemLocationsFile(const char* spoilerFileName, bool silent) {
std::ifstream spoilerFileStream(sanitize(spoilerFileName));
if (!spoilerFileStream)
@ -1104,9 +1169,10 @@ void Randomizer::ParseItemLocationsFile(const char* spoilerFileName, bool silent
for (auto itemit = itemJson.begin(); itemit != itemJson.end(); ++itemit) {
// todo handle prices
if (itemit.key() == "item") {
gSaveContext.itemLocations[index].check = SpoilerfileCheckNameToEnum[it.key()];
gSaveContext.itemLocations[index].get = SpoilerfileGetNameToEnum[itemit.value()];
} else if (itemit.key() == "price") {
randomizerMerchantPrices[gSaveContext.itemLocations[index].check] = itemit.value();
}
}
} else {
@ -1127,6 +1193,10 @@ void Randomizer::ParseItemLocationsFile(const char* spoilerFileName, bool silent
}
}
bool Randomizer::IsTrialRequired(RandomizerInf trial) {
return this->trialsRequired.contains(trial);
}
s16 Randomizer::GetRandomizedItemId(GetItemID ogId, s16 actorId, s16 actorParams, s16 sceneNum) {
s16 itemId = GetItemFromActor(actorId, actorParams, sceneNum, ogId);
return itemId;
@ -1199,8 +1269,6 @@ s16 Randomizer::GetItemFromGet(RandomizerGet randoGet, GetItemID ogItemId) {
case RG_MAGIC_BEAN:
return GI_BEAN;
case RG_MAGIC_BEAN_PACK:
return GI_BEAN; //todo make it 10 of them
case RG_WEIRD_EGG:
return GI_WEIRD_EGG;
@ -1359,7 +1427,71 @@ s16 Randomizer::GetItemFromGet(RandomizerGet randoGet, GetItemID ogItemId) {
return GI_BOTTLE;
case RG_BOTTLE_WITH_MILK:
return GI_MILK_BOTTLE;
case RG_DEKU_TREE_MAP:
case RG_DODONGOS_CAVERN_MAP:
case RG_JABU_JABUS_BELLY_MAP:
case RG_FOREST_TEMPLE_MAP:
case RG_FIRE_TEMPLE_MAP:
case RG_WATER_TEMPLE_MAP:
case RG_SPIRIT_TEMPLE_MAP:
case RG_SHADOW_TEMPLE_MAP:
case RG_BOTTOM_OF_THE_WELL_MAP:
case RG_ICE_CAVERN_MAP:
if (GetRandoSettingValue(RSK_STARTING_MAPS_COMPASSES) < 3) {
return GI_MAP;
} else {
return randoGet;
}
case RG_DEKU_TREE_COMPASS:
case RG_DODONGOS_CAVERN_COMPASS:
case RG_JABU_JABUS_BELLY_COMPASS:
case RG_FOREST_TEMPLE_COMPASS:
case RG_FIRE_TEMPLE_COMPASS:
case RG_WATER_TEMPLE_COMPASS:
case RG_SPIRIT_TEMPLE_COMPASS:
case RG_SHADOW_TEMPLE_COMPASS:
case RG_BOTTOM_OF_THE_WELL_COMPASS:
case RG_ICE_CAVERN_COMPASS:
if (GetRandoSettingValue(RSK_STARTING_MAPS_COMPASSES) < 3) {
return GI_COMPASS;
} else {
return randoGet;
}
case RG_FOREST_TEMPLE_BOSS_KEY:
case RG_FIRE_TEMPLE_BOSS_KEY:
case RG_WATER_TEMPLE_BOSS_KEY:
case RG_SPIRIT_TEMPLE_BOSS_KEY:
case RG_SHADOW_TEMPLE_BOSS_KEY:
case RG_GANONS_CASTLE_BOSS_KEY:
if (GetRandoSettingValue(RSK_BOSS_KEYSANITY) < 3) {
return GI_KEY_BOSS;
} else {
return randoGet;
}
case RG_FOREST_TEMPLE_SMALL_KEY:
case RG_FIRE_TEMPLE_SMALL_KEY:
case RG_WATER_TEMPLE_SMALL_KEY:
case RG_SPIRIT_TEMPLE_SMALL_KEY:
case RG_SHADOW_TEMPLE_SMALL_KEY:
case RG_BOTTOM_OF_THE_WELL_SMALL_KEY:
case RG_GERUDO_TRAINING_GROUNDS_SMALL_KEY:
case RG_GANONS_CASTLE_SMALL_KEY:
if (GetRandoSettingValue(RSK_KEYSANITY) < 3) {
return GI_KEY_SMALL;
} else {
return randoGet;
}
case RG_GERUDO_FORTRESS_SMALL_KEY:
if (GetRandoSettingValue(RSK_GERUDO_KEYS) == 0) {
return GI_KEY_SMALL;
} else {
return randoGet;
}
// todo test this with keys in own dungeon
case RG_TREASURE_GAME_SMALL_KEY:
return GI_DOOR_KEY;
@ -1570,6 +1702,61 @@ bool Randomizer::IsItemVanilla(RandomizerGet randoGet) {
case RG_BUY_RED_POTION_40:
case RG_BUY_RED_POTION_50:
return true;
case RG_FOREST_TEMPLE_SMALL_KEY:
case RG_FIRE_TEMPLE_SMALL_KEY:
case RG_WATER_TEMPLE_SMALL_KEY:
case RG_SPIRIT_TEMPLE_SMALL_KEY:
case RG_SHADOW_TEMPLE_SMALL_KEY:
case RG_BOTTOM_OF_THE_WELL_SMALL_KEY:
case RG_GERUDO_TRAINING_GROUNDS_SMALL_KEY:
case RG_GANONS_CASTLE_SMALL_KEY:
if (GetRandoSettingValue(RSK_KEYSANITY) > 2) {
return false;
}
return true;
case RG_GERUDO_FORTRESS_SMALL_KEY:
if (GetRandoSettingValue(RSK_GERUDO_KEYS) != 0) {
return false;
}
return true;
case RG_FOREST_TEMPLE_BOSS_KEY:
case RG_FIRE_TEMPLE_BOSS_KEY:
case RG_WATER_TEMPLE_BOSS_KEY:
case RG_SPIRIT_TEMPLE_BOSS_KEY:
case RG_SHADOW_TEMPLE_BOSS_KEY:
if (GetRandoSettingValue(RSK_BOSS_KEYSANITY) > 2) {
return false;
}
return true;
case RG_GANONS_CASTLE_BOSS_KEY:
if (GetRandoSettingValue(RSK_GANONS_BOSS_KEY) > 2) {
return false;
}
return true;
case RG_DEKU_TREE_COMPASS:
case RG_DODONGOS_CAVERN_COMPASS:
case RG_JABU_JABUS_BELLY_COMPASS:
case RG_FOREST_TEMPLE_COMPASS:
case RG_FIRE_TEMPLE_COMPASS:
case RG_WATER_TEMPLE_COMPASS:
case RG_SPIRIT_TEMPLE_COMPASS:
case RG_SHADOW_TEMPLE_COMPASS:
case RG_BOTTOM_OF_THE_WELL_COMPASS:
case RG_ICE_CAVERN_COMPASS:
case RG_DEKU_TREE_MAP:
case RG_DODONGOS_CAVERN_MAP:
case RG_JABU_JABUS_BELLY_MAP:
case RG_FOREST_TEMPLE_MAP:
case RG_FIRE_TEMPLE_MAP:
case RG_WATER_TEMPLE_MAP:
case RG_SPIRIT_TEMPLE_MAP:
case RG_SHADOW_TEMPLE_MAP:
case RG_BOTTOM_OF_THE_WELL_MAP:
case RG_ICE_CAVERN_MAP:
if (GetRandoSettingValue(RSK_STARTING_MAPS_COMPASSES) > 2) {
return false;
}
return true;
default:
return false;
}
@ -1596,6 +1783,280 @@ std::string Randomizer::GetGanonHintText() const {
return ganonHintText;
}
ScrubIdentity Randomizer::IdentifyScrub(s32 sceneNum, s32 actorParams, s32 respawnData) {
struct ScrubIdentity scrubIdentity;
scrubIdentity.randomizerCheck = RC_UNKNOWN_CHECK;
scrubIdentity.getItemId = GI_NONE;
scrubIdentity.itemPrice = -1;
scrubIdentity.isShuffled = GetRandoSettingValue(RSK_SHUFFLE_SCRUBS) > 0;
// Based on z_en_dns.c 93-113
switch (actorParams) {
case 0x00:
scrubIdentity.getItemId = GI_NUTS_5_2;
break;
case 0x01:
scrubIdentity.getItemId = GI_STICKS_1;
break;
case 0x02:
scrubIdentity.getItemId = GI_HEART_PIECE;
break;
case 0x03:
scrubIdentity.getItemId = GI_SEEDS_30;
break;
case 0x04:
scrubIdentity.getItemId = GI_SHIELD_DEKU;
break;
case 0x05:
scrubIdentity.getItemId = GI_BOMBS_5;
break;
case 0x06:
scrubIdentity.getItemId = GI_ARROWS_LARGE;
break;
case 0x07:
scrubIdentity.getItemId = GI_POTION_RED;
break;
case 0x08:
scrubIdentity.getItemId = GI_POTION_GREEN;
break;
case 0x09:
scrubIdentity.getItemId = GI_STICK_UPGRADE_20;
break;
case 0x0A:
scrubIdentity.getItemId = GI_NUT_UPGRADE_30;
break;
}
// TODO: Handle MQ scrubs
switch (sceneNum) {
case SCENE_DDAN: // Dodongo's Cavern
switch (actorParams) {
case 0x00:
scrubIdentity.randomizerInf = RAND_INF_SCRUBS_PURCHASED_DODONGOS_CAVERN_DEKU_SCRUB_NEAR_BOMB_BAG_LEFT;
scrubIdentity.randomizerCheck = RC_DODONGOS_CAVERN_DEKU_SCRUB_NEAR_BOMB_BAG_LEFT;
break;
case 0x01:
scrubIdentity.randomizerInf = RAND_INF_SCRUBS_PURCHASED_DODONGOS_CAVERN_DEKU_SCRUB_SIDE_ROOM_NEAR_DODONGOS;
scrubIdentity.randomizerCheck = RC_DODONGOS_CAVERN_DEKU_SCRUB_SIDE_ROOM_NEAR_DODONGOS;
break;
case 0x03:
case 0x06:
scrubIdentity.randomizerInf = RAND_INF_SCRUBS_PURCHASED_DODONGOS_CAVERN_DEKU_SCRUB_NEAR_BOMB_BAG_RIGHT;
scrubIdentity.randomizerCheck = RC_DODONGOS_CAVERN_DEKU_SCRUB_NEAR_BOMB_BAG_RIGHT;
break;
case 0x04:
scrubIdentity.randomizerInf = RAND_INF_SCRUBS_PURCHASED_DODONGOS_CAVERN_DEKU_SCRUB_LOBBY;
scrubIdentity.randomizerCheck = RC_DODONGOS_CAVERN_DEKU_SCRUB_LOBBY;
break;
}
break;
case SCENE_BDAN: // Jabu Jabu's Belly
switch (actorParams) {
case 0x00:
scrubIdentity.randomizerInf = RAND_INF_SCRUBS_PURCHASED_JABU_JABUS_BELLY_DEKU_SCRUB;
scrubIdentity.randomizerCheck = RC_JABU_JABUS_BELLY_DEKU_SCRUB;
break;
}
break;
case SCENE_GANONTIKA: // Ganon's Castle
switch (actorParams) {
case 0x05:
scrubIdentity.randomizerInf = RAND_INF_SCRUBS_PURCHASED_GANONS_CASTLE_DEKU_SCRUB_CENTER_LEFT;
scrubIdentity.randomizerCheck = RC_GANONS_CASTLE_DEKU_SCRUB_CENTER_LEFT;
break;
case 0x03:
case 0x06:
scrubIdentity.randomizerInf = RAND_INF_SCRUBS_PURCHASED_GANONS_CASTLE_DEKU_SCRUB_CENTER_RIGHT;
scrubIdentity.randomizerCheck = RC_GANONS_CASTLE_DEKU_SCRUB_CENTER_RIGHT;
break;
case 0x07:
scrubIdentity.randomizerInf = RAND_INF_SCRUBS_PURCHASED_GANONS_CASTLE_DEKU_SCRUB_RIGHT;
scrubIdentity.randomizerCheck = RC_GANONS_CASTLE_DEKU_SCRUB_RIGHT;
break;
case 0x08:
scrubIdentity.randomizerInf = RAND_INF_SCRUBS_PURCHASED_GANONS_CASTLE_DEKU_SCRUB_LEFT;
scrubIdentity.randomizerCheck = RC_GANONS_CASTLE_DEKU_SCRUB_LEFT;
break;
}
break;
case SCENE_KAKUSIANA: // Grotto
switch (respawnData) {
case 0xE6: // Hyrule Field Scrub Grotto
switch (actorParams) {
case 0x02:
scrubIdentity.randomizerInf = RAND_INF_SCRUBS_PURCHASED_HF_DEKU_SCRUB_GROTTO;
scrubIdentity.randomizerCheck = RC_HF_DEKU_SCRUB_GROTTO;
scrubIdentity.isShuffled = true;
break;
}
break;
case 0xEB: // ZR Scrub Grotto
switch (actorParams) {
case 0x07:
scrubIdentity.randomizerInf = RAND_INF_SCRUBS_PURCHASED_ZR_DEKU_SCRUB_GROTTO_REAR;
scrubIdentity.randomizerCheck = RC_ZR_DEKU_SCRUB_GROTTO_REAR;
break;
case 0x08:
scrubIdentity.randomizerInf = RAND_INF_SCRUBS_PURCHASED_ZR_DEKU_SCRUB_GROTTO_FRONT;
scrubIdentity.randomizerCheck = RC_ZR_DEKU_SCRUB_GROTTO_FRONT;
break;
}
break;
case 0xEE: // Sacred Forest Meadow Scrub Grotto
switch (actorParams) {
case 0x07:
scrubIdentity.randomizerInf = RAND_INF_SCRUBS_PURCHASED_SFM_DEKU_SCRUB_GROTTO_REAR;
scrubIdentity.randomizerCheck = RC_SFM_DEKU_SCRUB_GROTTO_REAR;
break;
case 0x08:
scrubIdentity.randomizerInf = RAND_INF_SCRUBS_PURCHASED_SFM_DEKU_SCRUB_GROTTO_FRONT;
scrubIdentity.randomizerCheck = RC_SFM_DEKU_SCRUB_GROTTO_FRONT;
break;
}
break;
case 0xEF: // Lake Hylia Scrub Grotto
switch (actorParams) {
case 0x00:
scrubIdentity.randomizerInf = RAND_INF_SCRUBS_PURCHASED_LH_DEKU_SCRUB_GROTTO_LEFT;
scrubIdentity.randomizerCheck = RC_LH_DEKU_SCRUB_GROTTO_LEFT;
break;
case 0x05:
scrubIdentity.randomizerInf = RAND_INF_SCRUBS_PURCHASED_LH_DEKU_SCRUB_GROTTO_RIGHT;
scrubIdentity.randomizerCheck = RC_LH_DEKU_SCRUB_GROTTO_RIGHT;
break;
case 0x03:
case 0x06:
scrubIdentity.randomizerInf = RAND_INF_SCRUBS_PURCHASED_LH_DEKU_SCRUB_GROTTO_CENTER;
scrubIdentity.randomizerCheck = RC_LH_DEKU_SCRUB_GROTTO_CENTER;
break;
}
break;
case 0xF0: // Gerudo Valley Scrub Grotto
switch (actorParams) {
case 0x07:
scrubIdentity.randomizerInf = RAND_INF_SCRUBS_PURCHASED_GV_DEKU_SCRUB_GROTTO_REAR;
scrubIdentity.randomizerCheck = RC_GV_DEKU_SCRUB_GROTTO_REAR;
break;
case 0x08:
scrubIdentity.randomizerInf = RAND_INF_SCRUBS_PURCHASED_GV_DEKU_SCRUB_GROTTO_FRONT;
scrubIdentity.randomizerCheck = RC_GV_DEKU_SCRUB_GROTTO_FRONT;
break;
}
break;
case 0xF5: // Lost Woods Scrub Grotto
switch (actorParams) {
case 0x03:
case 0x06:
scrubIdentity.randomizerInf = RAND_INF_SCRUBS_PURCHASED_LW_DEKU_SCRUB_GROTTO_REAR;
scrubIdentity.randomizerCheck = RC_LW_DEKU_SCRUB_GROTTO_REAR;
break;
case 0x0A:
scrubIdentity.randomizerInf = RAND_INF_SCRUBS_PURCHASED_LW_DEKU_SCRUB_GROTTO_FRONT;
scrubIdentity.randomizerCheck = RC_LW_DEKU_SCRUB_GROTTO_FRONT;
scrubIdentity.isShuffled = true;
break;
}
break;
case 0xF9: // Death Mountain Crater Scrub Grotto
switch (actorParams) {
case 0x00:
scrubIdentity.randomizerInf = RAND_INF_SCRUBS_PURCHASED_DMC_DEKU_SCRUB_GROTTO_LEFT;
scrubIdentity.randomizerCheck = RC_DMC_DEKU_SCRUB_GROTTO_LEFT;
break;
case 0x05:
scrubIdentity.randomizerInf = RAND_INF_SCRUBS_PURCHASED_DMC_DEKU_SCRUB_GROTTO_RIGHT;
scrubIdentity.randomizerCheck = RC_DMC_DEKU_SCRUB_GROTTO_RIGHT;
break;
case 0x03:
case 0x06:
scrubIdentity.randomizerInf = RAND_INF_SCRUBS_PURCHASED_DMC_DEKU_SCRUB_GROTTO_CENTER;
scrubIdentity.randomizerCheck = RC_DMC_DEKU_SCRUB_GROTTO_CENTER;
break;
}
break;
case 0xFB: // Gerudo City Scrub Grotto
switch (actorParams) {
case 0x00:
scrubIdentity.randomizerInf = RAND_INF_SCRUBS_PURCHASED_GC_DEKU_SCRUB_GROTTO_LEFT;
scrubIdentity.randomizerCheck = RC_GC_DEKU_SCRUB_GROTTO_LEFT;
break;
case 0x05:
scrubIdentity.randomizerInf = RAND_INF_SCRUBS_PURCHASED_GC_DEKU_SCRUB_GROTTO_RIGHT;
scrubIdentity.randomizerCheck = RC_GC_DEKU_SCRUB_GROTTO_RIGHT;
break;
case 0x03:
case 0x06:
scrubIdentity.randomizerInf = RAND_INF_SCRUBS_PURCHASED_GC_DEKU_SCRUB_GROTTO_CENTER;
scrubIdentity.randomizerCheck = RC_GC_DEKU_SCRUB_GROTTO_CENTER;
break;
}
break;
case 0xFC: // Lon Lon Ranch Scrub Grotto
switch (actorParams) {
case 0x00:
scrubIdentity.randomizerInf = RAND_INF_SCRUBS_PURCHASED_LLR_DEKU_SCRUB_GROTTO_LEFT;
scrubIdentity.randomizerCheck = RC_LLR_DEKU_SCRUB_GROTTO_LEFT;
break;
case 0x05:
scrubIdentity.randomizerInf = RAND_INF_SCRUBS_PURCHASED_LLR_DEKU_SCRUB_GROTTO_RIGHT;
scrubIdentity.randomizerCheck = RC_LLR_DEKU_SCRUB_GROTTO_RIGHT;
break;
case 0x03:
case 0x06:
scrubIdentity.randomizerInf = RAND_INF_SCRUBS_PURCHASED_LLR_DEKU_SCRUB_GROTTO_CENTER;
scrubIdentity.randomizerCheck = RC_LLR_DEKU_SCRUB_GROTTO_CENTER;
break;
}
break;
case 0xFD: // Desert Colossus Scrub Grotto
switch (actorParams) {
case 0x07:
scrubIdentity.randomizerInf = RAND_INF_SCRUBS_PURCHASED_COLOSSUS_DEKU_SCRUB_GROTTO_REAR;
scrubIdentity.randomizerCheck = RC_COLOSSUS_DEKU_SCRUB_GROTTO_REAR;
break;
case 0x08:
scrubIdentity.randomizerInf = RAND_INF_SCRUBS_PURCHASED_COLOSSUS_DEKU_SCRUB_GROTTO_FRONT;
scrubIdentity.randomizerCheck = RC_COLOSSUS_DEKU_SCRUB_GROTTO_FRONT;
break;
}
break;
}
break;
case SCENE_SPOT10: // Lost woods
switch (actorParams) {
case 0x00:
scrubIdentity.randomizerInf = RAND_INF_SCRUBS_PURCHASED_LW_DEKU_SCRUB_NEAR_DEKU_THEATER_RIGHT;
scrubIdentity.randomizerCheck = RC_LW_DEKU_SCRUB_NEAR_DEKU_THEATER_RIGHT;
break;
case 0x01:
scrubIdentity.randomizerInf = RAND_INF_SCRUBS_PURCHASED_LW_DEKU_SCRUB_NEAR_DEKU_THEATER_LEFT;
scrubIdentity.randomizerCheck = RC_LW_DEKU_SCRUB_NEAR_DEKU_THEATER_LEFT;
break;
case 0x09:
scrubIdentity.randomizerInf = RAND_INF_SCRUBS_PURCHASED_LW_DEKU_SCRUB_NEAR_BRIDGE;
scrubIdentity.randomizerCheck = RC_LW_DEKU_SCRUB_NEAR_BRIDGE;
scrubIdentity.isShuffled = true;
break;
}
break;
case SCENE_SPOT17: // Death Mountain Crater
switch (actorParams) {
case 0x05:
scrubIdentity.randomizerInf = RAND_INF_SCRUBS_PURCHASED_DMC_DEKU_SCRUB;
scrubIdentity.randomizerCheck = RC_DMC_DEKU_SCRUB;
break;
}
break;
}
if (randomizerMerchantPrices.find(scrubIdentity.randomizerCheck) != randomizerMerchantPrices.end()) {
scrubIdentity.itemPrice = randomizerMerchantPrices[scrubIdentity.randomizerCheck];
}
return scrubIdentity;
}
u8 Randomizer::GetRandoSettingValue(RandomizerSettingKey randoSettingKey) {
return this->randoSettings[randoSettingKey];
}
@ -2189,10 +2650,6 @@ RandomizerCheck Randomizer::GetCheckFromActor(s16 sceneNum, s16 actorId, s16 act
break;
case 62:
switch (actorParams) {
case 2:
return RC_HF_DEKU_SCRUB_GROTTO;
case 10:
return RC_LW_DEKU_SCRUB_GROTTO_FRONT;
case 22988:
return RC_KF_STORMS_GROTTO_CHEST;
case -22988:
@ -2457,8 +2914,6 @@ RandomizerCheck Randomizer::GetCheckFromActor(s16 sceneNum, s16 actorId, s16 act
break;
case 91:
switch (actorParams) {
case 9:
return RC_LW_DEKU_SCRUB_NEAR_BRIDGE;
case 14365:
return RC_LW_GOSSIP_STONE;
case 27905:
@ -2623,8 +3078,10 @@ void GenerateRandomizerImgui() {
cvarSettings[RSK_SHUFFLE_DUNGEON_REWARDS] = CVar_GetS32("gRandomizeShuffleDungeonReward", 0);
cvarSettings[RSK_SHUFFLE_SONGS] = CVar_GetS32("gRandomizeShuffleSongs", 0);
cvarSettings[RSK_SHUFFLE_TOKENS] = CVar_GetS32("gRandomizeShuffleTokens", 0);
cvarSettings[RSK_SHUFFLE_SCRUBS] = CVar_GetS32("gRandomizeShuffleScrubs", 0);
cvarSettings[RSK_SHUFFLE_COWS] = CVar_GetS32("gRandomizeShuffleCows", 0);
cvarSettings[RSK_SHUFFLE_ADULT_TRADE] = CVar_GetS32("gRandomizeShuffleAdultTrade", 0);
cvarSettings[RSK_SHUFFLE_MAGIC_BEANS] = CVar_GetS32("gRandomizeShuffleBeans", 0);
cvarSettings[RSK_BOMBCHUS_IN_LOGIC] = CVar_GetS32("gRandomizeBombchusInLogic", 0);
cvarSettings[RSK_SKIP_CHILD_ZELDA] = CVar_GetS32("gRandomizeSkipChildZelda", 0);
@ -2706,7 +3163,7 @@ void DrawRandoEditor(bool& open) {
const char* randoGerudoFortress[3] = { "Normal", "Fast", "Open" };
const char* randoRainbowBridge[7] = { "Vanilla", "Always open", "Stones", "Medallions",
"Dungeon rewards", "Dungeons", "Tokens" };
const char* randoGanonsTrial[2] = { "Off", "On" };
const char* randoGanonsTrial[3] = { "Skip", "Set Number", "Random Number" };
// World Settings
const char* randoStartingAge[3] = { "Child", "Adult", "Random" };
@ -3080,21 +3537,18 @@ void DrawRandoEditor(bool& open) {
PaddedSeparator();
// Random Ganon's Trials
/*
ImGui::Text("Random Ganon's Trials");
InsertHelpHoverText("Sets a random number or required trials to enter\nGanon's Tower.");
SohImGui::EnhancementCombobox("gRandomizeGanonTrial", randoGanonsTrial, 2, 0);
if (CVar_GetS32("gRandomizeGanonTrial", 0) == 0) {
ImGui::PopItemWidth();
ImGui::Text("Ganon's Trials");
InsertHelpHoverText("Sets the number of Ganon's Trials required to dispel the barrier\n\n"
"Skip - No Trials are required and the barrier is already dispelled.\n\n"
"Set Number - Select a number of trials that will be required from the"
"slider below. Which specific trials you need to complete will be random.\n\n"
"Random Number - A Random number and set of trials will be required.");
SohImGui::EnhancementCombobox("gRandomizeGanonTrial", randoGanonsTrial, 3, 0);
if (CVar_GetS32("gRandomizeGanonTrial", 0) == 1) {
SohImGui::EnhancementSliderInt("Ganon's Trial Count: %d", "##RandoTrialCount",
"gRandomizeGanonTrialCount", 0, 6, "", 6);
"gRandomizeGanonTrialCount", 1, 6, "", 6);
InsertHelpHoverText("Set the number of trials required to enter Ganon's Tower.");
RANDTODO: Switch back to slider when pre-completing some of Ganon's Trials is properly implemnted.
}
*/
SohImGui::EnhancementCheckbox("Skip Ganon's Trials", "gRandomizeGanonTrialCount");
InsertHelpHoverText(
"Sets whether or not Ganon's Castle Trials are required to enter Ganon's Tower.");
}
// COLUMN 2 - Shuffle Settings
@ -3120,6 +3574,20 @@ void DrawRandoEditor(bool& open) {
SohImGui::EnhancementCombobox("gRandomizeShuffleSongs", randoShuffleSongs, 3, 0);
PaddedSeparator();
// Shuffle Scrubs
ImGui::Text(Settings::Scrubsanity.GetName().c_str());
InsertHelpHoverText(
"Off - Scrubs will not be shuffled. The 3 Scrubs that give one-time items in the vanilla game (PoH, Deku Nut capacity, and Deku Stick capacity) will have random items.\n"
"\n"
"Affordable - Scrubs will be shuffled and their item will cost 10 rupees.\n"
"\n"
"Expensive - Scrubs will be shuffled and their item will cost the vanilla price.\n"
"\n"
"Random - Scrubs will be shuffled and their item will cost will be between 0-95 rupees.\n"
);
SohImGui::EnhancementCombobox("gRandomizeShuffleScrubs", randoShuffleScrubs, 4, 0);
PaddedSeparator();
// Shuffle Tokens
ImGui::Text(Settings::Tokensanity.GetName().c_str());
InsertHelpHoverText("Shuffles Golden Skulltula Tokens into the item pool. This means "
@ -3140,10 +3608,10 @@ void DrawRandoEditor(bool& open) {
"expected to be collected after getting Sun's Song.");
PaddedSeparator();
// Shuffle Cows
SohImGui::EnhancementCheckbox(Settings::ShuffleCows.GetName().c_str(), "gRandomizeShuffleCows");
InsertHelpHoverText(
"Cows give a randomized item from the pool upon performing Epona's Song in front of them.");
InsertHelpHoverText("Cows give a randomized item from the pool upon performing Epona's Song in front of them.");
PaddedSeparator();
// Shuffle Adult Trade Quest
@ -3159,6 +3627,11 @@ void DrawRandoEditor(bool& open) {
"If disabled, only the Claim Check will be found in the pool.");
PaddedSeparator();
SohImGui::EnhancementCheckbox(Settings::ShuffleMagicBeans.GetName().c_str(), "gRandomizeShuffleBeans");
InsertHelpHoverText("Enabling this adds a pack of 10 beans to the item pool and changes the Magic Bean Salesman to sell a"
"random item at a price of 60 rupees.");
PaddedSeparator();
if (CVar_GetS32("gRandomizeStartingKokiriSword", 0) == 0) {
// Shuffle Kokiri Sword
SohImGui::EnhancementCheckbox(Settings::ShuffleKokiriSword.GetName().c_str(),
@ -3745,12 +4218,20 @@ void CreateGetItemMessages(std::vector<GetItemMessage> messageEntries) {
}
}
void CreateScrubMessages() {
// Currently these are generated at runtime, one for each price between 0-95. We're soon going to migrate this
// to being generated at save load, with only messages specific to each scrub.
void CreateMerchantMessages() {
CustomMessageManager* customMessageManager = CustomMessageManager::Instance;
customMessageManager->AddCustomMessageTable(Randomizer::scrubMessageTableID);
const std::vector<u8> prices = { 10, 40 };
for (u8 price : prices) {
customMessageManager->CreateMessage(Randomizer::scrubMessageTableID, price,
customMessageManager->AddCustomMessageTable(Randomizer::merchantMessageTableID);
customMessageManager->CreateMessage(Randomizer::merchantMessageTableID, 0,
{ TEXTBOX_TYPE_BLACK, TEXTBOX_POS_BOTTOM,
"\x12\x38\x82\All right! You win! In return for&sparing me, I will give you a&%gmysterious item%w!&Please, take it!\x07\x10\xA3",
"\x12\x38\x82\In Ordnung! Du gewinnst! Im Austausch&dafür, dass du mich verschont hast,&werde ich dir einen %gmysteriösen&Gegenstand%w geben! Bitte nimm ihn!\x07\x10\xA3",
"\x12\x38\x82\D'accord! Vous avez gagné! En échange&de m'épargner, je vous donnerai un &%gobjet mystérieux%w! S'il vous plaît,&prenez-le!\x07\x10\xA3",
});
for (u32 price = 5; price <= 95; price += 5) {
customMessageManager->CreateMessage(Randomizer::merchantMessageTableID, price,
{ TEXTBOX_TYPE_BLACK, TEXTBOX_POS_BOTTOM,
"\x12\x38\x82\All right! You win! In return for&sparing me, I will sell you a&%gmysterious item%w!&%r" +
std::to_string(price) + " Rupees%w it is!\x07\x10\xA3",
@ -3761,6 +4242,18 @@ void CreateScrubMessages() {
std::to_string(price) + " Rubis%w!\x07\x10\xA3"
});
}
customMessageManager->CreateMessage(
Randomizer::merchantMessageTableID, TEXT_BEAN_SALESMAN,
{
TEXTBOX_TYPE_BLACK,
TEXTBOX_POS_BOTTOM,
"I tried to be a %rmagic bean%w salesman,&but it turns out my marketing skills&weren't worth "
"beans!^Anyway, want to buy my&%gmysterious item%w for 60 Rupees?\x1B&%gYes&No%w",
"Möchten Sie einen geheimnisvollen&Gegenstand für 60 Rubine?\x1B&%gJa&Nein%w",
"J'ai essayé d'être un vendeur de&%rharicots magiques%w, mais j'étais&mauvais au niveau du marketing et&ça "
"me courait sur le haricot...^Enfin bref, ça te dirait de m'acheter un&"
"%gobjet mystérieux%w pour 60 Rubis?\x1B&%gOui&Non%w",
});
}
void CreateRupeeMessages() {
@ -3905,75 +4398,122 @@ void Randomizer::CreateCustomMessages() {
const std::vector<GetItemMessage> getItemMessages = {
GIMESSAGE(RG_ICE_TRAP, ITEM_NONE, "\x08\x06\x30You are a %bFOWL%w!\x0E\x20",
"\x08\x06\x15 Du bist ein %bDUMMKOPF%w!\x0E\x20", "\x08\x06\x50%bIDIOT%w\x0E\x20"),
GIMESSAGE_NO_GERMAN(RG_BOTTLE_WITH_BLUE_FIRE, ITEM_BLUE_FIRE,
"You got a %rBottle with Blue &Fire%w! Use it to melt Red Ice!",
"Vous obtenez une %rBouteille avec&une Flamme Bleue%w! Utilisez-la&pour faire fondre la %rGlace&Rouge%w!"),
GIMESSAGE_NO_GERMAN(
RG_BOTTLE_WITH_BLUE_FIRE, ITEM_BLUE_FIRE, "You got a %rBottle with Blue &Fire%w! Use it to melt Red Ice!",
"Vous obtenez une %rBouteille avec&une Flamme Bleue%w! Utilisez-la&pour faire fondre la %rGlace&Rouge%w!"),
GIMESSAGE_NO_GERMAN(RG_BOTTLE_WITH_BIG_POE, ITEM_BIG_POE,
"You got a %rBig Poe in a Bottle%w!&Sell it to the Ghost Shop!",
"Vous obtenez une %rBouteille avec&une Âme%w! Vendez-la au Marchand&d'Âme"),
"You got a %rBig Poe in a Bottle%w!&Sell it to the Ghost Shop!",
"Vous obtenez une %rBouteille avec&une Âme%w! Vendez-la au Marchand&d'Âme"),
GIMESSAGE_NO_GERMAN(RG_BOTTLE_WITH_BLUE_POTION, ITEM_POTION_BLUE,
"You got a %rBottle of Blue Potion%w!&Drink it to replenish your&%ghealth%w and %bmagic%w!",
"Vous obtenez une %rBouteille avec&une Potion Bleue%w! Buvez-la pour&restaurer votre %rénergie vitale%w&ainsi que votre %gmagie%w!"),
GIMESSAGE_NO_GERMAN(RG_BOTTLE_WITH_FISH, ITEM_FISH,
"You got a %rFish in a Bottle%w!&It looks fresh and delicious!&They say Jabu-Jabu loves them!",
"Vous obtenez une %rBouteille avec&un Poisson%w! Il a l'air délicieux!&Il paraîtrait que %bJabu-Jabu %wen&serait friand!"),
GIMESSAGE_NO_GERMAN(RG_BOTTLE_WITH_BUGS, ITEM_BUG,
"You got a %rBug in a Bottle%w!&They love to burrow in&dirt holes!",
"Vous obtenez une %rBouteille avec&des Insectes%w! Ils adorent creuser&dans la terre meuble!"),
GIMESSAGE_NO_GERMAN(RG_BOTTLE_WITH_FAIRY, ITEM_FAIRY,
"You got a %rFairy in a Bottle%w!&Use it wisely!",
"Vous obtenez une %rBouteille avec&une Fée%w! Faites-en bon usage!"),
GIMESSAGE_NO_GERMAN(RG_BOTTLE_WITH_RED_POTION, ITEM_POTION_RED,
"You got a %rBottle of Red Potion%w!&Drink it to replenish your&%ghealth%w!",
"Vous obtenez une %rBouteille avec&une Potion Rouge%w! Buvez-la pour&restaurer votre %rénergie vitale%w!"),
GIMESSAGE_NO_GERMAN(RG_BOTTLE_WITH_GREEN_POTION, ITEM_POTION_GREEN,
"You got a %rBottle of Green Potion%w!&Drink it to replenish your&%bmagic%w!",
"Vous obtenez une %rBouteille avec&une Potion Verte%w! Buvez-la pour&restaurer votre %gmagie%w!"),
GIMESSAGE_NO_GERMAN(RG_BOTTLE_WITH_POE, ITEM_POE,
"You got a %rPoe in a Bottle%w!&That creepy Ghost Shop might&be interested in this...",
"Vous obtenez une %rBouteille avec&un Esprit%w! Ça intéresserait&peut-être le vendeur d'Âme "),
"You got a %rBottle of Blue Potion%w!&Drink it to replenish your&%ghealth%w and %bmagic%w!",
"Vous obtenez une %rBouteille avec&une Potion Bleue%w! Buvez-la pour&restaurer votre "
"%rénergie vitale%w&ainsi que votre %gmagie%w!"),
GIMESSAGE_NO_GERMAN(
RG_BOTTLE_WITH_FISH, ITEM_FISH,
"You got a %rFish in a Bottle%w!&It looks fresh and delicious!&They say Jabu-Jabu loves them!",
"Vous obtenez une %rBouteille avec&un Poisson%w! Il a l'air délicieux!&Il paraîtrait que %bJabu-Jabu "
"%wen&serait friand!"),
GIMESSAGE_NO_GERMAN(
RG_BOTTLE_WITH_BUGS, ITEM_BUG, "You got a %rBug in a Bottle%w!&They love to burrow in&dirt holes!",
"Vous obtenez une %rBouteille avec&des Insectes%w! Ils adorent creuser&dans la terre meuble!"),
GIMESSAGE_NO_GERMAN(RG_BOTTLE_WITH_FAIRY, ITEM_FAIRY, "You got a %rFairy in a Bottle%w!&Use it wisely!",
"Vous obtenez une %rBouteille avec&une Fée%w! Faites-en bon usage!"),
GIMESSAGE_NO_GERMAN(
RG_BOTTLE_WITH_RED_POTION, ITEM_POTION_RED,
"You got a %rBottle of Red Potion%w!&Drink it to replenish your&%ghealth%w!",
"Vous obtenez une %rBouteille avec&une Potion Rouge%w! Buvez-la pour&restaurer votre %rénergie vitale%w!"),
GIMESSAGE_NO_GERMAN(
RG_BOTTLE_WITH_GREEN_POTION, ITEM_POTION_GREEN,
"You got a %rBottle of Green Potion%w!&Drink it to replenish your&%bmagic%w!",
"Vous obtenez une %rBouteille avec&une Potion Verte%w! Buvez-la pour&restaurer votre %gmagie%w!"),
GIMESSAGE_NO_GERMAN(
RG_BOTTLE_WITH_POE, ITEM_POE,
"You got a %rPoe in a Bottle%w!&That creepy Ghost Shop might&be interested in this...",
"Vous obtenez une %rBouteille avec&un Esprit%w! Ça intéresserait&peut-être le vendeur d'Âme "),
GIMESSAGE_NO_GERMAN(RG_GERUDO_FORTRESS_SMALL_KEY, ITEM_KEY_SMALL, "You found a %yThieves Hideout &%wSmall Key!", "Vous obtenez une %rPetite Clé %w&du %yRepaire des Voleurs%w!"),
GIMESSAGE_NO_GERMAN(RG_FOREST_TEMPLE_SMALL_KEY, ITEM_KEY_SMALL, "You found a %gForest Temple &%wSmall Key!", "Vous obtenez une %rPetite Clé %w&du %gTemple de la Forêt%w!"),
GIMESSAGE_NO_GERMAN(RG_FIRE_TEMPLE_SMALL_KEY, ITEM_KEY_SMALL, "You found a %rFire Temple &%wSmall Key!", "Vous obtenez une %rPetite Clé %w&du %rTemple du Feu%w!"),
GIMESSAGE_NO_GERMAN(RG_WATER_TEMPLE_SMALL_KEY, ITEM_KEY_SMALL, "You found a %bWater Temple &%wSmall Key!", "Vous obtenez une %rPetite Clé %w&du %bTemple de l'Eau%w!"),
GIMESSAGE_NO_GERMAN(RG_SPIRIT_TEMPLE_SMALL_KEY, ITEM_KEY_SMALL, "You found a %ySpirit Temple &%wSmall Key!", "Vous obtenez une %rPetite Clé %w&du %yTemple de l'Esprit%w!"),
GIMESSAGE_NO_GERMAN(RG_SHADOW_TEMPLE_SMALL_KEY, ITEM_KEY_SMALL, "You found a %pShadow Temple &%wSmall Key!", "Vous obtenez une %rPetite Clé %w&du %pTemple de l'Ombre%w!"),
GIMESSAGE_NO_GERMAN(RG_BOTTOM_OF_THE_WELL_SMALL_KEY, ITEM_KEY_SMALL, "You found a %pBottom of the &Well %wSmall Key!", "Vous obtenez une %rPetite Clé %w&du %Puits%w!"),
GIMESSAGE_NO_GERMAN(RG_GERUDO_TRAINING_GROUNDS_SMALL_KEY, ITEM_KEY_SMALL, "You found a %yGerudo Training &Grounds %wSmall Key!", "Vous obtenez une %rPetite Clé %w&du %yGymnase Gerudo%w!"),
GIMESSAGE_NO_GERMAN(RG_GANONS_CASTLE_SMALL_KEY, ITEM_KEY_SMALL, "You found a %rGanon's Castle &%wSmall Key!", "Vous obtenez une %rPetite Clé %w&du %Château de Ganon%w!"),
GIMESSAGE_NO_GERMAN(RG_GERUDO_FORTRESS_SMALL_KEY, ITEM_KEY_SMALL, "You found a %yThieves Hideout &%wSmall Key!",
"Vous obtenez une %rPetite Clé %w&du %yRepaire des Voleurs%w!"),
GIMESSAGE_NO_GERMAN(RG_FOREST_TEMPLE_SMALL_KEY, ITEM_KEY_SMALL, "You found a %gForest Temple &%wSmall Key!",
"Vous obtenez une %rPetite Clé %w&du %gTemple de la Forêt%w!"),
GIMESSAGE_NO_GERMAN(RG_FIRE_TEMPLE_SMALL_KEY, ITEM_KEY_SMALL, "You found a %rFire Temple &%wSmall Key!",
"Vous obtenez une %rPetite Clé %w&du %rTemple du Feu%w!"),
GIMESSAGE_NO_GERMAN(RG_WATER_TEMPLE_SMALL_KEY, ITEM_KEY_SMALL, "You found a %bWater Temple &%wSmall Key!",
"Vous obtenez une %rPetite Clé %w&du %bTemple de l'Eau%w!"),
GIMESSAGE_NO_GERMAN(RG_SPIRIT_TEMPLE_SMALL_KEY, ITEM_KEY_SMALL, "You found a %ySpirit Temple &%wSmall Key!",
"Vous obtenez une %rPetite Clé %w&du %yTemple de l'Esprit%w!"),
GIMESSAGE_NO_GERMAN(RG_SHADOW_TEMPLE_SMALL_KEY, ITEM_KEY_SMALL, "You found a %pShadow Temple &%wSmall Key!",
"Vous obtenez une %rPetite Clé %w&du %pTemple de l'Ombre%w!"),
GIMESSAGE_NO_GERMAN(RG_BOTTOM_OF_THE_WELL_SMALL_KEY, ITEM_KEY_SMALL,
"You found a %pBottom of the &Well %wSmall Key!",
"Vous obtenez une %rPetite Clé %w&du %Puits%w!"),
GIMESSAGE_NO_GERMAN(RG_GERUDO_TRAINING_GROUNDS_SMALL_KEY, ITEM_KEY_SMALL,
"You found a %yGerudo Training &Grounds %wSmall Key!",
"Vous obtenez une %rPetite Clé %w&du %yGymnase Gerudo%w!"),
GIMESSAGE_NO_GERMAN(RG_GANONS_CASTLE_SMALL_KEY, ITEM_KEY_SMALL, "You found a %rGanon's Castle &%wSmall Key!",
"Vous obtenez une %rPetite Clé %w&du %Château de Ganon%w!"),
GIMESSAGE_NO_GERMAN(RG_FOREST_TEMPLE_BOSS_KEY, ITEM_KEY_BOSS, "You found the %gForest Temple &%wBoss Key!", "Vous obtenez la %rClé d'or %wdu&%gTemple de la Forêt%w!"),
GIMESSAGE_NO_GERMAN(RG_FIRE_TEMPLE_BOSS_KEY, ITEM_KEY_BOSS, "You found the %rFire Temple &%wBoss Key!", "Vous obtenez la %rClé d'or %wdu&%rTemple du Feu%w!"),
GIMESSAGE_NO_GERMAN(RG_WATER_TEMPLE_BOSS_KEY, ITEM_KEY_BOSS, "You found the %bWater Temple &%wBoss Key!", "Vous obtenez la %rClé d'or %wdu&%bTemple de l'Eau%w!"),
GIMESSAGE_NO_GERMAN(RG_SPIRIT_TEMPLE_BOSS_KEY, ITEM_KEY_BOSS, "You found the %ySpirit Temple &%wBoss Key!", "Vous obtenez la %rClé d'or %wdu&%yTemple de l'Esprit%w!"),
GIMESSAGE_NO_GERMAN(RG_SHADOW_TEMPLE_BOSS_KEY, ITEM_KEY_BOSS, "You found the %pShadow Temple &%wBoss Key!", "Vous obtenez la %rClé d'or %wdu&%pTemple de l'Ombre%w!"),
GIMESSAGE_NO_GERMAN(RG_GANONS_CASTLE_BOSS_KEY, ITEM_KEY_BOSS, "You found the %rGanon's Castle &%wBoss Key!", "Vous obtenez la %rClé d'or %wdu&%rChâteau de Ganon%w!"),
GIMESSAGE_NO_GERMAN(RG_DEKU_TREE_MAP, ITEM_DUNGEON_MAP, "You found the %gDeku Tree &%wMap!", "Vous obtenez la %rCarte %wde&l'%gArbre Mojo%w!"),
GIMESSAGE_NO_GERMAN(RG_DODONGOS_CAVERN_MAP, ITEM_DUNGEON_MAP, "You found the %rDodongo's Cavern &%wMap!", "Vous obtenez la %rCarte %wde la&%rCaverne Dodongo%w!"),
GIMESSAGE_NO_GERMAN(RG_JABU_JABUS_BELLY_MAP, ITEM_DUNGEON_MAP, "You found the %bJabu Jabu's Belly &%wMap!", "Vous obtenez la %rCarte %wdu &%bVentre de Jabu-Jabu%w!"),
GIMESSAGE_NO_GERMAN(RG_FOREST_TEMPLE_MAP, ITEM_DUNGEON_MAP, "You found the %gForest Temple &%wMap!", "Vous obtenez la %rCarte %wdu &%gTemple de la Forêt%w!"),
GIMESSAGE_NO_GERMAN(RG_FIRE_TEMPLE_MAP, ITEM_DUNGEON_MAP, "You found the %rFire Temple &%wMap!", "Vous obtenez la %rCarte %wdu &%rTemple du Feu%w!"),
GIMESSAGE_NO_GERMAN(RG_WATER_TEMPLE_MAP, ITEM_DUNGEON_MAP, "You found the %bWater Temple &%wMap!", "Vous obtenez la %rCarte %wdu &%bTemple de l'Eau%w!"),
GIMESSAGE_NO_GERMAN(RG_SPIRIT_TEMPLE_MAP, ITEM_DUNGEON_MAP, "You found the %ySpirit Temple &%wMap!", "Vous obtenez la %rCarte %wdu &%yTemple de l'Esprit%w!"),
GIMESSAGE_NO_GERMAN(RG_SHADOW_TEMPLE_MAP, ITEM_DUNGEON_MAP, "You found the %pShadow Temple &%wMap!", "Vous obtenez la %rCarte %wdu &%pTemple de l'Ombre%w!"),
GIMESSAGE_NO_GERMAN(RG_BOTTOM_OF_THE_WELL_MAP, ITEM_DUNGEON_MAP, "You found the %pBottom of the &Well %wMap!", "Vous obtenez la %rCarte %wdu &%pPuits%w!"),
GIMESSAGE_NO_GERMAN(RG_ICE_CAVERN_MAP, ITEM_DUNGEON_MAP, "You found the %cIce Cavern &%wMap!", "Vous obtenez la %rCarte %wde &la %cCaverne Polaire%w!"),
GIMESSAGE_NO_GERMAN(RG_FOREST_TEMPLE_BOSS_KEY, ITEM_KEY_BOSS, "You found the %gForest Temple &%wBoss Key!",
"Vous obtenez la %rClé d'or %wdu&%gTemple de la Forêt%w!"),
GIMESSAGE_NO_GERMAN(RG_FIRE_TEMPLE_BOSS_KEY, ITEM_KEY_BOSS, "You found the %rFire Temple &%wBoss Key!",
"Vous obtenez la %rClé d'or %wdu&%rTemple du Feu%w!"),
GIMESSAGE_NO_GERMAN(RG_WATER_TEMPLE_BOSS_KEY, ITEM_KEY_BOSS, "You found the %bWater Temple &%wBoss Key!",
"Vous obtenez la %rClé d'or %wdu&%bTemple de l'Eau%w!"),
GIMESSAGE_NO_GERMAN(RG_SPIRIT_TEMPLE_BOSS_KEY, ITEM_KEY_BOSS, "You found the %ySpirit Temple &%wBoss Key!",
"Vous obtenez la %rClé d'or %wdu&%yTemple de l'Esprit%w!"),
GIMESSAGE_NO_GERMAN(RG_SHADOW_TEMPLE_BOSS_KEY, ITEM_KEY_BOSS, "You found the %pShadow Temple &%wBoss Key!",
"Vous obtenez la %rClé d'or %wdu&%pTemple de l'Ombre%w!"),
GIMESSAGE_NO_GERMAN(RG_GANONS_CASTLE_BOSS_KEY, ITEM_KEY_BOSS, "You found the %rGanon's Castle &%wBoss Key!",
"Vous obtenez la %rClé d'or %wdu&%rChâteau de Ganon%w!"),
GIMESSAGE_NO_GERMAN(RG_DEKU_TREE_COMPASS, ITEM_COMPASS, "You found the %gDeku Tree &%wCompass!", "Vous obtenez la %rBoussole %wde&l'%gArbre Mojo%w!"),
GIMESSAGE_NO_GERMAN(RG_DODONGOS_CAVERN_COMPASS, ITEM_COMPASS, "You found the %rDodongo's Cavern &%wCompass!", "Vous obtenez la %rBoussole %wde la&%rCaverne Dodongo%w!"),
GIMESSAGE_NO_GERMAN(RG_JABU_JABUS_BELLY_COMPASS, ITEM_COMPASS, "You found the %bJabu Jabu's Belly &%wCompass!", "Vous obtenez la %rBoussole %wdu &%bVentre de Jabu-Jabu%w!"),
GIMESSAGE_NO_GERMAN(RG_FOREST_TEMPLE_COMPASS, ITEM_COMPASS, "You found the %gForest Temple &%wCompass!", "Vous obtenez la %rBoussole %wdu &%gTemple de la Forêt%w!"),
GIMESSAGE_NO_GERMAN(RG_FIRE_TEMPLE_COMPASS, ITEM_COMPASS, "You found the %rFire Temple &%wCompass!", "Vous obtenez la %rBoussole %wdu &%rTemple du Feu%w!"),
GIMESSAGE_NO_GERMAN(RG_WATER_TEMPLE_COMPASS, ITEM_COMPASS, "You found the %bWater Temple &%wCompass!", "Vous obtenez la %rBoussole %wdu &%bTemple de l'Eau%w!"),
GIMESSAGE_NO_GERMAN(RG_SPIRIT_TEMPLE_COMPASS, ITEM_COMPASS, "You found the %ySpirit Temple &%wCompass!", "Vous obtenez la %rBoussole %wdu &%yTemple de l'Esprit%w!"),
GIMESSAGE_NO_GERMAN(RG_SHADOW_TEMPLE_COMPASS, ITEM_COMPASS, "You found the %pShadow Temple &%wCompass!", "Vous obtenez la %rBoussole %wdu &%pTemple de l'Ombre%w!"),
GIMESSAGE_NO_GERMAN(RG_BOTTOM_OF_THE_WELL_COMPASS, ITEM_COMPASS, "You found the %pBottom of the &Well %wCompass!", "Vous obtenez la %rBoussole %wdu &%pPuits%w!"),
GIMESSAGE_NO_GERMAN(RG_ICE_CAVERN_COMPASS, ITEM_COMPASS, "You found the %cIce Cavern &%wCompass!", "Vous obtenez la %rBoussole %wde &la %cCaverne Polaire%w!"),
GIMESSAGE_NO_GERMAN(RG_DEKU_TREE_MAP, ITEM_DUNGEON_MAP, "You found the %gDeku Tree &%wMap!",
"Vous obtenez la %rCarte %wde&l'%gArbre Mojo%w!"),
GIMESSAGE_NO_GERMAN(RG_DODONGOS_CAVERN_MAP, ITEM_DUNGEON_MAP, "You found the %rDodongo's Cavern &%wMap!",
"Vous obtenez la %rCarte %wde la&%rCaverne Dodongo%w!"),
GIMESSAGE_NO_GERMAN(RG_JABU_JABUS_BELLY_MAP, ITEM_DUNGEON_MAP, "You found the %bJabu Jabu's Belly &%wMap!",
"Vous obtenez la %rCarte %wdu &%bVentre de Jabu-Jabu%w!"),
GIMESSAGE_NO_GERMAN(RG_FOREST_TEMPLE_MAP, ITEM_DUNGEON_MAP, "You found the %gForest Temple &%wMap!",
"Vous obtenez la %rCarte %wdu &%gTemple de la Forêt%w!"),
GIMESSAGE_NO_GERMAN(RG_FIRE_TEMPLE_MAP, ITEM_DUNGEON_MAP, "You found the %rFire Temple &%wMap!",
"Vous obtenez la %rCarte %wdu &%rTemple du Feu%w!"),
GIMESSAGE_NO_GERMAN(RG_WATER_TEMPLE_MAP, ITEM_DUNGEON_MAP, "You found the %bWater Temple &%wMap!",
"Vous obtenez la %rCarte %wdu &%bTemple de l'Eau%w!"),
GIMESSAGE_NO_GERMAN(RG_SPIRIT_TEMPLE_MAP, ITEM_DUNGEON_MAP, "You found the %ySpirit Temple &%wMap!",
"Vous obtenez la %rCarte %wdu &%yTemple de l'Esprit%w!"),
GIMESSAGE_NO_GERMAN(RG_SHADOW_TEMPLE_MAP, ITEM_DUNGEON_MAP, "You found the %pShadow Temple &%wMap!",
"Vous obtenez la %rCarte %wdu &%pTemple de l'Ombre%w!"),
GIMESSAGE_NO_GERMAN(RG_BOTTOM_OF_THE_WELL_MAP, ITEM_DUNGEON_MAP, "You found the %pBottom of the &Well %wMap!",
"Vous obtenez la %rCarte %wdu &%pPuits%w!"),
GIMESSAGE_NO_GERMAN(RG_ICE_CAVERN_MAP, ITEM_DUNGEON_MAP, "You found the %cIce Cavern &%wMap!",
"Vous obtenez la %rCarte %wde &la %cCaverne Polaire%w!"),
GIMESSAGE_NO_GERMAN(RG_DEKU_TREE_COMPASS, ITEM_COMPASS, "You found the %gDeku Tree &%wCompass!",
"Vous obtenez la %rBoussole %wde&l'%gArbre Mojo%w!"),
GIMESSAGE_NO_GERMAN(RG_DODONGOS_CAVERN_COMPASS, ITEM_COMPASS, "You found the %rDodongo's Cavern &%wCompass!",
"Vous obtenez la %rBoussole %wde la&%rCaverne Dodongo%w!"),
GIMESSAGE_NO_GERMAN(RG_JABU_JABUS_BELLY_COMPASS, ITEM_COMPASS, "You found the %bJabu Jabu's Belly &%wCompass!",
"Vous obtenez la %rBoussole %wdu &%bVentre de Jabu-Jabu%w!"),
GIMESSAGE_NO_GERMAN(RG_FOREST_TEMPLE_COMPASS, ITEM_COMPASS, "You found the %gForest Temple &%wCompass!",
"Vous obtenez la %rBoussole %wdu &%gTemple de la Forêt%w!"),
GIMESSAGE_NO_GERMAN(RG_FIRE_TEMPLE_COMPASS, ITEM_COMPASS, "You found the %rFire Temple &%wCompass!",
"Vous obtenez la %rBoussole %wdu &%rTemple du Feu%w!"),
GIMESSAGE_NO_GERMAN(RG_WATER_TEMPLE_COMPASS, ITEM_COMPASS, "You found the %bWater Temple &%wCompass!",
"Vous obtenez la %rBoussole %wdu &%bTemple de l'Eau%w!"),
GIMESSAGE_NO_GERMAN(RG_SPIRIT_TEMPLE_COMPASS, ITEM_COMPASS, "You found the %ySpirit Temple &%wCompass!",
"Vous obtenez la %rBoussole %wdu &%yTemple de l'Esprit%w!"),
GIMESSAGE_NO_GERMAN(RG_SHADOW_TEMPLE_COMPASS, ITEM_COMPASS, "You found the %pShadow Temple &%wCompass!",
"Vous obtenez la %rBoussole %wdu &%pTemple de l'Ombre%w!"),
GIMESSAGE_NO_GERMAN(RG_BOTTOM_OF_THE_WELL_COMPASS, ITEM_COMPASS,
"You found the %pBottom of the &Well %wCompass!",
"Vous obtenez la %rBoussole %wdu &%pPuits%w!"),
GIMESSAGE_NO_GERMAN(RG_ICE_CAVERN_COMPASS, ITEM_COMPASS, "You found the %cIce Cavern &%wCompass!",
"Vous obtenez la %rBoussole %wde &la %cCaverne Polaire%w!"),
GIMESSAGE(RG_MAGIC_BEAN_PACK, ITEM_BEAN,
"You got a %rPack of Magic Beans%w!&Find a suitable spot for a garden&and plant them. Then, wait for&something fun to happen!",
"Du hast eine %rPackung&Magic Beans%w! Finde&einen geeigneten Platz fur einen&Garten und pflanze sie. Dann^warte auf etwas Lustiges passiert!",
"Vous avez un %rPack de&haricots magiques%w ! Trouvez&un endroit convenable pour un&jardin et plantez-les.^Ensuite, attendez quelque&chose d'amusant doit arriver !")
};
CreateGetItemMessages(getItemMessages);
CreateScrubMessages();
CreateMerchantMessages();
CreateRupeeMessages();
CreateNaviRandoMessages();
}
@ -4070,6 +4610,7 @@ void InitRandoItemTable() {
GET_ITEM(RG_SHADOW_TEMPLE_COMPASS, OBJECT_GI_COMPASS, GID_COMPASS, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_LONG, MOD_RANDOMIZER, RG_SHADOW_TEMPLE_COMPASS),
GET_ITEM(RG_BOTTOM_OF_THE_WELL_COMPASS, OBJECT_GI_COMPASS, GID_COMPASS, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_LONG, MOD_RANDOMIZER, RG_BOTTOM_OF_THE_WELL_COMPASS),
GET_ITEM(RG_ICE_CAVERN_COMPASS, OBJECT_GI_COMPASS, GID_COMPASS, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_LONG, MOD_RANDOMIZER, RG_ICE_CAVERN_COMPASS),
GET_ITEM(RG_MAGIC_BEAN_PACK, OBJECT_GI_BEAN, GID_BEAN, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_LONG, MOD_RANDOMIZER, RG_MAGIC_BEAN_PACK)
};
ItemTableManager::Instance->AddItemTable(MOD_RANDOMIZER);
for (int i = 0; i < ARRAY_COUNT(extendedVanillaGetItemTable); i++) {

View File

@ -14,15 +14,18 @@ class Randomizer {
private:
std::unordered_map<RandomizerCheck, RandomizerGet> itemLocations;
std::unordered_map<RandomizerCheck, std::string> hintLocations;
std::unordered_map<RandomizerInf, bool> trialsRequired;
std::string childAltarText;
std::string adultAltarText;
std::string ganonHintText;
std::string ganonText;
std::unordered_map<RandomizerSettingKey, u8> randoSettings;
std::unordered_map<RandomizerCheck, u16> randomizerMerchantPrices;
s16 GetItemFromGet(RandomizerGet randoGet, GetItemID ogItemId);
s16 GetItemFromActor(s16 actorId, s16 actorParams, s16 sceneNum, GetItemID ogItemId);
void ParseRandomizerSettingsFile(const char* spoilerFileName);
void ParseHintLocationsFile(const char* spoilerFileName);
void ParseRequiredTrialsFile(const char* spoilerFileName);
void ParseItemLocationsFile(const char* spoilerFileName, bool silent);
bool IsItemVanilla(RandomizerGet randoGet);
@ -33,7 +36,7 @@ class Randomizer {
static const std::string getItemMessageTableID;
static const std::string hintMessageTableID;
static const std::string scrubMessageTableID;
static const std::string merchantMessageTableID;
static const std::string rupeeMessageTableID;
static const std::string NaviRandoMessageTableID;
@ -43,13 +46,16 @@ class Randomizer {
bool SpoilerFileExists(const char* spoilerFileName);
void LoadRandomizerSettings(const char* spoilerFileName);
void LoadHintLocations(const char* spoilerFileName);
void LoadItemLocations(const char* spoilerFileName, bool silent);
void LoadRequiredTrials(const char* spoilerFileName);
void LoadItemLocations(const char* spoilerFileName,bool silent);
bool IsTrialRequired(RandomizerInf trial);
u8 GetRandoSettingValue(RandomizerSettingKey randoSettingKey);
RandomizerCheck GetCheckFromActor(s16 sceneNum, s16 actorId, s16 actorParams);
std::string GetChildAltarText() const;
std::string GetAdultAltarText() const;
std::string GetGanonText() const;
std::string GetGanonHintText() const;
ScrubIdentity IdentifyScrub(s32 sceneNum, s32 actorParams, s32 respawnData);
s16 GetRandomizedItemIdFromKnownCheck(RandomizerCheck randomizerCheck, GetItemID ogId);
s16 GetRandomizedItemId(GetItemID ogId, s16 actorId, s16 actorParams, s16 sceneNum);
static void CreateCustomMessages();

View File

@ -1,6 +1,8 @@
#pragma once
#include <stdint.h>
#include "z64item.h"
#include "randomizer_inf.h"
// This should probably go in a less rando-specific location
// but the best location will probably be in the modding engine
@ -987,6 +989,7 @@ typedef enum {
RSK_SHUFFLE_DUNGEON_REWARDS,
RSK_SHUFFLE_SONGS,
RSK_SHUFFLE_TOKENS,
RSK_SHUFFLE_SCRUBS,
RSK_SHUFFLE_COWS,
RSK_SHUFFLE_WEIRD_EGG,
RSK_SHUFFLE_GERUDO_MEMBERSHIP_CARD,
@ -1014,5 +1017,14 @@ typedef enum {
RSK_ENABLE_GLITCH_CUTSCENES,
RSK_SKULLS_SUNS_SONG,
RSK_SHUFFLE_ADULT_TRADE,
RSK_SHUFFLE_MAGIC_BEANS,
RSK_BOMBCHUS_IN_LOGIC
} RandomizerSettingKey;
typedef struct ScrubIdentity {
RandomizerInf randomizerInf;
RandomizerCheck randomizerCheck;
GetItemID getItemId;
int32_t itemPrice;
bool isShuffled;
} ScrubIdentity;

View File

@ -29,6 +29,43 @@ typedef enum {
RAND_INF_COWS_MILKED_JABU_JABUS_BELLY_MQ_COW,
RAND_INF_COWS_MILKED_HF_COW_GROTTO_GOSSIP_STONE,
RAND_INF_SCRUBS_PURCHASED_DODONGOS_CAVERN_DEKU_SCRUB_NEAR_BOMB_BAG_LEFT,
RAND_INF_SCRUBS_PURCHASED_DODONGOS_CAVERN_DEKU_SCRUB_SIDE_ROOM_NEAR_DODONGOS,
RAND_INF_SCRUBS_PURCHASED_DODONGOS_CAVERN_DEKU_SCRUB_NEAR_BOMB_BAG_RIGHT,
RAND_INF_SCRUBS_PURCHASED_DODONGOS_CAVERN_DEKU_SCRUB_LOBBY,
RAND_INF_SCRUBS_PURCHASED_JABU_JABUS_BELLY_DEKU_SCRUB,
RAND_INF_SCRUBS_PURCHASED_GANONS_CASTLE_DEKU_SCRUB_CENTER_LEFT,
RAND_INF_SCRUBS_PURCHASED_GANONS_CASTLE_DEKU_SCRUB_CENTER_RIGHT,
RAND_INF_SCRUBS_PURCHASED_GANONS_CASTLE_DEKU_SCRUB_RIGHT,
RAND_INF_SCRUBS_PURCHASED_GANONS_CASTLE_DEKU_SCRUB_LEFT,
RAND_INF_SCRUBS_PURCHASED_HF_DEKU_SCRUB_GROTTO,
RAND_INF_SCRUBS_PURCHASED_ZR_DEKU_SCRUB_GROTTO_REAR,
RAND_INF_SCRUBS_PURCHASED_ZR_DEKU_SCRUB_GROTTO_FRONT,
RAND_INF_SCRUBS_PURCHASED_SFM_DEKU_SCRUB_GROTTO_REAR,
RAND_INF_SCRUBS_PURCHASED_SFM_DEKU_SCRUB_GROTTO_FRONT,
RAND_INF_SCRUBS_PURCHASED_LH_DEKU_SCRUB_GROTTO_LEFT,
RAND_INF_SCRUBS_PURCHASED_LH_DEKU_SCRUB_GROTTO_RIGHT,
RAND_INF_SCRUBS_PURCHASED_LH_DEKU_SCRUB_GROTTO_CENTER,
RAND_INF_SCRUBS_PURCHASED_GV_DEKU_SCRUB_GROTTO_REAR,
RAND_INF_SCRUBS_PURCHASED_GV_DEKU_SCRUB_GROTTO_FRONT,
RAND_INF_SCRUBS_PURCHASED_LW_DEKU_SCRUB_GROTTO_REAR,
RAND_INF_SCRUBS_PURCHASED_LW_DEKU_SCRUB_GROTTO_FRONT,
RAND_INF_SCRUBS_PURCHASED_DMC_DEKU_SCRUB_GROTTO_LEFT,
RAND_INF_SCRUBS_PURCHASED_DMC_DEKU_SCRUB_GROTTO_RIGHT,
RAND_INF_SCRUBS_PURCHASED_DMC_DEKU_SCRUB_GROTTO_CENTER,
RAND_INF_SCRUBS_PURCHASED_GC_DEKU_SCRUB_GROTTO_LEFT,
RAND_INF_SCRUBS_PURCHASED_GC_DEKU_SCRUB_GROTTO_RIGHT,
RAND_INF_SCRUBS_PURCHASED_GC_DEKU_SCRUB_GROTTO_CENTER,
RAND_INF_SCRUBS_PURCHASED_LLR_DEKU_SCRUB_GROTTO_LEFT,
RAND_INF_SCRUBS_PURCHASED_LLR_DEKU_SCRUB_GROTTO_RIGHT,
RAND_INF_SCRUBS_PURCHASED_LLR_DEKU_SCRUB_GROTTO_CENTER,
RAND_INF_SCRUBS_PURCHASED_COLOSSUS_DEKU_SCRUB_GROTTO_REAR,
RAND_INF_SCRUBS_PURCHASED_COLOSSUS_DEKU_SCRUB_GROTTO_FRONT,
RAND_INF_SCRUBS_PURCHASED_LW_DEKU_SCRUB_NEAR_DEKU_THEATER_RIGHT,
RAND_INF_SCRUBS_PURCHASED_LW_DEKU_SCRUB_NEAR_DEKU_THEATER_LEFT,
RAND_INF_SCRUBS_PURCHASED_LW_DEKU_SCRUB_NEAR_BRIDGE,
RAND_INF_SCRUBS_PURCHASED_DMC_DEKU_SCRUB,
// If you add anything to this list, you need to update the size of randomizerInf in z64save.h to be ceil(RAND_INF_MAX / 16)
RAND_INF_MAX,

View File

@ -1562,10 +1562,18 @@ extern "C" void Randomizer_LoadHintLocations(const char* spoilerFileName) {
OTRGlobals::Instance->gRandomizer->LoadHintLocations(spoilerFileName);
}
extern "C" void Randomizer_LoadRequiredTrials(const char* spoilerFileName) {
OTRGlobals::Instance->gRandomizer->LoadRequiredTrials(spoilerFileName);
}
extern "C" void Randomizer_LoadItemLocations(const char* spoilerFileName, bool silent) {
OTRGlobals::Instance->gRandomizer->LoadItemLocations(spoilerFileName, silent);
}
extern "C" bool Randomizer_IsTrialRequired(RandomizerInf trial) {
return OTRGlobals::Instance->gRandomizer->IsTrialRequired(trial);
}
extern "C" bool SpoilerFileExists(const char* spoilerFileName) {
return OTRGlobals::Instance->gRandomizer->SpoilerFileExists(spoilerFileName);
}
@ -1578,18 +1586,12 @@ extern "C" RandomizerCheck Randomizer_GetCheckFromActor(s16 sceneNum, s16 actorI
return OTRGlobals::Instance->gRandomizer->GetCheckFromActor(sceneNum, actorId, actorParams);
}
extern "C" CustomMessageEntry Randomizer_GetScrubMessage(u16 scrubTextId) {
int price = 0;
switch (scrubTextId) {
case TEXT_SCRUB_POH:
price = 10;
break;
case TEXT_SCRUB_STICK_UPGRADE:
case TEXT_SCRUB_NUT_UPGRADE:
price = 40;
break;
}
return CustomMessageManager::Instance->RetrieveMessage(Randomizer::scrubMessageTableID, price);
extern "C" ScrubIdentity Randomizer_IdentifyScrub(s32 sceneNum, s32 actorParams, s32 respawnData) {
return OTRGlobals::Instance->gRandomizer->IdentifyScrub(sceneNum, actorParams, respawnData);
}
extern "C" CustomMessageEntry Randomizer_GetScrubMessage(s16 itemPrice) {
return CustomMessageManager::Instance->RetrieveMessage(Randomizer::merchantMessageTableID, itemPrice);
}
extern "C" CustomMessageEntry Randomizer_GetNaviMessage() {
@ -1650,15 +1652,14 @@ extern "C" GetItemEntry Randomizer_GetItemFromKnownCheck(RandomizerCheck randomi
return ItemTable_RetrieveEntry(getItemModIndex, itemID);
}
extern "C" bool Randomizer_ObtainedFreestandingIceTrap(RandomizerCheck randomizerCheck, GetItemID ogId, Actor* actor) {
return gSaveContext.n64ddFlag && (actor->parent != NULL) &&
Randomizer_GetItemFromKnownCheck(randomizerCheck, ogId).getItemId == RG_ICE_TRAP;
}
extern "C" bool Randomizer_ItemIsIceTrap(RandomizerCheck randomizerCheck, GetItemID ogId) {
return gSaveContext.n64ddFlag && Randomizer_GetItemFromKnownCheck(randomizerCheck, ogId).getItemId == RG_ICE_TRAP;
}
extern "C" bool Randomizer_ObtainedFreestandingIceTrap(RandomizerCheck randomizerCheck, GetItemID ogId, Actor* actor) {
return Randomizer_ItemIsIceTrap(randomizerCheck, ogId) && actor->parent != NULL;
}
extern "C" CustomMessageEntry Randomizer_GetCustomGetItemMessage(Player* player) {
s16 giid;
if (player->getItemEntry.objectId != OBJECT_INVALID) {
@ -1718,8 +1719,8 @@ extern "C" int CustomMessage_RetrieveIfExists(GlobalContext* globalCtx) {
} else {
messageEntry = Randomizer_GetGanonHintText();
}
} else if (textId == TEXT_SCRUB_POH || textId == TEXT_SCRUB_STICK_UPGRADE || textId == TEXT_SCRUB_NUT_UPGRADE) {
messageEntry = Randomizer_GetScrubMessage(textId);
} else if (textId >= 0x9000 && textId <= 0x905F) {
messageEntry = Randomizer_GetScrubMessage((textId & ((1 << 8) - 1)));
} else if (CVar_GetS32("gRandomizeRupeeNames", 0) &&
(textId == TEXT_BLUE_RUPEE || textId == TEXT_RED_RUPEE || textId == TEXT_PURPLE_RUPEE ||
textId == TEXT_HUGE_RUPEE)) {
@ -1727,6 +1728,8 @@ extern "C" int CustomMessage_RetrieveIfExists(GlobalContext* globalCtx) {
// In rando, replace Navi's general overworld hints with rando-related gameplay tips
} else if (CVar_GetS32("gRandoRelevantNavi", 1) && textId >= 0x0140 && textId <= 0x015F) {
messageEntry = Randomizer_GetNaviMessage();
} else if (Randomizer_GetSettingValue(RSK_SHUFFLE_MAGIC_BEANS) && textId == TEXT_BEAN_SALESMAN) {
messageEntry = CustomMessageManager::Instance->RetrieveMessage(Randomizer::merchantMessageTableID, TEXT_BEAN_SALESMAN);
} else if (Randomizer_GetSettingValue(RSK_BOMBCHUS_IN_LOGIC) &&
(textId == TEXT_BUY_BOMBCHU_10_DESC || textId == TEXT_BUY_BOMBCHU_10_PROMPT)) {
messageEntry = CustomMessageManager::Instance->RetrieveMessage(customMessageTableID, textId);

View File

@ -96,8 +96,11 @@ Sprite* GetSeedTexture(uint8_t index);
void Randomizer_LoadSettings(const char* spoilerFileName);
u8 Randomizer_GetSettingValue(RandomizerSettingKey randoSettingKey);
RandomizerCheck Randomizer_GetCheckFromActor(s16 actorId, s16 actorParams, s16 sceneNum);
ScrubIdentity Randomizer_IdentifyScrub(s32 sceneNum, s32 actorParams, s32 respawnData);
void Randomizer_LoadHintLocations(const char* spoilerFileName);
void Randomizer_LoadRequiredTrials(const char* spoilerFileName);
void Randomizer_LoadItemLocations(const char* spoilerFileName, bool silent);
bool Randomizer_IsTrialRequired(RandomizerInf trial);
GetItemEntry Randomizer_GetRandomizedItem(GetItemID ogId, s16 actorId, s16 actorParams, s16 sceneNum);
GetItemEntry Randomizer_GetItemFromKnownCheck(RandomizerCheck randomizerCheck, GetItemID ogId);
bool Randomizer_ObtainedFreestandingIceTrap(RandomizerCheck randomizerCheck, GetItemID ogId, Actor* actor);

View File

@ -1290,6 +1290,7 @@ void EnItem00_CustomItemsParticles(Actor* Parent, GlobalContext* globalCtx, GetI
switch (giEntry.itemId) {
case RG_MAGIC_SINGLE:
case RG_MAGIC_DOUBLE:
case RG_MAGIC_BEAN_PACK:
color_slot = 0;
break;
case RG_DOUBLE_DEFENSE:

View File

@ -1642,12 +1642,16 @@ u8 Item_Give(GlobalContext* globalCtx, u8 item) {
} else if ((item >= ITEM_SWORD_KOKIRI) && (item <= ITEM_SWORD_BGS)) {
gSaveContext.inventory.equipment |= gBitFlags[item - ITEM_SWORD_KOKIRI] << gEquipShifts[EQUIP_SWORD];
// Both Giant's Knife and Biggoron Sword have the same Item ID, so this part handles both of them
if (item == ITEM_SWORD_BGS) {
gSaveContext.swordHealth = 8;
if (ALL_EQUIP_VALUE(EQUIP_SWORD) == 0xF
||(gSaveContext.n64ddFlag && ALL_EQUIP_VALUE(EQUIP_SWORD) == 0xE)) { // In rando, when buying Giant's Knife, also check
gSaveContext.inventory.equipment ^= 8 << gEquipShifts[EQUIP_SWORD]; // for 0xE in case we don't have Kokiri Sword
// In rando, when buying Giant's Knife, also check
// for 0xE in case we don't have Kokiri Sword
if (ALL_EQUIP_VALUE(EQUIP_SWORD) == 0xF || (gSaveContext.n64ddFlag && ALL_EQUIP_VALUE(EQUIP_SWORD) == 0xE)) {
gSaveContext.inventory.equipment ^= 8 << gEquipShifts[EQUIP_SWORD];
if (gSaveContext.equips.buttonItems[0] == ITEM_SWORD_KNIFE) {
gSaveContext.equips.buttonItems[0] = ITEM_SWORD_BGS;
Interface_LoadItemIcon1(globalCtx, 0);
@ -1686,6 +1690,28 @@ u8 Item_Give(GlobalContext* globalCtx, u8 item) {
}
return ITEM_NONE;
} else if (item == ITEM_KEY_SMALL) {
// Small key exceptions for rando with keysanity off.
if (gSaveContext.n64ddFlag) {
if (globalCtx->sceneNum == 10) { // ganon's tower -> ganon's castle
if (gSaveContext.inventory.dungeonKeys[13] < 0) {
gSaveContext.inventory.dungeonKeys[13] = 1;
return ITEM_NONE;
} else {
gSaveContext.inventory.dungeonKeys[13]++;
return ITEM_NONE;
}
}
if (globalCtx->sceneNum == 92) { // Desert Colossus -> Spirit Temple.
if (gSaveContext.inventory.dungeonKeys[6] < 0) {
gSaveContext.inventory.dungeonKeys[6] = 1;
return ITEM_NONE;
} else {
gSaveContext.inventory.dungeonKeys[6]++;
return ITEM_NONE;
}
}
}
if (gSaveContext.inventory.dungeonKeys[gSaveContext.mapIndex] < 0) {
gSaveContext.inventory.dungeonKeys[gSaveContext.mapIndex] = 1;
return ITEM_NONE;
@ -2177,6 +2203,14 @@ u16 Randomizer_Item_Give(GlobalContext* globalCtx, GetItemEntry giEntry) {
return RG_NONE;
}
if (item == RG_MAGIC_BEAN_PACK) {
if (INV_CONTENT(ITEM_BEAN) == ITEM_NONE) {
INV_CONTENT(ITEM_BEAN) = ITEM_BEAN;
AMMO(ITEM_BEAN) = 10;
}
return RG_NONE;
}
if (item == RG_DOUBLE_DEFENSE) {
gSaveContext.doubleDefense = true;
gSaveContext.inventory.defenseHearts = 20;
@ -2223,9 +2257,9 @@ u16 Randomizer_Item_Give(GlobalContext* globalCtx, GetItemEntry giEntry) {
}
}
} else if ((item >= RG_FOREST_TEMPLE_SMALL_KEY && item <= RG_GANONS_CASTLE_SMALL_KEY) ||
(item >= RG_FOREST_TEMPLE_BOSS_KEY && item <= RG_GANONS_CASTLE_BOSS_KEY) ||
(item >= RG_DEKU_TREE_MAP && item <= RG_ICE_CAVERN_MAP) ||
(item >= RG_DEKU_TREE_COMPASS && item <= RG_ICE_CAVERN_COMPASS)) {
(item >= RG_FOREST_TEMPLE_BOSS_KEY && item <= RG_GANONS_CASTLE_BOSS_KEY) ||
(item >= RG_DEKU_TREE_MAP && item <= RG_ICE_CAVERN_MAP) ||
(item >= RG_DEKU_TREE_COMPASS && item <= RG_ICE_CAVERN_COMPASS)) {
int mapIndex = gSaveContext.mapIndex;
switch (item) {
case RG_DEKU_TREE_MAP:
@ -2323,7 +2357,6 @@ u16 Randomizer_Item_Give(GlobalContext* globalCtx, GetItemEntry giEntry) {
return temp;
}
u8 Item_CheckObtainability(u8 item) {
s16 i;
s16 slot = SLOT(item);

View File

@ -255,10 +255,10 @@ void GivePlayerRandoRewardSariaGift(GlobalContext* globalCtx, RandomizerCheck ch
if (gSaveContext.entranceIndex == 0x05E0) {
GetItemEntry getItemEntry = Randomizer_GetItemFromKnownCheck(check, RG_ZELDAS_LULLABY);
if ((!Flags_GetEventChkInf(0xC1) || (player->getItemId == getItemEntry.getItemId && getItemEntry.getItemId != GI_ICE_TRAP)) &&
player != NULL && !Player_InBlockingCsMode(globalCtx, player)) {
if (!Flags_GetEventChkInf(0xC1) && player != NULL && !Player_InBlockingCsMode(globalCtx, player)) {
GiveItemEntryWithoutActor(globalCtx, getItemEntry);
Flags_SetEventChkInf(0xC1);
player->pendingFlag.flagType = FLAG_EVENT_CHECK_INF;
player->pendingFlag.flagID = 0xC1;
}
}
}

View File

@ -4,10 +4,11 @@
#include <string.h>
#include <soh/Enhancements/randomizer/randomizerTypes.h>
#include <soh/Enhancements/randomizer/randomizer_inf.h>
#include "soh/Enhancements/randomizer/adult_trade_shuffle.h"
#define NUM_DUNGEONS 8
#define NUM_TRIALS 6
#define NUM_COWS 10
#define NUM_SCRUBS 35
/**
* Initialize new save.
@ -308,6 +309,11 @@ void GiveLinkDekuNutUpgrade(GetItemID giid) {
}
}
void GiveLinkSkullToken() {
gSaveContext.inventory.questItems |= gBitFlags[QUEST_SKULL_TOKEN];
gSaveContext.inventory.gsTokens++;
}
void GiveLinkMagic(GetItemID giid) {
if (giid == RG_MAGIC_SINGLE) {
gSaveContext.magicLevel = 1;
@ -507,6 +513,53 @@ void GiveLinkDungeonItem(GetItemID getItemId) {
}
}
void GiveLinkAdultTradeItem(GetItemID giid) {
ItemID item;
switch (giid) {
case GI_POCKET_EGG:
item = ITEM_POCKET_EGG;
break;
case GI_POCKET_CUCCO:
item = ITEM_POCKET_CUCCO;
break;
case GI_COJIRO:
item = ITEM_COJIRO;
break;
case GI_ODD_MUSHROOM:
item = ITEM_ODD_MUSHROOM;
break;
case GI_ODD_POTION:
item = ITEM_ODD_POTION;
break;
case GI_SAW:
item = ITEM_SAW;
break;
case GI_SWORD_BROKEN:
item = ITEM_SWORD_BROKEN;
break;
case GI_PRESCRIPTION:
item = ITEM_PRESCRIPTION;
break;
case GI_FROG:
item = ITEM_FROG;
break;
case GI_EYEDROPS:
item = ITEM_EYEDROPS;
break;
case GI_CLAIM_CHECK:
item = ITEM_CLAIM_CHECK;
break;
}
if ((item == ITEM_SAW) && CVar_GetS32("gDekuNutUpgradeFix", 0) == 0) {
gSaveContext.itemGetInf[1] |= 0x8000;
}
if (item >= ITEM_POCKET_EGG) {
gSaveContext.adultTradeItems |= ADULT_TRADE_FLAG(item);
}
INV_CONTENT(ITEM_TRADE_ADULT) = item;
}
void GiveLinksPocketMedallion() {
GetItemEntry getItemEntry = Randomizer_GetItemFromKnownCheck(RC_LINKS_POCKET, RG_NONE);
@ -708,6 +761,15 @@ void Sram_InitSave(FileChooseContext* fileChooseCtx) {
gSaveContext.randomizerInf[i] = 0;
}
// Set all trials to cleared if trial count is random or anything other than 6
if (Randomizer_GetSettingValue(RSK_RANDOM_TRIALS) || (Randomizer_GetSettingValue(RSK_TRIAL_COUNT) != 6)) {
for (u16 i = RAND_INF_TRIALS_DONE_LIGHT_TRIAL; i <= RAND_INF_TRIALS_DONE_SHADOW_TRIAL; i++) {
if (!Randomizer_IsTrialRequired(i)) {
Flags_SetRandomizerInf(i);
}
}
}
// Set Cutscene flags to skip them
gSaveContext.eventChkInf[0xC] |= 0x10; // returned to tot with medallions
gSaveContext.eventChkInf[0xC] |= 0x20; //sheik at tot pedestal
@ -734,6 +796,11 @@ void Sram_InitSave(FileChooseContext* fileChooseCtx) {
gSaveContext.eventChkInf[3] |= 0x800;
gSaveContext.eventChkInf[12] |= 1;
// shuffle adult trade quest
if (Randomizer_GetSettingValue(RSK_SHUFFLE_ADULT_TRADE)) {
gSaveContext.adultTradeItems = 0;
}
// Give Link's pocket item
GiveLinksPocketMedallion();
@ -791,7 +858,7 @@ void Sram_InitSave(FileChooseContext* fileChooseCtx) {
s32 giid = getItem.getItemId;
if (getItem.modIndex == MOD_NONE) {
if(getItem.itemId >= ITEM_KOKIRI_EMERALD && getItem.itemId <= ITEM_MEDALLION_LIGHT) {
if (getItem.itemId >= ITEM_MEDALLION_FOREST && getItem.itemId <= ITEM_ZORA_SAPPHIRE) {
GiveLinkDungeonReward(getItem.getItemId);
} else if (giid == GI_RUPEE_GREEN || giid == GI_RUPEE_BLUE || giid == GI_RUPEE_RED ||
giid == GI_RUPEE_PURPLE || giid == GI_RUPEE_GOLD) {
@ -848,6 +915,10 @@ void Sram_InitSave(FileChooseContext* fileChooseCtx) {
GiveLinkDekuStickUpgrade(giid);
} else if (giid == GI_NUT_UPGRADE_30 || giid == GI_NUT_UPGRADE_40) {
GiveLinkDekuNutUpgrade(giid);
} else if (giid == GI_SKULL_TOKEN) {
GiveLinkSkullToken();
} else if (giid >= GI_POCKET_EGG && giid <= GI_CLAIM_CHECK || giid == GI_COJIRO) {
GiveLinkAdultTradeItem(giid);
} else {
s32 iid = getItem.itemId;
if (iid != -1) INV_CONTENT(iid) = iid;
@ -964,11 +1035,6 @@ void Sram_InitSave(FileChooseContext* fileChooseCtx) {
}
}
// shuffle adult trade quest
if (Randomizer_GetSettingValue(RSK_SHUFFLE_ADULT_TRADE)) {
gSaveContext.adultTradeItems = 0;
}
// complete mask quest
if (Randomizer_GetSettingValue(RSK_COMPLETE_MASK_QUEST)) {
gSaveContext.itemGetInf[3] |= 0x100; // Sold Keaton Mask

View File

@ -64,12 +64,33 @@ static u8 sEnergyColors[] = {
/* Forest prim */ 255, 255, 170, /* env */ 0, 200, 0,
};
// Translates from the barrier's actor params to their corresponding randInf flags.
RandomizerInf trialParamToRandInf(u16 params) {
switch (params) {
case KEKKAI_LIGHT:
return RAND_INF_TRIALS_DONE_LIGHT_TRIAL;
case KEKKAI_FOREST:
return RAND_INF_TRIALS_DONE_FOREST_TRIAL;
case KEKKAI_FIRE:
return RAND_INF_TRIALS_DONE_FIRE_TRIAL;
case KEKKAI_WATER:
return RAND_INF_TRIALS_DONE_WATER_TRIAL;
case KEKKAI_SPIRIT:
return RAND_INF_TRIALS_DONE_SPIRIT_TRIAL;
case KEKKAI_SHADOW:
return RAND_INF_TRIALS_DONE_SHADOW_TRIAL;
}
}
s32 DemoKekkai_CheckEventFlag(s32 params) {
static s32 eventFlags[] = { 0xC3, 0xBC, 0xBF, 0xBE, 0xBD, 0xAD, 0xBB };
if ((params < KEKKAI_TOWER) || (params > KEKKAI_FOREST)) {
return true;
}
if (gSaveContext.n64ddFlag) {
return Flags_GetRandomizerInf(trialParamToRandInf(params));
}
return Flags_GetEventChkInf(eventFlags[params]);
}
@ -128,8 +149,7 @@ void DemoKekkai_Init(Actor* thisx, GlobalContext* globalCtx) {
this->collider2.dim.yShift = 300;
if (gSaveContext.n64ddFlag) {
int trialsToComplete = Randomizer_GetSettingValue(RSK_TRIAL_COUNT);
if (trialsToComplete <= TrialsDoneCount()) {
if (TrialsDoneCount() == NUM_TRIALS) {
Actor_Kill(thisx);
return;
}
@ -141,6 +161,10 @@ void DemoKekkai_Init(Actor* thisx, GlobalContext* globalCtx) {
case KEKKAI_SHADOW:
case KEKKAI_SPIRIT:
case KEKKAI_FOREST:
if (gSaveContext.n64ddFlag && Flags_GetRandomizerInf(trialParamToRandInf(thisx->params))) {
Actor_Kill(thisx);
return;
}
this->energyAlpha = 1.0f;
this->orbScale = 1.0f;
Actor_SetScale(thisx, 0.1f);
@ -247,27 +271,10 @@ void DemoKekkai_TrialBarrierDispel(Actor* thisx, GlobalContext* globalCtx) {
DemoKekkai* this = (DemoKekkai*)thisx;
if (gSaveContext.n64ddFlag) {
switch (thisx->params) {
case KEKKAI_WATER:
Flags_SetRandomizerInf(RAND_INF_TRIALS_DONE_WATER_TRIAL);
break;
case KEKKAI_LIGHT:
Flags_SetRandomizerInf(RAND_INF_TRIALS_DONE_LIGHT_TRIAL);
break;
case KEKKAI_FIRE:
Flags_SetRandomizerInf(RAND_INF_TRIALS_DONE_FIRE_TRIAL);
break;
case KEKKAI_SHADOW:
Flags_SetRandomizerInf(RAND_INF_TRIALS_DONE_SHADOW_TRIAL);
break;
case KEKKAI_SPIRIT:
Flags_SetRandomizerInf(RAND_INF_TRIALS_DONE_SPIRIT_TRIAL);
break;
case KEKKAI_FOREST:
Flags_SetRandomizerInf(RAND_INF_TRIALS_DONE_FOREST_TRIAL);
break;
}
Flags_SetEventChkInf(eventFlags[thisx->params]);
Flags_SetRandomizerInf(trialParamToRandInf(thisx->params));
// May or may not be needed. Not sure if needed for anything
// that randoInf isn't already covering. Leaving it for safety.
Flags_SetEventChkInf(eventFlags[thisx->params]);
}
if (globalCtx->csCtx.frames == csFrames[this->actor.params]) {

View File

@ -15,6 +15,7 @@ void EnDns_Destroy(Actor* thisx, GlobalContext* globalCtx);
void EnDns_Update(Actor* thisx, GlobalContext* globalCtx);
void EnDns_Draw(Actor* thisx, GlobalContext* globalCtx);
u32 EnDns_RandomizerPurchaseableCheck(EnDns* this);
u32 func_809EF5A4(EnDns* this);
u32 func_809EF658(EnDns* this);
u32 func_809EF70C(EnDns* this);
@ -24,6 +25,7 @@ u32 func_809EF854(EnDns* this);
u32 func_809EF8F4(EnDns* this);
u32 func_809EF9A4(EnDns* this);
void EnDns_RandomizerPurchase(EnDns* this);
void func_809EF9F8(EnDns* this);
void func_809EFA28(EnDns* this);
void func_809EFA58(EnDns* this);
@ -155,7 +157,6 @@ void EnDns_Init(Actor* thisx, GlobalContext* globalCtx) {
Collider_InitCylinder(globalCtx, &this->collider);
Collider_SetCylinderType1(globalCtx, &this->collider, &this->actor, &sCylinderInit);
ActorShape_Init(&this->actor.shape, 0.0f, ActorShadow_DrawCircle, 35.0f);
this->actor.textId = D_809F040C[this->actor.params];
Actor_SetScale(&this->actor, 0.01f);
this->actor.colChkInfo.mass = MASS_IMMOVABLE;
this->maintainCollider = 1;
@ -164,7 +165,27 @@ void EnDns_Init(Actor* thisx, GlobalContext* globalCtx) {
this->actor.speedXZ = 0.0f;
this->actor.velocity.y = 0.0f;
this->actor.gravity = -1.0f;
this->actor.textId = D_809F040C[this->actor.params];
this->dnsItemEntry = sItemEntries[this->actor.params];
if (gSaveContext.n64ddFlag) {
// Ugly, but the best way we can identify which grotto we are in, same method 3DRando uses, but we'll need to account for entrance rando
s16 respawnData = gSaveContext.respawn[RESPAWN_MODE_RETURN].data & ((1 << 8) - 1);
this->scrubIdentity = Randomizer_IdentifyScrub(globalCtx->sceneNum, this->actor.params, respawnData);
if (Randomizer_GetSettingValue(RSK_SHUFFLE_SCRUBS) == 1 || Randomizer_GetSettingValue(RSK_SHUFFLE_SCRUBS) == 3 && this->scrubIdentity.itemPrice != -1) {
this->dnsItemEntry->itemPrice = this->scrubIdentity.itemPrice;
}
if (this->scrubIdentity.isShuffled) {
this->dnsItemEntry->getItemId = this->scrubIdentity.getItemId;
this->dnsItemEntry->purchaseableCheck = EnDns_RandomizerPurchaseableCheck;
this->dnsItemEntry->setRupeesAndFlags = EnDns_RandomizerPurchase;
this->dnsItemEntry->itemAmount = 1;
// Currently the textID is simply identified by the item price since that is the only thing
// unique to it, later on this will change to identifying by scrubIdentity.randomizerInf
this->actor.textId = 0x9000 + this->dnsItemEntry->itemPrice;
}
}
this->actionFunc = EnDns_SetupWait;
}
@ -185,6 +206,13 @@ void EnDns_ChangeAnim(EnDns* this, u8 index) {
/* Item give checking functions */
u32 EnDns_RandomizerPurchaseableCheck(EnDns* this) {
if (gSaveContext.rupees < this->dnsItemEntry->itemPrice || Flags_GetRandomizerInf(this->scrubIdentity.randomizerInf)) {
return 0;
}
return 4;
}
u32 func_809EF5A4(EnDns* this) {
if ((CUR_CAPACITY(UPG_NUTS) != 0) && (AMMO(ITEM_NUT) >= CUR_CAPACITY(UPG_NUTS))) {
return 1;
@ -281,6 +309,10 @@ u32 func_809EF9A4(EnDns* this) {
}
/* Paying and flagging functions */
void EnDns_RandomizerPurchase(EnDns* this) {
Rupees_ChangeBy(-this->dnsItemEntry->itemPrice);
Flags_SetRandomizerInf(this->scrubIdentity.randomizerInf);
}
void func_809EF9F8(EnDns* this) {
Rupees_ChangeBy(-this->dnsItemEntry->itemPrice);
@ -369,31 +401,25 @@ void EnDns_Talk(EnDns* this, GlobalContext* globalCtx) {
}
void func_809EFDD0(EnDns* this, GlobalContext* globalCtx) {
if (this->actor.params == 0x9) {
if (gSaveContext.n64ddFlag) {
GetItemEntry getItemEntry = Randomizer_GetRandomizedItem(GI_STICK_UPGRADE_30, this->actor.id, this->actor.params, globalCtx->sceneNum);
GiveItemEntryFromActor(&this->actor, globalCtx, getItemEntry, 130.0f, 100.0f);
} else if (CUR_UPG_VALUE(UPG_STICKS) < 2) {
func_8002F434(&this->actor, globalCtx, GI_STICK_UPGRADE_20, 130.0f, 100.0f);
if (!gSaveContext.n64ddFlag || !this->scrubIdentity.isShuffled) {
if (this->actor.params == 0x9) {
if (CUR_UPG_VALUE(UPG_STICKS) < 2) {
func_8002F434(&this->actor, globalCtx, GI_STICK_UPGRADE_20, 130.0f, 100.0f);
} else {
func_8002F434(&this->actor, globalCtx, GI_STICK_UPGRADE_30, 130.0f, 100.0f);
}
} else if (this->actor.params == 0xA) {
if (CUR_UPG_VALUE(UPG_NUTS) < 2) {
func_8002F434(&this->actor, globalCtx, GI_NUT_UPGRADE_30, 130.0f, 100.0f);
} else {
func_8002F434(&this->actor, globalCtx, GI_NUT_UPGRADE_40, 130.0f, 100.0f);
}
} else {
func_8002F434(&this->actor, globalCtx, GI_STICK_UPGRADE_30, 130.0f, 100.0f);
}
} else if (this->actor.params == 0xA) {
if (gSaveContext.n64ddFlag) {
GetItemEntry getItemEntry = Randomizer_GetRandomizedItem(GI_NUT_UPGRADE_40, this->actor.id, this->actor.params, globalCtx->sceneNum);
GiveItemEntryFromActor(&this->actor, globalCtx, getItemEntry, 130.0f, 100.0f);
} else if (CUR_UPG_VALUE(UPG_NUTS) < 2) {
func_8002F434(&this->actor, globalCtx, GI_NUT_UPGRADE_30, 130.0f, 100.0f);
} else {
func_8002F434(&this->actor, globalCtx, GI_NUT_UPGRADE_40, 130.0f, 100.0f);
func_8002F434(&this->actor, globalCtx, this->dnsItemEntry->getItemId, 130.0f, 100.0f);
}
} else {
if (!gSaveContext.n64ddFlag) {
func_8002F434(&this->actor, globalCtx, this->dnsItemEntry->getItemId, 130.0f, 100.0f);
} else {
GetItemEntry getItemEntry = Randomizer_GetRandomizedItem(this->dnsItemEntry->getItemId, this->actor.id, this->actor.params, globalCtx->sceneNum);
GiveItemEntryFromActor(&this->actor, globalCtx, getItemEntry, 130.0f, 100.0f);
}
GetItemEntry itemEntry = Randomizer_GetItemFromKnownCheck(this->scrubIdentity.randomizerCheck, this->scrubIdentity.getItemId);
GiveItemEntryFromActor(&this->actor, globalCtx, itemEntry, 130.0f, 100.0f);
}
}
@ -489,6 +515,11 @@ void EnDns_Update(Actor* thisx, GlobalContext* globalCtx) {
this->dustTimer++;
this->actor.textId = D_809F040C[this->actor.params];
if (gSaveContext.n64ddFlag && this->scrubIdentity.isShuffled) {
// Currently the textID is simply identified by the item price since that is the only thing
// unique to it, later on this will change to identifying by scrubIdentity.randomizerInf
this->actor.textId = 0x9000 + this->dnsItemEntry->itemPrice;
}
Actor_SetFocus(&this->actor, 60.0f);
Actor_SetScale(&this->actor, 0.01f);
SkelAnime_Update(&this->skelAnime);

View File

@ -32,6 +32,7 @@ typedef struct EnDns {
/* 0x02BD */ u8 dropCollectible;
/* 0x02C0 */ DnsItemEntry* dnsItemEntry;
/* 0x02C4 */ f32 yInitPos;
/* */ ScrubIdentity scrubIdentity;
} EnDns; // size = 0x02C8
#endif

View File

@ -90,7 +90,16 @@ static void* sEyeTextures[] = {
gMalonChildEyeClosedTex,
};
bool Randomizer_ObtainedMalonHCReward() {
return Flags_GetEventChkInf(0x12);
}
u16 EnMa1_GetText(GlobalContext* globalCtx, Actor* thisx) {
// Special case for Malon Hyrule Castle Text. Placing it here at the beginning
// has the added benefit of circumventing mask text if wearing bunny hood.
if (gSaveContext.n64ddFlag && globalCtx->sceneNum == SCENE_SPOT15) {
return Randomizer_ObtainedMalonHCReward() ? 0x2044 : 0x2043;
}
u16 faceReaction = Text_GetFaceReaction(globalCtx, 0x17);
if (faceReaction != 0) {
@ -191,25 +200,34 @@ s32 func_80AA08C4(EnMa1* this, GlobalContext* globalCtx) {
if (!LINK_IS_CHILD) {
return 0;
}
// Causes Malon to appear in the market if you haven't met her yet.
if (((globalCtx->sceneNum == SCENE_MARKET_NIGHT) || (globalCtx->sceneNum == SCENE_MARKET_DAY)) &&
!(gSaveContext.eventChkInf[1] & 0x10) && !(gSaveContext.infTable[8] & 0x800)) {
return 1;
}
if ((globalCtx->sceneNum == SCENE_SPOT15) && !(gSaveContext.eventChkInf[1] & 0x10)) {
if (gSaveContext.infTable[8] & 0x800) {
return 1;
} else {
gSaveContext.infTable[8] |= 0x800;
return 0;
if ((globalCtx->sceneNum == SCENE_SPOT15) && // if we're at hyrule castle
(!(gSaveContext.eventChkInf[1] & 0x10) || // and talon hasn't left
(gSaveContext.n64ddFlag &&
!Randomizer_ObtainedMalonHCReward()))) { // or we're rando'd and haven't gotten malon's HC check
if (gSaveContext.infTable[8] & 0x800) { // if we've met malon
return 1; // make her appear at the castle
} else { // if we haven't met malon
gSaveContext.infTable[8] |= 0x800; // set the flag for meeting malon
return 0; // don't make her appear at the castle
}
}
// Malon asleep in her bed if Talon has left Hyrule Castle and it is nighttime.
if ((globalCtx->sceneNum == SCENE_SOUKO) && IS_NIGHT && (gSaveContext.eventChkInf[1] & 0x10)) {
return 1;
}
// Don't spawn Malon if none of the above are true and we are not in Lon Lon Ranch.
if (globalCtx->sceneNum != SCENE_SPOT20) {
return 0;
}
if ((this->actor.shape.rot.z == 3) && IS_DAY && (gSaveContext.eventChkInf[1] & 0x10)) {
// If we've gotten this far, we're in Lon Lon Ranch. Spawn Malon if it is daytime, Talon has left Hyrule Castle, and
// either we are not randomized, or we are and we have received Malon's item at Hyrule Castle.
if ((this->actor.shape.rot.z == 3) && IS_DAY && (gSaveContext.eventChkInf[1] & 0x10) &&
((gSaveContext.n64ddFlag && Randomizer_ObtainedMalonHCReward()) || !gSaveContext.n64ddFlag)) {
return 1;
}
return 0;
@ -290,10 +308,16 @@ void EnMa1_Init(Actor* thisx, GlobalContext* globalCtx) {
this->actor.targetMode = 6;
this->unk_1E8.unk_00 = 0;
if (!(gSaveContext.eventChkInf[1] & 0x10) || (CHECK_QUEST_ITEM(QUEST_SONG_EPONA) && !gSaveContext.n64ddFlag) ||
// To avoid missing a check, we want Malon to have the actionFunc for singing, but not reacting to Ocarina, if any of
// the following are true.
// 1. Talon has not left Hyrule Castle.
// 2. We are Randomized and have not obtained Malon's Weird Egg Check.
// 3. We are not Randomized and have obtained Epona's Song
if (!(gSaveContext.eventChkInf[1] & 0x10) || (gSaveContext.n64ddFlag && !Randomizer_ObtainedMalonHCReward()) || (CHECK_QUEST_ITEM(QUEST_SONG_EPONA) && !gSaveContext.n64ddFlag) ||
(gSaveContext.n64ddFlag && Flags_GetTreasure(globalCtx, 0x1F))) {
this->actionFunc = func_80AA0D88;
EnMa1_ChangeAnim(this, ENMA1_ANIM_2);
// If none of the above conditions were true, set Malon up to teach Epona's Song.
} else {
if (gSaveContext.n64ddFlag) { // Skip straight to "let's sing it together" textbox in the ranch
gSaveContext.eventChkInf[1] |= 0x40;
@ -322,9 +346,16 @@ void func_80AA0D88(EnMa1* this, GlobalContext* globalCtx) {
}
}
if ((globalCtx->sceneNum == SCENE_SPOT15) && (gSaveContext.eventChkInf[1] & 0x10)) {
// We want to Kill Malon's Actor outside of randomizer when Talon is freed. In Randomizer we don't kill Malon's
// Actor here, otherwise if we wake up Talon first and then get her check she will spontaneously
// disappear.
if ((globalCtx->sceneNum == SCENE_SPOT15) && (!gSaveContext.n64ddFlag && gSaveContext.eventChkInf[1] & 0x10)) {
Actor_Kill(&this->actor);
} else if (!(gSaveContext.eventChkInf[1] & 0x10) || (CHECK_QUEST_ITEM(QUEST_SONG_EPONA) && !gSaveContext.n64ddFlag)) {
// We want Malon to give the Weird Egg Check (see function below) in the following situations:
// 1. Talon as not left Hyrule Castle (Vanilla) OR
// 2. We haven't obtained Malon's Weird Egg Check (Randomizer only) OR
// 3. We have Epona's Song? (Vanilla only, not sure why it's here but I didn't write that one)
} else if ((!(gSaveContext.eventChkInf[1] & 0x10) || (gSaveContext.n64ddFlag && !Randomizer_ObtainedMalonHCReward())) || (CHECK_QUEST_ITEM(QUEST_SONG_EPONA) && !gSaveContext.n64ddFlag)) {
if (this->unk_1E8.unk_00 == 2) {
this->actionFunc = func_80AA0EA0;
globalCtx->msgCtx.stateTimer = 4;

View File

@ -125,11 +125,19 @@ void EnMs_Talk(EnMs* this, GlobalContext* globalCtx) {
} else if (Message_ShouldAdvance(globalCtx)) {
switch (globalCtx->msgCtx.choiceIndex) {
case 0: // yes
if (gSaveContext.rupees < sPrices[BEANS_BOUGHT]) {
if (gSaveContext.rupees <
((gSaveContext.n64ddFlag && Randomizer_GetSettingValue(RSK_SHUFFLE_MAGIC_BEANS))
? 60
: sPrices[BEANS_BOUGHT])) {
Message_ContinueTextbox(globalCtx, 0x4069); // not enough rupees text
return;
}
func_8002F434(&this->actor, globalCtx, GI_BEAN, 90.0f, 10.0f);
if (gSaveContext.n64ddFlag && Randomizer_GetSettingValue(RSK_SHUFFLE_MAGIC_BEANS)) {
GiveItemEntryFromActor(&this->actor, globalCtx,
Randomizer_GetItemFromKnownCheck(RC_ZR_MAGIC_BEAN_SALESMAN, GI_BEAN), 90.0f, 10.0f);
} else {
func_8002F434(&this->actor, globalCtx, GI_BEAN, 90.0f, 10.0f);
}
this->actionFunc = EnMs_Sell;
return;
case 1: // no
@ -142,11 +150,18 @@ void EnMs_Talk(EnMs* this, GlobalContext* globalCtx) {
void EnMs_Sell(EnMs* this, GlobalContext* globalCtx) {
if (Actor_HasParent(&this->actor, globalCtx)) {
Rupees_ChangeBy(-sPrices[BEANS_BOUGHT]);
Rupees_ChangeBy((gSaveContext.n64ddFlag && Randomizer_GetSettingValue(RSK_SHUFFLE_MAGIC_BEANS)) ? -60 : -sPrices[BEANS_BOUGHT]);
this->actor.parent = NULL;
this->actionFunc = EnMs_TalkAfterPurchase;
this->actionFunc =
(gSaveContext.n64ddFlag && Randomizer_GetSettingValue(RSK_SHUFFLE_MAGIC_BEANS)) ? EnMs_Wait : EnMs_TalkAfterPurchase;
} else {
func_8002F434(&this->actor, globalCtx, GI_BEAN, 90.0f, 10.0f);
if (gSaveContext.n64ddFlag && Randomizer_GetSettingValue(RSK_SHUFFLE_MAGIC_BEANS)) {
GiveItemEntryFromActor(&this->actor, globalCtx,
Randomizer_GetItemFromKnownCheck(RC_ZR_MAGIC_BEAN_SALESMAN, GI_BEAN), 90.0f, 10.0f);
BEANS_BOUGHT = 10;
} else {
func_8002F434(&this->actor, globalCtx, GI_BEAN, 90.0f, 10.0f);
}
}
}

View File

@ -69,6 +69,15 @@ void EnShopnuts_Init(Actor* thisx, GlobalContext* globalCtx) {
CollisionCheck_SetInfo(&this->actor.colChkInfo, NULL, &sColChkInfoInit);
Collider_UpdateCylinder(&this->actor, &this->collider);
if (gSaveContext.n64ddFlag) {
s16 respawnData = gSaveContext.respawn[RESPAWN_MODE_RETURN].data & ((1 << 8) - 1);
ScrubIdentity scrubIdentity = Randomizer_IdentifyScrub(globalCtx->sceneNum, this->actor.params, respawnData);
if (scrubIdentity.isShuffled && Flags_GetRandomizerInf(scrubIdentity.randomizerInf)) {
Actor_Kill(&this->actor);
}
}
if (((this->actor.params == 0x0002) && (gSaveContext.itemGetInf[0] & 0x800)) ||
((this->actor.params == 0x0009) && (gSaveContext.infTable[25] & 4)) ||
((this->actor.params == 0x000A) && (gSaveContext.infTable[25] & 8))) {

View File

@ -17,7 +17,8 @@ s32 func_80AFB748(EnSi* this, GlobalContext* globalCtx);
void func_80AFB768(EnSi* this, GlobalContext* globalCtx);
void func_80AFB89C(EnSi* this, GlobalContext* globalCtx);
void func_80AFB950(EnSi* this, GlobalContext* globalCtx);
void Randomizer_GrantSkullReward(EnSi* this, GlobalContext* globalCtx);
void Randomizer_UpdateSkullReward(EnSi* this, GlobalContext* globalCtx);
void Randomizer_GiveSkullReward(EnSi* this, GlobalContext* globalCtx);
s32 textId = 0xB4;
s32 giveItemId = ITEM_SKULL_TOKEN;
@ -99,21 +100,25 @@ void func_80AFB768(EnSi* this, GlobalContext* globalCtx) {
if (this->collider.base.ocFlags2 & OC2_HIT_PLAYER) {
this->collider.base.ocFlags2 &= ~OC2_HIT_PLAYER;
if (gSaveContext.n64ddFlag) {
Randomizer_GrantSkullReward(this, globalCtx);
Randomizer_UpdateSkullReward(this, globalCtx);
} else {
Item_Give(globalCtx, giveItemId);
}
if ((CVar_GetS32("gSkulltulaFreeze", 0) != 1 || giveItemId != ITEM_SKULL_TOKEN) && getItemId != RG_ICE_TRAP) {
player->actor.freezeTimer = 20;
}
Message_StartTextbox(globalCtx, textId, NULL);
if (gSaveContext.n64ddFlag && getItemId != RG_ICE_TRAP) {
Randomizer_GiveSkullReward(this, globalCtx);
Audio_PlayFanfare_Rando(getItem);
} else {
Audio_PlayFanfare(NA_BGM_SMALL_ITEM_GET);
}
player->getItemEntry = (GetItemEntry)GET_ITEM_NONE;
this->actionFunc = func_80AFB950;
} else {
@ -133,16 +138,20 @@ void func_80AFB89C(EnSi* this, GlobalContext* globalCtx) {
if (!CHECK_FLAG_ALL(this->actor.flags, ACTOR_FLAG_13)) {
if (gSaveContext.n64ddFlag) {
Randomizer_GrantSkullReward(this, globalCtx);
Randomizer_UpdateSkullReward(this, globalCtx);
} else {
Item_Give(globalCtx, giveItemId);
}
Message_StartTextbox(globalCtx, textId, NULL);
if (gSaveContext.n64ddFlag && getItemId != RG_ICE_TRAP) {
Randomizer_GiveSkullReward(this, globalCtx);
Audio_PlayFanfare_Rando(getItem);
} else {
Audio_PlayFanfare(NA_BGM_SMALL_ITEM_GET);
}
player->getItemEntry = (GetItemEntry)GET_ITEM_NONE;
this->actionFunc = func_80AFB950;
}
@ -190,7 +199,7 @@ void EnSi_Draw(Actor* thisx, GlobalContext* globalCtx) {
}
}
void Randomizer_GrantSkullReward(EnSi* this, GlobalContext* globalCtx) {
void Randomizer_UpdateSkullReward(EnSi* this, GlobalContext* globalCtx) {
Player* player = GET_PLAYER(globalCtx);
getItem = Randomizer_GetRandomizedItem(GI_SKULL_TOKEN, this->actor.id, this->actor.params, globalCtx->sceneNum);
@ -201,12 +210,20 @@ void Randomizer_GrantSkullReward(EnSi* this, GlobalContext* globalCtx) {
} else {
textId = getItem.textId;
giveItemId = getItem.itemId;
if (getItem.modIndex == MOD_NONE) {
Item_Give(globalCtx, giveItemId);
} else if (getItem.modIndex == MOD_RANDOMIZER) {
Randomizer_Item_Give(globalCtx, getItem);
}
}
// player->getItemId = getItemId;
player->getItemEntry = getItem;
}
void Randomizer_GiveSkullReward(EnSi* this, GlobalContext* globalCtx) {
Player* player = GET_PLAYER(globalCtx);
if (getItem.modIndex == MOD_NONE) {
Item_Give(globalCtx, giveItemId);
} else if (getItem.modIndex == MOD_RANDOMIZER) {
Randomizer_Item_Give(globalCtx, getItem);
}
// RANDOTOD: Move this into Item_Give() or some other more central location
if (getItem.getItemId == GI_SWORD_BGS) {
gSaveContext.bgsFlag = true;
}
}

View File

@ -135,6 +135,27 @@ void ObjectKankyo_Init(Actor* thisx, GlobalContext* globalCtx) {
this->effects[5].size = 0.0f;
}
if (gSaveContext.n64ddFlag) {
if (Flags_GetRandomizerInf(RAND_INF_TRIALS_DONE_FOREST_TRIAL)) {
this->effects[0].size = 0.0f;
}
if (Flags_GetRandomizerInf(RAND_INF_TRIALS_DONE_WATER_TRIAL)) {
this->effects[1].size = 0.0f;
}
if (Flags_GetRandomizerInf(RAND_INF_TRIALS_DONE_SHADOW_TRIAL)) {
this->effects[2].size = 0.0f;
}
if (Flags_GetRandomizerInf(RAND_INF_TRIALS_DONE_FIRE_TRIAL)) {
this->effects[3].size = 0.0f;
}
if (Flags_GetRandomizerInf(RAND_INF_TRIALS_DONE_LIGHT_TRIAL)) {
this->effects[4].size = 0.0f;
}
if (Flags_GetRandomizerInf(RAND_INF_TRIALS_DONE_SPIRIT_TRIAL)) {
this->effects[5].size = 0.0f;
}
}
if (gSaveContext.cutsceneTrigger != 0) {
if (gSaveContext.entranceIndex == 0x0538) {
this->effects[0].size = 0.1f;

View File

@ -6108,7 +6108,7 @@ s32 func_8083E5A8(Player* this, GlobalContext* globalCtx) {
}
GetItemEntry giEntry;
if (this->getItemEntry.objectId == OBJECT_INVALID) {
if (this->getItemEntry.objectId == OBJECT_INVALID || (this->getItemId != this->getItemEntry.getItemId)) {
giEntry = ItemTable_Retrieve(this->getItemId);
} else {
giEntry = this->getItemEntry;
@ -6165,7 +6165,7 @@ s32 func_8083E5A8(Player* this, GlobalContext* globalCtx) {
}
} else if (CHECK_BTN_ALL(sControlInput->press.button, BTN_A) && !(this->stateFlags1 & PLAYER_STATE1_11) &&
!(this->stateFlags2 & PLAYER_STATE2_10)) {
if (this->getItemId != GI_NONE && this->getItemEntry.objectId != OBJECT_INVALID) {
if (this->getItemId != GI_NONE) {
GetItemEntry giEntry;
if (this->getItemEntry.objectId == OBJECT_INVALID) {
giEntry = ItemTable_Retrieve(-this->getItemId);
@ -9687,8 +9687,8 @@ void func_808473D4(GlobalContext* globalCtx, Player* this) {
else if ((!(this->stateFlags1 & PLAYER_STATE1_11) || (heldActor == NULL)) &&
(interactRangeActor != NULL) &&
((!sp1C && (this->getItemId == GI_NONE)) ||
((this->getItemId < 0 && this->getItemEntry.getItemId < 0) && !(this->stateFlags1 & PLAYER_STATE1_27)))) {
if (this->getItemId < 0 && this->getItemEntry.getItemId < 0) {
(this->getItemId < 0 && !(this->stateFlags1 & PLAYER_STATE1_27)))) {
if (this->getItemId < 0) {
doAction = DO_ACTION_OPEN;
} else if ((interactRangeActor->id == ACTOR_BG_TOKI_SWD) && LINK_IS_ADULT) {
doAction = DO_ACTION_DROP;

View File

@ -433,6 +433,7 @@ void FileChoose_UpdateMainMenu(GameState* thisx) {
const char* fileLoc = CVar_GetString("gSpoilerLog", "");
Randomizer_LoadSettings(fileLoc);
Randomizer_LoadHintLocations(fileLoc);
Randomizer_LoadRequiredTrials(fileLoc);
Randomizer_LoadItemLocations(fileLoc, silent);
fileSelectSpoilerFileLoaded = true;
}