mirror of
https://github.com/HarbourMasters/Shipwright.git
synced 2025-02-22 05:31:54 -05:00
Merge branch 'rando-next' of https://github.com/HarbourMasters/Shipwright into shopsanity
This commit is contained in:
commit
43d8a11de5
2
.gitignore
vendored
2
.gitignore
vendored
@ -9,6 +9,7 @@ __pycache__/
|
||||
.idea/
|
||||
cmake-build-debug
|
||||
venv/
|
||||
.cache/
|
||||
|
||||
# Project-specific ignores
|
||||
build/
|
||||
@ -410,6 +411,7 @@ oot.otr
|
||||
*.sav
|
||||
shipofharkinian.ini
|
||||
shipofharkinian.json
|
||||
imgui.ini
|
||||
|
||||
# Switch Stuff
|
||||
|
||||
|
@ -334,6 +334,12 @@ set(Source_Files__Resources__mpq
|
||||
)
|
||||
source_group("Source Files\\Resources\\mpq" FILES ${Source_Files__Resources__mpq})
|
||||
|
||||
set(Source_Files__Crash_Handler
|
||||
"CrashHandler.cpp"
|
||||
"CrashHandler.h"
|
||||
)
|
||||
source_group("Source Files\\Crash Handler" FILES ${Source_Files__Crash_Handler})
|
||||
|
||||
if (CMAKE_SYSTEM_NAME STREQUAL "Darwin")
|
||||
set(Source_Files__Darwin
|
||||
"OSXFolderManager.mm"
|
||||
@ -388,6 +394,7 @@ set(ALL_FILES
|
||||
${Source_Files__Resources__Factories}
|
||||
${Source_Files__Resources__Files}
|
||||
${Source_Files__Resources__mpq}
|
||||
${Source_Files__Crash_Handler}
|
||||
${Source_Files__Darwin}
|
||||
${Source_Files__NintendoSwitch}
|
||||
${Source_Files__CafeOS}
|
||||
|
172
libultraship/libultraship/CrashHandler.cpp
Normal file
172
libultraship/libultraship/CrashHandler.cpp
Normal file
@ -0,0 +1,172 @@
|
||||
#include "spdlog/spdlog.h"
|
||||
#include "Utils/StringHelper.h"
|
||||
#include "CrashHandler.h"
|
||||
|
||||
#if defined(__linux__)
|
||||
#include <csignal>
|
||||
#include <cstdio>
|
||||
#include <cxxabi.h> // for __cxa_demangle
|
||||
#include <dlfcn.h> // for dladdr
|
||||
#include <execinfo.h>
|
||||
#include <unistd.h>
|
||||
|
||||
extern "C" void DeinitOTR(void);
|
||||
|
||||
|
||||
static void PrintRegisters(ucontext_t* ctx) {
|
||||
char regbuffer[1024];
|
||||
SPDLOG_CRITICAL("Registers:");
|
||||
#if defined(__x86_64__)
|
||||
snprintf(regbuffer, std::size(regbuffer), "0x%016llX", ctx->uc_mcontext.gregs[REG_RAX]);
|
||||
SPDLOG_CRITICAL("RAX: {} ", regbuffer);
|
||||
snprintf(regbuffer, std::size(regbuffer), "0x%016llX", ctx->uc_mcontext.gregs[REG_RDI]);
|
||||
SPDLOG_CRITICAL("RDI: {} ", regbuffer);
|
||||
snprintf(regbuffer, std::size(regbuffer), "0x%016llX", ctx->uc_mcontext.gregs[REG_RSI]);
|
||||
SPDLOG_CRITICAL("RSI: {} ", regbuffer);
|
||||
snprintf(regbuffer, std::size(regbuffer), "0x%016llX", ctx->uc_mcontext.gregs[REG_RDX]);
|
||||
SPDLOG_CRITICAL("RDX: {} ", regbuffer);
|
||||
snprintf(regbuffer, std::size(regbuffer), "0x%016llX", ctx->uc_mcontext.gregs[REG_RCX]);
|
||||
SPDLOG_CRITICAL("RCX: {} ", regbuffer);
|
||||
snprintf(regbuffer, std::size(regbuffer), "0x%016llX", ctx->uc_mcontext.gregs[REG_R8]);
|
||||
SPDLOG_CRITICAL("R8 : {} ", regbuffer);
|
||||
snprintf(regbuffer, std::size(regbuffer), "0x%016llX", ctx->uc_mcontext.gregs[REG_R9]);
|
||||
SPDLOG_CRITICAL("R9 : {} ", regbuffer);
|
||||
snprintf(regbuffer, std::size(regbuffer), "0x%016llX", ctx->uc_mcontext.gregs[REG_R10]);
|
||||
SPDLOG_CRITICAL("R10: {} ", regbuffer);
|
||||
snprintf(regbuffer, std::size(regbuffer), "0x%016llX", ctx->uc_mcontext.gregs[REG_R11]);
|
||||
SPDLOG_CRITICAL("R11: {} ", regbuffer);
|
||||
snprintf(regbuffer, std::size(regbuffer), "0x%016llX", ctx->uc_mcontext.gregs[REG_RSP]);
|
||||
SPDLOG_CRITICAL("RSP: {} ", regbuffer);
|
||||
snprintf(regbuffer, std::size(regbuffer), "0x%016llX", ctx->uc_mcontext.gregs[REG_RBX]);
|
||||
SPDLOG_CRITICAL("RBX: {} ", regbuffer);
|
||||
snprintf(regbuffer, std::size(regbuffer), "0x%016llX", ctx->uc_mcontext.gregs[REG_RBP]);
|
||||
SPDLOG_CRITICAL("RBP: {} ", regbuffer);
|
||||
snprintf(regbuffer, std::size(regbuffer), "0x%016llX", ctx->uc_mcontext.gregs[REG_R12]);
|
||||
SPDLOG_CRITICAL("R12: {} ", regbuffer);
|
||||
snprintf(regbuffer, std::size(regbuffer), "0x%016llX", ctx->uc_mcontext.gregs[REG_R13]);
|
||||
SPDLOG_CRITICAL("R13: {} ", regbuffer);
|
||||
snprintf(regbuffer, std::size(regbuffer), "0x%016llX", ctx->uc_mcontext.gregs[REG_R14]);
|
||||
SPDLOG_CRITICAL("R14: {} ", regbuffer);
|
||||
snprintf(regbuffer, std::size(regbuffer), "0x%016llX", ctx->uc_mcontext.gregs[REG_R15]);
|
||||
SPDLOG_CRITICAL("R15: {} ", regbuffer);
|
||||
snprintf(regbuffer, std::size(regbuffer), "0x%016llX", ctx->uc_mcontext.gregs[REG_RIP]);
|
||||
SPDLOG_CRITICAL("RIP: {} ", regbuffer);
|
||||
snprintf(regbuffer, std::size(regbuffer), "0x%016llX", ctx->uc_mcontext.gregs[REG_EFL]);
|
||||
SPDLOG_CRITICAL("EFLAGS: {} ", regbuffer);
|
||||
#else
|
||||
snprintf(regbuffer, std::size(regbuffer),"0x%08lX", ctx->uc_mcontext.gregs[REG_EDI]);
|
||||
SPDLOG_CRITICAL("EDI : {} ", regbuffer);
|
||||
snprintf(regbuffer, std::size(regbuffer),"0x%08lX", ctx->uc_mcontext.gregs[REG_ESI]);
|
||||
SPDLOG_CRITICAL("ESI : {} ", regbuffer);
|
||||
snprintf(regbuffer, std::size(regbuffer),"0x%08lX", ctx->uc_mcontext.gregs[REG_EBP]);
|
||||
SPDLOG_CRITICAL("EBP : {} ", regbuffer);
|
||||
snprintf(regbuffer, std::size(regbuffer),"0x%08lX", ctx->uc_mcontext.gregs[REG_ESP]);
|
||||
SPDLOG_CRITICAL("ESP : {} ", regbuffer);
|
||||
snprintf(regbuffer, std::size(regbuffer),"0x%08lX", ctx->uc_mcontext.gregs[REG_EBX]);
|
||||
SPDLOG_CRITICAL("EBX : {} ", regbuffer);
|
||||
snprintf(regbuffer, std::size(regbuffer),"0x%08lX", ctx->uc_mcontext.gregs[REG_EDX]);
|
||||
SPDLOG_CRITICAL("EDX : {} ", regbuffer);
|
||||
snprintf(regbuffer, std::size(regbuffer),"0x%08lX", ctx->uc_mcontext.gregs[REG_ECX]);
|
||||
SPDLOG_CRITICAL("ECX : {} ", regbuffer);
|
||||
snprintf(regbuffer, std::size(regbuffer),"0x%08lX", ctx->uc_mcontext.gregs[REG_EAX]);
|
||||
SPDLOG_CRITICAL("EAX : {} ", regbuffer);
|
||||
snprintf(regbuffer, std::size(regbuffer),"0x%08lX", ctx->uc_mcontext.gregs[REG_EIP]);
|
||||
SPDLOG_CRITICAL("EIP : {} ", regbuffer);
|
||||
snprintf(regbuffer, std::size(regbuffer),"0x%08lX", ctx->uc_mcontext.gregs[REG_EFL]);
|
||||
SPDLOG_CRITICAL("EFL : {} ", regbuffer);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void ErrorHandler(int sig, siginfo_t* sigInfo, void* data)
|
||||
{
|
||||
std::array<void*, 4096> arr;
|
||||
ucontext_t* ctx = static_cast<ucontext_t*>(data);
|
||||
constexpr size_t nMaxFrames = arr.size();
|
||||
size_t size = backtrace(arr.data(), nMaxFrames);
|
||||
char** symbols = backtrace_symbols(arr.data(), nMaxFrames);
|
||||
|
||||
SPDLOG_CRITICAL("(Signal: {})\n", sig);
|
||||
|
||||
switch (sig) {
|
||||
case SIGILL:
|
||||
SPDLOG_CRITICAL("ILLEGAL INSTRUCTION");
|
||||
break;
|
||||
case SIGABRT:
|
||||
SPDLOG_CRITICAL("ABORT");
|
||||
break;
|
||||
case SIGFPE:
|
||||
SPDLOG_CRITICAL("ERRONEUS ARITHEMETIC OPERATION");
|
||||
break;
|
||||
case SIGSEGV:
|
||||
SPDLOG_CRITICAL("INVALID ACCESS TO STORAGE");
|
||||
break;
|
||||
}
|
||||
|
||||
PrintRegisters(ctx);
|
||||
|
||||
SPDLOG_CRITICAL("Traceback:\n");
|
||||
for (size_t i = 1; i < size; i++)
|
||||
{
|
||||
Dl_info info;
|
||||
int gotAddress = dladdr(arr[i], &info);
|
||||
std::string functionName(symbols[i]);
|
||||
|
||||
if (gotAddress != 0 && info.dli_sname != nullptr)
|
||||
{
|
||||
FILE* pipe;
|
||||
int32_t status;
|
||||
char* demangled = abi::__cxa_demangle(info.dli_sname, nullptr, nullptr, &status);
|
||||
const char* nameFound = info.dli_sname;
|
||||
|
||||
if (status == 0)
|
||||
{
|
||||
nameFound = demangled;
|
||||
}
|
||||
#if 0
|
||||
char command[256];
|
||||
char addrLine[128];
|
||||
snprintf(command, sizeof(command), "addr2line -e soh.elf %s + 0x%lX", nameFound, (uintptr_t)arr[i] - (uintptr_t)info.dli_saddr);
|
||||
pipe = popen(command, "r");
|
||||
fgets(addrLine, 128, pipe);
|
||||
#endif
|
||||
|
||||
functionName = StringHelper::Sprintf("%s (+0x%X)", nameFound,
|
||||
(char*)arr[i] - (char*)info.dli_saddr);
|
||||
free(demangled);
|
||||
}
|
||||
|
||||
SPDLOG_CRITICAL("{} {}", i, functionName.c_str());
|
||||
}
|
||||
|
||||
free(symbols);
|
||||
DeinitOTR();
|
||||
exit(1);
|
||||
}
|
||||
|
||||
static void ShutdownHandler(int sig, siginfo_t* sigInfo, void* data) {
|
||||
DeinitOTR();
|
||||
exit(1);
|
||||
}
|
||||
|
||||
extern "C" void SetupHandlerLinux() {
|
||||
struct sigaction action;
|
||||
struct sigaction shutdownAction;
|
||||
|
||||
action.sa_flags = SA_SIGINFO;
|
||||
action.sa_sigaction = ErrorHandler;
|
||||
|
||||
sigaction(SIGILL, &action, nullptr);
|
||||
sigaction(SIGABRT, &action, nullptr);
|
||||
sigaction(SIGFPE, &action, nullptr);
|
||||
sigaction(SIGSEGV, &action, nullptr);
|
||||
|
||||
shutdownAction.sa_flags = SA_SIGINFO;
|
||||
shutdownAction.sa_sigaction = ShutdownHandler;
|
||||
sigaction(SIGINT, &shutdownAction, nullptr);
|
||||
sigaction(SIGTERM, &shutdownAction, nullptr);
|
||||
sigaction(SIGQUIT, &shutdownAction, nullptr);
|
||||
sigaction(SIGKILL, &shutdownAction, nullptr);
|
||||
|
||||
|
||||
}
|
||||
#endif
|
18
libultraship/libultraship/CrashHandler.h
Normal file
18
libultraship/libultraship/CrashHandler.h
Normal file
@ -0,0 +1,18 @@
|
||||
#ifndef CRASH_HANDLER_H
|
||||
#define CRASH_HANDLER_H
|
||||
|
||||
#ifdef __linux__
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
void SetupHandlerLinux(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // __linux__
|
||||
|
||||
#endif // CRASH_HANDLER_H
|
@ -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 "
|
||||
|
@ -1661,7 +1661,7 @@ void PadMgr_HandleRetraceMsg(PadMgr* padmgr);
|
||||
void PadMgr_HandlePreNMI(PadMgr* padmgr);
|
||||
// This function must remain commented out, because it is called incorrectly in
|
||||
// fault.c (actual bug in game), and the compiler notices and won't compile it
|
||||
// void PadMgr_RequestPadData(PadMgr* padmgr, Input* inputs, s32 mode);
|
||||
void PadMgr_RequestPadData(PadMgr* padmgr, Input* inputs, s32 mode);
|
||||
void PadMgr_Init(PadMgr* padmgr, OSMesgQueue* siIntMsgQ, IrqMgr* irqMgr, OSId id, OSPri priority, void* stack);
|
||||
void Sched_SwapFrameBuffer(CfbInfo* cfbInfo);
|
||||
void func_800C84E4(SchedContext* sc, CfbInfo* cfbInfo);
|
||||
|
@ -260,5 +260,6 @@ extern GraphicsContext* __gfxCtx;
|
||||
|
||||
#define SEG_ADDR(seg, addr) (addr | (seg << 24) | 1)
|
||||
|
||||
#define NUM_TRIALS 6
|
||||
|
||||
#endif
|
||||
|
@ -3,20 +3,20 @@
|
||||
|
||||
#include "z64.h"
|
||||
|
||||
#define DECLARE_SEGMENT(name) \
|
||||
//extern u8 _##name##SegmentStart[]; \
|
||||
#define DECLARE_SEGMENT(name)
|
||||
//extern u8 _##name##SegmentStart[];
|
||||
//extern u8 _##name##SegmentEnd[];
|
||||
|
||||
#define DECLARE_ROM_SEGMENT(name) \
|
||||
//extern u8 _##name##SegmentRomStart[]; \
|
||||
#define DECLARE_ROM_SEGMENT(name)
|
||||
//extern u8 _##name##SegmentRomStart[];
|
||||
//extern u8 _##name##SegmentRomEnd[];
|
||||
|
||||
#define DECLARE_BSS_SEGMENT(name) \
|
||||
extern u8 _##name##SegmentBssStart[]; \
|
||||
extern u8 _##name##SegmentBssEnd[];
|
||||
|
||||
#define DECLARE_OVERLAY_SEGMENT(name) \
|
||||
//DECLARE_SEGMENT(ovl_##name) \
|
||||
#define DECLARE_OVERLAY_SEGMENT(name)
|
||||
//DECLARE_SEGMENT(ovl_##name)
|
||||
//DECLARE_ROM_SEGMENT(ovl_##name)
|
||||
|
||||
DECLARE_SEGMENT(boot)
|
||||
|
@ -1,6 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
typedef enum {
|
||||
TEXT_BUY_BOMBCHU_10_PROMPT = 0x8C,
|
||||
TEXT_BUY_BOMBCHU_10_DESC = 0xBC,
|
||||
TEXT_GS_NO_FREEZE = 0xB4,
|
||||
TEXT_GS_FREEZE = 0xB5,
|
||||
TEXT_RANDOMIZER_CUSTOM_ITEM = 0xF8,
|
||||
|
@ -2517,18 +2517,16 @@ 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]);
|
||||
@ -2564,6 +2562,9 @@ namespace Settings {
|
||||
|
||||
MapsAndCompasses.SetSelectedIndex(cvarSettings[RSK_STARTING_MAPS_COMPASSES]);
|
||||
|
||||
BombchusInLogic.SetSelectedIndex(cvarSettings[RSK_BOMBCHUS_IN_LOGIC]);
|
||||
AmmoDrops.SetSelectedIndex(AMMODROPS_VANILLA); // Ensure logic knows bombchu drops aren't implemented yet
|
||||
|
||||
StartingConsumables.SetSelectedIndex(cvarSettings[RSK_STARTING_CONSUMABLES]);
|
||||
StartingMaxRupees.SetSelectedIndex(cvarSettings[RSK_FULL_WALLETS]);
|
||||
|
||||
|
@ -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.
|
||||
@ -723,7 +721,7 @@ const char* SpoilerLog_Write(int language) {
|
||||
// WriteEnabledGlitches(spoilerLog);
|
||||
//}
|
||||
//WriteMasterQuestDungeons(spoilerLog);
|
||||
//WriteRequiredTrials(spoilerLog);
|
||||
WriteRequiredTrials();
|
||||
WritePlaythrough();
|
||||
//WriteWayOfTheHeroLocation(spoilerLog);
|
||||
|
||||
@ -773,7 +771,7 @@ bool PlacementLog_Write() {
|
||||
WriteEnabledTricks(placementLog);
|
||||
WriteEnabledGlitches(placementLog);
|
||||
WriteMasterQuestDungeons(placementLog);
|
||||
WriteRequiredTrials(placementLog);
|
||||
//WriteRequiredTrials(placementLog);
|
||||
|
||||
placementtxt = "\n" + placementtxt;
|
||||
|
||||
|
@ -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,
|
||||
|
@ -37,17 +37,23 @@ const std::string Randomizer::merchantMessageTableID = "RandomizerMerchants";
|
||||
const std::string Randomizer::rupeeMessageTableID = "RandomizerRupees";
|
||||
const std::string Randomizer::NaviRandoMessageTableID = "RandomizerNavi";
|
||||
|
||||
static const char* englishRupeeNames[44] = {
|
||||
static const char* englishRupeeNames[52] = {
|
||||
"Rupees", "Bitcoin", "Bananas", "Cornflakes", "Gummybears", "Floopies", "Dollars", "Lemmings",
|
||||
"Emeralds", "Bucks", "Rubles", "Diamonds", "Moons", "Stars", "Mana", "Doll Hairs",
|
||||
"Dogecoin", "Mushrooms", "Experience", "Friends", "Coins", "Rings", "Gil", "Pokédollars",
|
||||
"Bells", "Orbs", "Bottle Caps", "Simoleons", "Pokémon", "Toys", "Smackaroos", "Zorkmids",
|
||||
"Zenny", "Bones", "Souls", "Studs", "Munny", "Rubies", "Gald", "Gold",
|
||||
"Shillings", "Pounds", "Glimmer", "Potch"
|
||||
"Shillings", "Pounds", "Glimmer", "Potch", "Robux", "V-Bucks", "Bratwürste", "Mesetas",
|
||||
"Coal", "Euro", "Spoons", "Cucumbers"
|
||||
};
|
||||
|
||||
static const char* germanRupeeNames[1] = {
|
||||
"Rubine"
|
||||
static const char* germanRupeeNames[41] = {
|
||||
"Rubine", "Mäuse", "Kröten", "Münzen", "Euro", "Mark", "Bananen",
|
||||
"Gummibären", "Bonbons", "Diamanten", "Bratwürste", "Bitcoin", "Dogecoin", "Monde",
|
||||
"Sterne", "Brause UFOs", "Taler", "Sternis", "Schillinge", "Freunde", "Seelen",
|
||||
"Gil", "Zenny", "Pfandflaschen", "Knochen", "Pilze", "Smaragde", "Kronkorken",
|
||||
"Pokédollar", "Brötchen", "EXP", "Wagenchips", "Moos", "Knete", "Kohle",
|
||||
"Kies", "Radieschen", "Diridari", "Steine", "Kartoffeln", "Penunze"
|
||||
};
|
||||
|
||||
static const char* frenchRupeeNames[36] = {
|
||||
@ -108,6 +114,21 @@ Randomizer::~Randomizer() {
|
||||
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 },
|
||||
@ -562,6 +583,7 @@ std::unordered_map<std::string, RandomizerSettingKey> SpoilerfileSettingNameToEn
|
||||
{ "Shuffle Dungeon Items:Gerudo Fortress Keys", RSK_GERUDO_KEYS },
|
||||
{ "Shuffle Dungeon Items:Boss Keys", RSK_BOSS_KEYSANITY },
|
||||
{ "Shuffle Dungeon Items:Ganon's Boss Key", RSK_GANONS_BOSS_KEY },
|
||||
{ "World Settings:Bombchus in Logic", RSK_BOMBCHUS_IN_LOGIC },
|
||||
{ "Misc Settings:Gossip Stone Hints", RSK_GOSSIP_STONE_HINTS },
|
||||
{ "Misc Settings:Hint Clarity", RSK_HINT_CLARITY },
|
||||
{ "Misc Settings:Hint Distribution", RSK_HINT_DISTRIBUTION },
|
||||
@ -762,6 +784,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)
|
||||
@ -901,6 +929,7 @@ void Randomizer::ParseRandomizerSettingsFile(const char* spoilerFileName) {
|
||||
case RSK_STARTING_KOKIRI_SWORD:
|
||||
case RSK_COMPLETE_MASK_QUEST:
|
||||
case RSK_ENABLE_GLITCH_CUTSCENES:
|
||||
case RSK_BOMBCHUS_IN_LOGIC:
|
||||
if(it.value() == "Off") {
|
||||
gSaveContext.randoSettings[index].value = 0;
|
||||
} else if(it.value() == "On") {
|
||||
@ -960,6 +989,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;
|
||||
@ -1197,6 +1236,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)
|
||||
@ -1247,6 +1305,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;
|
||||
@ -1451,7 +1513,13 @@ s16 Randomizer::GetItemFromGet(RandomizerGet randoGet, GetItemID ogItemId) {
|
||||
return GI_RUPEE_BLUE;
|
||||
|
||||
case RG_PROGRESSIVE_BOMBCHUS:
|
||||
return GI_BOMBCHUS_20; //todo progressive?
|
||||
if (INV_CONTENT(ITEM_BOMBCHU) == ITEM_NONE) {
|
||||
return GI_BOMBCHUS_20;
|
||||
}
|
||||
if (AMMO(ITEM_BOMBCHU) < 5) {
|
||||
return GI_BOMBCHUS_10;
|
||||
}
|
||||
return GI_BOMBCHUS_5;
|
||||
|
||||
case RG_PROGRESSIVE_MAGIC_METER:
|
||||
switch (gSaveContext.magicLevel) {
|
||||
@ -1478,7 +1546,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;
|
||||
@ -1722,6 +1854,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;
|
||||
}
|
||||
@ -3332,6 +3519,7 @@ void GenerateRandomizerImgui() {
|
||||
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);
|
||||
|
||||
// if we skip child zelda, we start with zelda's letter, and malon starts
|
||||
@ -3412,7 +3600,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" };
|
||||
@ -3786,21 +3974,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
|
||||
@ -4151,7 +4336,18 @@ void DrawRandoEditor(bool& open) {
|
||||
ImGui::TableNextColumn();
|
||||
window->DC.CurrLineTextBaseOffset = 0.0f;
|
||||
ImGui::PushItemWidth(-FLT_MIN);
|
||||
ImGui::Text("Coming soon");
|
||||
|
||||
// Bombchus in Logic
|
||||
SohImGui::EnhancementCheckbox(Settings::BombchusInLogic.GetName().c_str(), "gRandomizeBombchusInLogic");
|
||||
InsertHelpHoverText(
|
||||
"Bombchus are properly considered in logic.\n"
|
||||
"\n"
|
||||
"The first Bombchu pack will always be 20, and subsequent packs will be "
|
||||
"5 or 10 based on how many you have.\n"
|
||||
"Once found, they can be replenished at the Bombchu shop.\n"
|
||||
"\n"
|
||||
"Bombchu Bowling is opened by obtaining Bombchus."
|
||||
);
|
||||
|
||||
ImGui::PopItemWidth();
|
||||
|
||||
|
@ -14,6 +14,7 @@ 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;
|
||||
@ -24,6 +25,7 @@ class Randomizer {
|
||||
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);
|
||||
|
||||
@ -47,6 +49,8 @@ class Randomizer {
|
||||
void LoadHintLocations(const char* spoilerFileName);
|
||||
void LoadShopMessages(const char* spoilerFileName);
|
||||
void LoadItemLocations(const char* spoilerFileName, bool silent);
|
||||
void LoadRequiredTrials(const char* spoilerFileName);
|
||||
bool IsTrialRequired(RandomizerInf trial);
|
||||
u8 GetRandoSettingValue(RandomizerSettingKey randoSettingKey);
|
||||
RandomizerCheck GetCheckFromActor(s16 sceneNum, s16 actorId, s16 actorParams);
|
||||
std::string GetChildAltarText() const;
|
||||
|
@ -1018,7 +1018,8 @@ typedef enum {
|
||||
RSK_ENABLE_GLITCH_CUTSCENES,
|
||||
RSK_SKULLS_SUNS_SONG,
|
||||
RSK_SHUFFLE_ADULT_TRADE,
|
||||
RSK_SHUFFLE_MAGIC_BEANS
|
||||
RSK_SHUFFLE_MAGIC_BEANS,
|
||||
RSK_BOMBCHUS_IN_LOGIC
|
||||
} RandomizerSettingKey;
|
||||
|
||||
typedef struct ScrubIdentity {
|
||||
|
@ -1566,10 +1566,18 @@ extern "C" void Randomizer_LoadShopMessages(const char* spoilerFileName) {
|
||||
OTRGlobals::Instance->gRandomizer->LoadShopMessages(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);
|
||||
}
|
||||
@ -1656,15 +1664,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) {
|
||||
@ -1737,6 +1744,9 @@ extern "C" int CustomMessage_RetrieveIfExists(GlobalContext* globalCtx) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
if (textId == TEXT_GS_NO_FREEZE || textId == TEXT_GS_FREEZE) {
|
||||
|
@ -32,6 +32,8 @@ private:
|
||||
#endif
|
||||
|
||||
#ifndef __cplusplus
|
||||
void InitOTR(void);
|
||||
void DeinitOTR(void);
|
||||
void VanillaItemTable_Init();
|
||||
void OTRAudio_Init();
|
||||
void InitAudio();
|
||||
@ -100,7 +102,9 @@ ScrubIdentity Randomizer_IdentifyScrub(s32 sceneNum, s32 actorParams, s32 respaw
|
||||
ShopItemIdentity Randomizer_IdentifyShopItem(s32 sceneNum, s32 actorParams);
|
||||
void Randomizer_LoadHintLocations(const char* spoilerFileName);
|
||||
void Randomizer_LoadShopMessages(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);
|
||||
|
@ -111,5 +111,25 @@ extern "C" void OTRMessage_Init()
|
||||
TEXTBOX_TYPE_BLUE, TEXTBOX_POS_BOTTOM,
|
||||
"You got a %rGold Skulltula Token%w!&You've collected %r\x19%w tokens&in total!",
|
||||
"Du erhälst ein %rGoldene&Skulltula-Symbol%w! Du hast&insgesamt %r\x19%w symbol gesammelt!",
|
||||
"Vous obtenez un %rSymbole de&Skulltula d'or%w! Vous avez&collecté %r\x19\%w symboles en tout!" });
|
||||
"Vous obtenez un %rSymbole de&Skulltula d'or%w! Vous avez&collecté %r\x19\%w symboles en tout!"
|
||||
}
|
||||
);
|
||||
CustomMessageManager::Instance->CreateMessage(
|
||||
customMessageTableID, TEXT_BUY_BOMBCHU_10_DESC,
|
||||
{
|
||||
TEXTBOX_TYPE_BLACK, TEXTBOX_POS_BOTTOM,
|
||||
"\x08%rBombchu (10 pieces) 99 Rupees&%wThis looks like a toy mouse, but&it's actually a self-propelled time&bomb!\x09\x0A",
|
||||
"\x08%rKrabbelmine 10 Stück 99 Rubine&%wDas ist eine praktische Zeitbombe,&die Du als Distanzwaffe&einsetzen kannst!\x09\x0A",
|
||||
"\x08%rMissile 10 unités 99 Rubis&%wProfilée comme une souris&mécanique, cette arme est &destructrice!!!\x09\x0A",
|
||||
}
|
||||
);
|
||||
CustomMessageManager::Instance->CreateMessage(
|
||||
customMessageTableID, TEXT_BUY_BOMBCHU_10_PROMPT,
|
||||
{
|
||||
TEXTBOX_TYPE_BLACK, TEXTBOX_POS_BOTTOM,
|
||||
"\x08\Bombchu 10 pieces 99 Rupees\x09&&\x1B%gBuy&Don't buy%w",
|
||||
"\x08Krabbelmine 10 Stück 99 Rubine\x09&&\x1B%gKaufen!&Nicht kaufen!%w",
|
||||
"\x08Missiles 10 unités 99 Rubis\x09&&\x1B%gAcheter&Ne pas acheter%w",
|
||||
}
|
||||
);
|
||||
}
|
||||
|
@ -293,7 +293,7 @@ void Fault_Sleep(u32 duration) {
|
||||
|
||||
void Fault_PadCallback(Input* input) {
|
||||
//! @bug This function is not called correctly and thus will crash from reading a bad pointer at 0x800C7E4C
|
||||
PadMgr_RequestPadData(input, 0);
|
||||
PadMgr_RequestPadData(&gPadMgr, input, 0);
|
||||
}
|
||||
|
||||
void Fault_UpdatePadImpl()
|
||||
|
@ -3,6 +3,8 @@
|
||||
#include <soh/Enhancements/bootcommands.h>
|
||||
#include "soh/OTRGlobals.h"
|
||||
|
||||
#include "../libultraship/CrashHandler.h"
|
||||
|
||||
|
||||
s32 gScreenWidth = SCREEN_WIDTH;
|
||||
s32 gScreenHeight = SCREEN_HEIGHT;
|
||||
@ -38,6 +40,10 @@ void Main_LogSystemHeap(void) {
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
#ifdef __linux__
|
||||
SetupHandlerLinux();
|
||||
#endif
|
||||
|
||||
GameConsole_Init();
|
||||
InitOTR();
|
||||
BootCommands_Init();
|
||||
@ -64,14 +70,14 @@ void Main(void* arg) {
|
||||
Fault_Init();
|
||||
SysCfb_Init(0);
|
||||
Heaps_Alloc();
|
||||
sysHeap = gSystemHeap;
|
||||
sysHeap = (uintptr_t)gSystemHeap;
|
||||
fb = SysCfb_GetFbPtr(0);
|
||||
gSystemHeapSize = 1024 * 1024 * 4;
|
||||
// "System heap initalization"
|
||||
osSyncPrintf("システムヒープ初期化 %08x-%08x %08x\n", sysHeap, fb, gSystemHeapSize);
|
||||
SystemHeap_Init(sysHeap, gSystemHeapSize); // initializes the system heap
|
||||
SystemHeap_Init((void*)sysHeap, gSystemHeapSize); // initializes the system heap
|
||||
if (osMemSize >= 0x800000) {
|
||||
debugHeap = SysCfb_GetFbEnd();
|
||||
debugHeap = (void*)SysCfb_GetFbEnd();
|
||||
debugHeapSize = (0x80600000 - (uintptr_t)debugHeap);
|
||||
} else {
|
||||
debugHeapSize = 0x400;
|
||||
|
@ -1690,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;
|
||||
|
@ -7,7 +7,6 @@
|
||||
#include "soh/Enhancements/randomizer/adult_trade_shuffle.h"
|
||||
|
||||
#define NUM_DUNGEONS 8
|
||||
#define NUM_TRIALS 6
|
||||
#define NUM_COWS 10
|
||||
#define NUM_SCRUBS 35
|
||||
#define NUM_SHOP_ITEMS 35
|
||||
@ -559,7 +558,7 @@ void GiveLinkAdultTradeItem(GetItemID giid) {
|
||||
if (item >= ITEM_POCKET_EGG) {
|
||||
gSaveContext.adultTradeItems |= ADULT_TRADE_FLAG(item);
|
||||
}
|
||||
INV_CONTENT(item) = item;
|
||||
INV_CONTENT(ITEM_TRADE_ADULT) = item;
|
||||
}
|
||||
|
||||
void GiveLinksPocketMedallion() {
|
||||
@ -768,6 +767,15 @@ void Sram_InitSave(FileChooseContext* fileChooseCtx) {
|
||||
gSaveContext.shopItemsPurchased[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
|
||||
@ -794,6 +802,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();
|
||||
|
||||
@ -853,6 +866,8 @@ void Sram_InitSave(FileChooseContext* fileChooseCtx) {
|
||||
if (getItem.modIndex == MOD_NONE) {
|
||||
if (getItem.itemId >= ITEM_MEDALLION_FOREST && getItem.itemId <= ITEM_ZORA_SAPPHIRE) {
|
||||
GiveLinkDungeonReward(getItem.getItemId);
|
||||
} else if (getItem.itemId >= ITEM_SONG_MINUET && getItem.itemId <= ITEM_SONG_STORMS) {
|
||||
GiveLinkSong(getItem.getItemId);
|
||||
} else if (giid == GI_RUPEE_GREEN || giid == GI_RUPEE_BLUE || giid == GI_RUPEE_RED ||
|
||||
giid == GI_RUPEE_PURPLE || giid == GI_RUPEE_GOLD) {
|
||||
GiveLinkRupeesByGetItemId(giid);
|
||||
@ -910,7 +925,7 @@ void Sram_InitSave(FileChooseContext* fileChooseCtx) {
|
||||
GiveLinkDekuNutUpgrade(giid);
|
||||
} else if (giid == GI_SKULL_TOKEN) {
|
||||
GiveLinkSkullToken();
|
||||
} else if (giid >= GI_POCKET_EGG && giid <= GI_CLAIM_CHECK) {
|
||||
} else if (giid >= GI_POCKET_EGG && giid <= GI_CLAIM_CHECK || giid == GI_COJIRO) {
|
||||
GiveLinkAdultTradeItem(giid);
|
||||
} else {
|
||||
s32 iid = getItem.itemId;
|
||||
@ -1028,11 +1043,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
|
||||
|
@ -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]) {
|
||||
|
@ -143,13 +143,15 @@ void EnBomBowMan_BlinkAwake(EnBomBowlMan* this, GlobalContext* globalCtx) {
|
||||
}
|
||||
}
|
||||
|
||||
// Check for Bomb Bag if Rando is enabled
|
||||
// RANDOTODO: Check for bombchu pack instead of bomb bag if bombchus are in logic
|
||||
// In randomizer, only check for bomb bag when bombchus aren't in logic
|
||||
// and only check for bombchus when bombchus are in logic
|
||||
if (gSaveContext.n64ddFlag) {
|
||||
if (INV_CONTENT(ITEM_BOMB) != ITEM_NONE) {
|
||||
this->actor.textId = 0xBF;
|
||||
} else {
|
||||
u8 bombchusInLogic = Randomizer_GetSettingValue(RSK_BOMBCHUS_IN_LOGIC);
|
||||
if ((!bombchusInLogic && INV_CONTENT(ITEM_BOMB) == ITEM_NONE) ||
|
||||
(bombchusInLogic && INV_CONTENT(ITEM_BOMBCHU) == ITEM_NONE)) {
|
||||
this->actor.textId = 0x7058;
|
||||
} else {
|
||||
this->actor.textId = 0xBF;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -177,11 +179,17 @@ void EnBomBowMan_CheckBeatenDC(EnBomBowlMan* this, GlobalContext* globalCtx) {
|
||||
this->eyeMode = CHU_GIRL_EYES_AWAKE;
|
||||
this->blinkTimer = (s16)Rand_ZeroFloat(60.0f) + 20;
|
||||
|
||||
// Check for beaten Dodongo's Cavern if not rando'd
|
||||
// check for bomb bag if rando'd
|
||||
if ((!gSaveContext.n64ddFlag &&
|
||||
!((gSaveContext.eventChkInf[2] & 0x20) || BREG(2))) ||
|
||||
(gSaveContext.n64ddFlag && (INV_CONTENT(ITEM_BOMB) == ITEM_NONE))) {
|
||||
bool bombchuBowlingClosed;
|
||||
if (gSaveContext.n64ddFlag) {
|
||||
// when rando'd, check if we have bombchus if chus are in logic
|
||||
// and check if we have a bomb bag if chus aren't in logic
|
||||
u8 explosive = Randomizer_GetSettingValue(RSK_BOMBCHUS_IN_LOGIC) ? ITEM_BOMBCHU : ITEM_BOMB;
|
||||
bombchuBowlingClosed = (INV_CONTENT(explosive) == ITEM_NONE);
|
||||
} else {
|
||||
// if not rando'd, check if we have beaten Dodongo's Cavern
|
||||
bombchuBowlingClosed = !((gSaveContext.eventChkInf[2] & 0x20) || BREG(2));
|
||||
}
|
||||
if (bombchuBowlingClosed) {
|
||||
this->actionFunc = EnBomBowMan_WaitNotBeatenDC;
|
||||
} else {
|
||||
this->actor.textId = 0x18;
|
||||
|
@ -644,8 +644,16 @@ s32 EnGirlA_CanBuy_Unk20(GlobalContext* globalCtx, EnGirlA* this) {
|
||||
}
|
||||
|
||||
s32 EnGirlA_CanBuy_Bombchus(GlobalContext* globalCtx, EnGirlA* this) {
|
||||
// When in rando, don't allow buying bombchus when the player doesn't have a bomb bag
|
||||
if (AMMO(ITEM_BOMBCHU) >= 50 || (gSaveContext.n64ddFlag && CUR_CAPACITY(UPG_BOMB_BAG) == 0)) {
|
||||
// When in rando, don't allow buying bombchus when the player doesn't have required explosives
|
||||
// If bombchus are in logic, the player needs to have bombchus; otherwise they need a bomb bag
|
||||
if (gSaveContext.n64ddFlag) {
|
||||
u8 bombchusInLogic = Randomizer_GetSettingValue(RSK_BOMBCHUS_IN_LOGIC);
|
||||
if ((!bombchusInLogic && CUR_CAPACITY(UPG_BOMB_BAG) == 0) ||
|
||||
(bombchusInLogic && INV_CONTENT(ITEM_BOMBCHU) == ITEM_NONE)) {
|
||||
return CANBUY_RESULT_CANT_GET_NOW;
|
||||
}
|
||||
}
|
||||
if (AMMO(ITEM_BOMBCHU) >= 50) {
|
||||
return CANBUY_RESULT_CANT_GET_NOW;
|
||||
}
|
||||
if (gSaveContext.rupees < this->basePrice) {
|
||||
@ -901,6 +909,14 @@ void EnGirlA_BuyEvent_ZoraTunic(GlobalContext* globalCtx, EnGirlA* this) {
|
||||
}
|
||||
|
||||
void EnGirlA_BuyEvent_ObtainBombchuPack(GlobalContext* globalCtx, EnGirlA* this) {
|
||||
Rupees_ChangeBy(-this->basePrice);
|
||||
|
||||
// Normally, buying a bombchu pack sets a flag indicating the pack is now sold out
|
||||
// If they're in logic for rando, skip setting that flag so they can be purchased repeatedly
|
||||
if (gSaveContext.n64ddFlag && Randomizer_GetSettingValue(RSK_BOMBCHUS_IN_LOGIC)) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (this->actor.params) {
|
||||
case SI_BOMBCHU_10_2:
|
||||
gSaveContext.itemGetInf[0] |= 0x40;
|
||||
@ -927,7 +943,6 @@ void EnGirlA_BuyEvent_ObtainBombchuPack(GlobalContext* globalCtx, EnGirlA* this)
|
||||
gSaveContext.itemGetInf[0] |= 0x20;
|
||||
break;
|
||||
}
|
||||
Rupees_ChangeBy(-this->basePrice);
|
||||
}
|
||||
|
||||
void EnGirlA_BuyEvent_Randomizer(GlobalContext* globalCtx, EnGirlA* this) {
|
||||
@ -1138,7 +1153,13 @@ void EnGirlA_InitializeItemAction(EnGirlA* this, GlobalContext* globalCtx) {
|
||||
this->canBuyFunc = itemEntry->canBuyFunc;
|
||||
this->itemGiveFunc = itemEntry->itemGiveFunc;
|
||||
this->buyEventFunc = itemEntry->buyEventFunc;
|
||||
this->basePrice = itemEntry->price;
|
||||
// If chus are in logic, make the 10 pack affordable without a wallet upgrade
|
||||
if (gSaveContext.n64ddFlag && Randomizer_GetSettingValue(RSK_BOMBCHUS_IN_LOGIC) &&
|
||||
this->getItemId == GI_BOMBCHUS_10) {
|
||||
this->basePrice = 99;
|
||||
} else {
|
||||
this->basePrice = itemEntry->price;
|
||||
}
|
||||
this->itemCount = itemEntry->count;
|
||||
this->hiliteFunc = itemEntry->hiliteFunc;
|
||||
this->giDrawId = itemEntry->giDrawId;
|
||||
|
@ -95,7 +95,7 @@ u16 EnGo_GetTextID(GlobalContext* globalCtx, Actor* thisx) {
|
||||
|
||||
switch (thisx->params & 0xF0) {
|
||||
case 0x90:
|
||||
if (gSaveContext.bgsFlag) {
|
||||
if (!gSaveContext.n64ddFlag && gSaveContext.bgsFlag) {
|
||||
return 0x305E;
|
||||
} else if (INV_CONTENT(ITEM_TRADE_ADULT) >= ITEM_CLAIM_CHECK) {
|
||||
if (Environment_GetBgsDayCount() >= CVar_GetS32("gForgeTime", 3)) {
|
||||
@ -859,7 +859,7 @@ void func_80A405CC(EnGo* this, GlobalContext* globalCtx) {
|
||||
|
||||
void EnGo_BiggoronActionFunc(EnGo* this, GlobalContext* globalCtx) {
|
||||
if (((this->actor.params & 0xF0) == 0x90) && (this->unk_1E0.unk_00 == 2)) {
|
||||
if (gSaveContext.bgsFlag) {
|
||||
if (!gSaveContext.n64ddFlag && gSaveContext.bgsFlag) {
|
||||
this->unk_1E0.unk_00 = 0;
|
||||
} else {
|
||||
if (INV_CONTENT(ITEM_TRADE_ADULT) == ITEM_EYEDROPS) {
|
||||
|
@ -595,7 +595,7 @@ s16 EnGo2_GetStateGoronCityLink(GlobalContext* globalCtx, EnGo2* this) {
|
||||
u16 EnGo2_GetTextIdGoronDmtBiggoron(GlobalContext* globalCtx, EnGo2* this) {
|
||||
Player* player = GET_PLAYER(globalCtx);
|
||||
|
||||
if (gSaveContext.bgsFlag) {
|
||||
if (!gSaveContext.n64ddFlag && gSaveContext.bgsFlag) {
|
||||
player->exchangeItemId = EXCH_ITEM_CLAIM_CHECK;
|
||||
return 0x305E;
|
||||
} else if (INV_CONTENT(ITEM_TRADE_ADULT) >= ITEM_CLAIM_CHECK) {
|
||||
@ -622,10 +622,6 @@ s16 EnGo2_GetStateGoronDmtBiggoron(GlobalContext* globalCtx, EnGo2* this) {
|
||||
}
|
||||
|
||||
if(gSaveContext.n64ddFlag) {
|
||||
if (INV_CONTENT(ITEM_CLAIM_CHECK) != ITEM_CLAIM_CHECK) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
EnGo2_GetItemEntry(this, globalCtx, Randomizer_GetItemFromKnownCheck(RC_DMT_TRADE_CLAIM_CHECK, GI_SWORD_BGS));
|
||||
Flags_SetTreasure(globalCtx, 0x1F);
|
||||
} else {
|
||||
@ -1063,7 +1059,7 @@ void EnGo2_BiggoronSetTextId(EnGo2* this, GlobalContext* globalCtx, Player* play
|
||||
u16 textId;
|
||||
|
||||
if ((this->actor.params & 0x1F) == GORON_DMT_BIGGORON) {
|
||||
if (gSaveContext.bgsFlag) {
|
||||
if ((!gSaveContext.n64ddFlag && gSaveContext.bgsFlag)) {
|
||||
if (func_8002F368(globalCtx) == EXCH_ITEM_CLAIM_CHECK) {
|
||||
this->actor.textId = 0x3003;
|
||||
} else {
|
||||
@ -1071,16 +1067,20 @@ void EnGo2_BiggoronSetTextId(EnGo2* this, GlobalContext* globalCtx, Player* play
|
||||
}
|
||||
player->actor.textId = this->actor.textId;
|
||||
|
||||
} else if (!gSaveContext.bgsFlag && (INV_CONTENT(ITEM_TRADE_ADULT) == ITEM_CLAIM_CHECK)) {
|
||||
} else if (INV_CONTENT(ITEM_TRADE_ADULT) == ITEM_CLAIM_CHECK) {
|
||||
if (func_8002F368(globalCtx) == EXCH_ITEM_CLAIM_CHECK) {
|
||||
if (Environment_GetBgsDayCount() >= CVar_GetS32("gForgeTime", 3)) {
|
||||
if (gSaveContext.n64ddFlag && Flags_GetTreasure(globalCtx, 0x1F)) {
|
||||
textId = 0x3003;
|
||||
} else if (Environment_GetBgsDayCount() >= CVar_GetS32("gForgeTime", 3)) {
|
||||
textId = 0x305E;
|
||||
} else {
|
||||
textId = 0x305D;
|
||||
}
|
||||
this->actor.textId = textId;
|
||||
} else {
|
||||
if (Environment_GetBgsDayCount() >= CVar_GetS32("gForgeTime", 3)) {
|
||||
if (gSaveContext.n64ddFlag && Flags_GetTreasure(globalCtx, 0x1F)) {
|
||||
textId = 0x305E;
|
||||
} else if (Environment_GetBgsDayCount() >= CVar_GetS32("gForgeTime", 3)) {
|
||||
textId = 0x3002;
|
||||
} else {
|
||||
textId = 0x305D;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
@ -6125,6 +6125,8 @@ s32 func_8083E5A8(Player* this, GlobalContext* globalCtx) {
|
||||
this->actor.colChkInfo.damage = 0;
|
||||
func_80837C0C(globalCtx, this, 3, 0.0f, 0.0f, 0, 20);
|
||||
Player_SetPendingFlag(this, globalCtx);
|
||||
this->getItemId == GI_NONE;
|
||||
this->getItemEntry = (GetItemEntry)GET_ITEM_NONE;
|
||||
return 1;
|
||||
}
|
||||
|
||||
@ -6165,7 +6167,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 +9689,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;
|
||||
@ -12853,6 +12855,8 @@ void func_8084E6D4(Player* this, GlobalContext* globalCtx) {
|
||||
} else {
|
||||
this->actor.colChkInfo.damage = 0;
|
||||
func_80837C0C(globalCtx, this, 3, 0.0f, 0.0f, 0, 20);
|
||||
this->getItemId == GI_NONE;
|
||||
this->getItemEntry = (GetItemEntry)GET_ITEM_NONE;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
@ -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);
|
||||
Randomizer_LoadShopMessages(fileLoc);
|
||||
fileSelectSpoilerFileLoaded = true;
|
||||
|
Loading…
x
Reference in New Issue
Block a user