mirror of
https://github.com/HarbourMasters/Shipwright.git
synced 2025-03-12 04:35:32 -07:00
Merge pull request #4615 from garrettjoecox/let-it-snow
develop -> let-it-now
This commit is contained in:
commit
2808279f00
README.mdtimesaver_hook_handlers.cppOTRGlobals.cppSaveManager.cppSohGui.cppSohMenuBar.cppUIWidgets.cpp
soh
assets
include
soh
Enhancements
Holiday
TimeDisplay
TimeSavers
cosmetics
custom-collectible
custom-message
debugconsole.cppdebugger
game-interactor
kaleido.cppmods.cpppresets.hrandomizer
3drando
fill.cpphint_list.cpphints.cppitem_pool.cpplocation_access.cpp
Plandomizer.cppentrance.cppfishsanity.cppfishsanity.hhint.cpphook_handlers.cppitem_location.cppitem_location.hlogic.cpplogic.hoption.cppoption.hoption_descriptions.cpprandomizer.cpprandomizerTypes.hrandomizer_check_tracker.cppsavefile.cppsettings.cppstatic_data.cpplocation_access
playthrough.cppshops.cppspoiler_log.cppstarting_inventory.cpptimesplits
src
code
overlays/actors
ovl_En_Mag
ovl_En_Mk
ovl_En_Ru1
ovl_Magic_Dark
ovl_Magic_Fire
ovl_player_actor
16
README.md
16
README.md
@ -9,7 +9,7 @@ Official Website: https://www.shipofharkinian.com/
|
||||
|
||||
Official Discord: https://discord.com/invite/shipofharkinian
|
||||
|
||||
If you're having any trouble after reading through this `README`, feel free ask for help in the Support text channels. Please keep in mind that we do not condone piracy.
|
||||
If you're having any trouble after reading through this `README`, feel free to ask for help in the Support text channels. Please keep in mind that we do not condone piracy.
|
||||
|
||||
# Quick Start
|
||||
|
||||
@ -73,7 +73,7 @@ Congratulations, you are now sailing with the Ship of Harkinian! Have fun!
|
||||
# Project Overview
|
||||
Ship of Harkinian (SOH) is built atop a custom library dubbed libultraship (LUS). Back in the N64 days, there was an SDK distributed to developers named libultra; LUS is designed to mimic the functionality of libultra on modern hardware. In addition, we are dependant on the source code provided by the OOT decompilation project.
|
||||
|
||||
In order for the game to function, you will require a **legally aquired** ROM for Ocarina of Time. Click [here](https://ship.equipment/) to check the compatability of your specific rom. Any copyrighted assets are extracted from the ROM and reformated as a .otr archive file which the code uses.
|
||||
In order for the game to function, you will require a **legally acquired** ROM for Ocarina of Time. Click [here](https://ship.equipment/) to check the compatibility of your specific rom. Any copyrighted assets are extracted from the ROM and reformatted as a .otr archive file which the code uses.
|
||||
|
||||
### Graphics Backends
|
||||
Currently, there are three rendering APIs supported: DirectX11 (Windows), OpenGL (all platforms), and Metal (MacOS). You can change which API to use in the `Settings` menu of the menubar, which requires a restart. If you're having an issue with crashing, you can change the API in the `shipofharkinian.json` file by finding the line `gfxbackend:""` and changing the value to `sdl` for OpenGL. DirectX 11 is the default on Windows.
|
||||
@ -99,13 +99,13 @@ If you want to playtest a continuous integration build, you can find them at the
|
||||
* [Linux](https://nightly.link/HarbourMasters/Shipwright/workflows/generate-builds/develop/soh-linux.zip)
|
||||
|
||||
### Further Reading
|
||||
More detailed documentation can be found in the 'docs' directory, including the afformentioned [building instructions](docs/BUILDING.md).
|
||||
More detailed documentation can be found in the 'docs' directory, including the aforementioned [building instructions](docs/BUILDING.md).
|
||||
|
||||
*[Credits](docs/CREDITS.md)
|
||||
*[Custom Music](docs/CUSTOM_MUSIC.md)
|
||||
*[Controler Maping](docs/GAME_CONTROLLER_DB.md)
|
||||
*[Modding](docs/MODDING.md)
|
||||
*[Versioning](docs/VERSIONING.md)
|
||||
* [Credits](docs/CREDITS.md)
|
||||
* [Custom Music](docs/CUSTOM_MUSIC.md)
|
||||
* [Controller Mapping](docs/GAME_CONTROLLER_DB.md)
|
||||
* [Modding](docs/MODDING.md)
|
||||
* [Versioning](docs/VERSIONING.md)
|
||||
|
||||
<a href="https://github.com/Kenix3/libultraship/">
|
||||
<picture>
|
||||
|
BIN
soh/assets/custom/textures/parameter_static/gMoon.rgba32.png
Normal file
BIN
soh/assets/custom/textures/parameter_static/gMoon.rgba32.png
Normal file
Binary file not shown.
After ![]() (image error) Size: 6.0 KiB |
BIN
soh/assets/custom/textures/parameter_static/gNavi.rgba32.png
Normal file
BIN
soh/assets/custom/textures/parameter_static/gNavi.rgba32.png
Normal file
Binary file not shown.
After ![]() (image error) Size: 6.1 KiB |
BIN
soh/assets/custom/textures/parameter_static/gSun.rgba32.png
Normal file
BIN
soh/assets/custom/textures/parameter_static/gSun.rgba32.png
Normal file
Binary file not shown.
After ![]() (image error) Size: 6.3 KiB |
@ -178,6 +178,15 @@ static const ALIGN_ASSET(2) char gSplitEntranceTex[] = dgSplitEntrance;
|
||||
#define dgBossSoul "__OTR__textures/parameter_static/gBossSoul"
|
||||
static const ALIGN_ASSET(2) char gBossSoulTex[] = dgBossSoul;
|
||||
|
||||
#define dgMoonIcon "__OTR__textures/parameter_static/gMoon"
|
||||
static const ALIGN_ASSET(2) char gMoonIconTex[] = dgMoonIcon;
|
||||
|
||||
#define dgSunIcon "__OTR__textures/parameter_static/gSun"
|
||||
static const ALIGN_ASSET(2) char gSunIconTex[] = dgSunIcon;
|
||||
|
||||
#define dgNaviIcon "__OTR__textures/parameter_static/gNavi"
|
||||
static const ALIGN_ASSET(2) char gNaviIconTex[] = dgNaviIcon;
|
||||
|
||||
#define dgFileSelMQButtonTex "__OTR__textures/title_static/gFileSelMQButtonTex"
|
||||
static const ALIGN_ASSET(2) char gFileSelMQButtonTex[] = dgFileSelMQButtonTex;
|
||||
|
||||
|
@ -306,6 +306,7 @@ typedef enum {
|
||||
/* 0x99 */ ITEM_STICK_UPGRADE_30,
|
||||
/* 0x9A */ ITEM_NUT_UPGRADE_30,
|
||||
/* 0x9B */ ITEM_NUT_UPGRADE_40,
|
||||
/* 0x9C */ ITEM_SHIP, // SOH [Enhancement] Added to enable custom item gives
|
||||
/* 0xFC */ ITEM_LAST_USED = 0xFC,
|
||||
/* 0xFE */ ITEM_NONE_FE = 0xFE,
|
||||
/* 0xFF */ ITEM_NONE = 0xFF
|
||||
@ -455,9 +456,10 @@ typedef enum {
|
||||
/* 0x79 */ GI_NUT_UPGRADE_30,
|
||||
/* 0x7A */ GI_NUT_UPGRADE_40,
|
||||
/* 0x7B */ GI_BULLET_BAG_50,
|
||||
/* 0x7C */ GI_ICE_TRAP, // freezes link when opened from a chest
|
||||
/* 0x7D */ GI_TEXT_0, // no model appears over Link, shows text id 0 (pocket egg)
|
||||
/* 0x84 */ GI_MAX
|
||||
/* 0x7C */ GI_SHIP, // SOH [Enhancement] Added to enable custom item gives
|
||||
/* 0x7D */ GI_ICE_TRAP, // freezes link when opened from a chest
|
||||
/* 0x7E */ GI_TEXT_0, // no model appears over Link, shows text id 0 (pocket egg)
|
||||
/* 0x7F */ GI_MAX
|
||||
} GetItemID;
|
||||
|
||||
typedef enum {
|
||||
|
@ -735,14 +735,14 @@ typedef enum {
|
||||
#define INFTABLE_12A 0x12A
|
||||
#define INFTABLE_138 0x138
|
||||
#define INFTABLE_139 0x139
|
||||
#define INFTABLE_140 0x140
|
||||
#define INFTABLE_RUTO_IN_JJ_MEET_RUTO 0x141
|
||||
#define INFTABLE_RUTO_IN_JJ_TALK_FIRST_TIME 0x142
|
||||
#define INFTABLE_143 0x143
|
||||
#define INFTABLE_RUTO_IN_JJ_WANTS_TO_BE_TOSSED_TO_SAPPHIRE 0x144
|
||||
#define INFTABLE_145 0x145
|
||||
#define INFTABLE_146 0x146
|
||||
#define INFTABLE_147 0x147
|
||||
#define INFTABLE_140 0x140 // Left her on blue switch in fork room (causes her to spawn in fork room)
|
||||
#define INFTABLE_RUTO_IN_JJ_MEET_RUTO 0x141 // Jumped down hole from hole room
|
||||
#define INFTABLE_RUTO_IN_JJ_TALK_FIRST_TIME 0x142 // in the basement
|
||||
#define INFTABLE_143 0x143 // Sat down in basement (causes her to get upset if this is set when actor is spawned)
|
||||
#define INFTABLE_RUTO_IN_JJ_WANTS_TO_BE_TOSSED_TO_SAPPHIRE 0x144 // Entered the room with the sapphire
|
||||
#define INFTABLE_145 0x145 // Thrown to sapphire (not kidnapped yet)
|
||||
#define INFTABLE_146 0x146 // Kidnapped
|
||||
#define INFTABLE_147 0x147 // Brought ruto back up to holes room, causes her to spawn in holes room instead of basement
|
||||
#define INFTABLE_160 0x160
|
||||
#define INFTABLE_161 0x161
|
||||
#define INFTABLE_162 0x162
|
||||
|
@ -19,7 +19,7 @@ uint64_t GetUnixTimestamp();
|
||||
|
||||
bool isFeverDisabled = false;
|
||||
bool isExchangeDisabled = false;
|
||||
float fontScale = 1.0f;
|
||||
static float fontScale = 1.0f;
|
||||
|
||||
extern GetItemEntry vanillaQueuedItemEntry;
|
||||
|
||||
|
262
soh/soh/Enhancements/TimeDisplay/TimeDisplay.cpp
Normal file
262
soh/soh/Enhancements/TimeDisplay/TimeDisplay.cpp
Normal file
@ -0,0 +1,262 @@
|
||||
#include "TimeDisplay.h"
|
||||
#include "soh/Enhancements/gameplaystats.h"
|
||||
#include <global.h>
|
||||
|
||||
#include "assets/textures/parameter_static/parameter_static.h"
|
||||
#include "assets/soh_assets.h"
|
||||
#include "soh/ImGuiUtils.h"
|
||||
|
||||
extern "C" {
|
||||
#include "macros.h"
|
||||
#include "functions.h"
|
||||
#include "variables.h"
|
||||
extern PlayState* gPlayState;
|
||||
uint64_t GetUnixTimestamp();
|
||||
}
|
||||
|
||||
float fontScale = 1.0f;
|
||||
std::string timeDisplayTime = "";
|
||||
ImTextureID textureDisplay = 0;
|
||||
ImVec4 windowBG = ImVec4(0, 0, 0, 0.5f);
|
||||
ImVec4 textColor = ImVec4(1.0f, 1.0f, 1.0f, 1.0f);
|
||||
|
||||
// ImVec4 Colors
|
||||
#define COLOR_WHITE ImVec4(1.0f, 1.0f, 1.0f, 1.0f)
|
||||
#define COLOR_LIGHT_RED ImVec4(1.0f, 0.05f, 0, 1.0f)
|
||||
#define COLOR_LIGHT_BLUE ImVec4(0, 0.88f, 1.0f, 1.0f)
|
||||
#define COLOR_LIGHT_GREEN ImVec4(0.52f, 1.0f, 0.23f, 1.0f)
|
||||
#define COLOR_GREY ImVec4(0.78f, 0.78f, 0.78f, 1.0f)
|
||||
|
||||
const static std::vector<std::pair<std::string, const char*>> digitList = {
|
||||
{ "DIGIT_0_TEXTURE", gCounterDigit0Tex }, { "DIGIT_1_TEXTURE", gCounterDigit1Tex },
|
||||
{ "DIGIT_2_TEXTURE", gCounterDigit2Tex }, { "DIGIT_3_TEXTURE", gCounterDigit3Tex },
|
||||
{ "DIGIT_4_TEXTURE", gCounterDigit4Tex }, { "DIGIT_5_TEXTURE", gCounterDigit5Tex },
|
||||
{ "DIGIT_6_TEXTURE", gCounterDigit6Tex }, { "DIGIT_7_TEXTURE", gCounterDigit7Tex },
|
||||
{ "DIGIT_8_TEXTURE", gCounterDigit8Tex }, { "DIGIT_9_TEXTURE", gCounterDigit9Tex },
|
||||
{ "COLON_TEXTURE", gCounterColonTex },
|
||||
};
|
||||
|
||||
const std::vector<TimeObject> timeDisplayList = {
|
||||
{ DISPLAY_IN_GAME_TIMER, "Display Gameplay Timer", CVAR_ENHANCEMENT("TimeDisplay.Timers.InGameTimer") },
|
||||
{ DISPLAY_TIME_OF_DAY, "Display Time of Day", CVAR_ENHANCEMENT("TimeDisplay.Timers.TimeofDay") },
|
||||
{ DISPLAY_CONDITIONAL_TIMER, "Display Conditional Timer", CVAR_ENHANCEMENT("TimeDisplay.Timers.HotWater") },
|
||||
{ DISPLAY_NAVI_TIMER, "Display Navi Timer", CVAR_ENHANCEMENT("TimeDisplay.Timers.NaviTimer") }
|
||||
};
|
||||
|
||||
static std::vector<TimeObject> activeTimers;
|
||||
|
||||
std::string convertDayTime(uint32_t dayTime) {
|
||||
uint32_t totalSeconds = 24 * 60 * 60;
|
||||
uint32_t ss = static_cast<uint32_t>(static_cast<double>(dayTime) * (totalSeconds - 1) / 65535);
|
||||
uint32_t hh = ss / 3600;
|
||||
uint32_t mm = (ss % 3600) / 60;
|
||||
return fmt::format("{:0>2}:{:0>2}", hh, mm);
|
||||
}
|
||||
|
||||
std::string convertNaviTime(uint32_t value) {
|
||||
uint32_t totalSeconds = value * 0.05;
|
||||
uint32_t ss = totalSeconds % 60;
|
||||
uint32_t mm = totalSeconds / 60;
|
||||
return fmt::format("{:0>2}:{:0>2}", mm, ss);
|
||||
}
|
||||
|
||||
std::string formatHotWaterDisplay(uint32_t value) {
|
||||
uint32_t ss = value % 60;
|
||||
uint32_t mm = value / 60;
|
||||
return fmt::format("{:0>2}:{:0>2}", mm, ss);
|
||||
}
|
||||
|
||||
std::string formatTimeDisplay(uint32_t value) {
|
||||
uint32_t sec = value / 10;
|
||||
uint32_t hh = sec / 3600;
|
||||
uint32_t mm = (sec - hh * 3600) / 60;
|
||||
uint32_t ss = sec - hh * 3600 - mm * 60;
|
||||
uint32_t ds = value % 10;
|
||||
return fmt::format("{}:{:0>2}:{:0>2}.{}", hh, mm, ss, ds);
|
||||
}
|
||||
|
||||
static void TimeDisplayGetTimer(uint32_t timeID) {
|
||||
timeDisplayTime = "";
|
||||
textureDisplay = 0;
|
||||
textColor = COLOR_WHITE;
|
||||
|
||||
Player* player = GET_PLAYER(gPlayState);
|
||||
uint32_t timer1 = gSaveContext.timer1Value;
|
||||
|
||||
switch (timeID) {
|
||||
case DISPLAY_IN_GAME_TIMER:
|
||||
textureDisplay = Ship::Context::GetInstance()->GetWindow()->GetGui()->GetTextureByName("GAMEPLAY_TIMER");
|
||||
timeDisplayTime = formatTimeDisplay(GAMEPLAYSTAT_TOTAL_TIME).c_str();
|
||||
break;
|
||||
case DISPLAY_TIME_OF_DAY:
|
||||
if (gSaveContext.dayTime >= DAY_BEGINS && gSaveContext.dayTime < NIGHT_BEGINS) {
|
||||
textureDisplay =
|
||||
Ship::Context::GetInstance()->GetWindow()->GetGui()->GetTextureByName("DAY_TIME_TIMER");
|
||||
} else {
|
||||
textureDisplay =
|
||||
Ship::Context::GetInstance()->GetWindow()->GetGui()->GetTextureByName("NIGHT_TIME_TIMER");
|
||||
}
|
||||
timeDisplayTime = convertDayTime(gSaveContext.dayTime).c_str();
|
||||
break;
|
||||
case DISPLAY_CONDITIONAL_TIMER:
|
||||
if (gSaveContext.timer1State > 0) {
|
||||
timeDisplayTime = formatHotWaterDisplay(gSaveContext.timer1Value).c_str();
|
||||
textColor =
|
||||
gSaveContext.timer1State <= 4
|
||||
? (gPlayState->roomCtx.curRoom.behaviorType2 == ROOM_BEHAVIOR_TYPE2_3 ? COLOR_LIGHT_RED
|
||||
: COLOR_LIGHT_BLUE)
|
||||
: COLOR_WHITE;
|
||||
if (gSaveContext.timer1State <= 4) {
|
||||
textureDisplay = Ship::Context::GetInstance()->GetWindow()->GetGui()->GetTextureByName(
|
||||
gPlayState->roomCtx.curRoom.behaviorType2 == ROOM_BEHAVIOR_TYPE2_3
|
||||
? itemMapping[ITEM_TUNIC_GORON].name
|
||||
: itemMapping[ITEM_TUNIC_ZORA].name);
|
||||
}
|
||||
if (gSaveContext.timer1State >= 6) {
|
||||
textureDisplay = Ship::Context::GetInstance()->GetWindow()->GetGui()->GetTextureByName(
|
||||
itemMapping[ITEM_SWORD_MASTER].name);
|
||||
}
|
||||
} else {
|
||||
textureDisplay = Ship::Context::GetInstance()->GetWindow()->GetGui()->GetTextureByName(
|
||||
itemMapping[ITEM_TUNIC_KOKIRI].name);
|
||||
timeDisplayTime = "-:--";
|
||||
}
|
||||
break;
|
||||
case DISPLAY_NAVI_TIMER:
|
||||
if (gSaveContext.naviTimer <= NAVI_PREPARE) {
|
||||
timeDisplayTime = convertNaviTime(NAVI_PREPARE - gSaveContext.naviTimer).c_str();
|
||||
} else if (gSaveContext.naviTimer <= NAVI_ACTIVE) {
|
||||
timeDisplayTime = convertNaviTime(NAVI_ACTIVE - gSaveContext.naviTimer).c_str();
|
||||
textColor = COLOR_LIGHT_GREEN;
|
||||
} else {
|
||||
timeDisplayTime = convertNaviTime(NAVI_COOLDOWN - gSaveContext.naviTimer).c_str();
|
||||
textColor = COLOR_GREY;
|
||||
}
|
||||
textureDisplay = Ship::Context::GetInstance()->GetWindow()->GetGui()->GetTextureByName("NAVI_TIMER");
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void TimeDisplayUpdateDisplayOptions() {
|
||||
activeTimers.clear();
|
||||
for (auto& timer : timeDisplayList) {
|
||||
if (CVarGetInteger(timer.timeEnable, 0)) {
|
||||
activeTimers.push_back(timer);
|
||||
}
|
||||
}
|
||||
|
||||
//if (pushBack) {
|
||||
// activeTimers.push_back(timeDisplayList[timeID]);
|
||||
//} else {
|
||||
// uint32_t index = 0;
|
||||
// for (auto& check : activeTimers) {
|
||||
// if (check.timeID == timeID) {
|
||||
// activeTimers.erase(activeTimers.begin() + index);
|
||||
// return;
|
||||
// }
|
||||
// index++;
|
||||
// }
|
||||
//}
|
||||
}
|
||||
|
||||
void TimeDisplayWindow::Draw() {
|
||||
if (!gPlayState) {
|
||||
return;
|
||||
}
|
||||
if (!CVarGetInteger(CVAR_WINDOW("TimeDisplayEnabled"), 0)) {
|
||||
return;
|
||||
}
|
||||
|
||||
ImGui::PushStyleColor(ImGuiCol_WindowBg, windowBG);
|
||||
ImGui::PushStyleColor(ImGuiCol_Border, ImVec4(0, 0, 0, 0));
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 4.0f);
|
||||
|
||||
ImGui::Begin("TimerDisplay", nullptr,
|
||||
ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoNav | ImGuiWindowFlags_NoFocusOnAppearing |
|
||||
ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoDocking | ImGuiWindowFlags_NoTitleBar |
|
||||
ImGuiWindowFlags_NoScrollWithMouse | ImGuiWindowFlags_NoScrollbar);
|
||||
ImGui::SetWindowFontScale(fontScale);
|
||||
if (activeTimers.size() == 0) {
|
||||
ImGui::Text("No Enabled Timers...");
|
||||
} else {
|
||||
ImGui::BeginTable("Timer List", 2, ImGuiTableFlags_NoClip);
|
||||
for (auto& timers : activeTimers) {
|
||||
ImGui::PushID(timers.timeID);
|
||||
TimeDisplayGetTimer(timers.timeID);
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Image(textureDisplay, ImVec2(16.0f * fontScale, 16.0f * fontScale));
|
||||
ImGui::TableNextColumn();
|
||||
|
||||
if (timeDisplayTime != "-:--") {
|
||||
char* textToDecode = new char[timeDisplayTime.size() + 1];
|
||||
textToDecode = std::strcpy(textToDecode, timeDisplayTime.c_str());
|
||||
size_t textLength = timeDisplayTime.length();
|
||||
uint16_t textureIndex = 0;
|
||||
|
||||
for (size_t i = 0; i < textLength; i++) {
|
||||
ImVec2 originalCursorPos = ImGui::GetCursorPos();
|
||||
if (textToDecode[i] == ':' || textToDecode[i] == '.') {
|
||||
textureIndex = 10;
|
||||
} else {
|
||||
textureIndex = textToDecode[i] - '0';
|
||||
}
|
||||
if (textToDecode[i] == '.') {
|
||||
ImGui::SetCursorPosY(ImGui::GetCursorPosY() + (8.0f * fontScale));
|
||||
ImGui::Image(Ship::Context::GetInstance()->GetWindow()->GetGui()->GetTextureByName(
|
||||
digitList[textureIndex].first),
|
||||
ImVec2(8.0f * fontScale, 8.0f * fontScale), ImVec2(0, 0.5f), ImVec2(1, 1),
|
||||
textColor, ImVec4(0, 0, 0, 0));
|
||||
} else {
|
||||
ImGui::Image(Ship::Context::GetInstance()->GetWindow()->GetGui()->GetTextureByName(
|
||||
digitList[textureIndex].first),
|
||||
ImVec2(8.0f * fontScale, 16.0f * fontScale), ImVec2(0, 0), ImVec2(1, 1), textColor,
|
||||
ImVec4(0, 0, 0, 0));
|
||||
}
|
||||
ImGui::SameLine(0, 0);
|
||||
}
|
||||
}
|
||||
ImGui::PopID();
|
||||
}
|
||||
ImGui::EndTable();
|
||||
}
|
||||
ImGui::End();
|
||||
|
||||
ImGui::PopStyleColor(2);
|
||||
ImGui::PopStyleVar(1);
|
||||
}
|
||||
|
||||
void TimeDisplayInitSettings() {
|
||||
fontScale = CVarGetFloat(CVAR_ENHANCEMENT("TimeDisplay.FontScale"), 1.0f);
|
||||
if (fontScale < 1.0f) {
|
||||
fontScale = 1.0f;
|
||||
}
|
||||
if (CVarGetInteger(CVAR_ENHANCEMENT("TimeDisplay.ShowWindowBG"), 0)) {
|
||||
windowBG = ImVec4(0, 0, 0, 0);
|
||||
} else {
|
||||
windowBG = ImVec4(0, 0, 0, 0.5f);
|
||||
}
|
||||
}
|
||||
|
||||
static void TimeDisplayInitTimers() {
|
||||
for (auto& update : timeDisplayList) {
|
||||
if (CVarGetInteger(update.timeEnable, 0)) {
|
||||
activeTimers.push_back(update);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TimeDisplayWindow::InitElement() {
|
||||
Ship::Context::GetInstance()->GetWindow()->GetGui()->LoadGuiTexture("GAMEPLAY_TIMER", gClockIconTex, ImVec4(1, 1, 1, 1));
|
||||
Ship::Context::GetInstance()->GetWindow()->GetGui()->LoadGuiTexture("DAY_TIME_TIMER", gSunIconTex, ImVec4(1, 1, 1, 1));
|
||||
Ship::Context::GetInstance()->GetWindow()->GetGui()->LoadGuiTexture("NIGHT_TIME_TIMER", gMoonIconTex, ImVec4(1, 1, 1, 1));
|
||||
Ship::Context::GetInstance()->GetWindow()->GetGui()->LoadGuiTexture("NAVI_TIMER", gNaviIconTex, ImVec4(1, 1, 1, 1));
|
||||
|
||||
for (auto& load : digitList) {
|
||||
Ship::Context::GetInstance()->GetWindow()->GetGui()->LoadGuiTexture(load.first.c_str(), load.second, ImVec4(1, 1, 1, 1));
|
||||
}
|
||||
|
||||
TimeDisplayInitSettings();
|
||||
TimeDisplayInitTimers();
|
||||
}
|
37
soh/soh/Enhancements/TimeDisplay/TimeDisplay.h
Normal file
37
soh/soh/Enhancements/TimeDisplay/TimeDisplay.h
Normal file
@ -0,0 +1,37 @@
|
||||
#include <libultraship/libultraship.h>
|
||||
|
||||
class TimeDisplayWindow : public Ship::GuiWindow {
|
||||
public:
|
||||
using GuiWindow::GuiWindow;
|
||||
|
||||
void InitElement() override;
|
||||
void DrawElement() override {};
|
||||
void Draw() override;
|
||||
void UpdateElement() override {};
|
||||
};
|
||||
|
||||
void TimeDisplayUpdateDisplayOptions();
|
||||
void TimeDisplayInitSettings();
|
||||
|
||||
typedef enum {
|
||||
DISPLAY_IN_GAME_TIMER,
|
||||
DISPLAY_TIME_OF_DAY,
|
||||
DISPLAY_CONDITIONAL_TIMER,
|
||||
DISPLAY_NAVI_TIMER
|
||||
};
|
||||
|
||||
typedef enum {
|
||||
NAVI_PREPARE = 600,
|
||||
NAVI_ACTIVE = 3000,
|
||||
NAVI_COOLDOWN = 25800,
|
||||
DAY_BEGINS = 17759,
|
||||
NIGHT_BEGINS = 49155
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
uint32_t timeID;
|
||||
std::string timeLabel;
|
||||
const char* timeEnable;
|
||||
} TimeObject;
|
||||
|
||||
extern const std::vector<TimeObject> timeDisplayList;
|
@ -10,7 +10,7 @@ extern "C" {
|
||||
#include "variables.h"
|
||||
}
|
||||
|
||||
#define RAND_GET_OPTION(option) Rando::Context::GetInstance()->GetOption(option).GetSelectedOptionIndex()
|
||||
#define RAND_GET_OPTION(option) Rando::Context::GetInstance()->GetOption(option).GetContextOptionIndex()
|
||||
|
||||
static bool sEnteredBlueWarp = false;
|
||||
|
||||
|
@ -0,0 +1,27 @@
|
||||
#include "soh/Enhancements/game-interactor/GameInteractor.h"
|
||||
#include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h"
|
||||
#include "soh/OTRGlobals.h"
|
||||
|
||||
extern "C" {
|
||||
#include "src/overlays/actors/ovl_Bg_Bdan_Objects/z_bg_bdan_objects.h"
|
||||
}
|
||||
|
||||
/**
|
||||
* Adjusts the behavior of the elevator to start near the bottom if you are entering the room from the bottom
|
||||
*/
|
||||
void MoveJabuJabuElevator_Register() {
|
||||
GameInteractor::Instance->RegisterGameHookForID<GameInteractor::OnActorInit>(ACTOR_BG_BDAN_OBJECTS, [](void* actorRef) {
|
||||
Player* player = GET_PLAYER(gPlayState);
|
||||
BgBdanObjects* bgBdanObjects = static_cast<BgBdanObjects*>(actorRef);
|
||||
|
||||
if (!CVarGetInteger(CVAR_ENHANCEMENT("TimeSavers.SkipMiscInteractions"), IS_RANDO)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (bgBdanObjects->dyna.actor.params == 1) {
|
||||
if (player->actor.world.pos.y < -500.0f) {
|
||||
bgBdanObjects->timer = 220;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
@ -0,0 +1,93 @@
|
||||
#include "soh/Enhancements/game-interactor/GameInteractor.h"
|
||||
#include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h"
|
||||
#include "soh/OTRGlobals.h"
|
||||
|
||||
extern "C" {
|
||||
#include "overlays/actors/ovl_En_Ru1/z_en_ru1.h"
|
||||
#include "assets/objects/object_ru1/object_ru1.h"
|
||||
|
||||
Actor* func_80AEB124(PlayState* play);
|
||||
}
|
||||
|
||||
void SkipChildRutoInteractions_Register() {
|
||||
// Skips the Child Ruto introduction cutscene, where she drops down into the hole in Jabu-Jabu's Belly
|
||||
REGISTER_VB_SHOULD(VB_PLAY_CHILD_RUTO_INTRO, {
|
||||
EnRu1* enRu1 = va_arg(args, EnRu1*);
|
||||
|
||||
if (!CVarGetInteger(CVAR_ENHANCEMENT("TimeSavers.SkipMiscInteractions"), IS_RANDO)) {
|
||||
return;
|
||||
}
|
||||
|
||||
Flags_SetInfTable(INFTABLE_RUTO_IN_JJ_MEET_RUTO);
|
||||
Flags_SetInfTable(INFTABLE_RUTO_IN_JJ_TALK_FIRST_TIME);
|
||||
Flags_SetInfTable(INFTABLE_143);
|
||||
enRu1->drawConfig = 1;
|
||||
enRu1->actor.world.pos.x = 127.0f;
|
||||
enRu1->actor.world.pos.y = -340.0f;
|
||||
enRu1->actor.world.pos.z = -3041.0f;
|
||||
enRu1->actor.shape.rot.y = enRu1->actor.world.rot.y = -5098;
|
||||
|
||||
if (*should) {
|
||||
Animation_Change(&enRu1->skelAnime, (AnimationHeader*)&gRutoChildTurnAroundAnim, 1.0f, 0,
|
||||
Animation_GetLastFrame((void*)&gRutoChildTurnAroundAnim), ANIMMODE_ONCE, -8.0f);
|
||||
enRu1->action = 10;
|
||||
}
|
||||
|
||||
*should = false;
|
||||
});
|
||||
|
||||
// Skips a short dialogue sequence where Ruto tells you to throw her to the Sapphire
|
||||
REGISTER_VB_SHOULD(VB_RUTO_WANT_TO_BE_TOSSED_TO_SAPPHIRE, {
|
||||
if (!CVarGetInteger(CVAR_ENHANCEMENT("TimeSavers.SkipMiscInteractions"), IS_RANDO)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (*should) {
|
||||
Flags_SetInfTable(INFTABLE_RUTO_IN_JJ_WANTS_TO_BE_TOSSED_TO_SAPPHIRE);
|
||||
*should = false;
|
||||
}
|
||||
});
|
||||
|
||||
// Prevents Ruto from running to the Sapphire when she wants to be tossed to it, instead she just stands up and waits for link to get closer
|
||||
REGISTER_VB_SHOULD(VB_RUTO_RUN_TO_SAPPHIRE, {
|
||||
EnRu1* enRu1 = va_arg(args, EnRu1*);
|
||||
DynaPolyActor* dynaPolyActor = va_arg(args, DynaPolyActor*);
|
||||
|
||||
if (!CVarGetInteger(CVAR_ENHANCEMENT("TimeSavers.SkipMiscInteractions"), IS_RANDO)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (*should) {
|
||||
enRu1->unk_28C = (BgBdanObjects*)dynaPolyActor;
|
||||
Flags_SetInfTable(INFTABLE_145);
|
||||
Flags_SetSwitch(gPlayState, 0x02);
|
||||
Flags_SetSwitch(gPlayState, 0x1F);
|
||||
enRu1->action = 42;
|
||||
Animation_Change(&enRu1->skelAnime, (AnimationHeader*)&gRutoChildWait2Anim, 1.0f, 0,
|
||||
Animation_GetLastFrame((void*)&gRutoChildWait2Anim), ANIMMODE_LOOP, -8.0f);
|
||||
enRu1->unk_28C->cameraSetting = 1;
|
||||
Actor* sapphire = func_80AEB124(gPlayState);
|
||||
if (sapphire != NULL) {
|
||||
Actor_Kill(sapphire);
|
||||
}
|
||||
enRu1->actor.room = gPlayState->roomCtx.curRoom.num;
|
||||
*should = false;
|
||||
}
|
||||
});
|
||||
|
||||
// This overrides the behavior that causes Ruto to get upset at you before sitting back down again when INFTABLE_RUTO_IN_JJ_TALK_FIRST_TIME is set
|
||||
GameInteractor::Instance->RegisterGameHookForID<GameInteractor::OnActorInit>(ACTOR_EN_RU1, [](void* actorRef) {
|
||||
EnRu1* enRu1 = static_cast<EnRu1*>(actorRef);
|
||||
if (!CVarGetInteger(CVAR_ENHANCEMENT("TimeSavers.SkipMiscInteractions"), IS_RANDO)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (enRu1->action == 22) {
|
||||
enRu1->action = 27;
|
||||
enRu1->drawConfig = 1;
|
||||
enRu1->actor.flags |= ACTOR_FLAG_TARGETABLE | ACTOR_FLAG_FRIENDLY;
|
||||
Animation_Change(&enRu1->skelAnime, (AnimationHeader*)&gRutoChildSittingAnim, 1.0f, 0.0f,
|
||||
Animation_GetLastFrame((void*)&gRutoChildSittingAnim), ANIMMODE_LOOP, 0.0f);
|
||||
}
|
||||
});
|
||||
}
|
@ -10,7 +10,9 @@ void TimeSavers_Register() {
|
||||
SkipZeldaFleeingCastle_Register();
|
||||
SkipIntro_Register();
|
||||
// SkipMiscInteractions
|
||||
MoveJabuJabuElevator_Register();
|
||||
MoveMidoInKokiriForest_Register();
|
||||
SkipChildRutoInteractions_Register();
|
||||
FasterHeavyBlockLift_Register();
|
||||
FasterRupeeAccumulator_Register();
|
||||
}
|
||||
|
@ -12,7 +12,9 @@ void TimeSavers_Register();
|
||||
void SkipZeldaFleeingCastle_Register();
|
||||
void SkipIntro_Register();
|
||||
// SkipMiscInteractions
|
||||
void MoveJabuJabuElevator_Register();
|
||||
void MoveMidoInKokiriForest_Register();
|
||||
void SkipChildRutoInteractions_Register();
|
||||
void FasterHeavyBlockLift_Register();
|
||||
void FasterRupeeAccumulator_Register();
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,13 +1,6 @@
|
||||
#pragma once
|
||||
#include <libultraship/libultraship.h>
|
||||
|
||||
#define PATCH_GFX(path, name, cvar, index, instruction) \
|
||||
if (CVarGetInteger(cvar, 0)) { \
|
||||
ResourceMgr_PatchGfxByName(path, name, index, instruction); \
|
||||
} else { \
|
||||
ResourceMgr_UnpatchGfxByName(path, name); \
|
||||
}
|
||||
|
||||
// Not to be confused with tabs, groups are 1:1 with the boxes shown in the UI, grouping them allows us to reset/randomize
|
||||
// every item in a group at once. If you are looking for tabs they are rendered manually in ImGui in `DrawCosmeticsEditor`
|
||||
typedef enum {
|
||||
@ -28,9 +21,19 @@ typedef enum {
|
||||
COSMETICS_GROUP_TRAILS,
|
||||
COSMETICS_GROUP_NAVI,
|
||||
COSMETICS_GROUP_IVAN,
|
||||
COSMETICS_GROUP_MESSAGE,
|
||||
COSMETICS_GROUP_MAX
|
||||
} CosmeticGroup;
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif //__cplusplus
|
||||
|
||||
Color_RGBA8 CosmeticsEditor_GetDefaultValue(const char* id);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
const std::string Name;
|
||||
const std::string ToolTip;
|
||||
@ -46,8 +49,7 @@ static float TablesCellsWidth = 300.0f;
|
||||
static ImGuiTableColumnFlags FlagsTable = ImGuiTableFlags_BordersH | ImGuiTableFlags_BordersV;
|
||||
static ImGuiTableColumnFlags FlagsCell = ImGuiTableColumnFlags_WidthStretch | ImGuiTableColumnFlags_IndentEnable | ImGuiTableColumnFlags_NoSort;
|
||||
|
||||
void InitCosmeticsEditor();//Init the menu itself
|
||||
ImVec4 GetRandomValue(int MaximumPossible);
|
||||
ImVec4 GetRandomValue();
|
||||
void CosmeticsEditor_RandomizeAll();
|
||||
void CosmeticsEditor_RandomizeGroup(CosmeticGroup group);
|
||||
void CosmeticsEditor_ResetAll();
|
||||
@ -61,4 +63,5 @@ class CosmeticsEditorWindow : public Ship::GuiWindow {
|
||||
void InitElement() override;
|
||||
void DrawElement() override;
|
||||
void UpdateElement() override {};
|
||||
};
|
||||
};
|
||||
#endif //__cplusplus
|
209
soh/soh/Enhancements/custom-collectible/CustomCollectible.cpp
Normal file
209
soh/soh/Enhancements/custom-collectible/CustomCollectible.cpp
Normal file
@ -0,0 +1,209 @@
|
||||
#include "CustomCollectible.h"
|
||||
#include <libultraship/libultraship.h>
|
||||
#include "soh/Enhancements/game-interactor/GameInteractor.h"
|
||||
#include "soh/Enhancements/randomizer/3drando/random.hpp"
|
||||
#include "soh/frame_interpolation.h"
|
||||
#include "soh/Enhancements/custom-message/CustomMessageManager.h"
|
||||
|
||||
extern "C" {
|
||||
#include "z64actor.h"
|
||||
#include "functions.h"
|
||||
#include "variables.h"
|
||||
#include "macros.h"
|
||||
#include "objects/object_md/object_md.h"
|
||||
extern PlayState* gPlayState;
|
||||
}
|
||||
|
||||
EnItem00* CustomCollectible::Spawn(f32 posX, f32 posY, f32 posZ, s16 rot, s16 flags, s16 params, ActorFunc actionFunc,
|
||||
ActorFunc drawFunc) {
|
||||
if (!gPlayState) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Actor* actor = Actor_Spawn(&gPlayState->actorCtx, gPlayState, ACTOR_EN_ITEM00, posX, posY, posZ, flags, rot, params, ITEM00_NONE, 0);
|
||||
EnItem00* enItem00 = (EnItem00*)actor;
|
||||
|
||||
if (actionFunc != NULL) {
|
||||
enItem00->actionFunc = (EnItem00ActionFunc)actionFunc;
|
||||
}
|
||||
|
||||
if (drawFunc != NULL) {
|
||||
actor->draw = drawFunc;
|
||||
}
|
||||
|
||||
return enItem00;
|
||||
}
|
||||
|
||||
void CustomCollectible_Init(Actor* actor, PlayState* play) {
|
||||
EnItem00* enItem00 = (EnItem00*)actor;
|
||||
|
||||
if (CUSTOM_ITEM_FLAGS & CustomCollectible::STOP_BOBBING) {
|
||||
actor->shape.yOffset = 1250.0f;
|
||||
} else {
|
||||
actor->shape.yOffset = (Math_SinS(actor->shape.rot.y) * 150.0f) + 1250.0f;
|
||||
}
|
||||
|
||||
if (CUSTOM_ITEM_FLAGS & CustomCollectible::HIDE_TILL_OVERHEAD) {
|
||||
Actor_SetScale(actor, 0.0f);
|
||||
} else {
|
||||
Actor_SetScale(actor, 0.015f);
|
||||
}
|
||||
|
||||
if (CUSTOM_ITEM_FLAGS & CustomCollectible::KEEP_ON_PLAYER) {
|
||||
Math_Vec3f_Copy(&actor->world.pos, &GET_PLAYER(play)->actor.world.pos);
|
||||
}
|
||||
|
||||
if (CUSTOM_ITEM_FLAGS & CustomCollectible::TOSS_ON_SPAWN) {
|
||||
actor->velocity.y = 8.0f;
|
||||
actor->speedXZ = 2.0f;
|
||||
actor->gravity = -1.4f;
|
||||
actor->world.rot.y = Rand_ZeroOne() * 40000.0f;
|
||||
}
|
||||
|
||||
enItem00->unk_15A = -1;
|
||||
}
|
||||
|
||||
// By default this will just assume the GID was passed in as the rot z, if you want different functionality you should
|
||||
// override the draw
|
||||
void CustomCollectible_Draw(Actor* actor, PlayState* play) {
|
||||
Matrix_Scale(30.0f, 30.0f, 30.0f, MTXMODE_APPLY);
|
||||
GetItem_Draw(play, CUSTOM_ITEM_PARAM);
|
||||
}
|
||||
|
||||
void CustomCollectible_Update(Actor* actor, PlayState* play) {
|
||||
EnItem00* enItem00 = (EnItem00*)actor;
|
||||
Player* player = GET_PLAYER(play);
|
||||
|
||||
if (!(CUSTOM_ITEM_FLAGS & CustomCollectible::STOP_SPINNING)) {
|
||||
actor->shape.rot.y += 960;
|
||||
}
|
||||
|
||||
if (CUSTOM_ITEM_FLAGS & CustomCollectible::STOP_BOBBING) {
|
||||
actor->shape.yOffset = 1250.0f;
|
||||
} else {
|
||||
actor->shape.yOffset = (Math_SinS(actor->shape.rot.y) * 150.0f) + 1250.0f;
|
||||
}
|
||||
|
||||
if (CUSTOM_ITEM_FLAGS & CustomCollectible::HIDE_TILL_OVERHEAD) {
|
||||
Actor_SetScale(actor, 0.0f);
|
||||
}
|
||||
|
||||
if (CUSTOM_ITEM_FLAGS & CustomCollectible::KEEP_ON_PLAYER) {
|
||||
actor->gravity = 0.0f;
|
||||
Math_Vec3f_Copy(&actor->world.pos, &GET_PLAYER(play)->actor.world.pos);
|
||||
}
|
||||
|
||||
if (CUSTOM_ITEM_FLAGS & CustomCollectible::KILL_ON_TOUCH) {
|
||||
// Pretty self explanatory, if the player is within range, kill the actor and call the action function
|
||||
if ((actor->xzDistToPlayer <= 50.0f) && (fabsf(actor->yDistToPlayer) <= fabsf(20.0f))) {
|
||||
if (enItem00->actionFunc != NULL) {
|
||||
enItem00->actionFunc(enItem00, play);
|
||||
CUSTOM_ITEM_FLAGS |= CustomCollectible::CALLED_ACTION;
|
||||
}
|
||||
Actor_Kill(actor);
|
||||
}
|
||||
} else if (CUSTOM_ITEM_FLAGS & CustomCollectible::GIVE_OVERHEAD) {
|
||||
// If the item hasn't been picked up (unk_15A == -1) and the player is within range
|
||||
if (enItem00->unk_15A == -1 && (actor->xzDistToPlayer <= 50.0f) &&
|
||||
(fabsf(actor->yDistToPlayer) <= fabsf(20.0f))) {
|
||||
// Fire the action function
|
||||
if (enItem00->actionFunc != NULL) {
|
||||
enItem00->actionFunc(enItem00, play);
|
||||
CUSTOM_ITEM_FLAGS |= CustomCollectible::CALLED_ACTION;
|
||||
}
|
||||
Sfx_PlaySfxCentered(NA_SE_SY_GET_ITEM);
|
||||
// Set the unk_15A to 15, this indicates the item has been picked up and will start the overhead animation
|
||||
enItem00->unk_15A = 15;
|
||||
CUSTOM_ITEM_FLAGS |= CustomCollectible::STOP_BOBBING;
|
||||
CUSTOM_ITEM_FLAGS |= CustomCollectible::KEEP_ON_PLAYER;
|
||||
}
|
||||
|
||||
// If the item has been picked up
|
||||
if (enItem00->unk_15A > 0) {
|
||||
// Reduce the size a bit, but also makes it visible for HIDE_TILL_OVERHEAD
|
||||
Actor_SetScale(actor, 0.010f);
|
||||
|
||||
// Decrement the unk_15A, which will be used to bob the item up and down
|
||||
enItem00->unk_15A--;
|
||||
|
||||
// Account for the different heights of the player forms
|
||||
f32 height = 45.0f;
|
||||
// TODO: Check for adult?
|
||||
|
||||
// Bob the item up and down
|
||||
actor->world.pos.y += (height + (Math_SinS(enItem00->unk_15A * 15000) * (enItem00->unk_15A * 0.3f)));
|
||||
}
|
||||
|
||||
// Finally, once the bobbing animation is done, kill the actor
|
||||
if (enItem00->unk_15A == 0) {
|
||||
Actor_Kill(actor);
|
||||
}
|
||||
} else if (CUSTOM_ITEM_FLAGS & CustomCollectible::GIVE_ITEM_CUTSCENE) {
|
||||
// If the item hasn't been picked up and the player is within range
|
||||
if (!Actor_HasParent(actor, play) && enItem00->unk_15A == -1) {
|
||||
Actor_OfferGetItem(actor, play, GI_SHIP, 50.0f, 20.0f);
|
||||
} else {
|
||||
if (enItem00->unk_15A == -1) {
|
||||
CUSTOM_ITEM_FLAGS |= CustomCollectible::STOP_BOBBING;
|
||||
CUSTOM_ITEM_FLAGS |= CustomCollectible::KEEP_ON_PLAYER;
|
||||
CUSTOM_ITEM_FLAGS |= CustomCollectible::HIDE_TILL_OVERHEAD;
|
||||
}
|
||||
|
||||
// Begin incrementing the unk_15A, indicating the item has been picked up
|
||||
enItem00->unk_15A++;
|
||||
|
||||
// For the first 20 frames, wait while the player's animation plays
|
||||
if (enItem00->unk_15A >= 20) {
|
||||
// After the first 20 frames, show the item and call the action function
|
||||
if (enItem00->unk_15A == 20 && enItem00->actionFunc != NULL) {
|
||||
enItem00->actionFunc(enItem00, play);
|
||||
CUSTOM_ITEM_FLAGS |= CustomCollectible::CALLED_ACTION;
|
||||
}
|
||||
// Override the bobbing animation to be a fixed height
|
||||
actor->shape.yOffset = 900.0f;
|
||||
Actor_SetScale(actor, 0.007f);
|
||||
|
||||
f32 height = 45.0f;
|
||||
// TODO: Check for adult?
|
||||
|
||||
actor->world.pos.y += height;
|
||||
}
|
||||
|
||||
// Once the player is no longer in the "Give Item" state, kill the actor
|
||||
if (!(player->stateFlags1 & PLAYER_STATE1_GETTING_ITEM)) {
|
||||
Actor_Kill(actor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (actor->gravity != 0.0f) {
|
||||
Actor_MoveForward(actor);
|
||||
Actor_UpdateBgCheckInfo(play, actor, 20.0f, 15.0f, 15.0f, 0x1D);
|
||||
}
|
||||
|
||||
if (actor->bgCheckFlags & 0x0003) {
|
||||
actor->speedXZ = 0.0f;
|
||||
}
|
||||
|
||||
Collider_UpdateCylinder(actor, &enItem00->collider);
|
||||
CollisionCheck_SetAC(play, &play->colChkCtx, &enItem00->collider.base);
|
||||
}
|
||||
|
||||
void CustomCollectible::RegisterHooks() {
|
||||
GameInteractor::Instance->RegisterGameHookForID<GameInteractor::ShouldActorInit>(
|
||||
ACTOR_EN_ITEM00, [](void* actorRef, bool* should) {
|
||||
Actor* actor = (Actor*)actorRef;
|
||||
if (actor->params != ITEM00_NONE) {
|
||||
return;
|
||||
}
|
||||
|
||||
actor->init = CustomCollectible_Init;
|
||||
actor->update = CustomCollectible_Update;
|
||||
actor->draw = CustomCollectible_Draw;
|
||||
actor->destroy = NULL;
|
||||
|
||||
// Set the rotX/rotZ back to 0, the original values can be accessed from actor->home
|
||||
actor->world.rot.x = 0;
|
||||
actor->world.rot.z = 0;
|
||||
});
|
||||
}
|
24
soh/soh/Enhancements/custom-collectible/CustomCollectible.h
Normal file
24
soh/soh/Enhancements/custom-collectible/CustomCollectible.h
Normal file
@ -0,0 +1,24 @@
|
||||
extern "C" {
|
||||
#include "z64actor.h"
|
||||
}
|
||||
|
||||
#define CUSTOM_ITEM_FLAGS (actor->home.rot.x)
|
||||
#define CUSTOM_ITEM_PARAM (actor->home.rot.z)
|
||||
|
||||
namespace CustomCollectible {
|
||||
|
||||
enum CustomCollectibleFlags : int16_t {
|
||||
KILL_ON_TOUCH = 1 << 0, // 0000 0000 0000 0001
|
||||
GIVE_OVERHEAD = 1 << 1, // 0000 0000 0000 0010
|
||||
GIVE_ITEM_CUTSCENE = 1 << 2, // 0000 0000 0000 0100
|
||||
HIDE_TILL_OVERHEAD = 1 << 3, // 0000 0000 0000 1000
|
||||
KEEP_ON_PLAYER = 1 << 4, // 0000 0000 0001 0000
|
||||
STOP_BOBBING = 1 << 5, // 0000 0000 0010 0000
|
||||
STOP_SPINNING = 1 << 6, // 0000 0000 0100 0000
|
||||
CALLED_ACTION = 1 << 7, // 0000 0000 1000 0000
|
||||
TOSS_ON_SPAWN = 1 << 8, // 0000 0001 0000 0000
|
||||
};
|
||||
void RegisterHooks();
|
||||
EnItem00* Spawn(f32 posX, f32 posY, f32 posZ, s16 rot, s16 flags, s16 params, ActorFunc actionFunc = NULL,
|
||||
ActorFunc drawFunc = NULL);
|
||||
}; // namespace CustomCollectible
|
@ -357,6 +357,15 @@ static size_t NextLineLength(const std::string* textStr, const size_t lastNewlin
|
||||
nextPosJump = 1;
|
||||
// Assume worst case for player name 12 * 8 (widest character * longest name length)
|
||||
totalPixelWidth += 96;
|
||||
} else if (textStr->at(currentPos) == '\x05') {
|
||||
// Skip colour control characters.
|
||||
nextPosJump = 2;
|
||||
} else if (textStr->at(currentPos) == '\x1E') {
|
||||
//For the high score char, we have to take the next Char, then use that to get a worst case scenario.
|
||||
if (textStr->at(currentPos+1) == '\x01'){
|
||||
totalPixelWidth += 28;
|
||||
}
|
||||
nextPosJump = 2;
|
||||
} else {
|
||||
// Some characters only one byte while others are two bytes
|
||||
// So check both possibilities when checking for a character
|
||||
@ -382,6 +391,65 @@ static size_t NextLineLength(const std::string* textStr, const size_t lastNewlin
|
||||
}
|
||||
}
|
||||
|
||||
size_t CustomMessage::FindNEWLINE(std::string& str, size_t lastNewline) const {
|
||||
size_t newLine = str.find(NEWLINE()[0], lastNewline);
|
||||
bool done;
|
||||
|
||||
// Bail out early
|
||||
if (newLine == std::string::npos) {
|
||||
return newLine;
|
||||
}
|
||||
|
||||
do {
|
||||
done = true;
|
||||
if (newLine != 0) {
|
||||
switch (str[newLine - 1]) {
|
||||
case '\x05': // COLOR
|
||||
case '\x06': // SHIFT
|
||||
case '\x07': // TEXTID
|
||||
case '\x0C': // BOX_BREAK_DELAYED
|
||||
case '\x0E': // FADE
|
||||
case '\x11': // FADE2
|
||||
case '\x12': // SFX
|
||||
case '\x13': // ITEM_ICON
|
||||
case '\x14': // TEXT_SPEED
|
||||
case '\x15': // BACKGROUND
|
||||
case '\x1E': // POINTS/HIGH_SCORE
|
||||
done = false;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (newLine > 1) {
|
||||
switch (str[newLine - 2]) {
|
||||
case '\x07': // TEXTID
|
||||
case '\x11': // FADE2
|
||||
case '\x12': // SFX
|
||||
case '\x15': // BACKGROUND
|
||||
done = false;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (newLine > 2) {
|
||||
if (str[newLine - 3] == '\x15') { // BACKGROUND
|
||||
done = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!done) {
|
||||
newLine = str.find(NEWLINE()[0], newLine + 1);
|
||||
if (newLine == std::string::npos) {
|
||||
// if we reach the end of the string, quit now to save a loop
|
||||
done = true;
|
||||
}
|
||||
}
|
||||
} while (!done);
|
||||
|
||||
return newLine;
|
||||
}
|
||||
|
||||
void CustomMessage::AutoFormatString(std::string& str) const {
|
||||
ReplaceAltarIcons(str);
|
||||
ReplaceColors(str);
|
||||
@ -396,7 +464,7 @@ void CustomMessage::AutoFormatString(std::string& str) const {
|
||||
const size_t ampersand = str.find('&', lastNewline);
|
||||
const size_t lastSpace = str.rfind(' ', lastNewline + lineLength);
|
||||
size_t waitForInput = str.find(WAIT_FOR_INPUT()[0], lastNewline);
|
||||
size_t newLine = str.find(NEWLINE()[0], lastNewline);
|
||||
size_t newLine = FindNEWLINE(str, lastNewline);
|
||||
if (carrot < waitForInput){
|
||||
waitForInput = carrot;
|
||||
}
|
||||
|
@ -181,6 +181,12 @@ class CustomMessage {
|
||||
*/
|
||||
void FormatString(std::string& str) const;
|
||||
|
||||
/**
|
||||
* @brief finds NEWLINEs in a string, while filtering
|
||||
* /x01's that are used as opperands
|
||||
*/
|
||||
size_t FindNEWLINE(std::string& str, size_t lastNewline) const;
|
||||
|
||||
/**
|
||||
* @brief formats the string specifically to fit in OoT's
|
||||
* textboxes, and use it's formatting.
|
||||
|
@ -1312,6 +1312,7 @@ static constexpr std::array<std::pair<const char*, CosmeticGroup>, COSMETICS_GRO
|
||||
{"trials", COSMETICS_GROUP_TRAILS},
|
||||
{"navi", COSMETICS_GROUP_NAVI},
|
||||
{"ivan", COSMETICS_GROUP_IVAN},
|
||||
{"message", COSMETICS_GROUP_MESSAGE},
|
||||
}};
|
||||
|
||||
static bool CosmeticsHandler(std::shared_ptr<Ship::Console> Console, const std::vector<std::string>& args, std::string* output) {
|
||||
|
@ -4,14 +4,14 @@
|
||||
#include <string>
|
||||
#include <version>
|
||||
|
||||
static std::unordered_map<const char*, std::unordered_map<HOOK_ID, HookInfo>> hookData;
|
||||
static std::unordered_map<const char*, std::unordered_map<HOOK_ID, HookInfo>*> hookData;
|
||||
|
||||
const ImVec4 grey = ImVec4(0.75, 0.75, 0.75, 1);
|
||||
const ImVec4 yellow = ImVec4(1, 1, 0, 1);
|
||||
const ImVec4 red = ImVec4(1, 0, 0, 1);
|
||||
|
||||
void DrawHookRegisteringInfos(const char* hookName) {
|
||||
if (hookData[hookName].size() == 0) {
|
||||
if ((*hookData[hookName]).size() == 0) {
|
||||
ImGui::TextColored(grey, "No hooks found");
|
||||
return;
|
||||
}
|
||||
@ -27,7 +27,7 @@ void DrawHookRegisteringInfos(const char* hookName) {
|
||||
//ImGui::TableSetupColumn("Stub");
|
||||
ImGui::TableSetupColumn("Number of Calls");
|
||||
ImGui::TableHeadersRow();
|
||||
for (auto& [id, hookInfo] : hookData[hookName]) {
|
||||
for (auto& [id, hookInfo] : (*hookData[hookName])) {
|
||||
ImGui::TableNextRow();
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
@ -100,12 +100,10 @@ void HookDebuggerWindow::DrawElement() {
|
||||
}
|
||||
}
|
||||
|
||||
void HookDebuggerWindow::UpdateElement() {
|
||||
hookData.clear();
|
||||
|
||||
void HookDebuggerWindow::InitElement() {
|
||||
#define DEFINE_HOOK(name, _) hookData.insert({#name, GameInteractor::Instance->GetHookData<GameInteractor::name>()});
|
||||
|
||||
#include "../game-interactor/GameInteractor_HookTable.h"
|
||||
|
||||
#undef DEFINE_HOOK
|
||||
}
|
||||
}
|
||||
|
@ -4,7 +4,7 @@ class HookDebuggerWindow : public Ship::GuiWindow {
|
||||
public:
|
||||
using GuiWindow::GuiWindow;
|
||||
|
||||
void InitElement() override {};
|
||||
void InitElement() override;
|
||||
void DrawElement() override;
|
||||
void UpdateElement() override;
|
||||
void UpdateElement() override {};
|
||||
};
|
||||
|
@ -345,6 +345,19 @@ typedef enum {
|
||||
VB_INFLICT_VOID_DAMAGE,
|
||||
VB_GANONDORF_DECIDE_TO_FIGHT,
|
||||
VB_USE_ITEM,
|
||||
// Vanilla condition: Close enough & various cutscene checks
|
||||
// Opt: *EnRu1
|
||||
VB_PLAY_CHILD_RUTO_INTRO,
|
||||
// Vanilla condition: !INFTABLE_RUTO_IN_JJ_WANTS_TO_BE_TOSSED_TO_SAPPHIRE && in the big okto room
|
||||
// Opt: *EnRu1
|
||||
VB_RUTO_WANT_TO_BE_TOSSED_TO_SAPPHIRE,
|
||||
// Vanilla condition: Landed on the platform in the big okto room
|
||||
// Opt: *EnRu1
|
||||
VB_RUTO_RUN_TO_SAPPHIRE,
|
||||
// Vanilla condition: !Flags_GetInfTable(INFTABLE_145)
|
||||
// Opt: *EnRu1
|
||||
VB_RUTO_BE_CONSIDERED_NOT_KIDNAPPED,
|
||||
|
||||
|
||||
/*** Give Items ***/
|
||||
|
||||
@ -658,8 +671,8 @@ public:
|
||||
inline static std::vector<HOOK_ID> hooksForFilter;
|
||||
};
|
||||
|
||||
template <typename H> std::unordered_map<uint32_t, HookInfo> GetHookData() {
|
||||
return RegisteredGameHooks<H>::hookData;
|
||||
template <typename H> std::unordered_map<uint32_t, HookInfo>* GetHookData() {
|
||||
return &RegisteredGameHooks<H>::hookData;
|
||||
}
|
||||
|
||||
// General Hooks
|
||||
|
@ -125,8 +125,8 @@ namespace Rando {
|
||||
std::make_shared<KaleidoEntryIconCountRequired>(
|
||||
gTriforcePieceTex, G_IM_FMT_RGBA, G_IM_SIZ_32b, 32, 32, Color_RGBA8{ 255,255,255,255 }, 0,
|
||||
yOffset, reinterpret_cast<int*>(&gSaveContext.triforcePiecesCollected),
|
||||
ctx->GetOption(RSK_TRIFORCE_HUNT_PIECES_REQUIRED).GetSelectedOptionIndex() + 1,
|
||||
ctx->GetOption(RSK_TRIFORCE_HUNT_PIECES_TOTAL).GetSelectedOptionIndex() + 1));
|
||||
ctx->GetOption(RSK_TRIFORCE_HUNT_PIECES_REQUIRED).GetContextOptionIndex() + 1,
|
||||
ctx->GetOption(RSK_TRIFORCE_HUNT_PIECES_TOTAL).GetContextOptionIndex() + 1));
|
||||
yOffset += 18;
|
||||
}
|
||||
if (ctx->GetOption(RSK_SHUFFLE_OCARINA_BUTTONS)) {
|
||||
|
@ -1038,6 +1038,9 @@ std::vector<AltTrapType> getEnabledAddTraps () {
|
||||
std::vector<AltTrapType> enabledAddTraps;
|
||||
for (int i = 0; i < ADD_TRAP_MAX; i++) {
|
||||
if (CVarGetInteger(altTrapTypeCvars[i], 0)) {
|
||||
if (gSaveContext.equips.buttonItems[0] == ITEM_FISHING_POLE && (i == ADD_VOID_TRAP || i == ADD_TELEPORT_TRAP)) {
|
||||
continue; // don't add void or teleport if you're holding the fishing pole, as this causes issues
|
||||
}
|
||||
enabledAddTraps.push_back(static_cast<AltTrapType>(i));
|
||||
}
|
||||
}
|
||||
|
@ -242,6 +242,8 @@ const std::vector<const char*> enhancementsCvars = {
|
||||
CVAR_ENHANCEMENT("AuthenticLogo"),
|
||||
CVAR_ENHANCEMENT("PauseLiveLinkRotationSpeed"),
|
||||
CVAR_ENHANCEMENT("BowReticle"),
|
||||
CVAR_ENHANCEMENT("BoomerangFirstPerson"),
|
||||
CVAR_ENHANCEMENT("BoomerangReticle"),
|
||||
CVAR_ENHANCEMENT("FixTexturesOOB"),
|
||||
CVAR_ENHANCEMENT("IvanCoopModeEnabled"),
|
||||
CVAR_ENHANCEMENT("EnemySpawnsOverWaterboxes"),
|
||||
@ -304,6 +306,7 @@ const std::vector<const char*> enhancementsCvars = {
|
||||
CVAR_ENHANCEMENT("TimeSavers.SkipChildStealth"),
|
||||
CVAR_ENHANCEMENT("TimeSavers.SkipTowerEscape"),
|
||||
CVAR_ENHANCEMENT("TimeSavers.SkipForcedDialog"),
|
||||
CVAR_ENHANCEMENT("TimeSavers.SleepingWaterfall"),
|
||||
CVAR_ENHANCEMENT("SlowTextSpeed"),
|
||||
};
|
||||
|
||||
@ -549,6 +552,7 @@ const std::vector<const char*> randomizerCvars = {
|
||||
CVAR_RANDOMIZER_SETTING("SkipChildZelda"),
|
||||
CVAR_RANDOMIZER_SETTING("SkipEponaRace"),
|
||||
CVAR_RANDOMIZER_SETTING("SkipScarecrowsSong"),
|
||||
CVAR_RANDOMIZER_SETTING("SleepingWaterfall"),
|
||||
CVAR_RANDOMIZER_SETTING("StartingAge"),
|
||||
CVAR_RANDOMIZER_SETTING("StartingBoleroOfFire"),
|
||||
CVAR_RANDOMIZER_SETTING("StartingConsumables"),
|
||||
@ -1175,12 +1179,13 @@ const std::vector<PresetEntry> s6PresetEntries = {
|
||||
PRESET_ENTRY_S32(CVAR_RANDOMIZER_SETTING("SkipChildZelda"), 1),
|
||||
PRESET_ENTRY_S32(CVAR_RANDOMIZER_SETTING("SkipEponaRace"), 1),
|
||||
PRESET_ENTRY_S32(CVAR_RANDOMIZER_SETTING("SkipTowerEscape"), 1),
|
||||
PRESET_ENTRY_S32(CVAR_RANDOMIZER_SETTING("SleepingWaterfall"), RO_WATERFALL_CLOSED),
|
||||
PRESET_ENTRY_S32(CVAR_RANDOMIZER_SETTING("StartingAge"), RO_AGE_RANDOM),
|
||||
PRESET_ENTRY_S32(CVAR_RANDOMIZER_SETTING("StartingConsumables"), 1),
|
||||
PRESET_ENTRY_S32(CVAR_RANDOMIZER_SETTING("StartingDekuShield"), 1),
|
||||
PRESET_ENTRY_S32(CVAR_RANDOMIZER_SETTING("StartingMapsCompasses"), RO_DUNGEON_ITEM_LOC_STARTWITH),
|
||||
PRESET_ENTRY_S32(CVAR_RANDOMIZER_SETTING("StartingOcarina"), 1),
|
||||
PRESET_ENTRY_S32(CVAR_RANDOMIZER_SETTING("ZorasFountain"), 0),
|
||||
PRESET_ENTRY_S32(CVAR_RANDOMIZER_SETTING("ZorasFountain"), RO_ZF_CLOSED),
|
||||
};
|
||||
|
||||
const std::vector<PresetEntry> hellModePresetEntries = {
|
||||
@ -1235,16 +1240,18 @@ const std::vector<PresetEntry> hellModePresetEntries = {
|
||||
PRESET_ENTRY_S32(CVAR_RANDOMIZER_SETTING("SkipEponaRace"), 1),
|
||||
PRESET_ENTRY_S32(CVAR_RANDOMIZER_SETTING("SkipScarecrowsSong"), 1),
|
||||
PRESET_ENTRY_S32(CVAR_RANDOMIZER_SETTING("SkipTowerEscape"), 1),
|
||||
PRESET_ENTRY_S32(CVAR_RANDOMIZER_SETTING("SleepingWaterfall"), RO_WATERFALL_OPEN),
|
||||
PRESET_ENTRY_S32(CVAR_RANDOMIZER_SETTING("StartingAge"), RO_AGE_RANDOM),
|
||||
PRESET_ENTRY_S32(CVAR_RANDOMIZER_SETTING("StartingMapsCompasses"), RO_DUNGEON_ITEM_LOC_ANYWHERE),
|
||||
PRESET_ENTRY_S32(CVAR_RANDOMIZER_SETTING("SunlightArrows"), 1),
|
||||
PRESET_ENTRY_S32(CVAR_RANDOMIZER_SETTING("ZorasFountain"), 2),
|
||||
PRESET_ENTRY_S32(CVAR_RANDOMIZER_SETTING("ZorasFountain"), RO_ZF_OPEN),
|
||||
};
|
||||
|
||||
const std::vector<PresetEntry> BenchmarkPresetEntries = {
|
||||
PRESET_ENTRY_S32(CVAR_RANDOMIZER_SETTING("Forest"), RO_FOREST_CLOSED_DEKU),
|
||||
PRESET_ENTRY_S32(CVAR_RANDOMIZER_SETTING("KakarikoGate"), RO_KAK_GATE_OPEN),
|
||||
PRESET_ENTRY_S32(CVAR_RANDOMIZER_SETTING("DoorOfTime"), RO_DOOROFTIME_SONGONLY),
|
||||
PRESET_ENTRY_S32(CVAR_RANDOMIZER_SETTING("SleepingWaterfall"), RO_WATERFALL_CLOSED),
|
||||
PRESET_ENTRY_S32(CVAR_RANDOMIZER_SETTING("ZorasFountain"), RO_ZF_CLOSED),
|
||||
PRESET_ENTRY_S32(CVAR_RANDOMIZER_SETTING("GerudoFortress"), RO_GF_NORMAL),
|
||||
PRESET_ENTRY_S32(CVAR_RANDOMIZER_SETTING("RainbowBridge"), RO_BRIDGE_DUNGEON_REWARDS),
|
||||
|
@ -238,10 +238,10 @@ static int GetMaxGSCount() {
|
||||
int maxBridge = 0;
|
||||
int maxLACS = 0;
|
||||
if (ctx->GetOption(RSK_RAINBOW_BRIDGE).Is(RO_BRIDGE_TOKENS)) {
|
||||
maxBridge = ctx->GetOption(RSK_RAINBOW_BRIDGE_TOKEN_COUNT).Value<uint8_t>();
|
||||
maxBridge = ctx->GetOption(RSK_RAINBOW_BRIDGE_TOKEN_COUNT).GetContextOptionIndex();
|
||||
}
|
||||
if (ctx->GetOption(RSK_GANONS_BOSS_KEY).Is(RO_GANON_BOSS_KEY_LACS_TOKENS)) {
|
||||
maxLACS = ctx->GetOption(RSK_LACS_TOKEN_COUNT).Value<uint8_t>();
|
||||
maxLACS = ctx->GetOption(RSK_LACS_TOKEN_COUNT).GetContextOptionIndex();
|
||||
}
|
||||
maxBridge = std::max(maxBridge, maxLACS);
|
||||
//Get the max amount of GS which could be useful from token reward locations
|
||||
@ -266,7 +266,7 @@ static int GetMaxGSCount() {
|
||||
maxUseful = 10;
|
||||
}
|
||||
//Return max of the two possible reasons tokens could be important, minus the tokens in the starting inventory
|
||||
return std::max(maxUseful, maxBridge) - ctx->GetOption(RSK_STARTING_SKULLTULA_TOKEN).Value<uint8_t>();
|
||||
return std::max(maxUseful, maxBridge) - ctx->GetOption(RSK_STARTING_SKULLTULA_TOKEN).GetContextOptionIndex();
|
||||
}
|
||||
|
||||
std::string GetShopItemBaseName(std::string itemName) {
|
||||
@ -856,11 +856,6 @@ static void AssumedFill(const std::vector<RandomizerGet>& items, const std::vect
|
||||
SPDLOG_DEBUG(Rando::StaticData::RetrieveItem(item).GetName().GetEnglish());
|
||||
SPDLOG_DEBUG(". TRYING AGAIN...\n");
|
||||
|
||||
#ifdef ENABLE_DEBUG
|
||||
Regions::DumpWorldGraph(Rando::StaticData::RetrieveItem(item).GetName().GetEnglish());
|
||||
PlacementLog_Write();
|
||||
#endif
|
||||
|
||||
// reset any locations that got an item
|
||||
for (RandomizerCheck loc : attemptedLocations) {
|
||||
ctx->GetItemLocation(loc)->SetPlacedItem(RG_NONE);
|
||||
|
@ -1177,7 +1177,7 @@ void StaticData::HintTable_Init() {
|
||||
// /*spanish*/la tienda de pociones de la abuela
|
||||
|
||||
hintTextTable[RHT_GRAVEYARD_DAMPES_HOUSE] = HintText(CustomMessage("Dampé's Hut",
|
||||
/*german*/ "der #Hut von Boris#",
|
||||
/*german*/ "die #Hütte von Boris#",
|
||||
/*french*/ "la #Cabane du Fossoyeur#"));
|
||||
// /*spanish*/la cabaña de Dampé
|
||||
|
||||
@ -1277,12 +1277,12 @@ void StaticData::HintTable_Init() {
|
||||
// /*spanish*/la #tumba de la Canción del Sol#
|
||||
|
||||
hintTextTable[RHT_GRAVEYARD_COMPOSERS_GRAVE] = HintText(CustomMessage("the #Composers' Grave#",
|
||||
/*german*/ "das Königsgrab",
|
||||
/*german*/ "das #Königsgrab#",
|
||||
/*french*/ "la #Tombe royale#"));
|
||||
// /*spanish*/el #Panteón Real#
|
||||
|
||||
hintTextTable[RHT_GRAVEYARD_DAMPES_GRAVE] = HintText(CustomMessage("Dampé's Grave",
|
||||
/*german*/ "das Grab von Boris",
|
||||
/*german*/ "das #Grab von Boris#",
|
||||
/*french*/ "la #Tombe d'Igor#"));
|
||||
// /*spanish*/la #tumba de Dampé#
|
||||
|
||||
@ -2893,7 +2893,7 @@ void StaticData::HintTable_Init() {
|
||||
{QM_RED, QM_GREEN, QM_GREEN}));
|
||||
|
||||
hintTextTable[RHT_HBA_HINT_HAVE_1000] = HintText(CustomMessage("Hey, newcomer!&Want to take on the #Horseback Archery# challenge?^"
|
||||
"Prove yourself to be a horsemaster by scoring 1500 points to win my #[[1]]#!\x0B",
|
||||
"Prove yourself to be a horsemaster by scoring 1500 points to win my #[[1]]#!\x0B",
|
||||
{QM_RED, QM_GREEN}));
|
||||
|
||||
hintTextTable[RHT_MALON_HINT_HOW_IS_EPONA] = HintText(CustomMessage("@! You should come back with Epona and try to beat my time on the #Obstacle Course#!^"
|
||||
|
@ -222,18 +222,18 @@ uint8_t StonesRequiredBySettings() {
|
||||
auto ctx = Rando::Context::GetInstance();
|
||||
uint8_t stones = 0;
|
||||
if (ctx->GetOption(RSK_RAINBOW_BRIDGE).Is(RO_BRIDGE_STONES)) {
|
||||
stones = ctx->GetOption(RSK_RAINBOW_BRIDGE_STONE_COUNT).Value<uint8_t>();
|
||||
stones = ctx->GetOption(RSK_RAINBOW_BRIDGE_STONE_COUNT).GetContextOptionIndex();
|
||||
} else if (ctx->GetOption(RSK_RAINBOW_BRIDGE).Is(RO_BRIDGE_DUNGEON_REWARDS)) {
|
||||
stones = ctx->GetOption(RSK_RAINBOW_BRIDGE_REWARD_COUNT).Value<uint8_t>() - 6;
|
||||
stones = ctx->GetOption(RSK_RAINBOW_BRIDGE_REWARD_COUNT).GetContextOptionIndex() - 6;
|
||||
} else if ((ctx->GetOption(RSK_RAINBOW_BRIDGE).Is(RO_BRIDGE_DUNGEONS)) && (ctx->GetOption(RSK_SHUFFLE_DUNGEON_REWARDS).Is(RO_DUNGEON_REWARDS_END_OF_DUNGEON))) {
|
||||
stones = ctx->GetOption(RSK_RAINBOW_BRIDGE_DUNGEON_COUNT).Value<uint8_t>() - 6;
|
||||
stones = ctx->GetOption(RSK_RAINBOW_BRIDGE_DUNGEON_COUNT).GetContextOptionIndex() - 6;
|
||||
}
|
||||
if (ctx->GetOption(RSK_GANONS_BOSS_KEY).Is(RO_GANON_BOSS_KEY_LACS_STONES)) {
|
||||
stones = std::max<uint8_t>({ stones, ctx->GetOption(RSK_LACS_STONE_COUNT).Value<uint8_t>() });
|
||||
stones = std::max<uint8_t>({ stones, ctx->GetOption(RSK_LACS_STONE_COUNT).GetContextOptionIndex() });
|
||||
} else if (ctx->GetOption(RSK_GANONS_BOSS_KEY).Is(RO_GANON_BOSS_KEY_LACS_STONES)) {
|
||||
stones = std::max<uint8_t>({ stones, (uint8_t)(ctx->GetOption(RSK_LACS_REWARD_COUNT).Value<uint8_t>() - 6 )});
|
||||
stones = std::max<uint8_t>({ stones, (uint8_t)(ctx->GetOption(RSK_LACS_REWARD_COUNT).GetContextOptionIndex() - 6 )});
|
||||
} else if (ctx->GetOption(RSK_GANONS_BOSS_KEY).Is(RO_GANON_BOSS_KEY_LACS_DUNGEONS)) {
|
||||
stones = std::max<uint8_t>({ stones, (uint8_t)(ctx->GetOption(RSK_LACS_DUNGEON_COUNT).Value<uint8_t>() - 6 )});
|
||||
stones = std::max<uint8_t>({ stones, (uint8_t)(ctx->GetOption(RSK_LACS_DUNGEON_COUNT).GetContextOptionIndex() - 6 )});
|
||||
}
|
||||
return stones;
|
||||
}
|
||||
@ -242,18 +242,18 @@ uint8_t MedallionsRequiredBySettings() {
|
||||
auto ctx = Rando::Context::GetInstance();
|
||||
uint8_t medallions = 0;
|
||||
if (ctx->GetOption(RSK_RAINBOW_BRIDGE).Is(RO_BRIDGE_MEDALLIONS)) {
|
||||
medallions = ctx->GetOption(RSK_RAINBOW_BRIDGE_MEDALLION_COUNT).Value<uint8_t>();
|
||||
medallions = ctx->GetOption(RSK_RAINBOW_BRIDGE_MEDALLION_COUNT).GetContextOptionIndex();
|
||||
} else if (ctx->GetOption(RSK_RAINBOW_BRIDGE).Is(RO_BRIDGE_DUNGEON_REWARDS)) {
|
||||
medallions = ctx->GetOption(RSK_RAINBOW_BRIDGE_REWARD_COUNT).Value<uint8_t>() - 3;
|
||||
medallions = ctx->GetOption(RSK_RAINBOW_BRIDGE_REWARD_COUNT).GetContextOptionIndex() - 3;
|
||||
} else if (ctx->GetOption(RSK_RAINBOW_BRIDGE).Is(RO_BRIDGE_DUNGEONS) && ctx->GetOption(RSK_SHUFFLE_DUNGEON_REWARDS).Is(RO_DUNGEON_REWARDS_END_OF_DUNGEON)) {
|
||||
medallions = ctx->GetOption(RSK_RAINBOW_BRIDGE_DUNGEON_COUNT).Value<uint8_t>() - 3;
|
||||
medallions = ctx->GetOption(RSK_RAINBOW_BRIDGE_DUNGEON_COUNT).GetContextOptionIndex() - 3;
|
||||
}
|
||||
if (ctx->GetOption(RSK_GANONS_BOSS_KEY).Is(RO_GANON_BOSS_KEY_LACS_MEDALLIONS)) {
|
||||
medallions = std::max({ medallions, ctx->GetOption(RSK_LACS_MEDALLION_COUNT).Value<uint8_t>() });
|
||||
medallions = std::max({ medallions, ctx->GetOption(RSK_LACS_MEDALLION_COUNT).GetContextOptionIndex() });
|
||||
} else if (ctx->GetOption(RSK_GANONS_BOSS_KEY).Is(RO_GANON_BOSS_KEY_LACS_REWARDS)) {
|
||||
medallions = std::max({ medallions, (uint8_t)(ctx->GetOption(RSK_LACS_REWARD_COUNT).Value<uint8_t>() - 3 )});
|
||||
medallions = std::max({ medallions, (uint8_t)(ctx->GetOption(RSK_LACS_REWARD_COUNT).GetContextOptionIndex() - 3 )});
|
||||
} else if (ctx->GetOption(RSK_GANONS_BOSS_KEY).Is(RO_GANON_BOSS_KEY_LACS_DUNGEONS) && ctx->GetOption(RSK_SHUFFLE_DUNGEON_REWARDS).Is(RO_DUNGEON_REWARDS_END_OF_DUNGEON)) {
|
||||
medallions = std::max({ medallions, (uint8_t)(ctx->GetOption(RSK_LACS_DUNGEON_COUNT).Value<uint8_t>() - 3 )});
|
||||
medallions = std::max({ medallions, (uint8_t)(ctx->GetOption(RSK_LACS_DUNGEON_COUNT).GetContextOptionIndex() - 3 )});
|
||||
}
|
||||
return medallions;
|
||||
}
|
||||
@ -262,10 +262,10 @@ uint8_t TokensRequiredBySettings() {
|
||||
auto ctx = Rando::Context::GetInstance();
|
||||
uint8_t tokens = 0;
|
||||
if (ctx->GetOption(RSK_RAINBOW_BRIDGE).Is(RO_BRIDGE_TOKENS)) {
|
||||
tokens = ctx->GetOption(RSK_RAINBOW_BRIDGE_TOKEN_COUNT).Value<uint8_t>();
|
||||
tokens = ctx->GetOption(RSK_RAINBOW_BRIDGE_TOKEN_COUNT).GetContextOptionIndex();
|
||||
}
|
||||
if (ctx->GetOption(RSK_GANONS_BOSS_KEY).Is(RO_GANON_BOSS_KEY_LACS_TOKENS)) {
|
||||
tokens = std::max<uint8_t>({ tokens, ctx->GetOption(RSK_LACS_TOKEN_COUNT).Value<uint8_t>() });
|
||||
tokens = std::max<uint8_t>({ tokens, ctx->GetOption(RSK_LACS_TOKEN_COUNT).GetContextOptionIndex() });
|
||||
}
|
||||
return tokens;
|
||||
}
|
||||
@ -273,7 +273,7 @@ uint8_t TokensRequiredBySettings() {
|
||||
std::vector<std::pair<RandomizerCheck, std::function<bool()>>> conditionalAlwaysHints = {
|
||||
std::make_pair(RC_MARKET_10_BIG_POES, []() {
|
||||
auto ctx = Rando::Context::GetInstance();
|
||||
return ctx->GetOption(RSK_BIG_POE_COUNT).Value<uint8_t>() >= 3 && !ctx->GetOption(RSK_BIG_POES_HINT);
|
||||
return ctx->GetOption(RSK_BIG_POE_COUNT).GetContextOptionIndex() >= 3 && !ctx->GetOption(RSK_BIG_POES_HINT);
|
||||
}), // Remember, the option's value being 3 means 4 are required
|
||||
std::make_pair(RC_DEKU_THEATER_MASK_OF_TRUTH, []() {
|
||||
auto ctx = Rando::Context::GetInstance();
|
||||
@ -483,7 +483,7 @@ static void CreateTrialHints(uint8_t copies) {
|
||||
AddGossipStoneHintCopies(copies, HINT_TYPE_HINT_KEY, "Trial", {RHT_ZERO_TRIALS});
|
||||
} else {
|
||||
std::vector<TrialInfo*> trials = ctx->GetTrials()->GetTrialList(); //there's probably a way to remove this assignment
|
||||
if (ctx->GetOption(RSK_TRIAL_COUNT).Value<uint8_t>() >= 4) {//4 or 5 required trials, get skipped trials
|
||||
if (ctx->GetOption(RSK_TRIAL_COUNT).GetContextOptionIndex() >= 4) {//4 or 5 required trials, get skipped trials
|
||||
trials = FilterFromPool(trials, [](TrialInfo* trial){return trial->IsSkipped();});
|
||||
} else {//1 to 3 trials, get requried trials
|
||||
auto requiredTrials = FilterFromPool(trials, [](TrialInfo* trial){return trial->IsRequired();});
|
||||
@ -611,7 +611,7 @@ uint8_t PlaceHints(std::vector<uint8_t>& selectedHints, std::vector<HintDistribu
|
||||
void CreateStoneHints() {
|
||||
auto ctx = Rando::Context::GetInstance();
|
||||
SPDLOG_DEBUG("\nNOW CREATING HINTS\n");
|
||||
const HintSetting& hintSetting = hintSettingTable[ctx->GetOption(RSK_HINT_DISTRIBUTION).Value<uint8_t>()];
|
||||
const HintSetting& hintSetting = hintSettingTable[ctx->GetOption(RSK_HINT_DISTRIBUTION).GetContextOptionIndex()];
|
||||
std::vector<HintDistributionSetting> distTable = hintSetting.distTable;
|
||||
|
||||
// Apply impa's song exclusions when zelda is skipped
|
||||
|
@ -655,7 +655,7 @@ static void SetMinimalItemPool() {
|
||||
ReplaceMaxItem(RG_PROGRESSIVE_BOMB_BAG, 1);
|
||||
ReplaceMaxItem(RG_PIECE_OF_HEART, 0);
|
||||
// Need an extra heart container when starting with 1 heart to be able to reach 3 hearts
|
||||
ReplaceMaxItem(RG_HEART_CONTAINER, (ctx->GetOption(RSK_STARTING_HEARTS).Value<uint8_t>() == 18)? 1 : 0);
|
||||
ReplaceMaxItem(RG_HEART_CONTAINER, (ctx->GetOption(RSK_STARTING_HEARTS).GetContextOptionIndex() == 18)? 1 : 0);
|
||||
}
|
||||
|
||||
void GenerateItemPool() {
|
||||
@ -721,7 +721,7 @@ void GenerateItemPool() {
|
||||
|
||||
if (ctx->GetOption(RSK_TRIFORCE_HUNT)) {
|
||||
ctx->possibleIceTrapModels.push_back(RG_TRIFORCE_PIECE);
|
||||
AddItemToMainPool(RG_TRIFORCE_PIECE, (ctx->GetOption(RSK_TRIFORCE_HUNT_PIECES_TOTAL).Value<uint8_t>() + 1));
|
||||
AddItemToMainPool(RG_TRIFORCE_PIECE, (ctx->GetOption(RSK_TRIFORCE_HUNT_PIECES_TOTAL).GetContextOptionIndex() + 1));
|
||||
ctx->PlaceItemInLocation(RC_TRIFORCE_COMPLETED, RG_TRIFORCE); // Win condition
|
||||
ctx->PlaceItemInLocation(RC_GANON, GetJunkItem(), false, true);
|
||||
} else {
|
||||
@ -821,7 +821,7 @@ void GenerateItemPool() {
|
||||
if (fsMode.IsNot(RO_FISHSANITY_OFF)) {
|
||||
if (fsMode.Is(RO_FISHSANITY_POND) || fsMode.Is(RO_FISHSANITY_BOTH)) {
|
||||
// 17 max child pond fish
|
||||
uint8_t pondCt = ctx->GetOption(RSK_FISHSANITY_POND_COUNT).GetSelectedOptionIndex();
|
||||
uint8_t pondCt = ctx->GetOption(RSK_FISHSANITY_POND_COUNT).GetContextOptionIndex();
|
||||
for (uint8_t i = 0; i < pondCt; i++) {
|
||||
AddItemToMainPool(GetJunkItem());
|
||||
}
|
||||
@ -1348,7 +1348,7 @@ void GenerateItemPool() {
|
||||
|
||||
if (ctx->GetOption(RSK_GANONS_BOSS_KEY).Is(RO_GANON_BOSS_KEY_KAK_TOKENS)) {
|
||||
ctx->PlaceItemInLocation(RC_KAK_100_GOLD_SKULLTULA_REWARD, RG_GANONS_CASTLE_BOSS_KEY);
|
||||
} else if (ctx->GetOption(RSK_GANONS_BOSS_KEY).Value<uint8_t>() >= RO_GANON_BOSS_KEY_LACS_VANILLA && ctx->GetOption(RSK_GANONS_BOSS_KEY).IsNot(RO_GANON_BOSS_KEY_TRIFORCE_HUNT)) {
|
||||
} else if (ctx->GetOption(RSK_GANONS_BOSS_KEY).GetContextOptionIndex() >= RO_GANON_BOSS_KEY_LACS_VANILLA && ctx->GetOption(RSK_GANONS_BOSS_KEY).IsNot(RO_GANON_BOSS_KEY_TRIFORCE_HUNT)) {
|
||||
ctx->PlaceItemInLocation(RC_TOT_LIGHT_ARROWS_CUTSCENE, RG_GANONS_CASTLE_BOSS_KEY);
|
||||
} else if (ctx->GetOption(RSK_GANONS_BOSS_KEY).Is(RO_GANON_BOSS_KEY_VANILLA)) {
|
||||
ctx->PlaceItemInLocation(RC_GANONS_TOWER_BOSS_KEY_CHEST, RG_GANONS_CASTLE_BOSS_KEY);
|
||||
|
@ -260,7 +260,7 @@ void RegionTable_Init() {
|
||||
areaTable[RR_ROOT] = Region("Root", "", {RA_LINKS_POCKET}, NO_DAY_NIGHT_CYCLE, {}, {
|
||||
//Locations
|
||||
LOCATION(RC_LINKS_POCKET, true),
|
||||
LOCATION(RC_TRIFORCE_COMPLETED, logic->GetSaveContext()->triforcePiecesCollected >= ctx->GetOption(RSK_TRIFORCE_HUNT_PIECES_REQUIRED).Value<uint8_t>();),
|
||||
LOCATION(RC_TRIFORCE_COMPLETED, logic->GetSaveContext()->triforcePiecesCollected >= ctx->GetOption(RSK_TRIFORCE_HUNT_PIECES_REQUIRED).GetContextOptionIndex();),
|
||||
LOCATION(RC_SARIA_SONG_HINT, logic->CanUse(RG_SARIAS_SONG)),
|
||||
}, {
|
||||
//Exits
|
||||
@ -395,7 +395,7 @@ void ReplaceAllInString(std::string& s, std::string const& toReplace, std::strin
|
||||
std::string CleanCheckConditionString(std::string condition) {
|
||||
ReplaceAllInString(condition, "logic->", "");
|
||||
ReplaceAllInString(condition, "ctx->", "");
|
||||
ReplaceAllInString(condition, ".Value<uint8_t>()", "");
|
||||
ReplaceAllInString(condition, ".GetContextOptionIndex()", "");
|
||||
ReplaceAllInString(condition, "GetSaveContext()->", "");
|
||||
return condition;
|
||||
}
|
||||
|
@ -21,138 +21,140 @@ void RegionTable_Init_JabuJabusBelly() {
|
||||
if (ctx->GetDungeon(JABU_JABUS_BELLY)->IsVanilla()) {
|
||||
areaTable[RR_JABU_JABUS_BELLY_BEGINNING] = Region("Jabu Jabus Belly Beginning", "Jabu Jabus Belly", {RA_JABU_JABUS_BELLY}, NO_DAY_NIGHT_CYCLE, {}, {}, {
|
||||
//Exits
|
||||
Entrance(RR_JABU_JABUS_BELLY_ENTRYWAY, {[]{return true;}}),
|
||||
Entrance(RR_JABU_JABUS_BELLY_LIFT_MIDDLE, {[]{return logic->CanUseProjectile();}}),
|
||||
Entrance(RR_JABU_JABUS_BELLY_ENTRYWAY, {[]{return true;}}),
|
||||
Entrance(RR_JABU_JABUS_BELLY_MAIN, {[]{return logic->CanUseProjectile();}}),
|
||||
});
|
||||
|
||||
areaTable[RR_JABU_JABUS_BELLY_LIFT_MIDDLE] = Region("Jabu Jabus Belly Lift Middle", "Jabu Jabus Belly", {RA_JABU_JABUS_BELLY}, NO_DAY_NIGHT_CYCLE, {}, {}, {
|
||||
//Combines Lift room middle and lower, 1F holes room, the forked corridor, and it's side rooms
|
||||
areaTable[RR_JABU_JABUS_BELLY_MAIN] = Region("Jabu Jabus Belly Main", "Jabu Jabus Belly", {RA_JABU_JABUS_BELLY}, NO_DAY_NIGHT_CYCLE, {
|
||||
//Events
|
||||
EventAccess(&logic->JabuRutoInB1, {[]{return true;}}),
|
||||
EventAccess(&logic->JabuWestTentacle, {[]{return logic->JabuRutoIn1F && logic->CanKillEnemy(RE_TENTACLE, ED_BOOMERANG);}}),
|
||||
}, {
|
||||
//Locations
|
||||
LOCATION(RC_JABU_JABUS_BELLY_DEKU_SCRUB, logic->HasItem(RG_BRONZE_SCALE) && (logic->IsChild || logic->HasItem(RG_SILVER_SCALE) || ctx->GetTrickOption(RT_JABU_ALCOVE_JUMP_DIVE) || logic->CanUse(RG_IRON_BOOTS)) && logic->CanStunDeku()),
|
||||
//We can kill the Stingers with ruto
|
||||
LOCATION(RC_JABU_JABUS_BELLY_BOOMERANG_CHEST, logic->JabuRutoIn1F),
|
||||
LOCATION(RC_JABU_JABUS_BELLY_MAP_CHEST, logic->JabuWestTentacle),
|
||||
}, {
|
||||
//Exits
|
||||
Entrance(RR_JABU_JABUS_BELLY_BEGINNING, {[]{return true;}}),
|
||||
Entrance(RR_JABU_JABUS_BELLY_MAIN_UPPER, {[]{return true;}}),
|
||||
Entrance(RR_JABU_JABUS_BELLY_LIFT_LOWER, {[]{return true;}}),
|
||||
Entrance(RR_JABU_JABUS_BELLY_NEAR_BOSS_ROOM, {[]{return HasAccessTo(RR_JABU_JABUS_BELLY_LIFT_UPPER) || (ctx->GetTrickOption(RT_JABU_BOSS_HOVER) && logic->IsAdult && logic->CanUse(RG_HOVER_BOOTS));}}),
|
||||
});
|
||||
Entrance(RR_JABU_JABUS_BELLY_B1_NORTH, {[]{return true;}}),
|
||||
Entrance(RR_JABU_JABUS_BELLY_COMPASS_ROOM, {[]{return logic->JabuWestTentacle;}}),
|
||||
Entrance(RR_JABU_JABUS_BELLY_BLUE_TENTACLE, {[]{return logic->JabuWestTentacle;}}),
|
||||
Entrance(RR_JABU_JABUS_BELLY_GREEN_TENTACLE, {[]{return logic->JabuEastTentacle;}}),
|
||||
Entrance(RR_JABU_JABUS_BELLY_BIGOCTO_LEDGE, {[]{return logic->JabuNorthTentacle;}}),
|
||||
Entrance(RR_JABU_JABUS_BELLY_NEAR_BOSS_ROOM, {[]{return logic->LoweredJabuPath || (ctx->GetTrickOption(RT_JABU_BOSS_HOVER) && logic->CanUse(RG_HOVER_BOOTS));}}),
|
||||
});
|
||||
|
||||
areaTable[RR_JABU_JABUS_BELLY_MAIN_UPPER] = Region("Jabu Jabus Belly Main Upper", "Jabu Jabus Belly", {RA_JABU_JABUS_BELLY}, NO_DAY_NIGHT_CYCLE, {}, {}, {
|
||||
//Exits
|
||||
Entrance(RR_JABU_JABUS_BELLY_LIFT_MIDDLE, {[]{return true;}}),
|
||||
Entrance(RR_JABU_JABUS_BELLY_MAIN_LOWER, {[]{return true;}}),
|
||||
Entrance(RR_JABU_JABUS_BELLY_FORKED_CORRIDOR, {[]{return true;}}),
|
||||
Entrance(RR_JABU_JABUS_BELLY_BIGOCTO_ROOM, {[]{return Here(RR_JABU_JABUS_BELLY_GREEN_TENTACLE, []{return logic->CanUse(RG_BOOMERANG);});}}),
|
||||
});
|
||||
|
||||
areaTable[RR_JABU_JABUS_BELLY_MAIN_LOWER] = Region("Jabu Jabus Belly Main Lower", "Jabu Jabus Belly", {RA_JABU_JABUS_BELLY}, NO_DAY_NIGHT_CYCLE, {}, {
|
||||
//contains B1 of hole room (aside from the ledge leading to big octo), 2 octorock room and north water switch room
|
||||
areaTable[RR_JABU_JABUS_BELLY_B1_NORTH] = Region("Jabu Jabus Belly B1 North", "Jabu Jabus Belly", {RA_JABU_JABUS_BELLY}, NO_DAY_NIGHT_CYCLE, {
|
||||
//Events
|
||||
EventAccess(&logic->JabuRutoIn1F, {[]{return logic->IsAdult || logic->HasItem(RG_BRONZE_SCALE);}}),
|
||||
EventAccess(&logic->FairyPot, {[]{return logic->CanUse(RG_BOOMERANG) || logic->CanUse(RG_HOVER_BOOTS);}}),
|
||||
}, {
|
||||
//Locations
|
||||
LOCATION(RC_JABU_JABUS_BELLY_GS_LOBBY_BASEMENT_LOWER, logic->HookshotOrBoomerang()),
|
||||
LOCATION(RC_JABU_JABUS_BELLY_GS_LOBBY_BASEMENT_UPPER, logic->HookshotOrBoomerang()),
|
||||
}, {
|
||||
LOCATION(RC_JABU_JABUS_BELLY_GS_WATER_SWITCH_ROOM, logic->HookshotOrBoomerang()),
|
||||
//PUT 2 OCTO ROOM POTS HERE
|
||||
}, {
|
||||
//Exits
|
||||
Entrance(RR_JABU_JABUS_BELLY_MAIN_UPPER, {[]{return true;}}),
|
||||
Entrance(RR_JABU_JABUS_BELLY_SHABOMB_CORRIDOR, {[]{return true;}}),
|
||||
Entrance(RR_JABU_JABUS_BELLY_LOWER_SIDE_ROOM, {[]{return true;}}),
|
||||
Entrance(RR_JABU_JABUS_BELLY_MAIN, {[]{return true;}}),
|
||||
//there's tricks for getting here with bunny-jumps or just side-hops
|
||||
Entrance(RR_JABU_JABUS_BELLY_WATER_SWITCH_ROOM_LEDGE, {[]{return logic->HasItem(RG_BRONZE_SCALE) || logic->HasItem(RG_HOVER_BOOTS);}}),
|
||||
Entrance(RR_JABU_JABUS_BELLY_WATER_SWITCH_ROOM_SOUTH, {[]{return logic->IsAdult || logic->HasItem(RG_BRONZE_SCALE);}}),
|
||||
});
|
||||
|
||||
areaTable[RR_JABU_JABUS_BELLY_SHABOMB_CORRIDOR] = Region("Jabu Jabus Belly Shabomb Corridor", "Jabu Jabus Belly", {RA_JABU_JABUS_BELLY}, NO_DAY_NIGHT_CYCLE, {
|
||||
areaTable[RR_JABU_JABUS_BELLY_WATER_SWITCH_ROOM_LEDGE] = Region("Jabu Jabus Belly Water Switch Room Ledge", "Jabu Jabus Belly", {RA_JABU_JABUS_BELLY}, NO_DAY_NIGHT_CYCLE, {
|
||||
//Events
|
||||
EventAccess(&logic->FairyPot, {[]{return true;}}),
|
||||
}, {
|
||||
//Locations
|
||||
LOCATION(RC_JABU_JABUS_BELLY_GS_WATER_SWITCH_ROOM, true),
|
||||
//this is the logic for climbing back and forth to use the pots to kill the skull...
|
||||
LOCATION(RC_JABU_JABUS_BELLY_GS_WATER_SWITCH_ROOM, logic->HasItem(RG_BRONZE_SCALE) ||
|
||||
(logic->IsAdult && logic->CanUse(RG_HOVER_BOOTS)) ||
|
||||
//or killing the skull before climbing to grab the token
|
||||
logic->CanKillEnemy(RE_GOLD_SKULLTULA, ED_BOMB_THROW)),
|
||||
//PUT WATER SWITCH POTS HERE
|
||||
}, {
|
||||
//Exits
|
||||
Entrance(RR_JABU_JABUS_BELLY_MAIN_LOWER, {[]{return logic->HasItem(RG_BRONZE_SCALE);}}),
|
||||
Entrance(RR_JABU_JABUS_BELLY_LIFT_LOWER, {[]{return logic->HasItem(RG_BRONZE_SCALE) && logic->CanUseProjectile();}}),
|
||||
Entrance(RR_JABU_JABUS_BELLY_B1_NORTH, {[]{return true;}}),
|
||||
Entrance(RR_JABU_JABUS_BELLY_WATER_SWITCH_ROOM_SOUTH, {[]{return true;}}),
|
||||
});
|
||||
|
||||
areaTable[RR_JABU_JABUS_BELLY_LOWER_SIDE_ROOM] = Region("Jabu Jabus Belly Lower Side Room", "Jabu Jabus Belly", {RA_JABU_JABUS_BELLY}, NO_DAY_NIGHT_CYCLE, {
|
||||
//Events
|
||||
EventAccess(&logic->FairyPot, {[]{return logic->FairyPot || (logic->CanUse(RG_BOOMERANG) || logic->CanUse(RG_HOVER_BOOTS));}}),
|
||||
}, {}, {
|
||||
//Exits
|
||||
Entrance(RR_JABU_JABUS_BELLY_MAIN_LOWER, {[]{return true;}}),
|
||||
});
|
||||
|
||||
areaTable[RR_JABU_JABUS_BELLY_LIFT_LOWER] = Region("Jabu Jabus Belly Lift Lower", "Jabu Jabus Belly", {RA_JABU_JABUS_BELLY}, NO_DAY_NIGHT_CYCLE, {}, {
|
||||
areaTable[RR_JABU_JABUS_BELLY_WATER_SWITCH_ROOM_SOUTH] = Region("Jabu Jabus Belly Water Switch Room South", "Jabu Jabus Belly", {RA_JABU_JABUS_BELLY}, NO_DAY_NIGHT_CYCLE, {}, {
|
||||
//Locations
|
||||
LOCATION(RC_JABU_JABUS_BELLY_DEKU_SCRUB, logic->HasItem(RG_BRONZE_SCALE) && (logic->IsChild || logic->HasItem(RG_SILVER_SCALE) || ctx->GetTrickOption(RT_JABU_ALCOVE_JUMP_DIVE) || logic->CanUse(RG_IRON_BOOTS)) && logic->CanStunDeku()),
|
||||
LOCATION(RC_JABU_JABUS_BELLY_GS_WATER_SWITCH_ROOM, logic->HookshotOrBoomerang()),
|
||||
}, {
|
||||
//Exits
|
||||
Entrance(RR_JABU_JABUS_BELLY_SHABOMB_CORRIDOR, {[]{return true;}}),
|
||||
Entrance(RR_JABU_JABUS_BELLY_LIFT_MIDDLE, {[]{return true;}}),
|
||||
});
|
||||
|
||||
areaTable[RR_JABU_JABUS_BELLY_FORKED_CORRIDOR] = Region("Jabu Jabus Belly Forked Corridor", "Jabu Jabus Belly", {RA_JABU_JABUS_BELLY}, NO_DAY_NIGHT_CYCLE, {}, {}, {
|
||||
//Exits
|
||||
Entrance(RR_JABU_JABUS_BELLY_MAIN_UPPER, {[]{return true;}}),
|
||||
Entrance(RR_JABU_JABUS_BELLY_BOOMERANG_ROOM, {[]{return true;}}),
|
||||
Entrance(RR_JABU_JABUS_BELLY_MAP_ROOM, {[]{return true;}}),
|
||||
Entrance(RR_JABU_JABUS_BELLY_COMPASS_ROOM, {[]{return Here(RR_JABU_JABUS_BELLY_MAP_ROOM, []{return logic->CanUse(RG_BOOMERANG);});}}),
|
||||
Entrance(RR_JABU_JABUS_BELLY_BLUE_TENTACLE, {[]{return Here(RR_JABU_JABUS_BELLY_MAP_ROOM, []{return logic->CanUse(RG_BOOMERANG);});}}),
|
||||
Entrance(RR_JABU_JABUS_BELLY_GREEN_TENTACLE, {[]{return Here(RR_JABU_JABUS_BELLY_BLUE_TENTACLE, []{return logic->CanUse(RG_BOOMERANG);});}}),
|
||||
});
|
||||
|
||||
areaTable[RR_JABU_JABUS_BELLY_BOOMERANG_ROOM] = Region("Jabu Jabus Belly Boomerang Room", "Jabu Jabus Belly", {RA_JABU_JABUS_BELLY}, NO_DAY_NIGHT_CYCLE, {}, {
|
||||
//Locations
|
||||
LOCATION(RC_JABU_JABUS_BELLY_BOOMERANG_CHEST, true),
|
||||
}, {
|
||||
//Exits
|
||||
Entrance(RR_JABU_JABUS_BELLY_FORKED_CORRIDOR, {[]{return true;}}),
|
||||
});
|
||||
|
||||
areaTable[RR_JABU_JABUS_BELLY_MAP_ROOM] = Region("Jabu Jabus Belly Map Room", "Jabu Jabus Belly", {RA_JABU_JABUS_BELLY}, NO_DAY_NIGHT_CYCLE, {}, {
|
||||
//Locations
|
||||
LOCATION(RC_JABU_JABUS_BELLY_MAP_CHEST, logic->CanUse(RG_BOOMERANG)),
|
||||
}, {
|
||||
//Exits
|
||||
Entrance(RR_JABU_JABUS_BELLY_FORKED_CORRIDOR, {[]{return true;}}),
|
||||
Entrance(RR_JABU_JABUS_BELLY_B1_NORTH, {[]{return logic->IsAdult || logic->HasItem(RG_BRONZE_SCALE);}}),
|
||||
Entrance(RR_JABU_JABUS_BELLY_WATER_SWITCH_ROOM_LEDGE, {[]{return logic->HasItem(RG_BRONZE_SCALE) || logic->HasItem(RG_HOVER_BOOTS);}}),
|
||||
Entrance(RR_JABU_JABUS_BELLY_MAIN, {[]{return logic->CanUseProjectile();}}),
|
||||
});
|
||||
|
||||
areaTable[RR_JABU_JABUS_BELLY_COMPASS_ROOM] = Region("Jabu Jabus Belly Compass Room", "Jabu Jabus Belly", {RA_JABU_JABUS_BELLY}, NO_DAY_NIGHT_CYCLE, {}, {
|
||||
//Locations
|
||||
LOCATION(RC_JABU_JABUS_BELLY_COMPASS_CHEST, logic->CanAttack()),
|
||||
//ruto could theoretically clear this room, but it's hard because of the timer and she doesn't appear with you when you respawn after failing, which would force a savewarp
|
||||
LOCATION(RC_JABU_JABUS_BELLY_COMPASS_CHEST, logic->CanKillEnemy(RE_SHABOM)),
|
||||
}, {
|
||||
//Exits
|
||||
Entrance(RR_JABU_JABUS_BELLY_FORKED_CORRIDOR, {[]{return true;}}),
|
||||
Entrance(RR_JABU_JABUS_BELLY_MAIN, {[]{return Here(RR_JABU_JABUS_BELLY_COMPASS_ROOM, []{return logic->CanKillEnemy(RE_SHABOM);});}}),
|
||||
});
|
||||
|
||||
areaTable[RR_JABU_JABUS_BELLY_BLUE_TENTACLE] = Region("Jabu Jabus Belly Blue Tentacle", "Jabu Jabus Belly", {RA_JABU_JABUS_BELLY}, NO_DAY_NIGHT_CYCLE, {}, {}, {
|
||||
areaTable[RR_JABU_JABUS_BELLY_BLUE_TENTACLE] = Region("Jabu Jabus Belly Blue Tentacle", "Jabu Jabus Belly", {RA_JABU_JABUS_BELLY}, NO_DAY_NIGHT_CYCLE, {
|
||||
EventAccess(&logic->JabuEastTentacle, {[]{return logic->CanKillEnemy(RE_TENTACLE, ED_BOOMERANG);}}),
|
||||
}, {}, {
|
||||
//Exits
|
||||
Entrance(RR_JABU_JABUS_BELLY_FORKED_CORRIDOR, {[]{return Here(RR_JABU_JABUS_BELLY_BLUE_TENTACLE, []{return logic->CanUse(RG_BOOMERANG);});}}),
|
||||
Entrance(RR_JABU_JABUS_BELLY_MAIN, {[]{return logic->JabuEastTentacle;}}),
|
||||
});
|
||||
|
||||
areaTable[RR_JABU_JABUS_BELLY_GREEN_TENTACLE] = Region("Jabu Jabus Belly Green Tentacle", "Jabu Jabus Belly", {RA_JABU_JABUS_BELLY}, NO_DAY_NIGHT_CYCLE, {}, {}, {
|
||||
areaTable[RR_JABU_JABUS_BELLY_GREEN_TENTACLE] = Region("Jabu Jabus Belly Green Tentacle", "Jabu Jabus Belly", {RA_JABU_JABUS_BELLY}, NO_DAY_NIGHT_CYCLE, {
|
||||
EventAccess(&logic->JabuNorthTentacle, {[]{return logic->CanKillEnemy(RE_TENTACLE, ED_BOOMERANG);}}),
|
||||
}, {}, {
|
||||
//Exits
|
||||
Entrance(RR_JABU_JABUS_BELLY_FORKED_CORRIDOR, {[]{return Here(RR_JABU_JABUS_BELLY_GREEN_TENTACLE, []{return logic->CanUse(RG_BOOMERANG);});}}),
|
||||
//implied logic->CanKillEnemy(RE_BARI)
|
||||
Entrance(RR_JABU_JABUS_BELLY_MAIN, {[]{return logic->JabuNorthTentacle;}}),
|
||||
});
|
||||
|
||||
areaTable[RR_JABU_JABUS_BELLY_BIGOCTO_ROOM] = Region("Jabu Jabus Belly Bigocto Room", "Jabu Jabus Belly", {RA_JABU_JABUS_BELLY}, NO_DAY_NIGHT_CYCLE, {}, {}, {
|
||||
areaTable[RR_JABU_JABUS_BELLY_BIGOCTO_LEDGE] = Region("Jabu Jabus Belly Bigocto Room", "Jabu Jabus Belly", {RA_JABU_JABUS_BELLY}, NO_DAY_NIGHT_CYCLE, {}, {
|
||||
//Locations
|
||||
//Only adult can get the token without assistance
|
||||
LOCATION(RC_JABU_JABUS_BELLY_GS_LOBBY_BASEMENT_UPPER, logic->IsAdult && logic->CanGetEnemyDrop(RE_GOLD_SKULLTULA, ED_SHORT_JUMPSLASH)),
|
||||
//You can get the LOWER skull token from here as aduly with hovers backwalk and a backflip, but it's trickworthy and not relevant unless you can beat tentacles without rang
|
||||
}, {
|
||||
//Exits
|
||||
Entrance(RR_JABU_JABUS_BELLY_MAIN_LOWER, {[]{return true;}}),
|
||||
Entrance(RR_JABU_JABUS_BELLY_ABOVE_BIGOCTO, {[]{return Here(RR_JABU_JABUS_BELLY_BIGOCTO_ROOM, []{return (logic->CanUse(RG_BOOMERANG) || logic->CanUse(RG_NUTS)) && (logic->CanUse(RG_KOKIRI_SWORD) || logic->CanUse(RG_STICKS));});}}),
|
||||
Entrance(RR_JABU_JABUS_BELLY_B1_NORTH, {[]{return true;}}),
|
||||
Entrance(RR_JABU_JABUS_BELLY_ABOVE_BIGOCTO, {[]{return logic->JabuRutoIn1F && Here(RR_JABU_JABUS_BELLY_BIGOCTO_LEDGE, []{return logic->CanKillEnemy(RE_BIG_OCTO);});}}),
|
||||
});
|
||||
|
||||
areaTable[RR_JABU_JABUS_BELLY_ABOVE_BIGOCTO] = Region("Jabu Jabus Belly Above Bigocto", "Jabu Jabus Belly", {RA_JABU_JABUS_BELLY}, NO_DAY_NIGHT_CYCLE, {
|
||||
//Events
|
||||
EventAccess(&logic->FairyPot, {[]{return true;}}),
|
||||
EventAccess(&logic->NutPot, {[]{return true;}}),
|
||||
}, {}, {
|
||||
}, {
|
||||
//Locations
|
||||
//PUT AFTER OCTO POTS HERE
|
||||
}, {
|
||||
//Exits
|
||||
Entrance(RR_JABU_JABUS_BELLY_LIFT_UPPER, {[]{return logic->CanUse(RG_BOOMERANG);}}),
|
||||
});
|
||||
|
||||
areaTable[RR_JABU_JABUS_BELLY_LIFT_UPPER] = Region("Jabu Jabus Belly Lift Upper", "Jabu Jabus Belly", {RA_JABU_JABUS_BELLY}, NO_DAY_NIGHT_CYCLE, {}, {}, {
|
||||
areaTable[RR_JABU_JABUS_BELLY_LIFT_UPPER] = Region("Jabu Jabus Belly Lift Upper", "Jabu Jabus Belly", {RA_JABU_JABUS_BELLY}, NO_DAY_NIGHT_CYCLE, {
|
||||
//Events
|
||||
EventAccess(&logic->LoweredJabuPath, {[]{return true;}}),
|
||||
}, {}, {
|
||||
//Exits
|
||||
Entrance(RR_JABU_JABUS_BELLY_LIFT_MIDDLE, {[]{return true;}}),
|
||||
Entrance(RR_JABU_JABUS_BELLY_LIFT_LOWER, {[]{return true;}}),
|
||||
Entrance(RR_JABU_JABUS_BELLY_MAIN, {[]{return true;}}),
|
||||
});
|
||||
|
||||
areaTable[RR_JABU_JABUS_BELLY_NEAR_BOSS_ROOM] = Region("Jabu Jabus Belly Near Boss Room", "Jabu Jabus Belly", {RA_JABU_JABUS_BELLY}, NO_DAY_NIGHT_CYCLE, {}, {
|
||||
//Locations
|
||||
LOCATION(RC_JABU_JABUS_BELLY_GS_NEAR_BOSS, logic->CanAttack()),
|
||||
LOCATION(RC_JABU_JABUS_BELLY_GS_NEAR_BOSS, logic->CanKillEnemy(RE_GOLD_SKULLTULA, ED_BOMB_THROW)),
|
||||
}, {
|
||||
//Exits
|
||||
Entrance(RR_JABU_JABUS_BELLY_LIFT_MIDDLE, {[]{return true;}}),
|
||||
Entrance(RR_JABU_JABUS_BELLY_BOSS_ENTRYWAY, {[]{return logic->CanUse(RG_BOOMERANG) || (ctx->GetTrickOption(RT_JABU_NEAR_BOSS_RANGED) && ((logic->IsAdult && (logic->CanUse(RG_HOOKSHOT) || logic->CanUse(RG_FAIRY_BOW))) || (logic->IsChild && logic->CanUse(RG_FAIRY_SLINGSHOT)))) || (ctx->GetTrickOption(RT_JABU_NEAR_BOSS_EXPLOSIVES) && (logic->CanUse(RG_BOMBCHU_5) || (logic->IsAdult && logic->CanUse(RG_HOVER_BOOTS) && logic->CanUse(RG_BOMB_BAG))));}}),
|
||||
Entrance(RR_JABU_JABUS_BELLY_MAIN, {[]{return true;}}),
|
||||
Entrance(RR_JABU_JABUS_BELLY_BOSS_ENTRYWAY, {[]{return logic->CanUse(RG_BOOMERANG) || (ctx->GetTrickOption(RT_JABU_NEAR_BOSS_RANGED) && logic->CanUse(RG_HOOKSHOT) || logic->CanUse(RG_FAIRY_BOW) || logic->CanUse(RG_FAIRY_SLINGSHOT)) || (ctx->GetTrickOption(RT_JABU_NEAR_BOSS_EXPLOSIVES) && (logic->CanUse(RG_BOMBCHU_5) || (logic->CanUse(RG_HOVER_BOOTS) && logic->CanUse(RG_BOMB_BAG))));}}),
|
||||
});
|
||||
}
|
||||
|
||||
@ -205,7 +207,7 @@ void RegionTable_Init_JabuJabusBelly() {
|
||||
LOCATION(RC_JABU_JABUS_BELLY_MQ_BASEMENT_NEAR_SWITCHES_CHEST, logic->CanUse(RG_FAIRY_SLINGSHOT)),
|
||||
}, {
|
||||
//Exits
|
||||
Entrance(RR_JABU_JABUS_BELLY_MQ_BEGINNING, {[]{return true;}}),
|
||||
Entrance(RR_JABU_JABUS_BELLY_MQ_LIFT_ROOM, {[]{return true;}}),
|
||||
Entrance(RR_JABU_JABUS_BELLY_MQ_WATER_SWITCH_ROOM, {[]{return true;}}),
|
||||
Entrance(RR_JABU_JABUS_BELLY_MQ_FORKED_CORRIDOR, {[]{return logic->CanUse(RG_BOOMERANG) && logic->HasExplosives() && Here(RR_JABU_JABUS_BELLY_MQ_HOLES_ROOM, []{return logic->CanUse(RG_FAIRY_SLINGSHOT);});}}),
|
||||
Entrance(RR_JABU_JABUS_BELLY_MQ_INVISIBLE_KEESE_ROOM, {[]{return logic->JabuNorthTentacle;}}),
|
||||
|
@ -43,7 +43,12 @@ void RegionTable_Init_ZorasDomain() {
|
||||
Entrance(RR_ZR_FAIRY_GROTTO, {[]{return Here(RR_ZORAS_RIVER, []{return logic->BlastOrSmash();});}}),
|
||||
Entrance(RR_THE_LOST_WOODS, {[]{return logic->HasItem(RG_SILVER_SCALE) || logic->CanUse(RG_IRON_BOOTS);}}),
|
||||
Entrance(RR_ZR_STORMS_GROTTO, {[]{return logic->CanOpenStormsGrotto();}}),
|
||||
Entrance(RR_ZR_BEHIND_WATERFALL, {[]{return logic->CanUse(RG_ZELDAS_LULLABY) || (logic->IsChild && ctx->GetTrickOption(RT_ZR_CUCCO)) || (logic->IsAdult && logic->CanUse(RG_HOVER_BOOTS) && ctx->GetTrickOption(RT_ZR_HOVERS));}}),
|
||||
Entrance(RR_ZR_BEHIND_WATERFALL, {[]{
|
||||
return ctx->GetOption(RSK_SLEEPING_WATERFALL).Is(RO_WATERFALL_OPEN) ||
|
||||
Here(RR_ZORAS_RIVER, []{return logic->CanUse(RG_ZELDAS_LULLABY);}) ||
|
||||
(logic->IsChild && ctx->GetTrickOption(RT_ZR_CUCCO)) ||
|
||||
(logic->IsAdult && logic->CanUse(RG_HOVER_BOOTS) && ctx->GetTrickOption(RT_ZR_HOVERS));
|
||||
}}),
|
||||
});
|
||||
|
||||
areaTable[RR_ZR_BEHIND_WATERFALL] = Region("ZR Behind Waterfall", "Zora River", {RA_ZORAS_RIVER}, DAY_NIGHT_CYCLE, {}, {}, {
|
||||
|
@ -80,14 +80,6 @@ int Playthrough_Init(uint32_t seed, std::set<RandomizerCheck> excludedLocations,
|
||||
SPDLOG_ERROR("Writing Spoiler Log Failed");
|
||||
}
|
||||
StopPerformanceTimer(PT_SPOILER_LOG);
|
||||
#ifdef ENABLE_DEBUG
|
||||
SPDLOG_INFO("Writing Placement Log...");
|
||||
if (PlacementLog_Write()) {
|
||||
SPDLOG_INFO("Writing Placement Log Done");
|
||||
} else {
|
||||
SPDLOG_ERROR("Writing Placement Log Failed");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
ctx->playthroughLocations.clear();
|
||||
|
@ -156,7 +156,7 @@ int GetPriceFromMax(int max) {
|
||||
|
||||
uint16_t GetPriceFromSettings(Rando::Location *loc, PriceSettingsStruct priceSettings) {
|
||||
auto ctx = Rando::Context::GetInstance();
|
||||
switch (ctx->GetOption(priceSettings.main).Value<uint8_t>()){
|
||||
switch (ctx->GetOption(priceSettings.main).GetContextOptionIndex()){
|
||||
case RO_PRICE_VANILLA:
|
||||
return loc->GetVanillaPrice();
|
||||
case RO_PRICE_CHEAP_BALANCED:
|
||||
@ -172,19 +172,19 @@ uint16_t GetPriceFromSettings(Rando::Location *loc, PriceSettingsStruct priceSet
|
||||
return 150;
|
||||
}
|
||||
case RO_PRICE_FIXED:
|
||||
return (uint16_t)ctx->GetOption(priceSettings.fixedPrice).Value<uint8_t>() * 5;
|
||||
return (uint16_t)ctx->GetOption(priceSettings.fixedPrice).GetContextOptionIndex() * 5;
|
||||
case RO_PRICE_RANGE:{
|
||||
uint16_t range1 = (uint16_t)ctx->GetOption(priceSettings.range1).Value<uint8_t>() * 5;
|
||||
uint16_t range2 = (uint16_t)ctx->GetOption(priceSettings.range2).Value<uint8_t>() * 5;
|
||||
uint16_t range1 = (uint16_t)ctx->GetOption(priceSettings.range1).GetContextOptionIndex() * 5;
|
||||
uint16_t range2 = (uint16_t)ctx->GetOption(priceSettings.range2).GetContextOptionIndex() * 5;
|
||||
return range1 < range2 ? Random(range1, range2+1) : Random(range2, range1+1);
|
||||
}
|
||||
case RO_PRICE_SET_BY_WALLET:{
|
||||
bool isTycoon = ctx->GetOption(RSK_INCLUDE_TYCOON_WALLET).Value<bool>();
|
||||
uint16_t noWeight = ctx->GetOption(priceSettings.noWallet).Value<uint8_t>();
|
||||
uint16_t childWeight = ctx->GetOption(priceSettings.childWallet).Value<uint8_t>();
|
||||
uint16_t adultWeight = ctx->GetOption(priceSettings.adultWallet).Value<uint8_t>();
|
||||
uint16_t giantWeight = ctx->GetOption(priceSettings.giantWallet).Value<uint8_t>();
|
||||
uint16_t tycoonWeight = isTycoon ? ctx->GetOption(priceSettings.tycoonWallet).Value<uint8_t>() : 0;
|
||||
bool isTycoon = ctx->GetOption(RSK_INCLUDE_TYCOON_WALLET).GetContextOptionIndex();
|
||||
uint16_t noWeight = ctx->GetOption(priceSettings.noWallet).GetContextOptionIndex();
|
||||
uint16_t childWeight = ctx->GetOption(priceSettings.childWallet).GetContextOptionIndex();
|
||||
uint16_t adultWeight = ctx->GetOption(priceSettings.adultWallet).GetContextOptionIndex();
|
||||
uint16_t giantWeight = ctx->GetOption(priceSettings.giantWallet).GetContextOptionIndex();
|
||||
uint16_t tycoonWeight = isTycoon ? ctx->GetOption(priceSettings.tycoonWallet).GetContextOptionIndex() : 0;
|
||||
uint16_t totalWeight = noWeight + childWeight + adultWeight + giantWeight + tycoonWeight;
|
||||
if (totalWeight == 0){ //if no weight, return from sane range
|
||||
return Random(0, 501);
|
||||
|
@ -6,7 +6,6 @@
|
||||
#include "../entrance.h"
|
||||
#include "random.hpp"
|
||||
#include "../trial.h"
|
||||
#include "tinyxml2.h"
|
||||
#include "utils.hpp"
|
||||
#include "hints.hpp"
|
||||
#include "pool_functions.hpp"
|
||||
@ -56,9 +55,6 @@ void GenerateHash() {
|
||||
int number = std::stoi(hash.substr(j, 2));
|
||||
ctx->hashIconIndexes[i] = number;
|
||||
}
|
||||
|
||||
// Clear out spoiler log data here, in case we aren't going to re-generate it
|
||||
// spoilerData = { 0 };
|
||||
}
|
||||
|
||||
static auto GetGeneralPath() {
|
||||
@ -79,7 +75,6 @@ static void WriteLocation(
|
||||
Rando::Location* location = Rando::StaticData::GetLocation(locationKey);
|
||||
Rando::ItemLocation* itemLocation = Rando::Context::GetInstance()->GetItemLocation(locationKey);
|
||||
|
||||
// auto node = parentNode->InsertNewChildElement("location");
|
||||
switch (gSaveContext.language) {
|
||||
case LANGUAGE_ENG:
|
||||
default:
|
||||
@ -89,35 +84,6 @@ static void WriteLocation(
|
||||
jsonData["playthrough"][sphere][location->GetName()] = itemLocation->GetPlacedItemName().GetFrench();
|
||||
break;
|
||||
}
|
||||
// node->SetAttribute("name", location->GetName().c_str());
|
||||
// node->SetText(location->GetPlacedItemName().GetEnglish().c_str());
|
||||
|
||||
// if (withPadding) {
|
||||
// constexpr int16_t LONGEST_NAME = 56; // The longest name of a location.
|
||||
// constexpr int16_t PRICE_ATTRIBUTE = 12; // Length of a 3-digit price attribute.
|
||||
|
||||
// // Insert a padding so we get a kind of table in the XML document.
|
||||
// int16_t requiredPadding = LONGEST_NAME - location->GetName().length();
|
||||
// if (location->GetRCType() == RCTYPE_SHOP) {
|
||||
// // Shop items have short location names, but come with an additional price attribute.
|
||||
// requiredPadding -= PRICE_ATTRIBUTE;
|
||||
// }
|
||||
// if (requiredPadding >= 0) {
|
||||
// std::string padding(requiredPadding, ' ');
|
||||
// node->SetAttribute("_", padding.c_str());
|
||||
// }
|
||||
// }
|
||||
|
||||
// if (location->GetRCType() == RCTYPE_SHOP) {
|
||||
// char price[6];
|
||||
// sprintf(price, "%03d", location->GetPrice());
|
||||
// node->SetAttribute("price", price);
|
||||
// }
|
||||
// if (!location->IsAddedToPool()) {
|
||||
// #ifdef ENABLE_DEBUG
|
||||
// node->SetAttribute("not-added", true);
|
||||
// #endif
|
||||
// }
|
||||
}
|
||||
|
||||
//Writes a shuffled entrance to the specified node
|
||||
@ -176,7 +142,7 @@ static void WriteSettings() {
|
||||
auto allOptionGroups = ctx->GetSettings()->GetOptionGroups();
|
||||
for (const Rando::OptionGroup& optionGroup : allOptionGroups) {
|
||||
if (optionGroup.GetContainsType() == Rando::OptionGroupType::DEFAULT && optionGroup.PrintInSpoiler()) {
|
||||
for (const Rando::Option* option : optionGroup.GetOptions()) {
|
||||
for (Rando::Option* option : optionGroup.GetOptions()) {
|
||||
std::string settingName = optionGroup.GetName() + ":" + option->GetName();
|
||||
jsonData["settings"][settingName] = option->GetSelectedOptionText();
|
||||
}
|
||||
@ -186,26 +152,18 @@ static void WriteSettings() {
|
||||
|
||||
// Writes the excluded locations to the spoiler log, if there are any.
|
||||
static void WriteExcludedLocations() {
|
||||
// auto parentNode = spoilerLog.NewElement("excluded-locations");
|
||||
auto ctx = Rando::Context::GetInstance();
|
||||
|
||||
for (size_t i = 1; i < ctx->GetSettings()->GetExcludeLocationsOptions().size(); i++) {
|
||||
for (const auto& location : ctx->GetSettings()->GetExcludeLocationsOptions()[i]) {
|
||||
if (location->GetSelectedOptionIndex() == RO_LOCATION_INCLUDE) {
|
||||
if (location->GetContextOptionIndex() == RO_LOCATION_INCLUDE) {
|
||||
continue;
|
||||
}
|
||||
|
||||
jsonData["excludedLocations"].push_back(RemoveLineBreaks(location->GetName()));
|
||||
|
||||
// tinyxml2::XMLElement* node = spoilerLog.NewElement("location");
|
||||
// node->SetAttribute("name", RemoveLineBreaks(location->GetName()).c_str());
|
||||
// parentNode->InsertEndChild(node);
|
||||
}
|
||||
}
|
||||
|
||||
// if (!parentNode->NoChildren()) {
|
||||
// spoilerLog.RootElement()->InsertEndChild(parentNode);
|
||||
// }
|
||||
}
|
||||
|
||||
// Writes the starting inventory to the spoiler log, if there is any.
|
||||
@ -214,63 +172,27 @@ static void WriteStartingInventory() {
|
||||
const Rando::OptionGroup& optionGroup = ctx->GetSettings()->GetOptionGroup(RSG_STARTING_INVENTORY);
|
||||
for (const Rando::OptionGroup* subGroup : optionGroup.GetSubGroups()) {
|
||||
if (subGroup->GetContainsType() == Rando::OptionGroupType::DEFAULT) {
|
||||
for (const Rando::Option* option : subGroup->GetOptions()) {
|
||||
for (Rando::Option* option : subGroup->GetOptions()) {
|
||||
jsonData["settings"][option->GetName()] = option->GetSelectedOptionText();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Writes the enabled tricks to the spoiler log, if there are any.
|
||||
static void WriteEnabledTricks(tinyxml2::XMLDocument& spoilerLog) {
|
||||
//auto parentNode = spoilerLog.NewElement("enabled-tricks");
|
||||
//Writes the enabled tricks to the spoiler log, if there are any.
|
||||
static void WriteEnabledTricks() {
|
||||
auto ctx = Rando::Context::GetInstance();
|
||||
|
||||
for (const auto& setting : ctx->GetSettings()->GetOptionGroup(RSG_TRICKS).GetOptions()) {
|
||||
if (setting->GetSelectedOptionIndex() != RO_GENERIC_ON/* || !setting->IsCategory(OptionCategory::Setting)*/) {
|
||||
if (setting->GetContextOptionIndex() != RO_GENERIC_ON) {
|
||||
continue;
|
||||
}
|
||||
jsonData["enabledTricks"].push_back(RemoveLineBreaks(setting->GetName()).c_str());
|
||||
//auto node = parentNode->InsertNewChildElement("trick");
|
||||
//node->SetAttribute("name", RemoveLineBreaks(setting->GetName()).c_str());
|
||||
}
|
||||
|
||||
// if (!parentNode->NoChildren()) {
|
||||
// spoilerLog.RootElement()->InsertEndChild(parentNode);
|
||||
//}
|
||||
}
|
||||
|
||||
// Writes the enabled glitches to the spoiler log, if there are any.
|
||||
// TODO: Implement Glitches
|
||||
// static void WriteEnabledGlitches(tinyxml2::XMLDocument& spoilerLog) {
|
||||
// auto parentNode = spoilerLog.NewElement("enabled-glitches");
|
||||
|
||||
// for (const auto& setting : Settings::glitchCategories) {
|
||||
// if (setting->Value<uint8_t>() == 0) {
|
||||
// continue;
|
||||
// }
|
||||
|
||||
// auto node = parentNode->InsertNewChildElement("glitch-category");
|
||||
// node->SetAttribute("name", setting->GetName().c_str());
|
||||
// node->SetText(setting->GetSelectedOptionText().c_str());
|
||||
// }
|
||||
|
||||
// for (const auto& setting : Settings::miscGlitches) {
|
||||
// if (!setting->Value<bool>()) {
|
||||
// continue;
|
||||
// }
|
||||
|
||||
// auto node = parentNode->InsertNewChildElement("misc-glitch");
|
||||
// node->SetAttribute("name", RemoveLineBreaks(setting->GetName()).c_str());
|
||||
// }
|
||||
|
||||
// if (!parentNode->NoChildren()) {
|
||||
// spoilerLog.RootElement()->InsertEndChild(parentNode);
|
||||
// }
|
||||
// }
|
||||
|
||||
// Writes the Master Quest dungeons to the spoiler log, if there are any.
|
||||
static void WriteMasterQuestDungeons(tinyxml2::XMLDocument& spoilerLog) {
|
||||
static void WriteMasterQuestDungeons() {
|
||||
auto ctx = Rando::Context::GetInstance();
|
||||
for (const auto* dungeon : ctx->GetDungeons()->GetDungeonList()) {
|
||||
std::string dungeonName;
|
||||
@ -294,7 +216,6 @@ static void WriteRequiredTrials() {
|
||||
|
||||
// Writes the intended playthrough to the spoiler log, separated into spheres.
|
||||
static void WritePlaythrough() {
|
||||
// auto playthroughNode = spoilerLog.NewElement("playthrough");
|
||||
auto ctx = Rando::Context::GetInstance();
|
||||
|
||||
for (uint32_t i = 0; i < ctx->playthroughLocations.size(); ++i) {
|
||||
@ -306,8 +227,6 @@ static void WritePlaythrough() {
|
||||
WriteLocation(sphereString, key, true);
|
||||
}
|
||||
}
|
||||
|
||||
// spoilerLog.RootElement()->InsertEndChild(playthroughNode);
|
||||
}
|
||||
|
||||
//Write the randomized entrance playthrough to the spoiler log, if applicable
|
||||
@ -389,11 +308,6 @@ static void WriteAllLocations() {
|
||||
|
||||
const char* SpoilerLog_Write() {
|
||||
auto ctx = Rando::Context::GetInstance();
|
||||
auto spoilerLog = tinyxml2::XMLDocument(false);
|
||||
spoilerLog.InsertEndChild(spoilerLog.NewDeclaration());
|
||||
|
||||
auto rootNode = spoilerLog.NewElement("spoiler-log");
|
||||
spoilerLog.InsertEndChild(rootNode);
|
||||
|
||||
jsonData.clear();
|
||||
|
||||
@ -413,11 +327,8 @@ const char* SpoilerLog_Write() {
|
||||
WriteSettings();
|
||||
WriteExcludedLocations();
|
||||
WriteStartingInventory();
|
||||
WriteEnabledTricks(spoilerLog); //RANDOTODO clean up spoilerLog refernces
|
||||
//if (Settings::Logic.Is(LOGIC_GLITCHED)) {
|
||||
// WriteEnabledGlitches(spoilerLog);
|
||||
//}
|
||||
WriteMasterQuestDungeons(spoilerLog);
|
||||
WriteEnabledTricks();
|
||||
WriteMasterQuestDungeons();
|
||||
WriteRequiredTrials();
|
||||
WritePlaythrough();
|
||||
|
||||
@ -466,30 +377,3 @@ void PlacementLog_Clear() {
|
||||
placementtxt = "";
|
||||
}
|
||||
|
||||
// RANDOTODO: Do we even use this?
|
||||
bool PlacementLog_Write() {
|
||||
auto placementLog = tinyxml2::XMLDocument(false);
|
||||
placementLog.InsertEndChild(placementLog.NewDeclaration());
|
||||
|
||||
auto rootNode = placementLog.NewElement("placement-log");
|
||||
placementLog.InsertEndChild(rootNode);
|
||||
|
||||
// rootNode->SetAttribute("version", Settings::version.c_str());
|
||||
// rootNode->SetAttribute("seed", Settings::seed);
|
||||
|
||||
// WriteSettings(placementLog, true); // Include hidden settings.
|
||||
// WriteExcludedLocations(placementLog);
|
||||
// WriteStartingInventory(placementLog);
|
||||
WriteEnabledTricks(placementLog);
|
||||
//WriteEnabledGlitches(placementLog);
|
||||
WriteMasterQuestDungeons(placementLog);
|
||||
//WriteRequiredTrials(placementLog);
|
||||
|
||||
placementtxt = "\n" + placementtxt;
|
||||
|
||||
auto node = rootNode->InsertNewChildElement("log");
|
||||
auto contentNode = node->InsertNewText(placementtxt.c_str());
|
||||
contentNode->SetCData(true);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -112,7 +112,7 @@ void GenerateStartingInventory() {
|
||||
// AddItemToInventory(RG_EMPTY_BOTTLE, 1);
|
||||
// }
|
||||
// AddItemToInventory(RG_RUTOS_LETTER, StartingRutoBottle.Value<uint8_t>());
|
||||
AddItemToInventory(RG_PROGRESSIVE_OCARINA, ctx->GetOption(RSK_STARTING_OCARINA).Value<uint8_t>());
|
||||
AddItemToInventory(RG_PROGRESSIVE_OCARINA, ctx->GetOption(RSK_STARTING_OCARINA).GetContextOptionIndex());
|
||||
AddItemToInventory(RG_ZELDAS_LULLABY, ctx->GetOption(RSK_STARTING_ZELDAS_LULLABY) ? 1 : 0);
|
||||
AddItemToInventory(RG_EPONAS_SONG, ctx->GetOption(RSK_STARTING_EPONAS_SONG) ? 1 : 0);
|
||||
AddItemToInventory(RG_SARIAS_SONG, ctx->GetOption(RSK_STARTING_SARIAS_SONG) ? 1 : 0);
|
||||
@ -153,21 +153,21 @@ void GenerateStartingInventory() {
|
||||
// AddItemToInventory(RG_SPIRIT_MEDALLION, StartingSpiritMedallion.Value<uint8_t>());
|
||||
// AddItemToInventory(RG_SHADOW_MEDALLION, StartingShadowMedallion.Value<uint8_t>());
|
||||
// AddItemToInventory(RG_LIGHT_MEDALLION, StartingLightMedallion.Value<uint8_t>());
|
||||
AddItemToInventory(RG_GOLD_SKULLTULA_TOKEN, ctx->GetOption(RSK_STARTING_SKULLTULA_TOKEN).Value<uint8_t>());
|
||||
AddItemToInventory(RG_GOLD_SKULLTULA_TOKEN, ctx->GetOption(RSK_STARTING_SKULLTULA_TOKEN).GetContextOptionIndex());
|
||||
|
||||
int8_t hearts = ctx->GetOption(RSK_STARTING_HEARTS).Value<uint8_t>() - 2;
|
||||
int8_t hearts = ctx->GetOption(RSK_STARTING_HEARTS).GetContextOptionIndex() - 2;
|
||||
AdditionalHeartContainers = 0;
|
||||
if (hearts < 0) {
|
||||
AddItemToInventory(RG_PIECE_OF_HEART, 4);
|
||||
// Plentiful and minimal have less than 4 standard pieces of heart so also replace the winner heart
|
||||
if (ctx->GetOption(RSK_ITEM_POOL).Value<uint8_t>() == 0 || ctx->GetOption(RSK_ITEM_POOL).Value<uint8_t>() == 3) {
|
||||
if (ctx->GetOption(RSK_ITEM_POOL).GetContextOptionIndex() == 0 || ctx->GetOption(RSK_ITEM_POOL).GetContextOptionIndex() == 3) {
|
||||
AddItemToInventory(RG_TREASURE_GAME_HEART);
|
||||
}
|
||||
|
||||
AdditionalHeartContainers = 1 - hearts;
|
||||
} else if (hearts > 0) {
|
||||
// 16 containers in plentiful, 8 in balanced and 0 in the others
|
||||
uint8_t maxContainers = 8 * std::max(0, 2 - ctx->GetOption(RSK_ITEM_POOL).Value<uint8_t>());
|
||||
uint8_t maxContainers = 8 * std::max(0, 2 - ctx->GetOption(RSK_ITEM_POOL).GetContextOptionIndex());
|
||||
|
||||
if (hearts <= maxContainers) {
|
||||
AddItemToInventory(RG_HEART_CONTAINER, hearts);
|
||||
|
@ -270,6 +270,16 @@ Rando::Item plandomizerRandoRetrieveItem(RandomizerGet randoGetItem) {
|
||||
return randoGetItemEntry;
|
||||
}
|
||||
|
||||
void PlandoPushImageButtonStyle(){
|
||||
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(1.0f, 1.0f, 1.0f, 0.0f));
|
||||
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(1.0f, 1.0f, 1.0f, 0.2f));
|
||||
ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(1.0f, 1.0f, 1.0f, 0.1f));
|
||||
}
|
||||
|
||||
void PlandoPopImageButtonStyle(){
|
||||
ImGui::PopStyleColor(3);
|
||||
}
|
||||
|
||||
ImVec4 plandomizerGetItemColor(Rando::Item randoItem) {
|
||||
itemColor = ImVec4( 1.0f, 1.0f, 1.0f, 1.0f );
|
||||
if (randoItem.GetItemType() == ITEMTYPE_SMALLKEY || randoItem.GetItemType() == ITEMTYPE_FORTRESS_SMALLKEY
|
||||
@ -698,6 +708,7 @@ void PlandomizerOverlayText(std::pair<Rando::Item, uint32_t> drawObject ) {
|
||||
|
||||
void PlandomizerDrawItemPopup(uint32_t index) {
|
||||
if (shouldPopup && ImGui::BeginPopup("ItemList")) {
|
||||
PlandoPushImageButtonStyle();
|
||||
ImGui::SeparatorText("Resources");
|
||||
ImGui::BeginTable("Infinite Item Table", 7);
|
||||
for (auto& item : infiniteItemList) {
|
||||
@ -759,6 +770,7 @@ void PlandomizerDrawItemPopup(uint32_t index) {
|
||||
PlandomizerRemoveFromItemList(drawnItemsList[temporaryItemIndex].first);
|
||||
PlandomizerAddToItemList(temporaryItem);
|
||||
}
|
||||
PlandoPopImageButtonStyle();
|
||||
ImGui::EndTable();
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
@ -767,6 +779,7 @@ void PlandomizerDrawItemPopup(uint32_t index) {
|
||||
void PlandomizerDrawIceTrapPopUp(uint32_t index) {
|
||||
if (shouldTrapPopup && ImGui::BeginPopup("TrapList")) {
|
||||
ImGui::BeginTable("Ice Trap Table", 8);
|
||||
PlandoPushImageButtonStyle();
|
||||
for (auto& items : itemImageMap) {
|
||||
if (items.first == RG_ICE_TRAP) {
|
||||
continue;
|
||||
@ -785,6 +798,7 @@ void PlandomizerDrawIceTrapPopUp(uint32_t index) {
|
||||
|
||||
ImGui::PopID();
|
||||
}
|
||||
PlandoPopImageButtonStyle();
|
||||
ImGui::EndTable();
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
@ -792,12 +806,14 @@ void PlandomizerDrawIceTrapPopUp(uint32_t index) {
|
||||
|
||||
void PlandomizerDrawItemSlots(uint32_t index) {
|
||||
ImGui::PushID(index);
|
||||
PlandoPushImageButtonStyle();
|
||||
PlandomizerItemImageCorrection(plandoLogData[index].checkRewardItem);
|
||||
if (ImGui::ImageButton(textureID, imageSize, textureUV0, textureUV1, imagePadding, ImVec4(0, 0, 0, 0), itemColor)) {
|
||||
shouldPopup = true;
|
||||
temporaryItem = plandoLogData[index].checkRewardItem;
|
||||
ImGui::OpenPopup("ItemList");
|
||||
};
|
||||
PlandoPopImageButtonStyle();
|
||||
UIWidgets::Tooltip(plandoLogData[index].checkRewardItem.GetName().english.c_str());
|
||||
PlandomizerOverlayText(std::make_pair(plandoLogData[index].checkRewardItem, 1));
|
||||
PlandomizerDrawItemPopup(index);
|
||||
@ -808,9 +824,19 @@ void PlandomizerDrawShopSlider(uint32_t index) {
|
||||
ImGui::PushID(index);
|
||||
ImGui::Text("Price:");
|
||||
ImGui::SameLine();
|
||||
ImGui::PushItemWidth(ImGui::GetContentRegionAvail().x - 20.0f);
|
||||
std::string MinusBTNName = " - ##Price";
|
||||
if (ImGui::Button(MinusBTNName.c_str()) && plandoLogData[index].shopPrice > 0) {
|
||||
plandoLogData[index].shopPrice--;
|
||||
}
|
||||
ImGui::SameLine();
|
||||
ImGui::PushItemWidth(ImGui::GetContentRegionAvail().x - 40.0f);
|
||||
ImGui::SliderInt("", &plandoLogData[index].shopPrice, 0, 999, "%d Rupees");
|
||||
ImGui::PopItemWidth();
|
||||
ImGui::SameLine();
|
||||
std::string PlusBTNName = " + ##Price";
|
||||
if (ImGui::Button(PlusBTNName.c_str()) && plandoLogData[index].shopPrice < 999) {
|
||||
plandoLogData[index].shopPrice++;
|
||||
}
|
||||
ImGui::PopID();
|
||||
}
|
||||
|
||||
@ -825,10 +851,12 @@ void PlandomizerDrawIceTrapSetup(uint32_t index) {
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
PlandomizerItemImageCorrection(plandoLogData[index].iceTrapModel);
|
||||
PlandoPushImageButtonStyle();
|
||||
if (ImGui::ImageButton(textureID, imageSize, textureUV0, textureUV1, imagePadding, ImVec4(0, 0, 0, 0), itemColor)) {
|
||||
shouldTrapPopup = true;
|
||||
ImGui::OpenPopup("TrapList");
|
||||
};
|
||||
PlandoPopImageButtonStyle();
|
||||
UIWidgets::Tooltip(plandoLogData[index].iceTrapModel.GetName().english.c_str());
|
||||
PlandomizerDrawIceTrapPopUp(index);
|
||||
ImGui::SameLine();
|
||||
@ -847,7 +875,7 @@ void PlandomizerDrawIceTrapSetup(uint32_t index) {
|
||||
plandoLogData[index].iceTrapName = trapTextInput.c_str();
|
||||
}
|
||||
|
||||
if (plandoLogData[index].shopPrice > -1) {
|
||||
if (plandoLogData[index].shopPrice >= 0) {
|
||||
PlandomizerDrawShopSlider(index);
|
||||
}
|
||||
ImGui::EndTable();
|
||||
@ -860,7 +888,7 @@ void PlandomizerDrawOptions() {
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::SeparatorText("Load/Save Spoiler Log");
|
||||
PlandomizerPopulateSeedList();
|
||||
static int32_t selectedList = 0;
|
||||
static size_t selectedList = 0;
|
||||
if (existingSeedList.size() != 0) {
|
||||
if (ImGui::BeginCombo("##JsonFiles", existingSeedList[selectedList].c_str())) {
|
||||
for (size_t i = 0; i < existingSeedList.size(); i++) {
|
||||
@ -895,15 +923,13 @@ void PlandomizerDrawOptions() {
|
||||
ImGui::SetCursorPosX(ImGui::GetCursorPosX() + (ImGui::GetContentRegionAvail().x * 0.5f) - (34.0f * 5.0f));
|
||||
if (spoilerLogData.size() > 0) {
|
||||
ImGui::BeginTable("HashIcons", 5);
|
||||
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(1.0f, 1.0f, 1.0f, 0.0f));
|
||||
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(1.0f, 1.0f, 1.0f, 0.2f));
|
||||
ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(1.0f, 1.0f, 1.0f, 0.1f));
|
||||
for (int i = 0; i < 5; i++) {
|
||||
ImGui::TableSetupColumn("Icon", ImGuiTableColumnFlags_WidthFixed, 34.0f);
|
||||
}
|
||||
ImGui::TableNextColumn();
|
||||
|
||||
int32_t index = 0;
|
||||
size_t index = 0;
|
||||
PlandoPushImageButtonStyle();
|
||||
for (auto& hash : plandoHash) {
|
||||
ImGui::PushID(index);
|
||||
textureID = Ship::Context::GetInstance()->GetWindow()->GetGui()->GetTextureByName(gSeedTextures[hash].tex);
|
||||
@ -930,7 +956,7 @@ void PlandomizerDrawOptions() {
|
||||
ImGui::PopID();
|
||||
index++;
|
||||
}
|
||||
ImGui::PopStyleColor(3);
|
||||
PlandoPopImageButtonStyle();
|
||||
ImGui::EndTable();
|
||||
} else {
|
||||
ImGui::Text("No Spoiler Log Loaded");
|
||||
@ -1039,9 +1065,6 @@ void PlandomizerDrawLocationsWindow(RandomizerCheckArea rcArea) {
|
||||
ImGui::TableSetupColumn("Additional Options");
|
||||
ImGui::TableSetupScrollFreeze(0, 1);
|
||||
ImGui::TableHeadersRow();
|
||||
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(1.0f, 1.0f, 1.0f, 0.0f));
|
||||
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(1.0f, 1.0f, 1.0f, 0.2f));
|
||||
ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(1.0f, 1.0f, 1.0f, 0.1f));
|
||||
|
||||
for (auto& spoilerData : spoilerLogData) {
|
||||
auto checkID = Rando::StaticData::locationNameToEnum[spoilerData.checkName];
|
||||
@ -1070,7 +1093,6 @@ void PlandomizerDrawLocationsWindow(RandomizerCheckArea rcArea) {
|
||||
}
|
||||
index++;
|
||||
}
|
||||
ImGui::PopStyleColor(3);
|
||||
ImGui::EndTable();
|
||||
ImGui::EndChild();
|
||||
}
|
||||
|
@ -672,11 +672,8 @@ bool EntranceShuffler::PlaceOneWayPriorityEntrance(
|
||||
}
|
||||
}
|
||||
}
|
||||
#ifdef ENABLE_DEBUG
|
||||
auto message = "ERROR: Unable to place priority one-way entrance for " + priorityName + "\n";
|
||||
SPDLOG_DEBUG(message);
|
||||
PlacementLog_Write();
|
||||
#endif
|
||||
SPDLOG_DEBUG("ERROR: Unable to place priority one-way entrance for " + priorityName + "\n");
|
||||
assert(false);
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -1333,12 +1330,12 @@ int EntranceShuffler::ShuffleAllEntrances() {
|
||||
(ctx->GetOption(RSK_MIX_OVERWORLD_ENTRANCES) ? 1 : 0) + (ctx->GetOption(RSK_MIX_INTERIOR_ENTRANCES) ? 1 : 0) +
|
||||
(ctx->GetOption(RSK_MIX_GROTTO_ENTRANCES) ? 1 : 0);
|
||||
if (totalMixedPools < 2) {
|
||||
ctx->GetOption(RSK_MIXED_ENTRANCE_POOLS).SetSelectedIndex(RO_GENERIC_OFF);
|
||||
ctx->GetOption(RSK_MIX_DUNGEON_ENTRANCES).SetSelectedIndex(RO_GENERIC_OFF);
|
||||
ctx->GetOption(RSK_MIX_BOSS_ENTRANCES).SetSelectedIndex(RO_GENERIC_OFF);
|
||||
ctx->GetOption(RSK_MIX_OVERWORLD_ENTRANCES).SetSelectedIndex(RO_GENERIC_OFF);
|
||||
ctx->GetOption(RSK_MIX_INTERIOR_ENTRANCES).SetSelectedIndex(RO_GENERIC_OFF);
|
||||
ctx->GetOption(RSK_MIX_GROTTO_ENTRANCES).SetSelectedIndex(RO_GENERIC_OFF);
|
||||
ctx->GetOption(RSK_MIXED_ENTRANCE_POOLS).SetContextIndex(RO_GENERIC_OFF);
|
||||
ctx->GetOption(RSK_MIX_DUNGEON_ENTRANCES).SetContextIndex(RO_GENERIC_OFF);
|
||||
ctx->GetOption(RSK_MIX_BOSS_ENTRANCES).SetContextIndex(RO_GENERIC_OFF);
|
||||
ctx->GetOption(RSK_MIX_OVERWORLD_ENTRANCES).SetContextIndex(RO_GENERIC_OFF);
|
||||
ctx->GetOption(RSK_MIX_INTERIOR_ENTRANCES).SetContextIndex(RO_GENERIC_OFF);
|
||||
ctx->GetOption(RSK_MIX_GROTTO_ENTRANCES).SetContextIndex(RO_GENERIC_OFF);
|
||||
}
|
||||
if (ctx->GetOption(RSK_MIXED_ENTRANCE_POOLS)) {
|
||||
std::set<EntranceType> poolsToMix = {};
|
||||
|
@ -17,7 +17,7 @@ extern PlayState* gPlayState;
|
||||
|
||||
#define FSi OTRGlobals::Instance->gRandoContext->GetFishsanity()
|
||||
|
||||
#define RAND_GET_OPTION(option) Rando::Context::GetInstance()->GetOption(option).GetSelectedOptionIndex()
|
||||
#define RAND_GET_OPTION(option) Rando::Context::GetInstance()->GetOption(option).GetContextOptionIndex()
|
||||
|
||||
/**
|
||||
* @brief Parallel list of pond fish checks for both ages
|
||||
@ -59,6 +59,7 @@ namespace Rando {
|
||||
const FishIdentity Fishsanity::defaultIdentity = { RAND_INF_MAX, RC_UNKNOWN_CHECK };
|
||||
bool Fishsanity::fishsanityHelpersInit = false;
|
||||
s16 Fishsanity::fishGroupCounter = 0;
|
||||
bool Fishsanity::enableAdvance = false;
|
||||
std::unordered_map<RandomizerCheck, LinkAge> Fishsanity::pondFishAgeMap;
|
||||
std::vector<RandomizerCheck> Fishsanity::childPondFish;
|
||||
std::vector<RandomizerCheck> Fishsanity::adultPondFish;
|
||||
@ -395,22 +396,6 @@ namespace Rando {
|
||||
}
|
||||
}
|
||||
|
||||
void Fishsanity::OnFlagSetHandler(int16_t flagType, int16_t flag) {
|
||||
if (flagType != FLAG_RANDOMIZER_INF) {
|
||||
return;
|
||||
}
|
||||
RandomizerCheck rc = OTRGlobals::Instance->gRandomizer->GetCheckFromRandomizerInf((RandomizerInf)flag);
|
||||
FishsanityCheckType fsType = Rando::Fishsanity::GetCheckType(rc);
|
||||
if (fsType == FSC_NONE) {
|
||||
return;
|
||||
}
|
||||
|
||||
// When a pond fish is caught, advance the pond.
|
||||
if (fsType == FSC_POND) {
|
||||
OTRGlobals::Instance->gRandoContext->GetFishsanity()->AdvancePond();
|
||||
}
|
||||
}
|
||||
|
||||
void Fishsanity::OnActorUpdateHandler(void* refActor) {
|
||||
if (gPlayState->sceneNum != SCENE_GROTTOS && gPlayState->sceneNum != SCENE_ZORAS_DOMAIN && gPlayState->sceneNum != SCENE_FISHING_POND) {
|
||||
return;
|
||||
@ -428,6 +413,7 @@ namespace Rando {
|
||||
FishIdentity identity = OTRGlobals::Instance->gRandomizer->IdentifyFish(gPlayState->sceneNum, actor->params);
|
||||
if (identity.randomizerCheck != RC_UNKNOWN_CHECK) {
|
||||
Flags_SetRandomizerInf(identity.randomizerInf);
|
||||
enableAdvance = true;
|
||||
// Remove uncaught effect
|
||||
if (actor->shape.shadowDraw != NULL) {
|
||||
actor->shape.shadowDraw = NULL;
|
||||
@ -483,6 +469,13 @@ namespace Rando {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Fishsanity::OnItemReceiveHandler(GetItemEntry itemEntry) {
|
||||
if (enableAdvance) {
|
||||
enableAdvance = false;
|
||||
OTRGlobals::Instance->gRandoContext->GetFishsanity()->AdvancePond();
|
||||
}
|
||||
}
|
||||
} // namespace Rando
|
||||
|
||||
// C interface
|
||||
|
@ -133,11 +133,6 @@ class Fishsanity {
|
||||
*/
|
||||
static void OnActorInitHandler(void* refActor);
|
||||
|
||||
/**
|
||||
* @brief FlagSet hook handler for fishsanity
|
||||
*/
|
||||
static void OnFlagSetHandler(int16_t flagType, int16_t flag);
|
||||
|
||||
/**
|
||||
* @brief PlayerUpdate hook handler for fishsanity
|
||||
*/
|
||||
@ -158,6 +153,8 @@ class Fishsanity {
|
||||
*/
|
||||
static void OnVanillaBehaviorHandler(GIVanillaBehavior id, bool* should, va_list originalArgs);
|
||||
|
||||
static void OnItemReceiveHandler(GetItemEntry itemEntry);
|
||||
|
||||
private:
|
||||
/**
|
||||
* @brief Initialize helper statics if they have not been initialized yet
|
||||
@ -184,6 +181,7 @@ class Fishsanity {
|
||||
static bool fishsanityHelpersInit;
|
||||
|
||||
static s16 fishGroupCounter;
|
||||
static bool enableAdvance;
|
||||
|
||||
/////////////////////////////////////////////////////////
|
||||
//// Helper data structures derived from static data ////
|
||||
|
@ -559,23 +559,23 @@ CustomMessage Hint::GetBridgeReqsText() {
|
||||
}
|
||||
else if (ctx->GetOption(RSK_RAINBOW_BRIDGE).Is(RO_BRIDGE_STONES)) {
|
||||
bridgeMessage = StaticData::hintTextTable[RHT_BRIDGE_STONES_HINT].GetHintMessage();
|
||||
bridgeMessage.InsertNumber(ctx->GetOption(RSK_RAINBOW_BRIDGE_STONE_COUNT).Value<uint8_t>());
|
||||
bridgeMessage.InsertNumber(ctx->GetOption(RSK_RAINBOW_BRIDGE_STONE_COUNT).GetContextOptionIndex());
|
||||
}
|
||||
else if (ctx->GetOption(RSK_RAINBOW_BRIDGE).Is(RO_BRIDGE_MEDALLIONS)) {
|
||||
bridgeMessage = StaticData::hintTextTable[RHT_BRIDGE_MEDALLIONS_HINT].GetHintMessage();
|
||||
bridgeMessage.InsertNumber(ctx->GetOption(RSK_RAINBOW_BRIDGE_MEDALLION_COUNT).Value<uint8_t>());
|
||||
bridgeMessage.InsertNumber(ctx->GetOption(RSK_RAINBOW_BRIDGE_MEDALLION_COUNT).GetContextOptionIndex());
|
||||
}
|
||||
else if (ctx->GetOption(RSK_RAINBOW_BRIDGE).Is(RO_BRIDGE_DUNGEON_REWARDS)) {
|
||||
bridgeMessage = StaticData::hintTextTable[RHT_BRIDGE_REWARDS_HINT].GetHintMessage();
|
||||
bridgeMessage.InsertNumber(ctx->GetOption(RSK_RAINBOW_BRIDGE_REWARD_COUNT).Value<uint8_t>());
|
||||
bridgeMessage.InsertNumber(ctx->GetOption(RSK_RAINBOW_BRIDGE_REWARD_COUNT).GetContextOptionIndex());
|
||||
}
|
||||
else if (ctx->GetOption(RSK_RAINBOW_BRIDGE).Is(RO_BRIDGE_DUNGEONS)) {
|
||||
bridgeMessage = StaticData::hintTextTable[RHT_BRIDGE_DUNGEONS_HINT].GetHintMessage();
|
||||
bridgeMessage.InsertNumber(ctx->GetOption(RSK_RAINBOW_BRIDGE_DUNGEON_COUNT).Value<uint8_t>());
|
||||
bridgeMessage.InsertNumber(ctx->GetOption(RSK_RAINBOW_BRIDGE_DUNGEON_COUNT).GetContextOptionIndex());
|
||||
}
|
||||
else if (ctx->GetOption(RSK_RAINBOW_BRIDGE).Is(RO_BRIDGE_TOKENS)) {
|
||||
bridgeMessage = StaticData::hintTextTable[RHT_BRIDGE_TOKENS_HINT].GetHintMessage();
|
||||
bridgeMessage.InsertNumber(ctx->GetOption(RSK_RAINBOW_BRIDGE_TOKEN_COUNT).Value<uint8_t>());
|
||||
bridgeMessage.InsertNumber(ctx->GetOption(RSK_RAINBOW_BRIDGE_TOKEN_COUNT).GetContextOptionIndex());
|
||||
}
|
||||
else if (ctx->GetOption(RSK_RAINBOW_BRIDGE).Is(RO_BRIDGE_GREG)) {
|
||||
return StaticData::hintTextTable[RHT_BRIDGE_GREG_HINT].GetHintMessage();
|
||||
@ -613,23 +613,23 @@ CustomMessage Hint::GetGanonBossKeyText() {
|
||||
}
|
||||
else if (ctx->GetOption(RSK_GANONS_BOSS_KEY).Is(RO_GANON_BOSS_KEY_LACS_STONES)) {
|
||||
ganonBossKeyMessage = StaticData::hintTextTable[RHT_LACS_STONES_HINT].GetHintMessage();
|
||||
ganonBossKeyMessage.InsertNumber(ctx->GetOption(RSK_LACS_STONE_COUNT).Value<uint8_t>());
|
||||
ganonBossKeyMessage.InsertNumber(ctx->GetOption(RSK_LACS_STONE_COUNT).GetContextOptionIndex());
|
||||
}
|
||||
else if (ctx->GetOption(RSK_GANONS_BOSS_KEY).Is(RO_GANON_BOSS_KEY_LACS_MEDALLIONS)) {
|
||||
ganonBossKeyMessage = StaticData::hintTextTable[RHT_LACS_MEDALLIONS_HINT].GetHintMessage();
|
||||
ganonBossKeyMessage.InsertNumber(ctx->GetOption(RSK_LACS_MEDALLION_COUNT).Value<uint8_t>());
|
||||
ganonBossKeyMessage.InsertNumber(ctx->GetOption(RSK_LACS_MEDALLION_COUNT).GetContextOptionIndex());
|
||||
}
|
||||
else if (ctx->GetOption(RSK_GANONS_BOSS_KEY).Is(RO_GANON_BOSS_KEY_LACS_REWARDS)) {
|
||||
ganonBossKeyMessage = StaticData::hintTextTable[RHT_LACS_REWARDS_HINT].GetHintMessage();
|
||||
ganonBossKeyMessage.InsertNumber(ctx->GetOption(RSK_LACS_REWARD_COUNT).Value<uint8_t>());
|
||||
ganonBossKeyMessage.InsertNumber(ctx->GetOption(RSK_LACS_REWARD_COUNT).GetContextOptionIndex());
|
||||
}
|
||||
else if (ctx->GetOption(RSK_GANONS_BOSS_KEY).Is(RO_GANON_BOSS_KEY_LACS_DUNGEONS)) {
|
||||
ganonBossKeyMessage = StaticData::hintTextTable[RHT_LACS_DUNGEONS_HINT].GetHintMessage();
|
||||
ganonBossKeyMessage.InsertNumber(ctx->GetOption(RSK_LACS_DUNGEON_COUNT).Value<uint8_t>());
|
||||
ganonBossKeyMessage.InsertNumber(ctx->GetOption(RSK_LACS_DUNGEON_COUNT).GetContextOptionIndex());
|
||||
}
|
||||
else if (ctx->GetOption(RSK_GANONS_BOSS_KEY).Is(RO_GANON_BOSS_KEY_LACS_TOKENS)) {
|
||||
ganonBossKeyMessage = StaticData::hintTextTable[RHT_LACS_TOKENS_HINT].GetHintMessage();
|
||||
ganonBossKeyMessage.InsertNumber(ctx->GetOption(RSK_LACS_TOKEN_COUNT).Value<uint8_t>());
|
||||
ganonBossKeyMessage.InsertNumber(ctx->GetOption(RSK_LACS_TOKEN_COUNT).GetContextOptionIndex());
|
||||
}
|
||||
else if (ctx->GetOption(RSK_GANONS_BOSS_KEY).Is(RO_GANON_BOSS_KEY_TRIFORCE_HUNT)) {
|
||||
return StaticData::hintTextTable[RHT_GANON_BK_TRIFORCE_HINT].GetHintMessage();
|
||||
|
@ -51,6 +51,7 @@ extern "C" {
|
||||
#include "src/overlays/actors/ovl_Door_Gerudo/z_door_gerudo.h"
|
||||
#include "src/overlays/actors/ovl_En_Xc/z_en_xc.h"
|
||||
#include "src/overlays/actors/ovl_Fishing/z_fishing.h"
|
||||
#include "src/overlays/actors/ovl_En_Mk/z_en_mk.h"
|
||||
#include "adult_trade_shuffle.h"
|
||||
#include "draw.h"
|
||||
|
||||
@ -60,9 +61,10 @@ extern void func_8084DFAC(PlayState* play, Player* player);
|
||||
extern void Player_SetupActionPreserveAnimMovement(PlayState* play, Player* player, PlayerActionFunc actionFunc, s32 flags);
|
||||
extern s32 Player_SetupWaitForPutAway(PlayState* play, Player* player, AfterPutAwayFunc func);
|
||||
extern void Play_InitEnvironment(PlayState * play, s16 skyboxId);
|
||||
extern void EnMk_Wait(EnMk* enMk, PlayState* play);
|
||||
}
|
||||
|
||||
#define RAND_GET_OPTION(option) Rando::Context::GetInstance()->GetOption(option).GetSelectedOptionIndex()
|
||||
#define RAND_GET_OPTION(option) Rando::Context::GetInstance()->GetOption(option).GetContextOptionIndex()
|
||||
|
||||
bool LocMatchesQuest(Rando::Location loc) {
|
||||
if (loc.GetQuest() == RCQUEST_BOTH) {
|
||||
@ -619,11 +621,11 @@ void func_8083A434_override(PlayState* play, Player* player) {
|
||||
bool ShouldGiveFishingPrize(f32 sFishOnHandLength){
|
||||
// RANDOTODO: update the enhancement sliders to not allow
|
||||
// values above rando fish weight values when rando'd
|
||||
if(LINK_IS_CHILD) {
|
||||
if(LINK_IS_CHILD) {
|
||||
int32_t weight = CVarGetInteger(CVAR_ENHANCEMENT("CustomizeFishing"), 0) ? CVarGetInteger(CVAR_ENHANCEMENT("MinimumFishWeightChild"), 10) : 10;
|
||||
f32 score = sqrt(((f32)weight - 0.5f) / 0.0036f);
|
||||
return sFishOnHandLength >= score && (IS_RANDO ? !Flags_GetRandomizerInf(RAND_INF_CHILD_FISHING) : !(HIGH_SCORE(HS_FISHING) & HS_FISH_PRIZE_CHILD));
|
||||
} else
|
||||
} else
|
||||
{
|
||||
int32_t weight = CVarGetInteger(CVAR_ENHANCEMENT("CustomizeFishing"), 0) ? CVarGetInteger(CVAR_ENHANCEMENT("MinimumFishWeightAdult"), 13) : 13;
|
||||
f32 score = sqrt(((f32)weight - 0.5f) / 0.0036f);
|
||||
@ -631,6 +633,147 @@ bool ShouldGiveFishingPrize(f32 sFishOnHandLength){
|
||||
}
|
||||
}
|
||||
|
||||
void RandomizerOnDialogMessageHandler() {
|
||||
MessageContext *msgCtx = &gPlayState->msgCtx;
|
||||
Actor *actor = msgCtx->talkActor;
|
||||
auto ctx = Rando::Context::GetInstance();
|
||||
bool revealMerchant = ctx->GetOption(RSK_MERCHANT_TEXT_HINT).GetContextOptionIndex() != RO_GENERIC_OFF;
|
||||
bool nonBeanMerchants = ctx->GetOption(RSK_SHUFFLE_MERCHANTS).Is(RO_SHUFFLE_MERCHANTS_ALL_BUT_BEANS) ||
|
||||
ctx->GetOption(RSK_SHUFFLE_MERCHANTS).Is(RO_SHUFFLE_MERCHANTS_ALL);
|
||||
|
||||
RandomizerCheck reveal = RC_UNKNOWN_CHECK;
|
||||
if (ctx->GetOption(RSK_CHICKENS_HINT) && (msgCtx->textId >= TEXT_ANJU_PLEASE_BRING_MY_CUCCOS_BACK && msgCtx->textId <= TEXT_ANJU_PLEASE_BRING_1_CUCCO)) {
|
||||
reveal = RC_KAK_ANJU_AS_CHILD;
|
||||
} else {
|
||||
switch (msgCtx->textId) {
|
||||
case TEXT_SKULLTULA_PEOPLE_IM_CURSED:
|
||||
if (actor->params == 1 && ctx->GetOption(RSK_KAK_10_SKULLS_HINT)){
|
||||
reveal = RC_KAK_10_GOLD_SKULLTULA_REWARD;
|
||||
} else if (actor->params == 2 && ctx->GetOption(RSK_KAK_20_SKULLS_HINT)){
|
||||
reveal = RC_KAK_20_GOLD_SKULLTULA_REWARD;
|
||||
} else if (actor->params == 3 && ctx->GetOption(RSK_KAK_30_SKULLS_HINT)){
|
||||
reveal = RC_KAK_30_GOLD_SKULLTULA_REWARD;
|
||||
} else if (actor->params == 4 && ctx->GetOption(RSK_KAK_40_SKULLS_HINT)){
|
||||
reveal = RC_KAK_40_GOLD_SKULLTULA_REWARD;
|
||||
} else if (ctx->GetOption(RSK_KAK_50_SKULLS_HINT)){
|
||||
reveal = RC_KAK_50_GOLD_SKULLTULA_REWARD;
|
||||
}
|
||||
break;
|
||||
case TEXT_SKULLTULA_PEOPLE_MAKE_YOU_VERY_RICH:
|
||||
if (ctx->GetOption(RSK_KAK_100_SKULLS_HINT)) {
|
||||
reveal = RC_KAK_100_GOLD_SKULLTULA_REWARD;
|
||||
}
|
||||
break;
|
||||
case TEXT_MASK_SHOP_SIGN:
|
||||
if (ctx->GetOption(RSK_MASK_SHOP_HINT)) {
|
||||
auto itemSkull_loc = ctx->GetItemLocation(RC_DEKU_THEATER_SKULL_MASK);
|
||||
if (itemSkull_loc->GetCheckStatus() == RCSHOW_UNCHECKED) {
|
||||
itemSkull_loc->SetCheckStatus(RCSHOW_IDENTIFIED);
|
||||
}
|
||||
reveal = RC_DEKU_THEATER_MASK_OF_TRUTH;
|
||||
}
|
||||
break;
|
||||
case TEXT_GHOST_SHOP_EXPLAINATION:
|
||||
case TEXT_GHOST_SHOP_CARD_HAS_POINTS:
|
||||
if (ctx->GetOption(RSK_BIG_POES_HINT)) {
|
||||
reveal = RC_MARKET_10_BIG_POES;
|
||||
}
|
||||
break;
|
||||
case TEXT_MALON_EVERYONE_TURNING_EVIL:
|
||||
case TEXT_MALON_I_SING_THIS_SONG:
|
||||
case TEXT_MALON_HOW_IS_EPONA_DOING:
|
||||
case TEXT_MALON_OBSTICLE_COURSE:
|
||||
case TEXT_MALON_INGO_MUST_HAVE_BEEN_TEMPTED:
|
||||
if (ctx->GetOption(RSK_MALON_HINT)) {
|
||||
reveal = RC_KF_LINKS_HOUSE_COW;
|
||||
}
|
||||
break;
|
||||
case TEXT_FROGS_UNDERWATER:
|
||||
if (ctx->GetOption(RSK_FROGS_HINT)) {
|
||||
reveal = RC_ZR_FROGS_OCARINA_GAME;
|
||||
}
|
||||
break;
|
||||
case TEXT_GF_HBA_SIGN:
|
||||
case TEXT_HBA_NOT_ON_HORSE:
|
||||
case TEXT_HBA_INITIAL_EXPLAINATION:
|
||||
case TEXT_HBA_ALREADY_HAVE_1000:
|
||||
if (ctx->GetOption(RSK_HBA_HINT)) {
|
||||
auto item1000_loc = ctx->GetItemLocation(RC_GF_HBA_1000_POINTS);
|
||||
if (item1000_loc->GetCheckStatus() == RCSHOW_UNCHECKED) {
|
||||
item1000_loc->SetCheckStatus(RCSHOW_IDENTIFIED);
|
||||
}
|
||||
reveal = RC_GF_HBA_1500_POINTS;
|
||||
}
|
||||
break;
|
||||
case TEXT_SCRUB_RANDOM:
|
||||
if (ctx->GetOption(RSK_SCRUB_TEXT_HINT).GetContextOptionIndex() != RO_GENERIC_OFF) {
|
||||
EnDns* enDns = (EnDns*)actor;
|
||||
reveal = OTRGlobals::Instance->gRandomizer->GetCheckFromRandomizerInf((RandomizerInf)enDns->sohScrubIdentity.randomizerInf);
|
||||
}
|
||||
break;
|
||||
case TEXT_BEAN_SALESMAN_BUY_FOR_10:
|
||||
if (revealMerchant && (ctx->GetOption(RSK_SHUFFLE_MERCHANTS).Is(RO_SHUFFLE_MERCHANTS_BEANS_ONLY) ||
|
||||
ctx->GetOption(RSK_SHUFFLE_MERCHANTS).Is(RO_SHUFFLE_MERCHANTS_ALL))) {
|
||||
reveal = RC_ZR_MAGIC_BEAN_SALESMAN;
|
||||
}
|
||||
break;
|
||||
case TEXT_GRANNYS_SHOP:
|
||||
if (revealMerchant && nonBeanMerchants &&
|
||||
(ctx->GetOption(RSK_SHUFFLE_ADULT_TRADE) || INV_CONTENT(ITEM_CLAIM_CHECK) == ITEM_CLAIM_CHECK)) {
|
||||
reveal = RC_KAK_GRANNYS_SHOP;
|
||||
}
|
||||
break;
|
||||
case TEXT_MEDIGORON:
|
||||
if (revealMerchant && nonBeanMerchants) {
|
||||
reveal = RC_GC_MEDIGORON;
|
||||
}
|
||||
break;
|
||||
case TEXT_CARPET_SALESMAN_1:
|
||||
if (revealMerchant && nonBeanMerchants) {
|
||||
reveal = RC_WASTELAND_BOMBCHU_SALESMAN;
|
||||
}
|
||||
break;
|
||||
case TEXT_BIGGORON_BETTER_AT_SMITHING:
|
||||
case TEXT_BIGGORON_WAITING_FOR_YOU:
|
||||
case TEXT_BIGGORON_RETURN_AFTER_A_FEW_DAYS:
|
||||
case TEXT_BIGGORON_I_MAAAADE_THISSSS:
|
||||
if (ctx->GetOption(RSK_BIGGORON_HINT)) {
|
||||
reveal = RC_DMT_TRADE_CLAIM_CHECK;
|
||||
}
|
||||
break;
|
||||
case TEXT_SHEIK_NEED_HOOK:
|
||||
case TEXT_SHEIK_HAVE_HOOK:
|
||||
if (ctx->GetOption(RSK_OOT_HINT) && gPlayState->sceneNum == SCENE_TEMPLE_OF_TIME &&
|
||||
!ctx->GetItemLocation(RC_SONG_FROM_OCARINA_OF_TIME)->HasObtained()) {
|
||||
auto itemoot_loc = ctx->GetItemLocation(RC_HF_OCARINA_OF_TIME_ITEM);
|
||||
if (itemoot_loc->GetCheckStatus() == RCSHOW_UNCHECKED) {
|
||||
itemoot_loc->SetCheckStatus(RCSHOW_IDENTIFIED);
|
||||
}
|
||||
reveal = RC_SONG_FROM_OCARINA_OF_TIME;
|
||||
}
|
||||
break;
|
||||
case TEXT_FISHING_CLOUDY:
|
||||
case TEXT_FISHING_TRY_ANOTHER_LURE:
|
||||
case TEXT_FISHING_SECRETS:
|
||||
case TEXT_FISHING_GOOD_FISHERMAN:
|
||||
case TEXT_FISHING_DIFFERENT_POND:
|
||||
case TEXT_FISHING_SCRATCHING:
|
||||
case TEXT_FISHING_TRY_ANOTHER_LURE_WITH_SINKING_LURE:
|
||||
if (ctx->GetOption(RSK_LOACH_HINT)) {
|
||||
reveal = RC_LH_HYRULE_LOACH;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (reveal != RC_UNKNOWN_CHECK) {
|
||||
auto item_loc = ctx->GetItemLocation(reveal);
|
||||
if (item_loc->GetCheckStatus() == RCSHOW_UNCHECKED) {
|
||||
item_loc->SetCheckStatus(RCSHOW_IDENTIFIED);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void RandomizerOnVanillaBehaviorHandler(GIVanillaBehavior id, bool* should, va_list originalArgs) {
|
||||
va_list args;
|
||||
va_copy(args, originalArgs);
|
||||
@ -860,7 +1003,8 @@ void RandomizerOnVanillaBehaviorHandler(GIVanillaBehavior id, bool* should, va_l
|
||||
|
||||
// This is typically called when you close the text box after getting an item, in case a previous
|
||||
// function hid the interface.
|
||||
Interface_ChangeAlpha(gSaveContext.unk_13EE);
|
||||
gSaveContext.unk_13EA = 0;
|
||||
Interface_ChangeAlpha(0x32);
|
||||
// EnItem00_SetupAction(item00, func_8001E5C8);
|
||||
// *should = false;
|
||||
} else if (item00->actor.params == ITEM00_SOH_GIVE_ITEM_ENTRY_GI) {
|
||||
@ -1399,9 +1543,23 @@ void RandomizerOnVanillaBehaviorHandler(GIVanillaBehavior id, bool* should, va_l
|
||||
}
|
||||
break;
|
||||
}
|
||||
case VB_TRADE_TIMER_EYEDROPS:{
|
||||
EnMk* enMk = va_arg(args, EnMk*);
|
||||
Flags_SetRandomizerInf(RAND_INF_ADULT_TRADES_LH_TRADE_FROG);
|
||||
enMk->actor.flags &= ~ACTOR_FLAG_WILL_TALK;
|
||||
enMk->actionFunc = EnMk_Wait;
|
||||
enMk->flags |= 1;
|
||||
*should = false;
|
||||
break;
|
||||
}
|
||||
// We need to override the vanilla behavior here because the player might sequence break and get Ruto kidnapped before accessing other
|
||||
// checks that require Ruto. So if she's kidnapped we allow her to spawn again
|
||||
case VB_RUTO_BE_CONSIDERED_NOT_KIDNAPPED: {
|
||||
*should = !Flags_GetInfTable(INFTABLE_145) || Flags_GetInfTable(INFTABLE_146);
|
||||
break;
|
||||
}
|
||||
case VB_FREEZE_ON_SKULL_TOKEN:
|
||||
case VB_TRADE_TIMER_ODD_MUSHROOM:
|
||||
case VB_TRADE_TIMER_EYEDROPS:
|
||||
case VB_TRADE_TIMER_FROG:
|
||||
case VB_ANJU_SET_OBTAINED_TRADE_ITEM:
|
||||
case VB_GIVE_ITEM_FROM_TARGET_IN_WOODS:
|
||||
@ -2107,6 +2265,7 @@ void RandomizerRegisterHooks() {
|
||||
static uint32_t onPlayerUpdateForRCQueueHook = 0;
|
||||
static uint32_t onPlayerUpdateForItemQueueHook = 0;
|
||||
static uint32_t onItemReceiveHook = 0;
|
||||
static uint32_t onDialogMessageHook = 0;
|
||||
static uint32_t onVanillaBehaviorHook = 0;
|
||||
static uint32_t onSceneInitHook = 0;
|
||||
static uint32_t onActorInitHook = 0;
|
||||
@ -2119,10 +2278,10 @@ void RandomizerRegisterHooks() {
|
||||
static uint32_t onKaleidoUpdateHook = 0;
|
||||
|
||||
static uint32_t fishsanityOnActorInitHook = 0;
|
||||
static uint32_t fishsanityOnFlagSetHook = 0;
|
||||
static uint32_t fishsanityOnActorUpdateHook = 0;
|
||||
static uint32_t fishsanityOnSceneInitHook = 0;
|
||||
static uint32_t fishsanityOnVanillaBehaviorHook = 0;
|
||||
static uint32_t fishsanityOnItemReceiveHook = 0;
|
||||
|
||||
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnLoadGame>([](int32_t fileNum) {
|
||||
randomizerQueuedChecks = std::queue<RandomizerCheck>();
|
||||
@ -2134,6 +2293,7 @@ void RandomizerRegisterHooks() {
|
||||
GameInteractor::Instance->UnregisterGameHook<GameInteractor::OnPlayerUpdate>(onPlayerUpdateForRCQueueHook);
|
||||
GameInteractor::Instance->UnregisterGameHook<GameInteractor::OnPlayerUpdate>(onPlayerUpdateForItemQueueHook);
|
||||
GameInteractor::Instance->UnregisterGameHook<GameInteractor::OnItemReceive>(onItemReceiveHook);
|
||||
GameInteractor::Instance->UnregisterGameHook<GameInteractor::OnItemReceive>(onDialogMessageHook);
|
||||
GameInteractor::Instance->UnregisterGameHook<GameInteractor::OnVanillaBehavior>(onVanillaBehaviorHook);
|
||||
GameInteractor::Instance->UnregisterGameHook<GameInteractor::OnSceneInit>(onSceneInitHook);
|
||||
GameInteractor::Instance->UnregisterGameHook<GameInteractor::OnActorInit>(onActorInitHook);
|
||||
@ -2146,16 +2306,17 @@ void RandomizerRegisterHooks() {
|
||||
GameInteractor::Instance->UnregisterGameHook<GameInteractor::OnKaleidoscopeUpdate>(onKaleidoUpdateHook);
|
||||
|
||||
GameInteractor::Instance->UnregisterGameHook<GameInteractor::OnActorInit>(fishsanityOnActorInitHook);
|
||||
GameInteractor::Instance->UnregisterGameHook<GameInteractor::OnFlagSet>(fishsanityOnFlagSetHook);
|
||||
GameInteractor::Instance->UnregisterGameHook<GameInteractor::OnActorUpdate>(fishsanityOnActorUpdateHook);
|
||||
GameInteractor::Instance->UnregisterGameHook<GameInteractor::OnSceneInit>(fishsanityOnSceneInitHook);
|
||||
GameInteractor::Instance->UnregisterGameHook<GameInteractor::OnVanillaBehavior>(fishsanityOnVanillaBehaviorHook);
|
||||
GameInteractor::Instance->UnregisterGameHook<GameInteractor::OnItemReceive>(fishsanityOnItemReceiveHook);
|
||||
|
||||
onFlagSetHook = 0;
|
||||
onSceneFlagSetHook = 0;
|
||||
onPlayerUpdateForRCQueueHook = 0;
|
||||
onPlayerUpdateForItemQueueHook = 0;
|
||||
onItemReceiveHook = 0;
|
||||
onDialogMessageHook = 0;
|
||||
onVanillaBehaviorHook = 0;
|
||||
onSceneInitHook = 0;
|
||||
onActorInitHook = 0;
|
||||
@ -2168,10 +2329,10 @@ void RandomizerRegisterHooks() {
|
||||
onKaleidoUpdateHook = 0;
|
||||
|
||||
fishsanityOnActorInitHook = 0;
|
||||
fishsanityOnFlagSetHook = 0;
|
||||
fishsanityOnActorUpdateHook = 0;
|
||||
fishsanityOnSceneInitHook = 0;
|
||||
fishsanityOnVanillaBehaviorHook = 0;
|
||||
fishsanityOnItemReceiveHook = 0;
|
||||
|
||||
if (!IS_RANDO) return;
|
||||
|
||||
@ -2188,6 +2349,7 @@ void RandomizerRegisterHooks() {
|
||||
onPlayerUpdateForRCQueueHook = GameInteractor::Instance->RegisterGameHook<GameInteractor::OnPlayerUpdate>(RandomizerOnPlayerUpdateForRCQueueHandler);
|
||||
onPlayerUpdateForItemQueueHook = GameInteractor::Instance->RegisterGameHook<GameInteractor::OnPlayerUpdate>(RandomizerOnPlayerUpdateForItemQueueHandler);
|
||||
onItemReceiveHook = GameInteractor::Instance->RegisterGameHook<GameInteractor::OnItemReceive>(RandomizerOnItemReceiveHandler);
|
||||
onDialogMessageHook = GameInteractor::Instance->RegisterGameHook<GameInteractor::OnDialogMessage>(RandomizerOnDialogMessageHandler);
|
||||
onVanillaBehaviorHook = GameInteractor::Instance->RegisterGameHook<GameInteractor::OnVanillaBehavior>(RandomizerOnVanillaBehaviorHandler);
|
||||
onSceneInitHook = GameInteractor::Instance->RegisterGameHook<GameInteractor::OnSceneInit>(RandomizerOnSceneInitHandler);
|
||||
onActorInitHook = GameInteractor::Instance->RegisterGameHook<GameInteractor::OnActorInit>(RandomizerOnActorInitHandler);
|
||||
@ -2203,10 +2365,10 @@ void RandomizerRegisterHooks() {
|
||||
OTRGlobals::Instance->gRandoContext->GetFishsanity()->InitializeFromSave();
|
||||
|
||||
fishsanityOnActorInitHook = GameInteractor::Instance->RegisterGameHook<GameInteractor::OnActorInit>(Rando::Fishsanity::OnActorInitHandler);
|
||||
fishsanityOnFlagSetHook = GameInteractor::Instance->RegisterGameHook<GameInteractor::OnFlagSet>(Rando::Fishsanity::OnFlagSetHandler);
|
||||
fishsanityOnActorUpdateHook = GameInteractor::Instance->RegisterGameHook<GameInteractor::OnActorUpdate>(Rando::Fishsanity::OnActorUpdateHandler);
|
||||
fishsanityOnSceneInitHook = GameInteractor::Instance->RegisterGameHook<GameInteractor::OnSceneInit>(Rando::Fishsanity::OnSceneInitHandler);
|
||||
fishsanityOnVanillaBehaviorHook = GameInteractor::Instance->RegisterGameHook<GameInteractor::OnVanillaBehavior>(Rando::Fishsanity::OnVanillaBehaviorHandler);
|
||||
fishsanityOnItemReceiveHook = GameInteractor::Instance->RegisterGameHook<GameInteractor::OnItemReceive>(Rando::Fishsanity::OnItemReceiveHandler);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -176,8 +176,8 @@ void ItemLocation::SetHidden(const bool hidden_) {
|
||||
hidden = hidden_;
|
||||
}
|
||||
|
||||
bool ItemLocation::IsExcluded() const {
|
||||
return excludedOption.Value<bool>();
|
||||
bool ItemLocation::IsExcluded() {
|
||||
return excludedOption.GetContextOptionIndex();
|
||||
}
|
||||
|
||||
Option* ItemLocation::GetExcludedOption() {
|
||||
@ -197,7 +197,7 @@ void ItemLocation::AddExcludeOption() {
|
||||
// RANDOTODO: this without string compares and loops
|
||||
bool alreadyAdded = false;
|
||||
const Location* loc = StaticData::GetLocation(rc);
|
||||
for (const Option* location : Context::GetInstance()->GetSettings()->GetExcludeOptionsForArea(loc->GetArea())) {
|
||||
for (Option* location : Context::GetInstance()->GetSettings()->GetExcludeOptionsForArea(loc->GetArea())) {
|
||||
if (location->GetName() == excludedOption.GetName()) {
|
||||
alreadyAdded = true;
|
||||
}
|
||||
|
@ -45,7 +45,7 @@ class ItemLocation {
|
||||
const std::vector<RandomizerHint>& GetHintedBy() const;
|
||||
void AddHintedBy(RandomizerHint hintKey);
|
||||
bool IsHidden() const;
|
||||
bool IsExcluded() const;
|
||||
bool IsExcluded();
|
||||
void AddExcludeOption();
|
||||
Option* GetExcludedOption();
|
||||
void SetHidden(bool hidden_);
|
||||
|
@ -415,6 +415,38 @@ namespace Rando {
|
||||
bool killed = false;
|
||||
switch(enemy) {
|
||||
case RE_GOLD_SKULLTULA:
|
||||
switch (distance){
|
||||
case ED_CLOSE:
|
||||
//hammer jumpslash cannot damage these, but hammer swing can
|
||||
killed = CanUse(RG_MEGATON_HAMMER);
|
||||
[[fallthrough]];
|
||||
case ED_SHORT_JUMPSLASH:
|
||||
killed = killed || CanUse(RG_KOKIRI_SWORD);
|
||||
[[fallthrough]];
|
||||
case ED_MASTER_SWORD_JUMPSLASH:
|
||||
killed = killed || CanUse(RG_MASTER_SWORD);
|
||||
[[fallthrough]];
|
||||
case ED_LONG_JUMPSLASH:
|
||||
killed = killed || CanUse(RG_BIGGORON_SWORD) || CanUse(RG_STICKS);
|
||||
[[fallthrough]];
|
||||
case ED_BOMB_THROW:
|
||||
killed = killed || CanUse(RG_BOMB_BAG);
|
||||
[[fallthrough]];
|
||||
case ED_BOOMERANG:
|
||||
killed = killed || CanUse(RG_BOOMERANG) || CanUse(RG_DINS_FIRE);
|
||||
[[fallthrough]];
|
||||
case ED_HOOKSHOT:
|
||||
//RANDOTODO test dins and chu range in a practical example
|
||||
killed = killed || CanUse(RG_HOOKSHOT) || (wallOrFloor && CanUse(RG_BOMBCHU_5));
|
||||
[[fallthrough]];
|
||||
case ED_LONGSHOT:
|
||||
killed = killed || CanUse(RG_LONGSHOT);
|
||||
[[fallthrough]];
|
||||
case ED_FAR:
|
||||
killed = killed || CanUse(RG_FAIRY_SLINGSHOT) || CanUse(RG_FAIRY_BOW);
|
||||
break;
|
||||
}
|
||||
return killed;
|
||||
case RE_GOHMA_LARVA:
|
||||
case RE_MAD_SCRUB:
|
||||
case RE_DEKU_BABA:
|
||||
@ -618,8 +650,15 @@ namespace Rando {
|
||||
case RE_PURPLE_LEEVER:
|
||||
//dies on it's own, so this is the conditions to spawn it (killing 10 normal leevers)
|
||||
//Sticks and Ice arrows work but will need ammo capacity logic
|
||||
//other mothods can damage them but not kill them, and they run when hit, making them impractical
|
||||
return CanUse(RG_MASTER_SWORD) || CanUse(RG_BIGGORON_SWORD);
|
||||
//other methods can damage them but not kill them, and they run when hit, making them impractical
|
||||
return CanUse(RG_MASTER_SWORD) || CanUse(RG_BIGGORON_SWORD);
|
||||
case RE_TENTACLE:
|
||||
return CanUse(RG_BOOMERANG);
|
||||
case RE_BARI:
|
||||
return HookshotOrBoomerang() || CanUse(RG_FAIRY_BOW) || HasExplosives() || CanUse(RG_MEGATON_HAMMER) || CanUse(RG_STICKS) || CanUse(RG_DINS_FIRE) || (TakeDamage() && (CanUse(RG_KOKIRI_SWORD) || CanUse(RG_MASTER_SWORD) || CanUse(RG_BIGGORON_SWORD)));
|
||||
case RE_SHABOM:
|
||||
//RANDOTODO when you add better damage logic, you can kill this by taking hits
|
||||
return CanUse(RG_BOOMERANG) || CanUse(RG_NUTS) || CanJumpslash() || CanUse(RG_DINS_FIRE) || CanUse(RG_ICE_ARROWS);
|
||||
default:
|
||||
SPDLOG_ERROR("CanKillEnemy reached `default`.");
|
||||
assert(false);
|
||||
@ -974,7 +1013,7 @@ namespace Rando {
|
||||
10 for OHKO.
|
||||
This is the number of shifts to apply, not a real multiplier
|
||||
*/
|
||||
uint8_t Multiplier = (ctx->GetOption(RSK_DAMAGE_MULTIPLIER).Value<uint8_t>() < 6) ? ctx->GetOption(RSK_DAMAGE_MULTIPLIER).Value<uint8_t>() : 10;
|
||||
uint8_t Multiplier = (ctx->GetOption(RSK_DAMAGE_MULTIPLIER).GetContextOptionIndex() < 6) ? ctx->GetOption(RSK_DAMAGE_MULTIPLIER).GetContextOptionIndex() : 10;
|
||||
//(Hearts() << (2 + HasItem(RG_DOUBLE_DEFENSE))) is quarter hearts after DD
|
||||
//>> Multiplier halves on normal and does nothing on half, meaning we're working with half hearts on normal damage
|
||||
return ((Hearts() << (2 + HasItem(RG_DOUBLE_DEFENSE))) >> Multiplier) +
|
||||
@ -1103,21 +1142,21 @@ namespace Rando {
|
||||
bool Logic::CanBuildRainbowBridge(){
|
||||
return ctx->GetOption(RSK_RAINBOW_BRIDGE).Is(RO_BRIDGE_ALWAYS_OPEN) ||
|
||||
(ctx->GetOption(RSK_RAINBOW_BRIDGE).Is(RO_BRIDGE_VANILLA) && HasItem(RG_SHADOW_MEDALLION) && HasItem(RG_SPIRIT_MEDALLION) && CanUse(RG_LIGHT_ARROWS)) ||
|
||||
(ctx->GetOption(RSK_RAINBOW_BRIDGE).Is(RO_BRIDGE_STONES) && StoneCount() + (HasItem(RG_GREG_RUPEE) && ctx->GetOption(RSK_BRIDGE_OPTIONS).Is(RO_BRIDGE_GREG_REWARD)) >= ctx->GetOption(RSK_RAINBOW_BRIDGE_STONE_COUNT).Value<uint8_t>()) ||
|
||||
(ctx->GetOption(RSK_RAINBOW_BRIDGE).Is(RO_BRIDGE_MEDALLIONS) && MedallionCount() + (HasItem(RG_GREG_RUPEE) && ctx->GetOption(RSK_BRIDGE_OPTIONS).Is(RO_BRIDGE_GREG_REWARD)) >= ctx->GetOption(RSK_RAINBOW_BRIDGE_MEDALLION_COUNT).Value<uint8_t>()) ||
|
||||
(ctx->GetOption(RSK_RAINBOW_BRIDGE).Is(RO_BRIDGE_DUNGEON_REWARDS) && StoneCount() + MedallionCount() + (HasItem(RG_GREG_RUPEE) && ctx->GetOption(RSK_BRIDGE_OPTIONS).Is(RO_BRIDGE_GREG_REWARD)) >= ctx->GetOption(RSK_RAINBOW_BRIDGE_REWARD_COUNT).Value<uint8_t>()) ||
|
||||
(ctx->GetOption(RSK_RAINBOW_BRIDGE).Is(RO_BRIDGE_DUNGEONS) && DungeonCount() + (HasItem(RG_GREG_RUPEE) && ctx->GetOption(RSK_BRIDGE_OPTIONS).Is(RO_BRIDGE_GREG_REWARD)) >= ctx->GetOption(RSK_RAINBOW_BRIDGE_DUNGEON_COUNT).Value<uint8_t>()) ||
|
||||
(ctx->GetOption(RSK_RAINBOW_BRIDGE).Is(RO_BRIDGE_TOKENS) && GetGSCount() >= ctx->GetOption(RSK_RAINBOW_BRIDGE_TOKEN_COUNT).Value<uint8_t>()) ||
|
||||
(ctx->GetOption(RSK_RAINBOW_BRIDGE).Is(RO_BRIDGE_STONES) && StoneCount() + (HasItem(RG_GREG_RUPEE) && ctx->GetOption(RSK_BRIDGE_OPTIONS).Is(RO_BRIDGE_GREG_REWARD)) >= ctx->GetOption(RSK_RAINBOW_BRIDGE_STONE_COUNT).GetContextOptionIndex()) ||
|
||||
(ctx->GetOption(RSK_RAINBOW_BRIDGE).Is(RO_BRIDGE_MEDALLIONS) && MedallionCount() + (HasItem(RG_GREG_RUPEE) && ctx->GetOption(RSK_BRIDGE_OPTIONS).Is(RO_BRIDGE_GREG_REWARD)) >= ctx->GetOption(RSK_RAINBOW_BRIDGE_MEDALLION_COUNT).GetContextOptionIndex()) ||
|
||||
(ctx->GetOption(RSK_RAINBOW_BRIDGE).Is(RO_BRIDGE_DUNGEON_REWARDS) && StoneCount() + MedallionCount() + (HasItem(RG_GREG_RUPEE) && ctx->GetOption(RSK_BRIDGE_OPTIONS).Is(RO_BRIDGE_GREG_REWARD)) >= ctx->GetOption(RSK_RAINBOW_BRIDGE_REWARD_COUNT).GetContextOptionIndex()) ||
|
||||
(ctx->GetOption(RSK_RAINBOW_BRIDGE).Is(RO_BRIDGE_DUNGEONS) && DungeonCount() + (HasItem(RG_GREG_RUPEE) && ctx->GetOption(RSK_BRIDGE_OPTIONS).Is(RO_BRIDGE_GREG_REWARD)) >= ctx->GetOption(RSK_RAINBOW_BRIDGE_DUNGEON_COUNT).GetContextOptionIndex()) ||
|
||||
(ctx->GetOption(RSK_RAINBOW_BRIDGE).Is(RO_BRIDGE_TOKENS) && GetGSCount() >= ctx->GetOption(RSK_RAINBOW_BRIDGE_TOKEN_COUNT).GetContextOptionIndex()) ||
|
||||
(ctx->GetOption(RSK_RAINBOW_BRIDGE).Is(RO_BRIDGE_GREG) && HasItem(RG_GREG_RUPEE));
|
||||
}
|
||||
|
||||
bool Logic::CanTriggerLACS(){
|
||||
return (ctx->GetSettings()->LACSCondition() == RO_LACS_VANILLA && HasItem(RG_SHADOW_MEDALLION) && HasItem(RG_SPIRIT_MEDALLION)) ||
|
||||
(ctx->GetSettings()->LACSCondition() == RO_LACS_STONES && StoneCount() + (HasItem(RG_GREG_RUPEE) && ctx->GetOption(RSK_LACS_OPTIONS).Is(RO_LACS_GREG_REWARD)) >= ctx->GetOption(RSK_LACS_STONE_COUNT).Value<uint8_t>()) ||
|
||||
(ctx->GetSettings()->LACSCondition() == RO_LACS_MEDALLIONS && MedallionCount() + (HasItem(RG_GREG_RUPEE) && ctx->GetOption(RSK_LACS_OPTIONS).Is(RO_LACS_GREG_REWARD)) >= ctx->GetOption(RSK_LACS_MEDALLION_COUNT).Value<uint8_t>()) ||
|
||||
(ctx->GetSettings()->LACSCondition() == RO_LACS_REWARDS && StoneCount() + MedallionCount() + (HasItem(RG_GREG_RUPEE) && ctx->GetOption(RSK_LACS_OPTIONS).Is(RO_LACS_GREG_REWARD)) >= ctx->GetOption(RSK_LACS_REWARD_COUNT).Value<uint8_t>()) ||
|
||||
(ctx->GetSettings()->LACSCondition() == RO_LACS_DUNGEONS && DungeonCount() + (HasItem(RG_GREG_RUPEE) && ctx->GetOption(RSK_LACS_OPTIONS).Is(RO_LACS_GREG_REWARD)) >= ctx->GetOption(RSK_LACS_DUNGEON_COUNT).Value<uint8_t>()) ||
|
||||
(ctx->GetSettings()->LACSCondition() == RO_LACS_TOKENS && GetGSCount() >= ctx->GetOption(RSK_LACS_TOKEN_COUNT).Value<uint8_t>());
|
||||
(ctx->GetSettings()->LACSCondition() == RO_LACS_STONES && StoneCount() + (HasItem(RG_GREG_RUPEE) && ctx->GetOption(RSK_LACS_OPTIONS).Is(RO_LACS_GREG_REWARD)) >= ctx->GetOption(RSK_LACS_STONE_COUNT).GetContextOptionIndex()) ||
|
||||
(ctx->GetSettings()->LACSCondition() == RO_LACS_MEDALLIONS && MedallionCount() + (HasItem(RG_GREG_RUPEE) && ctx->GetOption(RSK_LACS_OPTIONS).Is(RO_LACS_GREG_REWARD)) >= ctx->GetOption(RSK_LACS_MEDALLION_COUNT).GetContextOptionIndex()) ||
|
||||
(ctx->GetSettings()->LACSCondition() == RO_LACS_REWARDS && StoneCount() + MedallionCount() + (HasItem(RG_GREG_RUPEE) && ctx->GetOption(RSK_LACS_OPTIONS).Is(RO_LACS_GREG_REWARD)) >= ctx->GetOption(RSK_LACS_REWARD_COUNT).GetContextOptionIndex()) ||
|
||||
(ctx->GetSettings()->LACSCondition() == RO_LACS_DUNGEONS && DungeonCount() + (HasItem(RG_GREG_RUPEE) && ctx->GetOption(RSK_LACS_OPTIONS).Is(RO_LACS_GREG_REWARD)) >= ctx->GetOption(RSK_LACS_DUNGEON_COUNT).GetContextOptionIndex()) ||
|
||||
(ctx->GetSettings()->LACSCondition() == RO_LACS_TOKENS && GetGSCount() >= ctx->GetOption(RSK_LACS_TOKEN_COUNT).GetContextOptionIndex());
|
||||
}
|
||||
|
||||
bool Logic::SmallKeys(RandomizerRegion dungeon, uint8_t requiredAmount) {
|
||||
@ -2089,7 +2128,7 @@ namespace Rando {
|
||||
//CanPlantBean = false;
|
||||
BigPoeKill = false;
|
||||
|
||||
BaseHearts = ctx->GetOption(RSK_STARTING_HEARTS).Value<uint8_t>() + 1;
|
||||
BaseHearts = ctx->GetOption(RSK_STARTING_HEARTS).GetContextOptionIndex() + 1;
|
||||
|
||||
|
||||
//Bridge Requirements
|
||||
@ -2145,6 +2184,7 @@ namespace Rando {
|
||||
GTGPlatformSilverRupees = false;
|
||||
MQJabuHolesRoomDoor = false;
|
||||
JabuWestTentacle = false;
|
||||
JabuEastTentacle = false;
|
||||
JabuNorthTentacle = false;
|
||||
LoweredJabuPath = false;
|
||||
MQJabuLiftRoomCow = false;
|
||||
@ -2158,6 +2198,9 @@ namespace Rando {
|
||||
MQSpiritCrawlBoulder = false;
|
||||
MQSpiritMapRoomEnemies = false;
|
||||
MQSpirit3SunsEnemies = false;
|
||||
Spirit1FSilverRupees = false;
|
||||
JabuRutoInB1 = false;
|
||||
JabuRutoIn1F = false;
|
||||
|
||||
StopPerformanceTimer(PT_LOGIC_RESET);
|
||||
}
|
||||
|
@ -156,6 +156,7 @@ class Logic {
|
||||
bool GTGPlatformSilverRupees = false;
|
||||
bool MQJabuHolesRoomDoor = false;
|
||||
bool JabuWestTentacle = false;
|
||||
bool JabuEastTentacle = false;
|
||||
bool JabuNorthTentacle = false;
|
||||
bool LoweredJabuPath = false;
|
||||
bool MQJabuLiftRoomCow = false;
|
||||
@ -171,6 +172,8 @@ class Logic {
|
||||
bool MQSpiritTimeTravelChest = false;
|
||||
bool MQSpirit3SunsEnemies = false;
|
||||
bool Spirit1FSilverRupees = false;
|
||||
bool JabuRutoInB1 = false;
|
||||
bool JabuRutoIn1F = false;
|
||||
|
||||
/* --- END OF HELPERS AND LOCATION ACCESS --- */
|
||||
|
||||
|
@ -31,10 +31,7 @@ Option Option::LogicTrick(std::string name_) {
|
||||
}
|
||||
|
||||
Option::operator bool() const {
|
||||
if (std::holds_alternative<bool>(var)) {
|
||||
return Value<bool>();
|
||||
}
|
||||
return Value<uint8_t>() != 0;
|
||||
return contextSelection != 0;
|
||||
}
|
||||
|
||||
size_t Option::GetOptionCount() const {
|
||||
@ -49,12 +46,16 @@ const std::string& Option::GetDescription() const {
|
||||
return description;
|
||||
}
|
||||
|
||||
uint8_t Option::GetSelectedOptionIndex() const {
|
||||
return selectedOption;
|
||||
uint8_t Option::GetMenuOptionIndex() const {
|
||||
return menuSelection;
|
||||
}
|
||||
|
||||
uint8_t Option::GetContextOptionIndex() const {
|
||||
return contextSelection;
|
||||
}
|
||||
|
||||
const std::string& Option::GetSelectedOptionText() const {
|
||||
return options[selectedOption];
|
||||
return options[contextSelection];
|
||||
}
|
||||
|
||||
const std::string& Option::GetCVarName() const {
|
||||
@ -63,39 +64,45 @@ const std::string& Option::GetCVarName() const {
|
||||
|
||||
void Option::SetVariable() {
|
||||
if (std::holds_alternative<bool>(var)) {
|
||||
var.emplace<bool>(selectedOption != 0);
|
||||
var.emplace<bool>(menuSelection != 0);
|
||||
} else {
|
||||
var.emplace<uint8_t>(selectedOption);
|
||||
var.emplace<uint8_t>(menuSelection);
|
||||
}
|
||||
}
|
||||
|
||||
void Option::SetCVar() const {
|
||||
void Option::SaveCVar() const {
|
||||
if (!cvarName.empty()) {
|
||||
CVarSetInteger(cvarName.c_str(), GetSelectedOptionIndex());
|
||||
CVarSetInteger(cvarName.c_str(), GetMenuOptionIndex());
|
||||
}
|
||||
}
|
||||
|
||||
void Option::SetFromCVar() {
|
||||
if (!cvarName.empty()) {
|
||||
SetSelectedIndex(CVarGetInteger(cvarName.c_str(), defaultOption));
|
||||
SetMenuIndex(CVarGetInteger(cvarName.c_str(), defaultOption));
|
||||
}
|
||||
}
|
||||
|
||||
void Option::SetDelayedOption() {
|
||||
delayedOption = selectedOption;
|
||||
delayedSelection = contextSelection;
|
||||
}
|
||||
|
||||
void Option::RestoreDelayedOption() {
|
||||
selectedOption = delayedOption;
|
||||
contextSelection = delayedSelection;
|
||||
}
|
||||
|
||||
void Option::SetMenuIndex(size_t idx) {
|
||||
menuSelection = idx;
|
||||
if (menuSelection > options.size() - 1) {
|
||||
menuSelection = options.size() - 1;
|
||||
}
|
||||
SetVariable();
|
||||
}
|
||||
|
||||
void Option::SetSelectedIndex(size_t idx) {
|
||||
selectedOption = idx;
|
||||
if (selectedOption > options.size() - 1) {
|
||||
selectedOption = options.size() - 1;
|
||||
void Option::SetContextIndex(size_t idx) {
|
||||
contextSelection = idx;
|
||||
if (contextSelection > options.size() - 1) {
|
||||
contextSelection = options.size() - 1;
|
||||
}
|
||||
SetVariable();
|
||||
}
|
||||
|
||||
void Option::Hide() {
|
||||
@ -111,8 +118,8 @@ bool Option::IsHidden() const {
|
||||
}
|
||||
|
||||
void Option::ChangeOptions(std::vector<std::string> opts) {
|
||||
if (selectedOption >= opts.size()) {
|
||||
selectedOption = opts.size() - 1;
|
||||
if (menuSelection >= opts.size()) {
|
||||
menuSelection = opts.size() - 1;
|
||||
}
|
||||
options = std::move(opts);
|
||||
}
|
||||
@ -177,7 +184,7 @@ Option::Option(uint8_t var_, std::string name_, std::vector<std::string> options
|
||||
: var(var_), name(std::move(name_)), options(std::move(options_)), category(category_),
|
||||
cvarName(std::move(cvarName_)), description(std::move(description_)), widgetType(widgetType_),
|
||||
defaultOption(defaultOption_), defaultHidden(defaultHidden_), imFlags(imFlags_) {
|
||||
selectedOption = defaultOption;
|
||||
menuSelection = contextSelection = defaultOption;
|
||||
hidden = defaultHidden;
|
||||
SetFromCVar();
|
||||
}
|
||||
@ -187,7 +194,7 @@ Option::Option(bool var_, std::string name_, std::vector<std::string> options_,
|
||||
: var(var_), name(std::move(name_)), options(std::move(options_)), category(category_),
|
||||
cvarName(std::move(cvarName_)), description(std::move(description_)), widgetType(widgetType_),
|
||||
defaultOption(defaultOption_), defaultHidden(defaultHidden_), imFlags(imFlags_) {
|
||||
selectedOption = defaultOption;
|
||||
menuSelection = contextSelection = defaultOption;
|
||||
hidden = defaultHidden;
|
||||
SetFromCVar();
|
||||
}
|
||||
@ -270,7 +277,7 @@ bool Option::RenderCombobox() {
|
||||
|
||||
bool Option::RenderSlider() {
|
||||
bool changed = false;
|
||||
int val = GetSelectedOptionIndex();
|
||||
int val = GetMenuOptionIndex();
|
||||
if (val > options.size() - 1) {
|
||||
val = options.size() - 1;
|
||||
CVarSetInteger(cvarName.c_str(), val);
|
||||
|
@ -126,42 +126,25 @@ class Option {
|
||||
*/
|
||||
static Option LogicTrick(std::string name_);
|
||||
|
||||
/**
|
||||
* @brief Gets the selected index or boolean value of the Option.
|
||||
*
|
||||
* @tparam T uint8_t or bool, depending on how the option was constructed.
|
||||
* @return T
|
||||
*/
|
||||
template <typename T> T Value() const {
|
||||
return std::get<T>(var);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Determines if the value/selected index of this Option matches the provided value.
|
||||
*
|
||||
* @tparam T uint8_t, bool, or an enum (which will be cast to uint8_t).
|
||||
* @param other The value to compare.
|
||||
* @return true
|
||||
* @return false
|
||||
*/
|
||||
template <typename T> bool Is(T other) const {
|
||||
static_assert(std::is_integral_v<T> || std::is_enum_v<T>, "T must be an integral type or an enum.");
|
||||
if constexpr ((std::is_integral_v<T> && !std::is_same_v<bool, T>) || std::is_enum_v<T>) {
|
||||
return Value<uint8_t>() == static_cast<uint8_t>(other);
|
||||
} else {
|
||||
return Value<bool>() == static_cast<bool>(other);
|
||||
}
|
||||
bool Is(uint32_t other) const {
|
||||
return contextSelection == other;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Determines if the value/selected index of this Option does not match the provided value.
|
||||
*
|
||||
* @tparam T uint8_t, book, or an enum (which will be cast to uint8_t).
|
||||
* @param other The value to compare.
|
||||
* @return true
|
||||
* @return false
|
||||
*/
|
||||
template <typename T> bool IsNot(T other) const {
|
||||
bool IsNot(uint32_t other) const {
|
||||
return !Is(other);
|
||||
}
|
||||
|
||||
@ -203,11 +186,18 @@ class Option {
|
||||
const std::string& GetCVarName() const;
|
||||
|
||||
/**
|
||||
* @brief Get the selected index for this Option.
|
||||
* @brief Get the menu index for this Option.
|
||||
*
|
||||
* @return uint8_t
|
||||
*/
|
||||
uint8_t GetSelectedOptionIndex() const;
|
||||
uint8_t GetMenuOptionIndex() const;
|
||||
|
||||
/**
|
||||
* @brief Get the rando context index for this Option.
|
||||
*
|
||||
* @return uint8_t
|
||||
*/
|
||||
uint8_t GetContextOptionIndex() const;
|
||||
|
||||
/**
|
||||
* @brief Sets the variable to the currently selected index for this Option.
|
||||
@ -218,7 +208,7 @@ class Option {
|
||||
* @brief Sets the CVar corresponding to the property `cvarName` equal to the value
|
||||
* of the property `selectedValue`.
|
||||
*/
|
||||
void SetCVar() const;
|
||||
void SaveCVar() const;
|
||||
|
||||
/**
|
||||
* @brief Sets the value of property `selectedValue` equal to the CVar corresponding
|
||||
@ -237,11 +227,18 @@ class Option {
|
||||
void RestoreDelayedOption();
|
||||
|
||||
/**
|
||||
* @brief Set the selected index for this Option. Also calls `SetVariable()`.
|
||||
* @brief Set the menu index for this Option. Also calls `SetVariable()`.
|
||||
*
|
||||
* @param idx the index to set as the selected index.
|
||||
*/
|
||||
void SetSelectedIndex(size_t idx);
|
||||
void SetMenuIndex(size_t idx);
|
||||
|
||||
/**
|
||||
* @brief Set the rando context index for this Option. Also calls `SetVariable()`.
|
||||
*
|
||||
* @param idx the index to set as the selected index.
|
||||
*/
|
||||
void SetContextIndex(size_t idx);
|
||||
|
||||
/**
|
||||
* @brief Hides this Option in the menu. (Not currently being used afaik, we prefer to
|
||||
@ -324,8 +321,9 @@ protected:
|
||||
std::variant<bool, uint8_t> var;
|
||||
std::string name;
|
||||
std::vector<std::string> options;
|
||||
uint8_t selectedOption = 0;
|
||||
uint8_t delayedOption = 0;
|
||||
uint8_t menuSelection = 0;
|
||||
uint8_t contextSelection = 0;
|
||||
uint8_t delayedSelection = 0;
|
||||
bool hidden = false;
|
||||
OptionCategory category = OptionCategory::Setting;
|
||||
std::string cvarName;
|
||||
|
@ -2,30 +2,30 @@
|
||||
|
||||
namespace Rando {
|
||||
void Settings::CreateOptionDescriptions() {
|
||||
mOptionDescriptions[RSK_FOREST] = "Closed - Kokiri sword & shield are required to access "
|
||||
mOptionDescriptions[RSK_FOREST] = "Closed - Kokiri Sword & Deku Shield are required to access "
|
||||
"the Deku Tree, and completing the Deku Tree is required to "
|
||||
"access the Hyrule Field exit.\n"
|
||||
"\n"
|
||||
"Closed Deku - Kokiri boy no longer blocks the path to Hyrule "
|
||||
"Field but Mido still requires the Kokiri sword and Deku shield "
|
||||
"Field but Mido still requires the Kokiri Sword and Deku Shield "
|
||||
"to access the tree.\n"
|
||||
"\n"
|
||||
"Open - Mido no longer blocks the path to the Deku Tree. Kokiri "
|
||||
"boy no longer blocks the path out of the forest.";
|
||||
mOptionDescriptions[RSK_KAK_GATE] = "Closed - The gate will remain closed until Zelda's letter "
|
||||
mOptionDescriptions[RSK_KAK_GATE] = "Closed - The gate will remain closed until Zelda's Letter "
|
||||
"is shown to the guard.\n"
|
||||
"\n"
|
||||
"Open - The gate is always open. The happy mask shop "
|
||||
"will open immediately after obtaining Zelda's letter.";
|
||||
"Open - The gate is always open. The Happy Mask Shop "
|
||||
"will open immediately after obtaining Zelda's Letter.";
|
||||
mOptionDescriptions[RSK_DOOR_OF_TIME] = "Closed - The Ocarina of Time, the Song of Time and all "
|
||||
"three spiritual stones are required to open the Door of Time.\n"
|
||||
"three Spiritual Stones are required to open the Door of Time.\n"
|
||||
"\n"
|
||||
"Song only - Play the Song of Time in front of the Door of "
|
||||
"Time to open it.\n"
|
||||
"\n"
|
||||
"Open - The Door of Time is permanently open with no requirements.";
|
||||
mOptionDescriptions[RSK_ZORAS_FOUNTAIN] = "Closed - King Zora obstructs the way to Zora's Fountain. "
|
||||
"Ruto's letter must be shown as child Link in order to move "
|
||||
"Ruto's Letter must be shown as child Link in order to move "
|
||||
"him in both time periods.\n"
|
||||
"\n"
|
||||
"Closed as child - Ruto's Letter is only required to move King Zora "
|
||||
@ -33,6 +33,12 @@ void Settings::CreateOptionDescriptions() {
|
||||
"\n"
|
||||
"Open - King Zora has already mweeped out of the way in both "
|
||||
"time periods. Ruto's Letter is removed from the item pool.";
|
||||
mOptionDescriptions[RSK_SLEEPING_WATERFALL] = "Closed - Sleeping Waterfall obstructs the entrance to Zora's "
|
||||
"Domain. Zelda's Lullaby must be played in order to open it "
|
||||
"(but only once; then it stays open in both time periods).\n"
|
||||
"\n"
|
||||
"Open - Sleeping Waterfall is always open. "
|
||||
"Link may always enter Zora's Domain.";
|
||||
mOptionDescriptions[RSK_STARTING_AGE] =
|
||||
"Choose which age Link will start as.\n\n"
|
||||
"Starting as adult means you start with the Master Sword in your inventory.\n"
|
||||
@ -54,12 +60,12 @@ void Settings::CreateOptionDescriptions() {
|
||||
"\n"
|
||||
"Always open - No requirements.\n"
|
||||
"\n"
|
||||
"Stones - Obtain the specified amount of spiritual stones.\n"
|
||||
"Stones - Obtain the specified amount of Spiritual Stones.\n"
|
||||
"\n"
|
||||
"Medallions - Obtain the specified amount of medallions.\n"
|
||||
"\n"
|
||||
"Dungeon rewards - Obtain the specified total sum of spiritual "
|
||||
"stones or medallions.\n"
|
||||
"Dungeon rewards - Obtain the specified total sum of Spiritual "
|
||||
"Stones or medallions.\n"
|
||||
"\n"
|
||||
"Dungeons - Complete the specified amount of dungeons. Dungeons "
|
||||
"are considered complete after stepping in to the blue warp after "
|
||||
@ -83,10 +89,10 @@ void Settings::CreateOptionDescriptions() {
|
||||
"\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"
|
||||
"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.";
|
||||
"Random Number - A random number and set of trials will be required.";
|
||||
mOptionDescriptions[RSK_TRIAL_COUNT] = "Set the number of trials required to enter Ganon's Tower.";
|
||||
mOptionDescriptions[RSK_MQ_DUNGEON_RANDOM] =
|
||||
"Sets the number of Master Quest Dungeons that are shuffled into the pool.\n"
|
||||
@ -96,7 +102,7 @@ void Settings::CreateOptionDescriptions() {
|
||||
"Set Number - Select a number of dungeons that will be their Master Quest versions "
|
||||
"using the slider below. Which dungeons are set to be the Master Quest variety will be random.\n"
|
||||
"\n"
|
||||
"Random Number - A Random number and set of dungeons will be their Master Quest varieties.\n"
|
||||
"Random Number - A random number and set of dungeons will be their Master Quest varieties.\n"
|
||||
"\n"
|
||||
"Selection Only - Specify which dungeons are Vanilla, Master Quest or a 50/50 between the two.\n"
|
||||
"Differs from Random Number in that they are rolled individually, making the exact total a bell curve.";
|
||||
@ -118,14 +124,14 @@ void Settings::CreateOptionDescriptions() {
|
||||
"Keep in mind seed generation can fail if more ornaments are placed than there are junk items in the item pool.";
|
||||
mOptionDescriptions[RSK_TRIFORCE_HUNT_PIECES_REQUIRED] = "The amount of Ornaments required to win the game.";
|
||||
mOptionDescriptions[RSK_SHUFFLE_DUNGEON_ENTRANCES] =
|
||||
"Shuffle the pool of dungeon entrances, including Bottom of the Well, Ice Cavern and Gerudo Training Grounds.\n"
|
||||
"Shuffle the pool of dungeon entrances, including Bottom of the Well, Ice Cavern and Gerudo Training Ground.\n"
|
||||
"\n"
|
||||
"Shuffling Ganon's Castle can be enabled separately.\n"
|
||||
"\n"
|
||||
"Additionally, the entrances of Deku Tree, Fire Temple, Bottom of the Well and Gerudo Training Ground are "
|
||||
"opened for both child and adult.\n"
|
||||
"\n"
|
||||
"- Deku Tree will be open for adult after Mido has seen child Link with a sword and shield.\n"
|
||||
"- Deku Tree will be open for adult after Mido has seen child Link with a sword and a shield.\n"
|
||||
"- Bottom of the Well will be open for adult after playing Song of Storms to the Windmill guy as child.\n"
|
||||
"- Gerudo Training Ground will be open for child after adult has paid to open the gate once.";
|
||||
mOptionDescriptions[RSK_SHUFFLE_BOSS_ENTRANCES] =
|
||||
@ -171,23 +177,23 @@ void Settings::CreateOptionDescriptions() {
|
||||
"This also adds the one-way entrance from Gerudo Valley to Lake Hylia in the pool of "
|
||||
"overworld entrances when they are shuffled.";
|
||||
mOptionDescriptions[RSK_MIXED_ENTRANCE_POOLS] =
|
||||
"Shuffle entrances into a mixed pool instead of separate ones. Has no affect on pools whose "
|
||||
"Shuffle entrances into a mixed pool instead of separate ones. Has no effect on pools whose "
|
||||
"entrances aren't shuffled, and \"Shuffle Boss Entrances\" must be set to \"Full\" to include them.\n"
|
||||
"\n"
|
||||
"For example, enabling the settings to shuffle grotto, dungeon, and overworld entrances and "
|
||||
"selecting grotto and dungeon entrances here will allow a dungeon to be inside a grotto or "
|
||||
"vice versa, while overworld entrances are shuffled in their own separate pool and indoors stay vanilla.";
|
||||
mOptionDescriptions[RSK_MIX_DUNGEON_ENTRANCES] = "Dungeon entrances will be part of the mixed pool";
|
||||
mOptionDescriptions[RSK_MIX_BOSS_ENTRANCES] = "Boss entrances will be part of the mixed pool";
|
||||
mOptionDescriptions[RSK_MIX_OVERWORLD_ENTRANCES] = "Overworld entrances will be part of the mixed pool";
|
||||
mOptionDescriptions[RSK_MIX_INTERIOR_ENTRANCES] = "Interior entrances will be part of the mixed pool";
|
||||
mOptionDescriptions[RSK_MIX_GROTTO_ENTRANCES] = "Grotto entrances will be part of the mixed pool";
|
||||
mOptionDescriptions[RSK_MIX_DUNGEON_ENTRANCES] = "Dungeon entrances will be part of the mixed pool.";
|
||||
mOptionDescriptions[RSK_MIX_BOSS_ENTRANCES] = "Boss entrances will be part of the mixed pool.";
|
||||
mOptionDescriptions[RSK_MIX_OVERWORLD_ENTRANCES] = "Overworld entrances will be part of the mixed pool.";
|
||||
mOptionDescriptions[RSK_MIX_INTERIOR_ENTRANCES] = "Interior entrances will be part of the mixed pool.";
|
||||
mOptionDescriptions[RSK_MIX_GROTTO_ENTRANCES] = "Grotto entrances will be part of the mixed pool.";
|
||||
mOptionDescriptions[RSK_SHUFFLE_SONGS] =
|
||||
"Song locations - Songs will only appear at locations that normally teach songs.\n"
|
||||
"\n"
|
||||
"Dungeon rewards - Songs appear after beating a major dungeon boss.\n"
|
||||
"The 4 remaining songs are located at:\n"
|
||||
" - Zelda's lullaby location\n"
|
||||
" - Zelda's Lullaby location\n"
|
||||
" - Ice Cavern's Serenade of Water location\n"
|
||||
" - Bottom of the Well Lens of Truth location\n"
|
||||
" - Gerudo Training Ground's Ice Arrows location\n"
|
||||
@ -240,33 +246,33 @@ void Settings::CreateOptionDescriptions() {
|
||||
"\n"
|
||||
"The Weird Egg is required to unlock several events:\n"
|
||||
" - Zelda's Lullaby from Impa\n"
|
||||
" - Saria's song in Sacred Forest Meadow\n"
|
||||
" - Epona's song and chicken minigame at Lon Lon Ranch\n"
|
||||
" - Zelda's letter for Kakariko gate (if set to closed)\n"
|
||||
" - Saria's Song in Sacred Forest Meadow\n"
|
||||
" - Epona's Song and chicken minigame at Lon Lon Ranch\n"
|
||||
" - Zelda's Letter for Kakariko gate (if set to closed)\n"
|
||||
" - Happy Mask Shop sidequest\n";
|
||||
mOptionDescriptions[RSK_SHUFFLE_GERUDO_MEMBERSHIP_CARD] =
|
||||
"Shuffles the Gerudo Membership Card into the item pool.\n"
|
||||
"\n"
|
||||
"The Gerudo Card is required to enter the Gerudo Training Grounds, opening "
|
||||
"The Gerudo Card is required to enter the Gerudo Training Ground, opening "
|
||||
"the gate to Haunted Wasteland and the Horseback Archery minigame.";
|
||||
mOptionDescriptions[RSK_SHUFFLE_FISHING_POLE] = "Shuffles the fishing pole into the item pool.\n"
|
||||
"\n"
|
||||
"The fishing pole is required to play the fishing pond minigame.";
|
||||
mOptionDescriptions[RSK_INFINITE_UPGRADES] = "Adds upgrades that hold infinite quanities of items (bombs, arrows, etc.)\n"
|
||||
mOptionDescriptions[RSK_INFINITE_UPGRADES] = "Adds upgrades that hold infinite quantities of items (bombs, arrows, etc.).\n"
|
||||
"\n"
|
||||
"Progressive - The infinite upgrades are obtained after getting the last normal capacity upgrade\n"
|
||||
"Progressive - The infinite upgrades are obtained after getting the last normal capacity upgrade.\n"
|
||||
"\n"
|
||||
"Condensed Progressive - The infinite upgrades are obtained as the first capacity upgrade (doesn't apply to the infinite wallet or to infinite magic)";
|
||||
mOptionDescriptions[RSK_SHUFFLE_DEKU_STICK_BAG] = "Shuffles the deku stick bag into the item pool.\n"
|
||||
"Condensed Progressive - The infinite upgrades are obtained as the first capacity upgrade (doesn't apply to the infinite wallet or to infinite magic).";
|
||||
mOptionDescriptions[RSK_SHUFFLE_DEKU_STICK_BAG] = "Shuffles the Deku Stick bag into the item pool.\n"
|
||||
"\n"
|
||||
"The deku stick bag is required to hold deku sticks.";
|
||||
mOptionDescriptions[RSK_SHUFFLE_DEKU_NUT_BAG] = "Shuffles the deku nut bag into the item pool.\n"
|
||||
"The Deku Stick bag is required to hold Deku Sticks.";
|
||||
mOptionDescriptions[RSK_SHUFFLE_DEKU_NUT_BAG] = "Shuffles the Deku Nut bag into the item pool.\n"
|
||||
"\n"
|
||||
"The deku nut bag is required to hold deku nuts.";
|
||||
"The Deku Nut bag is required to hold Deku Nuts.";
|
||||
mOptionDescriptions[RSK_SHOPSANITY] = "Off - All shop items will be the same as vanilla.\n"
|
||||
"\n"
|
||||
"Specifc Count - Vanilla shop items will be shuffled among different shops, and "
|
||||
"each shop will contain a specifc number (0-7) of non-vanilla shop items.\n"
|
||||
"Specific Count - Vanilla shop items will be shuffled among different shops, and "
|
||||
"each shop will contain a specific number (0-7) of non-vanilla shop items.\n"
|
||||
"\n"
|
||||
"Random - Vanilla shop items will be shuffled among different shops, and "
|
||||
"each shop will contain a random number (1-7) of non-vanilla shop items.";
|
||||
@ -279,12 +285,12 @@ void Settings::CreateOptionDescriptions() {
|
||||
"8 Items - All shops will contain 8 non-vanilla shop items.\n"
|
||||
*/;
|
||||
mOptionDescriptions[RSK_SHOPSANITY_PRICES] =
|
||||
"Vanilla - The same price as the item it replaced\n"
|
||||
"Cheap Balanced - Prices will range between 0 to 95 rupees, favoring lower numbers\n"
|
||||
"Balanced - Prices will range between 0 to 300 rupees, favoring lower numbers\n"
|
||||
"Fixed - A fixed number\n"
|
||||
"Range - A random point between specific ranges\n"
|
||||
"Set By Wallet - Set weights that decide the choice of each wallet, and get a random price in that range if that wallet is chosen";
|
||||
"Vanilla - The same price as the item it replaced.\n"
|
||||
"Cheap Balanced - Prices will range between 0 to 95 rupees, favoring lower numbers.\n"
|
||||
"Balanced - Prices will range between 0 to 300 rupees, favoring lower numbers.\n"
|
||||
"Fixed - A fixed number.\n"
|
||||
"Range - A random point between specific ranges.\n"
|
||||
"Set By Wallet - Set weights that decide the choice of each wallet, and get a random price in that range if that wallet is chosen.";
|
||||
mOptionDescriptions[RSK_SHOPSANITY_PRICES_FIXED_PRICE] =
|
||||
"The price for Shopsanity checks.";
|
||||
mOptionDescriptions[RSK_SHOPSANITY_PRICES_RANGE_1] =
|
||||
@ -300,13 +306,13 @@ void Settings::CreateOptionDescriptions() {
|
||||
mOptionDescriptions[RSK_SHOPSANITY_PRICES_GIANT_WALLET_WEIGHT] =
|
||||
"The chance for Shopsanity checks to be purchasable with Giant's Wallet (201-500).";
|
||||
mOptionDescriptions[RSK_SHOPSANITY_PRICES_TYCOON_WALLET_WEIGHT] =
|
||||
"The chance for Shopsanity checks to be purchasable with Tycoon Wallet. (500+)";
|
||||
"The chance for Shopsanity checks to be purchasable with Tycoon Wallet (500+).";
|
||||
mOptionDescriptions[RSK_SHOPSANITY_PRICES_AFFORDABLE] =
|
||||
"After choosing a price, set it to the affordable amount based on the wallet required.\n\n"
|
||||
"Affordable prices per tier: starter = 1, adult = 100, giant = 201, tycoon = 501\n\n"
|
||||
"Use this to enable wallet tier locking, but make shop items not as expensive as they could be.";
|
||||
mOptionDescriptions[RSK_FISHSANITY] = "Off - Fish will not be shuffled. No changes will be made to fishing behavior.\n\n"
|
||||
"Shuffle only Hyrule Loach - Allows you to earn an item by catching the hyrule loach at the fishing pond and giving it to the owner.\n\n"
|
||||
"Shuffle only Hyrule Loach - Allows you to earn an item by catching the Hyrule Loach at the fishing pond and giving it to the owner.\n\n"
|
||||
"Shuffle Fishing Pond - The fishing pond's fish will be shuffled. Catching a fish in the fishing pond will grant a reward.\n\n"
|
||||
"Shuffle Overworld Fish - Fish in generic grottos and Zora's Domain will be shuffled. Catching a fish in a bottle will give a reward.\n\n"
|
||||
"Shuffle Both - Both overworld fish and fish in the fishing pond will be shuffled.";
|
||||
@ -326,12 +332,12 @@ void Settings::CreateOptionDescriptions() {
|
||||
"\n"
|
||||
"All - All Scrubs are shuffled.";
|
||||
mOptionDescriptions[RSK_SCRUBS_PRICES] =
|
||||
"Vanilla - The same price as the item it replaced\n"
|
||||
"Cheap Balanced - Prices will range between 0 to 95 rupees, favoring lower numbers\n"
|
||||
"Balanced - Prices will range between 0 to 300 rupees, favoring lower numbers\n"
|
||||
"Fixed - A fixed number\n"
|
||||
"Range - A random point between specific ranges\n"
|
||||
"Set By Wallet - Set weights that decide the choice of each wallet, and get a random price in that range if that wallet is chosen";
|
||||
"Vanilla - The same price as the item it replaced.\n"
|
||||
"Cheap Balanced - Prices will range between 0 to 95 rupees, favoring lower numbers.\n"
|
||||
"Balanced - Prices will range between 0 to 300 rupees, favoring lower numbers.\n"
|
||||
"Fixed - A fixed number.\n"
|
||||
"Range - A random point between specific ranges.\n"
|
||||
"Set By Wallet - Set weights that decide the choice of each wallet, and get a random price in that range if that wallet is chosen.";
|
||||
mOptionDescriptions[RSK_SCRUBS_PRICES_FIXED_PRICE] =
|
||||
"The price for Scrub checks.";
|
||||
mOptionDescriptions[RSK_SCRUBS_PRICES_RANGE_1] =
|
||||
@ -347,7 +353,7 @@ void Settings::CreateOptionDescriptions() {
|
||||
mOptionDescriptions[RSK_SCRUBS_PRICES_GIANT_WALLET_WEIGHT] =
|
||||
"The chance for Scrub checks to be purchasable with Giant's Wallet (201-500).";
|
||||
mOptionDescriptions[RSK_SCRUBS_PRICES_TYCOON_WALLET_WEIGHT] =
|
||||
"The chance for Scrub checks to be purchasable with Tycoon Wallet. (500+)";
|
||||
"The chance for Scrub checks to be purchasable with Tycoon Wallet (500+).";
|
||||
mOptionDescriptions[RSK_SCRUBS_PRICES_AFFORDABLE] =
|
||||
"After choosing a price, set it to the affordable amount based on the wallet required.\n\n"
|
||||
"Affordable prices per tier: starter = 1, adult = 100, giant = 201, tycoon = 501\n\n"
|
||||
@ -360,7 +366,7 @@ void Settings::CreateOptionDescriptions() {
|
||||
"This setting governs if the Bean Salesman, Medigoron, Granny and the Carpet Salesman "
|
||||
"sell a random item.\n"
|
||||
"Beans Only - Only the Bean Salesman will have a check, and a pack of Magic Beans will be added "
|
||||
" to the item pool."
|
||||
"to the item pool."
|
||||
"All But Beans - Medigoron, Granny and the Carpet Salesman will have checks, "
|
||||
"A Giant's Knife and a pack of Bombchus will be added to the item pool, and "
|
||||
"one of the bottles will contain a Blue Potion.\n\n"
|
||||
@ -370,12 +376,12 @@ void Settings::CreateOptionDescriptions() {
|
||||
"Otherwise when off, you will need to have found the Claim Check to buy her item (simulating the trade quest "
|
||||
"is complete).";
|
||||
mOptionDescriptions[RSK_MERCHANT_PRICES] =
|
||||
"Vanilla - The same price as the Check in vanilla, 60 for the Bean Salesman\n"
|
||||
"Cheap Balanced - Prices will range between 0 to 95 rupees, favoring lower numbers\n"
|
||||
"Balanced - Prices will range between 0 to 300 rupees, favoring lower numbers\n"
|
||||
"Fixed - A fixed number\n"
|
||||
"Range - A random point between specific ranges\n"
|
||||
"Set By Wallet - Set weights that decide the choice of each wallet, and get a random price in that range if that wallet is chosen";
|
||||
"Vanilla - The same price as the Check in vanilla, 60 for the Bean Salesman.\n"
|
||||
"Cheap Balanced - Prices will range between 0 to 95 rupees, favoring lower numbers.\n"
|
||||
"Balanced - Prices will range between 0 to 300 rupees, favoring lower numbers.\n"
|
||||
"Fixed - A fixed number.\n"
|
||||
"Range - A random point between specific ranges.\n"
|
||||
"Set By Wallet - Set weights that decide the choice of each wallet, and get a random price in that range if that wallet is chosen.";
|
||||
mOptionDescriptions[RSK_MERCHANT_PRICES_FIXED_PRICE] =
|
||||
"The price for Merchant checks.";
|
||||
mOptionDescriptions[RSK_MERCHANT_PRICES_RANGE_1] =
|
||||
@ -391,7 +397,7 @@ void Settings::CreateOptionDescriptions() {
|
||||
mOptionDescriptions[RSK_MERCHANT_PRICES_GIANT_WALLET_WEIGHT] =
|
||||
"The chance for Merchant checks to be purchasable with Giant's Wallet (201-500).";
|
||||
mOptionDescriptions[RSK_MERCHANT_PRICES_TYCOON_WALLET_WEIGHT] =
|
||||
"The chance for Merchant checks to be purchasable with Tycoon Wallet. (500+)";
|
||||
"The chance for Merchant checks to be purchasable with Tycoon Wallet (500+).";
|
||||
mOptionDescriptions[RSK_MERCHANT_PRICES_AFFORDABLE] =
|
||||
"After choosing a price, set it to the affordable amount based on the wallet required.\n\n"
|
||||
"Affordable prices per tier: starter = 1, adult = 100, giant = 201, tycoon = 501\n\n"
|
||||
@ -416,16 +422,16 @@ void Settings::CreateOptionDescriptions() {
|
||||
"\n"
|
||||
"You can still talk to him multiple times to get Huge Rupees.";
|
||||
mOptionDescriptions[RSK_SHUFFLE_DUNGEON_REWARDS] =
|
||||
"Shuffles the location of spiritual stones and medallions.\n"
|
||||
"Shuffles the location of Spiritual Stones and medallions.\n"
|
||||
"\n"
|
||||
"End of dungeons - Spiritual stones and medallions will be given as rewards "
|
||||
"End of dungeons - Spiritual Stones and medallions will be given as rewards "
|
||||
"for beating major dungeons. Link will always start with one stone or medallion.\n"
|
||||
"\n"
|
||||
"Any dungeon - Spiritual stones and medallions can be found inside any dungeon.\n"
|
||||
"Any dungeon - Spiritual Stones and medallions can be found inside any dungeon.\n"
|
||||
"\n"
|
||||
"Overworld - Spiritual stones and medallions can only be found outside of dungeons.\n"
|
||||
"Overworld - Spiritual Stones and medallions can only be found outside of dungeons.\n"
|
||||
"\n"
|
||||
"Anywhere - Spiritual stones and medallions can appear anywhere.";
|
||||
"Anywhere - Spiritual Stones and medallions can appear anywhere.";
|
||||
mOptionDescriptions[RSK_SHUFFLE_MAPANDCOMPASS] =
|
||||
"Start with - You will start with Maps & Compasses from all dungeons.\n"
|
||||
"\n"
|
||||
@ -433,7 +439,7 @@ void Settings::CreateOptionDescriptions() {
|
||||
"\n"
|
||||
"Own dungeon - Maps & Compasses can only appear in their respective dungeon.\n"
|
||||
"\n"
|
||||
"Any dungeon - Maps & Compasses can only appear inside of any dungon.\n"
|
||||
"Any dungeon - Maps & Compasses can only appear inside of any dungeon.\n"
|
||||
"\n"
|
||||
"Overworld - Maps & Compasses can only appear outside of dungeons.\n"
|
||||
"\n"
|
||||
@ -447,18 +453,18 @@ void Settings::CreateOptionDescriptions() {
|
||||
"Own dungeon - Small Keys can only appear in their respective dungeon. "
|
||||
"If Fire Temple is not a Master Quest dungeon, the door to the Boss Key chest will be unlocked.\n"
|
||||
"\n"
|
||||
"Any dungeon - Small Keys can only appear inside of any dungon.\n"
|
||||
"Any dungeon - Small Keys can only appear inside of any dungeon.\n"
|
||||
"\n"
|
||||
"Overworld - Small Keys can only appear outside of dungeons.\n"
|
||||
"\n"
|
||||
"Anywhere - Small Keys can appear anywhere in the world.";
|
||||
mOptionDescriptions[RSK_KEYRINGS] =
|
||||
"Keyrings will replace all small keys from a particular dungeon with a single keyring that awards all keys for "
|
||||
"it's associated dungeon\n"
|
||||
"its associated dungeon.\n"
|
||||
"\n"
|
||||
"Off - No dungeons will have their keys replaced with keyrings.\n"
|
||||
"\n"
|
||||
"Random - A random amount of dungeons(0-8 or 9) will have their keys replaced with keyrings.\n"
|
||||
"Random - A random amount of dungeons (0-8 or 9) will have their keys replaced with keyrings.\n"
|
||||
"\n"
|
||||
"Count - A specified amount of randomly selected dungeons will have their keys replaced with keyrings.\n"
|
||||
"\n"
|
||||
@ -470,20 +476,20 @@ void Settings::CreateOptionDescriptions() {
|
||||
"If Gerudo Fortress Carpenters is set to Normal, and Gerudo Fortress Keys is set to anything "
|
||||
"other than Vanilla, then the maximum amount of Key Rings that can be selected by Random or "
|
||||
"Count will be 9. Otherwise, the maximum amount of Key Rings will be 8.";
|
||||
mOptionDescriptions[RSK_GERUDO_KEYS] = "Vanilla - Thieve's Hideout Keys will appear in their vanilla locations.\n"
|
||||
mOptionDescriptions[RSK_GERUDO_KEYS] = "Vanilla - Thieves' Hideout Keys will appear in their vanilla locations.\n"
|
||||
"\n"
|
||||
"Any dungeon - Thieve's Hideout Keys can only appear inside of any dungon.\n"
|
||||
"Any dungeon - Thieves' Hideout Keys can only appear inside of any dungon.\n"
|
||||
"\n"
|
||||
"Overworld - Thieve's Hideout Keys can only appear outside of dungeons.\n"
|
||||
"Overworld - Thieves' Hideout Keys can only appear outside of dungeons.\n"
|
||||
"\n"
|
||||
"Anywhere - Thieve's Hideout Keys can appear anywhere in the world.";
|
||||
"Anywhere - Thieves' Hideout Keys can appear anywhere in the world.";
|
||||
mOptionDescriptions[RSK_BOSS_KEYSANITY] = "Start with - You will start with Boss keys from all dungeons.\n"
|
||||
"\n"
|
||||
"Vanilla - Boss Keys will appear in their vanilla locations.\n"
|
||||
"\n"
|
||||
"Own dungeon - Boss Keys can only appear in their respective dungeon.\n"
|
||||
"\n"
|
||||
"Any dungeon - Boss Keys can only appear inside of any dungon.\n"
|
||||
"Any dungeon - Boss Keys can only appear inside of any dungeon.\n"
|
||||
"\n"
|
||||
"Overworld - Boss Keys can only appear outside of dungeons.\n"
|
||||
"\n"
|
||||
@ -495,7 +501,7 @@ void Settings::CreateOptionDescriptions() {
|
||||
"\n"
|
||||
"Start with - Places Ganon's Boss Key in your starting inventory."
|
||||
"\n"
|
||||
"Any dungeon - Ganon's Boss Key Key can only appear inside of any dungon.\n"
|
||||
"Any dungeon - Ganon's Boss Key Key can only appear inside of any dungeon.\n"
|
||||
"\n"
|
||||
"Overworld - Ganon's Boss Key Key can only appear outside of dungeons.\n"
|
||||
"\n"
|
||||
@ -504,9 +510,9 @@ void Settings::CreateOptionDescriptions() {
|
||||
"LACS - These settings put the boss key on the Light Arrow Cutscene location, from Zelda in Temple of Time as "
|
||||
"adult, with differing requirements:\n"
|
||||
"- Vanilla: Obtain the Shadow Medallion and Spirit Medallion\n"
|
||||
"- Stones: Obtain the specified amount of spiritual stones.\n"
|
||||
"- Stones: Obtain the specified amount of Spiritual Stones.\n"
|
||||
"- Medallions: Obtain the specified amount of medallions.\n"
|
||||
"- Dungeon rewards: Obtain the specified total sum of spiritual stones or medallions.\n"
|
||||
"- Dungeon rewards: Obtain the specified total sum of Spiritual Stones or medallions.\n"
|
||||
"- Dungeons: Complete the specified amount of dungeons. Dungeons are considered complete after stepping in to "
|
||||
"the blue warp after the boss.\n"
|
||||
"- Tokens: Obtain the specified amount of Skulltula tokens.\n"
|
||||
@ -523,7 +529,7 @@ void Settings::CreateOptionDescriptions() {
|
||||
"\n"
|
||||
"Greg as Wildcard - Greg does not change logic, Greg helps obtain GBK, max number of "
|
||||
"rewards on slider does not change.";
|
||||
mOptionDescriptions[RSK_CUCCO_COUNT] = "The amount of cuccos needed to claim the reward from Anju the cucco lady";
|
||||
mOptionDescriptions[RSK_CUCCO_COUNT] = "The amount of cuccos needed to claim the reward from Anju the Cucco Lady.";
|
||||
mOptionDescriptions[RSK_BIG_POE_COUNT] = "The Poe collector will give a reward for turning in this many Big Poes.";
|
||||
mOptionDescriptions[RSK_SKIP_CHILD_STEALTH] =
|
||||
"The crawlspace into Hyrule Castle goes straight to Zelda, skipping the guards.";
|
||||
@ -532,11 +538,11 @@ void Settings::CreateOptionDescriptions() {
|
||||
"until after meeting Zelda. Disables the ability to shuffle Weird Egg.";
|
||||
mOptionDescriptions[RSK_SKIP_EPONA_RACE] = "Epona can be summoned with Epona's Song without needing to race Ingo.";
|
||||
mOptionDescriptions[RSK_COMPLETE_MASK_QUEST] =
|
||||
"Once the happy mask shop is opened, all masks will be available to be borrowed.";
|
||||
"Once the Happy Mask Shop is opened, all masks will be available to be borrowed.";
|
||||
mOptionDescriptions[RSK_SKIP_SCARECROWS_SONG] =
|
||||
"Start with the ability to summon Pierre the scarecrow. Pulling out an ocarina in the usual locations will "
|
||||
"Start with the ability to summon Pierre the Scarecrow. Pulling out an Ocarina in the usual locations will "
|
||||
"automatically summon him.\n"
|
||||
"With \"Shuffle Ocarina Buttons\" enabled, you'll need at least two ocarina buttons to summon him.";
|
||||
"With \"Shuffle Ocarina Buttons\" enabled, you'll need at least two Ocarina buttons to summon him.";
|
||||
mOptionDescriptions[RSK_ITEM_POOL] = "Sets how many major items appear in the item pool.\n"
|
||||
"\n"
|
||||
"Plentiful - Extra major items are added to the pool.\n"
|
||||
@ -592,12 +598,12 @@ void Settings::CreateOptionDescriptions() {
|
||||
"Very Strong - Many powerful hints.";
|
||||
mOptionDescriptions[RSK_TOT_ALTAR_HINT] =
|
||||
"Reading the Temple of Time altar as child will tell you the locations of the Spiritual Stones.\n"
|
||||
"Reading the Temple of Time altar as adult will tell you the locations of the Medallions, as well as the "
|
||||
"Reading the Temple of Time altar as adult will tell you the locations of the medallions, as well as the "
|
||||
"conditions for building the Rainbow Bridge and getting the Boss Key for Ganon's Castle.";
|
||||
mOptionDescriptions[RSK_GANONDORF_HINT] =
|
||||
"Talking to Ganondorf in his boss room will tell you the location of the Light Arrows and Master Sword."
|
||||
"If this option is enabled and Ganondorf is reachable without these items, Gossip Stones will never hint the "
|
||||
"appropriote items.";//RANDOTODO make this hint text about no dupe hints a global hint for static hints. Add to navi?
|
||||
"appropriate items.";//RANDOTODO make this hint text about no dupe hints a global hint for static hints. Add to navi?
|
||||
mOptionDescriptions[RSK_SHEIK_LA_HINT] =
|
||||
"Talking to Sheik inside Ganon's Castle will tell you the location of the Light Arrows."
|
||||
"If this option is enabled and Sheik is reachable without Light Arrows, Gossip Stones will never hint the "
|
||||
@ -613,37 +619,37 @@ void Settings::CreateOptionDescriptions() {
|
||||
mOptionDescriptions[RSK_FISHING_POLE_HINT] = "Talking to the fishing pond owner without the fishing pole will tell you its location.";
|
||||
mOptionDescriptions[RSK_OOT_HINT] = "Sheik in the Temple of Time will tell you the item and song on the Ocarina of Time.";
|
||||
mOptionDescriptions[RSK_FROGS_HINT] = "Standing near the pedestal for the frogs in Zora's River will tell you the "
|
||||
"reward for the frogs' ocarina game.";
|
||||
"reward for the frogs' Ocarina game.";
|
||||
mOptionDescriptions[RSK_BIGGORON_HINT] = "Talking to Biggoron will tell you the item he will give you in exchange for the Claim Check.";
|
||||
mOptionDescriptions[RSK_BIG_POES_HINT] = "Talking to the Poe Collector in the Market Guardhouse while adult will tell you what you receive for handing in Big Poes.";
|
||||
mOptionDescriptions[RSK_CHICKENS_HINT] = "Talking to Anju as a child will tell you the item she will give you for delivering her Cuccos to the pen";
|
||||
mOptionDescriptions[RSK_MALON_HINT] = "Talking to Malon as adult will tell you the item on \"Link's cow\", the cow you win from beating her time on the Lon Lon Obsticle Course.";
|
||||
mOptionDescriptions[RSK_CHICKENS_HINT] = "Talking to Anju as a child will tell you the item she will give you for delivering her cuccos to the pen.";
|
||||
mOptionDescriptions[RSK_MALON_HINT] = "Talking to Malon as adult will tell you the item on \"Link's cow\", the cow you win from beating her time on the Lon Lon Obstacle Course.";
|
||||
mOptionDescriptions[RSK_HBA_HINT] = "Talking to the Horseback Archery gerudo in Gerudo Fortress, or the nearby sign, will tell you what you win for scoring 1000 and 1500 points on Horseback Archery.";
|
||||
mOptionDescriptions[RSK_WARP_SONG_HINTS] = "Playing a warp song will tell you where it leads. (If warp song destinations are vanilla, this is always enabled.)";
|
||||
mOptionDescriptions[RSK_SCRUB_TEXT_HINT] = "Business scrubs will reveal the identity of what they're selling.";
|
||||
mOptionDescriptions[RSK_MERCHANT_TEXT_HINT] = "Merchants will reveal the identity of what they're selling (Shops are not affected by this setting).";
|
||||
mOptionDescriptions[RSK_KAK_10_SKULLS_HINT] = "Talking to the Cursed Resident in the Skultulla House who is saved after 10 tokens will tell you the reward";
|
||||
mOptionDescriptions[RSK_KAK_20_SKULLS_HINT] = "Talking to the Cursed Resident in the Skultulla House who is saved after 20 tokens will tell you the reward";
|
||||
mOptionDescriptions[RSK_KAK_30_SKULLS_HINT] = "Talking to the Cursed Resident in the Skultulla House who is saved after 30 tokens will tell you the reward";
|
||||
mOptionDescriptions[RSK_KAK_40_SKULLS_HINT] = "Talking to the Cursed Resident in the Skultulla House who is saved after 40 tokens will tell you the reward";
|
||||
mOptionDescriptions[RSK_KAK_50_SKULLS_HINT] = "Talking to the Cursed Resident in the Skultulla House who is saved after 50 tokens will tell you the reward";
|
||||
mOptionDescriptions[RSK_KAK_100_SKULLS_HINT] = "Talking to the Cursed Resident in the Skultulla House who is saved after 100 tokens will tell you the reward";
|
||||
mOptionDescriptions[RSK_KAK_10_SKULLS_HINT] = "Talking to the Cursed Resident in the Skulltula House who is saved after 10 tokens will tell you the reward.";
|
||||
mOptionDescriptions[RSK_KAK_20_SKULLS_HINT] = "Talking to the Cursed Resident in the Skulltula House who is saved after 20 tokens will tell you the reward.";
|
||||
mOptionDescriptions[RSK_KAK_30_SKULLS_HINT] = "Talking to the Cursed Resident in the Skulltula House who is saved after 30 tokens will tell you the reward.";
|
||||
mOptionDescriptions[RSK_KAK_40_SKULLS_HINT] = "Talking to the Cursed Resident in the Skulltula House who is saved after 40 tokens will tell you the reward.";
|
||||
mOptionDescriptions[RSK_KAK_50_SKULLS_HINT] = "Talking to the Cursed Resident in the Skulltula House who is saved after 50 tokens will tell you the reward.";
|
||||
mOptionDescriptions[RSK_KAK_100_SKULLS_HINT] = "Talking to the Cursed Resident in the Skulltula House who is saved after 100 tokens will tell you the reward.";
|
||||
mOptionDescriptions[RSK_MASK_SHOP_HINT] = "Reading the mask shop sign will tell you rewards from showing masks at the Deku Theatre.";
|
||||
mOptionDescriptions[RSK_FULL_WALLETS] = "Start with a full wallet. All wallet upgrades come filled with rupees.";
|
||||
mOptionDescriptions[RSK_BOMBCHUS_IN_LOGIC] =
|
||||
"Bombchus are properly considered in logic. Without this setting, any Bombchu requirement"
|
||||
" is filled by Bomb Bag + a renewable source of Bombchus\n"
|
||||
"Bombchus are properly considered in logic. Without this setting, any Bombchu requirement "
|
||||
"is filled by Bomb Bag + a renewable source of Bombchus.\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.";
|
||||
mOptionDescriptions[RSK_ENABLE_BOMBCHU_DROPS] = "Once you obtain bombchus for the first time, refills can be found "
|
||||
mOptionDescriptions[RSK_ENABLE_BOMBCHU_DROPS] = "Once you obtain Bombchus for the first time, refills can be found "
|
||||
"in bushes and other places where bomb drops can normally spawn."
|
||||
"\n"
|
||||
"If you have Bombchus in Logic disabled, you will also need a"
|
||||
"Bomb bag for bombchus to drop";
|
||||
"If you have Bombchus in Logic disabled, you will also need a "
|
||||
"Bomb Bag for Bombchus to drop.";
|
||||
mOptionDescriptions[RSK_BLUE_FIRE_ARROWS] =
|
||||
"Ice Arrows act like Blue Fire, making them able to melt red ice. "
|
||||
"Item placement logic will respect this option, so it might be required to use this to progress.";
|
||||
@ -671,4 +677,4 @@ void Settings::CreateOptionDescriptions() {
|
||||
mOptionDescriptions[RSK_SHUFFLE_BOSS_SOULS] = "Shuffles 8 boss souls (one for each blue warp dungeon). A boss will not appear until you collect its respective soul."
|
||||
"\n\"On + Ganon\" will also hide Ganon and Ganondorf behind a boss soul.";
|
||||
}
|
||||
} // namespace Rando
|
||||
} // namespace Rando
|
||||
|
@ -1270,7 +1270,7 @@ FishIdentity Randomizer::IdentifyFish(s32 sceneNum, s32 actorParams) {
|
||||
}
|
||||
|
||||
u8 Randomizer::GetRandoSettingValue(RandomizerSettingKey randoSettingKey) {
|
||||
return Rando::Context::GetInstance()->GetOption(randoSettingKey).GetSelectedOptionIndex();
|
||||
return Rando::Context::GetInstance()->GetOption(randoSettingKey).GetContextOptionIndex();
|
||||
}
|
||||
|
||||
GetItemEntry Randomizer::GetItemFromKnownCheck(RandomizerCheck randomizerCheck, GetItemID ogItemId, bool checkObtainability) {
|
||||
|
@ -568,19 +568,14 @@ typedef enum {
|
||||
RR_DODONGOS_CAVERN_BOSS_ROOM,
|
||||
|
||||
RR_JABU_JABUS_BELLY_BEGINNING,
|
||||
RR_JABU_JABUS_BELLY_LIFT_MIDDLE,
|
||||
RR_JABU_JABUS_BELLY_MAIN_UPPER,
|
||||
RR_JABU_JABUS_BELLY_MAIN_LOWER,
|
||||
RR_JABU_JABUS_BELLY_SHABOMB_CORRIDOR,
|
||||
RR_JABU_JABUS_BELLY_LOWER_SIDE_ROOM,
|
||||
RR_JABU_JABUS_BELLY_LIFT_LOWER,
|
||||
RR_JABU_JABUS_BELLY_FORKED_CORRIDOR,
|
||||
RR_JABU_JABUS_BELLY_BOOMERANG_ROOM,
|
||||
RR_JABU_JABUS_BELLY_MAP_ROOM,
|
||||
RR_JABU_JABUS_BELLY_MAIN,
|
||||
RR_JABU_JABUS_BELLY_B1_NORTH,
|
||||
RR_JABU_JABUS_BELLY_WATER_SWITCH_ROOM_SOUTH,
|
||||
RR_JABU_JABUS_BELLY_WATER_SWITCH_ROOM_LEDGE,
|
||||
RR_JABU_JABUS_BELLY_COMPASS_ROOM,
|
||||
RR_JABU_JABUS_BELLY_BLUE_TENTACLE,
|
||||
RR_JABU_JABUS_BELLY_GREEN_TENTACLE,
|
||||
RR_JABU_JABUS_BELLY_BIGOCTO_ROOM,
|
||||
RR_JABU_JABUS_BELLY_BIGOCTO_LEDGE,
|
||||
RR_JABU_JABUS_BELLY_ABOVE_BIGOCTO,
|
||||
RR_JABU_JABUS_BELLY_LIFT_UPPER,
|
||||
RR_JABU_JABUS_BELLY_NEAR_BOSS_ROOM,
|
||||
@ -3947,6 +3942,7 @@ typedef enum {
|
||||
RSK_KAK_GATE,
|
||||
RSK_DOOR_OF_TIME,
|
||||
RSK_ZORAS_FOUNTAIN,
|
||||
RSK_SLEEPING_WATERFALL,
|
||||
RSK_STARTING_AGE,
|
||||
RSK_GERUDO_FORTRESS,
|
||||
RSK_RAINBOW_BRIDGE,
|
||||
@ -4049,7 +4045,8 @@ typedef enum {
|
||||
RSK_GANONS_BOSS_KEY,
|
||||
RSK_SKIP_CHILD_STEALTH,
|
||||
RSK_SKIP_CHILD_ZELDA,
|
||||
RSK_STARTING_CONSUMABLES,
|
||||
RSK_STARTING_STICKS,
|
||||
RSK_STARTING_NUTS,
|
||||
RSK_FULL_WALLETS,
|
||||
RSK_SHUFFLE_CHEST_MINIGAME,
|
||||
RSK_CUCCO_COUNT,
|
||||
@ -4184,6 +4181,12 @@ typedef enum {
|
||||
RO_ZF_OPEN,
|
||||
} RandoOptionZorasFountain;
|
||||
|
||||
//Sleeping Waterfall settings (closed, open)
|
||||
typedef enum {
|
||||
RO_WATERFALL_CLOSED,
|
||||
RO_WATERFALL_OPEN,
|
||||
} RandoOptionSleepingWaterfall;
|
||||
|
||||
//Starting Age settings (child, adult, random)
|
||||
typedef enum {
|
||||
RO_AGE_CHILD,
|
||||
@ -4630,6 +4633,9 @@ typedef enum {
|
||||
RE_BEAMOS,
|
||||
RE_WALLMASTER,
|
||||
RE_PURPLE_LEEVER,
|
||||
RE_TENTACLE,
|
||||
RE_BARI,
|
||||
RE_SHABOM,
|
||||
} RandomizerEnemy;
|
||||
|
||||
//RANDOTODO compare child long jumpslash range with adult short
|
||||
|
@ -1503,7 +1503,10 @@ void DrawLocation(RandomizerCheck rc) {
|
||||
txt = itemLoc->GetPlacedItem().GetName().GetForLanguage(gSaveContext.language);
|
||||
}
|
||||
if (IsVisibleInCheckTracker(rc) && status == RCSHOW_IDENTIFIED && !mystery && !itemLoc->IsAddedToPool()) {
|
||||
txt += fmt::format(" - {}", OTRGlobals::Instance->gRandoContext->GetItemLocation(rc)->GetPrice());
|
||||
auto price = OTRGlobals::Instance->gRandoContext->GetItemLocation(rc)->GetPrice();
|
||||
if (price) {
|
||||
txt += fmt::format(" - {}", price);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (IsHeartPiece((GetItemID)Rando::StaticData::RetrieveItem(loc->GetVanillaItem()).GetItemID())) {
|
||||
|
@ -13,7 +13,10 @@ uint8_t Randomizer_GetSettingValue(RandomizerSettingKey randoSettingKey);
|
||||
GetItemEntry Randomizer_GetItemFromKnownCheck(RandomizerCheck randomizerCheck, GetItemID ogId);
|
||||
}
|
||||
|
||||
void StartingItemGive(GetItemEntry getItemEntry) {
|
||||
void StartingItemGive(GetItemEntry getItemEntry, RandomizerCheck randomizerCheck) {
|
||||
if (randomizerCheck != RC_MAX) {
|
||||
OTRGlobals::Instance->gRandoContext->GetItemLocation(randomizerCheck)->SetCheckStatus(RCSHOW_SAVED);
|
||||
}
|
||||
if (getItemEntry.modIndex == MOD_NONE) {
|
||||
if (getItemEntry.getItemId == GI_SWORD_BGS) {
|
||||
gSaveContext.bgsFlag = true;
|
||||
@ -96,8 +99,7 @@ void GiveLinkDekuNuts(int howManyNuts) {
|
||||
void GiveLinksPocketItem() {
|
||||
if (Randomizer_GetSettingValue(RSK_LINKS_POCKET) != RO_LINKS_POCKET_NOTHING) {
|
||||
GetItemEntry getItemEntry = Randomizer_GetItemFromKnownCheck(RC_LINKS_POCKET, (GetItemID)RG_NONE);
|
||||
StartingItemGive(getItemEntry);
|
||||
Rando::Context::GetInstance()->GetItemLocation(RC_LINKS_POCKET)->SetCheckStatus(RCSHOW_SAVED);
|
||||
StartingItemGive(getItemEntry, RC_LINKS_POCKET);
|
||||
// If we re-add the above, we'll get the item on save creation, now it's given on first load
|
||||
Flags_SetRandomizerInf(RAND_INF_LINKS_POCKET);
|
||||
}
|
||||
@ -149,13 +151,11 @@ void SetStartingItems() {
|
||||
INV_CONTENT(ITEM_OCARINA_FAIRY) = ITEM_OCARINA_FAIRY;
|
||||
}
|
||||
|
||||
if (Randomizer_GetSettingValue(RSK_STARTING_CONSUMABLES)) {
|
||||
if (!Randomizer_GetSettingValue(RSK_SHUFFLE_DEKU_STICK_BAG)) {
|
||||
GiveLinkDekuSticks(10);
|
||||
}
|
||||
if (!Randomizer_GetSettingValue(RSK_SHUFFLE_DEKU_NUT_BAG)) {
|
||||
GiveLinkDekuNuts(20);
|
||||
}
|
||||
if (Randomizer_GetSettingValue(RSK_STARTING_STICKS) && !Randomizer_GetSettingValue(RSK_SHUFFLE_DEKU_STICK_BAG)) {
|
||||
GiveLinkDekuSticks(10);
|
||||
}
|
||||
if (Randomizer_GetSettingValue(RSK_STARTING_NUTS) && !Randomizer_GetSettingValue(RSK_SHUFFLE_DEKU_NUT_BAG)) {
|
||||
GiveLinkDekuNuts(20);
|
||||
}
|
||||
|
||||
if (Randomizer_GetSettingValue(RSK_FULL_WALLETS)) {
|
||||
@ -251,10 +251,11 @@ extern "C" void Randomizer_InitSaveFile() {
|
||||
// Flags_SetInfTable(INFTABLE_CHILD_MALON_SAID_EPONA_WAS_AFRAID_OF_YOU);
|
||||
// Flags_SetInfTable(INFTABLE_SPOKE_TO_INGO_ONCE_AS_ADULT);
|
||||
|
||||
// Now handled by cutscene skips
|
||||
// Ruto already met in jabu and spawns down the hole immediately
|
||||
Flags_SetInfTable(INFTABLE_RUTO_IN_JJ_MEET_RUTO);
|
||||
Flags_SetInfTable(INFTABLE_RUTO_IN_JJ_TALK_FIRST_TIME);
|
||||
Flags_SetInfTable(INFTABLE_RUTO_IN_JJ_WANTS_TO_BE_TOSSED_TO_SAPPHIRE);
|
||||
// Flags_SetInfTable(INFTABLE_RUTO_IN_JJ_MEET_RUTO);
|
||||
// Flags_SetInfTable(INFTABLE_RUTO_IN_JJ_TALK_FIRST_TIME);
|
||||
// Flags_SetInfTable(INFTABLE_RUTO_IN_JJ_WANTS_TO_BE_TOSSED_TO_SAPPHIRE);
|
||||
|
||||
// Now handled by cutscene skips
|
||||
// Skip cutscenes before Nabooru fight
|
||||
@ -381,7 +382,7 @@ extern "C" void Randomizer_InitSaveFile() {
|
||||
|
||||
if (Randomizer_GetSettingValue(RSK_SKIP_CHILD_ZELDA)) {
|
||||
GetItemEntry getItemEntry = Randomizer_GetItemFromKnownCheck(RC_SONG_FROM_IMPA, (GetItemID)RG_ZELDAS_LULLABY);
|
||||
StartingItemGive(getItemEntry);
|
||||
StartingItemGive(getItemEntry, RC_SONG_FROM_IMPA);
|
||||
|
||||
// malon/talon back at ranch
|
||||
Flags_SetEventChkInf(EVENTCHKINF_OBTAINED_POCKET_EGG);
|
||||
@ -403,7 +404,7 @@ extern "C" void Randomizer_InitSaveFile() {
|
||||
|
||||
if (Randomizer_GetSettingValue(RSK_SHUFFLE_MASTER_SWORD) && startingAge == RO_AGE_ADULT) {
|
||||
GetItemEntry getItemEntry = Randomizer_GetItemFromKnownCheck(RC_TOT_MASTER_SWORD, GI_NONE);
|
||||
StartingItemGive(getItemEntry);
|
||||
StartingItemGive(getItemEntry, RC_TOT_MASTER_SWORD);
|
||||
Flags_SetRandomizerInf(RAND_INF_TOT_MASTER_SWORD);
|
||||
}
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -13,7 +13,7 @@ std::unordered_map<uint32_t, CustomMessage> StaticData::hintTypeNames = {
|
||||
{HINT_TYPE_ITEM_AREA, CustomMessage("Item Area")},
|
||||
{HINT_TYPE_ALTAR_CHILD, CustomMessage("Child Altar")},
|
||||
{HINT_TYPE_ALTAR_ADULT, CustomMessage("Adult Altar")},
|
||||
{HINT_TYPE_WOTH, CustomMessage("Way of the Hero")},
|
||||
{HINT_TYPE_WOTH, CustomMessage("Way of the Hero")},
|
||||
{HINT_TYPE_FOOLISH, CustomMessage("Foolish")},
|
||||
{HINT_TYPE_MESSAGE, CustomMessage("Hardcoded Message")}
|
||||
};
|
||||
@ -60,7 +60,7 @@ std::unordered_map<uint32_t, CustomMessage> StaticData::hintNames = {
|
||||
{RH_LH_SOUTHEAST_GOSSIP_STONE, CustomMessage("LH Southeast Gossip Stone")},
|
||||
{RH_LH_SOUTHWEST_GOSSIP_STONE, CustomMessage("LH Southwest Gossip Stone")},
|
||||
{RH_GV_GOSSIP_STONE, CustomMessage("Gerudo Valley Gossip Stone")},
|
||||
{RH_COLOSSUS_GOSSIP_STONE, CustomMessage("Desert Collosus Gossip Stone")},
|
||||
{RH_COLOSSUS_GOSSIP_STONE, CustomMessage("Desert Colossus Gossip Stone")},
|
||||
{RH_DODONGOS_CAVERN_GOSSIP_STONE, CustomMessage("Dodongo's Cavern Gossip Stone")},
|
||||
{RH_GANONDORF_HINT, CustomMessage("Ganondorf Hint")},
|
||||
{RH_GANONDORF_JOKE, CustomMessage("Ganondorf Joke")},
|
||||
@ -189,7 +189,7 @@ std::unordered_map<uint32_t, RandomizerHintTextKey> StaticData::trialData = {
|
||||
std::unordered_map<RandomizerHint, StaticHintInfo> StaticData::staticHintInfoMap = {
|
||||
// RH_GANONDORF_HINT is special cased due to being different based on master sword shuffle
|
||||
// Altar hints are special cased due to special hint marking rules
|
||||
// warp song hints are special cased due to entrences not being done properly yet
|
||||
// warp song hints are special cased due to entrances not being done properly yet
|
||||
// Ganondorf Joke is special cased as the text is random
|
||||
{RH_SHEIK_HINT, StaticHintInfo(HINT_TYPE_AREA, {RHT_SHEIK_HINT_LA_ONLY}, RSK_SHEIK_LA_HINT, true, {}, {RG_LIGHT_ARROWS}, {RC_SHEIK_HINT_GC, RC_SHEIK_HINT_MQ_GC}, true)},
|
||||
{RH_DAMPES_DIARY, StaticHintInfo(HINT_TYPE_AREA, {RHT_DAMPE_DIARY}, RSK_DAMPES_DIARY_HINT, true, {}, {RG_PROGRESSIVE_HOOKSHOT}, {RC_DAMPE_HINT})},
|
||||
@ -301,4 +301,4 @@ std::unordered_map<u32, RandomizerHint> StaticData::grottoChestParamsToHint{
|
||||
};
|
||||
|
||||
std::array<HintText, RHT_MAX> StaticData::hintTextTable = {};
|
||||
}
|
||||
}
|
||||
|
@ -35,10 +35,13 @@ extern SaveContext gSaveContext;
|
||||
extern PlayState* gPlayState;
|
||||
extern int32_t D_8011D3AC;
|
||||
|
||||
extern void func_808ADEF0(BgSpot03Taki* bgSpot03Taki, PlayState* play);
|
||||
extern void BgSpot03Taki_ApplyOpeningAlpha(BgSpot03Taki* bgSpot03Taki, s32 bufferIndex);
|
||||
|
||||
extern void func_80AF36EC(EnRu2* enRu2, PlayState* play);
|
||||
}
|
||||
|
||||
#define RAND_GET_OPTION(option) Rando::Context::GetInstance()->GetOption(option).GetSelectedOptionIndex()
|
||||
#define RAND_GET_OPTION(option) Rando::Context::GetInstance()->GetOption(option).GetContextOptionIndex()
|
||||
|
||||
void EnMa1_EndTeachSong(EnMa1* enMa1, PlayState* play) {
|
||||
if (Message_GetState(&gPlayState->msgCtx) == TEXT_STATE_CLOSING) {
|
||||
@ -96,6 +99,9 @@ void EnDntDemo_JudgeSkipToReward(EnDntDemo* enDntDemo, PlayState* play) {
|
||||
}
|
||||
}
|
||||
|
||||
void BgSpot03Taki_KeepOpen(BgSpot03Taki* bgSpot03Taki, PlayState* play) {
|
||||
}
|
||||
|
||||
static int successChimeCooldown = 0;
|
||||
void RateLimitedSuccessChime() {
|
||||
if (successChimeCooldown == 0) {
|
||||
@ -263,7 +269,7 @@ void TimeSaverOnVanillaBehaviorHandler(GIVanillaBehavior id, bool* should, va_li
|
||||
// The switch in jabu that you are intended to press with a box to reach barrinade
|
||||
// can be skipped by either a frame perfect roll open or with OI
|
||||
// The One Point for that switch is used in common setups for the former and is required for the latter to work
|
||||
if (actor->params == 14848 && gPlayState->sceneNum == SCENE_JABU_JABU && !CVarGetInteger(CVAR_ENHANCEMENT("TimeSavers.SkipCutscene.GlitchAiding"), 0)){
|
||||
if (actor->params == 14848 && gPlayState->sceneNum == SCENE_JABU_JABU && CVarGetInteger(CVAR_ENHANCEMENT("TimeSavers.SkipCutscene.GlitchAiding"), 0)){
|
||||
break;
|
||||
}
|
||||
BgBdanSwitch* switchActor = (BgBdanSwitch*)actor;
|
||||
@ -280,19 +286,11 @@ void TimeSaverOnVanillaBehaviorHandler(GIVanillaBehavior id, bool* should, va_li
|
||||
RateLimitedSuccessChime();
|
||||
break;
|
||||
}
|
||||
case ACTOR_BG_HIDAN_FWBIG: {
|
||||
*should = false;
|
||||
break;
|
||||
}
|
||||
case ACTOR_EN_EX_ITEM: {
|
||||
*should = false;
|
||||
break;
|
||||
}
|
||||
case ACTOR_EN_DNT_NOMAL: {
|
||||
*should = false;
|
||||
break;
|
||||
}
|
||||
case ACTOR_EN_DNT_DEMO: {
|
||||
case ACTOR_BG_HIDAN_FWBIG:
|
||||
case ACTOR_EN_EX_ITEM:
|
||||
case ACTOR_EN_DNT_NOMAL:
|
||||
case ACTOR_EN_DNT_DEMO:
|
||||
case ACTOR_BG_HAKA_ZOU: {
|
||||
*should = false;
|
||||
break;
|
||||
}
|
||||
@ -311,6 +309,8 @@ void TimeSaverOnVanillaBehaviorHandler(GIVanillaBehavior id, bool* should, va_li
|
||||
case ACTOR_BG_SPOT18_BASKET:
|
||||
case ACTOR_BG_HIDAN_CURTAIN:
|
||||
case ACTOR_BG_MORI_HINERI:
|
||||
case ACTOR_BG_MIZU_SHUTTER:
|
||||
case ACTOR_SHOT_SUN:
|
||||
*should = false;
|
||||
RateLimitedSuccessChime();
|
||||
break;
|
||||
@ -699,6 +699,8 @@ static uint32_t enFuUpdateHook = 0;
|
||||
static uint32_t enFuKillHook = 0;
|
||||
static uint32_t bgSpot02UpdateHook = 0;
|
||||
static uint32_t bgSpot02KillHook = 0;
|
||||
static uint32_t bgSpot03UpdateHook = 0;
|
||||
static uint32_t bgSpot03KillHook = 0;
|
||||
static uint32_t enPoSistersUpdateHook = 0;
|
||||
static uint32_t enPoSistersKillHook = 0;
|
||||
void TimeSaverOnActorInitHandler(void* actorRef) {
|
||||
@ -754,6 +756,10 @@ void TimeSaverOnActorInitHandler(void* actorRef) {
|
||||
});
|
||||
}
|
||||
|
||||
if (actor->id == ACTOR_EN_OWL && gPlayState->sceneNum == SCENE_ZORAS_RIVER && CVarGetInteger(CVAR_ENHANCEMENT("TimeSavers.SleepingWaterfall"), 0) == 2) {
|
||||
Actor_Kill(actor);
|
||||
}
|
||||
|
||||
if (actor->id == ACTOR_BG_SPOT02_OBJECTS && actor->params == 2) {
|
||||
bgSpot02UpdateHook = GameInteractor::Instance->RegisterGameHook<GameInteractor::OnActorUpdate>([](void* innerActorRef) mutable {
|
||||
Actor* innerActor = static_cast<Actor*>(innerActorRef);
|
||||
@ -776,6 +782,61 @@ void TimeSaverOnActorInitHandler(void* actorRef) {
|
||||
});
|
||||
}
|
||||
|
||||
if (actor->id == ACTOR_BG_SPOT03_TAKI) {
|
||||
bgSpot03UpdateHook = GameInteractor::Instance->RegisterGameHook<GameInteractor::OnActorUpdate>([](void* innerActorRef) mutable {
|
||||
Actor* innerActor = static_cast<Actor*>(innerActorRef);
|
||||
|
||||
if (innerActor->id != ACTOR_BG_SPOT03_TAKI) {
|
||||
return;
|
||||
}
|
||||
|
||||
bool shouldKeepOpen;
|
||||
switch (CVarGetInteger(CVAR_ENHANCEMENT("TimeSavers.SleepingWaterfall"), 0)) {
|
||||
case 1:
|
||||
shouldKeepOpen = Flags_GetEventChkInf(EVENTCHKINF_OPENED_ZORAS_DOMAIN);
|
||||
break;
|
||||
case 2:
|
||||
if (IS_RANDO && RAND_GET_OPTION(RSK_SLEEPING_WATERFALL) == RO_WATERFALL_OPEN) {
|
||||
shouldKeepOpen = true;
|
||||
} else {
|
||||
shouldKeepOpen = CHECK_QUEST_ITEM(QUEST_SONG_LULLABY) &&
|
||||
(INV_CONTENT(ITEM_OCARINA_TIME) == ITEM_OCARINA_TIME ||
|
||||
INV_CONTENT(ITEM_OCARINA_FAIRY) == ITEM_OCARINA_FAIRY);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
shouldKeepOpen = false;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!shouldKeepOpen) {
|
||||
return;
|
||||
}
|
||||
|
||||
BgSpot03Taki* bgSpot03 = static_cast<BgSpot03Taki*>(innerActorRef);
|
||||
if (bgSpot03->actionFunc == func_808ADEF0) {
|
||||
bgSpot03->actionFunc = BgSpot03Taki_KeepOpen;
|
||||
bgSpot03->state = WATERFALL_OPENED;
|
||||
bgSpot03->openingAlpha = 0.0f;
|
||||
Flags_SetSwitch(gPlayState, bgSpot03->switchFlag);
|
||||
func_8003EBF8(gPlayState, &gPlayState->colCtx.dyna, bgSpot03->dyna.bgId);
|
||||
BgSpot03Taki_ApplyOpeningAlpha(bgSpot03, 0);
|
||||
BgSpot03Taki_ApplyOpeningAlpha(bgSpot03, 1);
|
||||
|
||||
GameInteractor::Instance->UnregisterGameHook<GameInteractor::OnActorUpdate>(bgSpot03UpdateHook);
|
||||
GameInteractor::Instance->UnregisterGameHook<GameInteractor::OnSceneInit>(bgSpot03KillHook);
|
||||
bgSpot03UpdateHook = 0;
|
||||
bgSpot03KillHook = 0;
|
||||
}
|
||||
});
|
||||
bgSpot03KillHook = GameInteractor::Instance->RegisterGameHook<GameInteractor::OnSceneInit>([](int16_t sceneNum) mutable {
|
||||
GameInteractor::Instance->UnregisterGameHook<GameInteractor::OnActorUpdate>(bgSpot03UpdateHook);
|
||||
GameInteractor::Instance->UnregisterGameHook<GameInteractor::OnSceneInit>(bgSpot03KillHook);
|
||||
bgSpot03UpdateHook = 0;
|
||||
bgSpot03KillHook = 0;
|
||||
});
|
||||
}
|
||||
|
||||
if (actor->id == ACTOR_EN_DNT_DEMO && (IS_RANDO || CVarGetInteger(CVAR_ENHANCEMENT("TimeSavers.SkipMiscInteractions"), IS_RANDO))) {
|
||||
EnDntDemo* enDntDemo = static_cast<EnDntDemo*>(actorRef);
|
||||
enDntDemo->actionFunc = EnDntDemo_JudgeSkipToReward;
|
||||
@ -786,7 +847,7 @@ void TimeSaverOnActorInitHandler(void* actorRef) {
|
||||
// or poes from which the cutscene is triggered until we can have a "BeforeActorInit" hook.
|
||||
// So for now we're just going to set the flag before they get to the room the cutscene is in
|
||||
if (gPlayState->sceneNum == SCENE_FOREST_TEMPLE && actor->id == ACTOR_EN_ST && !Flags_GetSwitch(gPlayState, 0x1B)) {
|
||||
if (CVarGetInteger(CVAR_ENHANCEMENT("TimeSavers.SkipCutscene.GlitchAiding"), 0)) {
|
||||
if (CVarGetInteger(CVAR_ENHANCEMENT("TimeSavers.SkipCutscene.Story"), 0) && !CVarGetInteger(CVAR_ENHANCEMENT("TimeSavers.SkipCutscene.GlitchAiding"), 0)) {
|
||||
Flags_SetSwitch(gPlayState, 0x1B);
|
||||
}
|
||||
}
|
||||
@ -812,7 +873,7 @@ void TimeSaverOnActorInitHandler(void* actorRef) {
|
||||
|
||||
// Fire Temple Darunia cutscene
|
||||
if (actor->id == ACTOR_EN_DU && gPlayState->sceneNum == SCENE_FIRE_TEMPLE) {
|
||||
if (CVarGetInteger(CVAR_ENHANCEMENT("TimeSavers.SkipCutscene.GlitchAiding"), 0)) {
|
||||
if (CVarGetInteger(CVAR_ENHANCEMENT("TimeSavers.SkipCutscene.Story"), 0) && !CVarGetInteger(CVAR_ENHANCEMENT("TimeSavers.SkipCutscene.GlitchAiding"), 0)) {
|
||||
Flags_SetInfTable(INFTABLE_SPOKE_TO_DARUNIA_IN_FIRE_TEMPLE);
|
||||
Actor_Kill(actor);
|
||||
}
|
||||
|
@ -301,6 +301,16 @@ void TimeSplitsGetImageSize(uint32_t item) {
|
||||
}
|
||||
}
|
||||
|
||||
void SplitsPushImageButtonStyle(){
|
||||
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(1.0f, 1.0f, 1.0f, 0.0f));
|
||||
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(1.0f, 1.0f, 1.0f, 0.2f));
|
||||
ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(1.0f, 1.0f, 1.0f, 0.1f));
|
||||
}
|
||||
|
||||
void SplitsPopImageButtonStyle(){
|
||||
ImGui::PopStyleColor(3);
|
||||
}
|
||||
|
||||
void TimeSplitsUpdateSplitStatus() {
|
||||
uint32_t index = 0;
|
||||
for (auto& data : splitList) {
|
||||
@ -310,7 +320,7 @@ void TimeSplitsUpdateSplitStatus() {
|
||||
}
|
||||
index++;
|
||||
}
|
||||
for (int i = index; i < splitList.size(); i++) {
|
||||
for (size_t i = index; i < splitList.size(); i++) {
|
||||
if (splitList[i].splitTimeStatus != SPLIT_STATUS_ACTIVE && splitList[i].splitTimeStatus != SPLIT_STATUS_COLLECTED) {
|
||||
splitList[i].splitTimeStatus = SPLIT_STATUS_INACTIVE;
|
||||
}
|
||||
@ -421,11 +431,28 @@ void TimeSplitsPopUpContext() {
|
||||
if (popupID == ITEM_SKULL_TOKEN) {
|
||||
ImGui::BeginTable("Token Table", 2);
|
||||
ImGui::TableNextColumn();
|
||||
SplitsPushImageButtonStyle();
|
||||
ImGui::ImageButton(Ship::Context::GetInstance()->GetWindow()->GetGui()->GetTextureByName("QUEST_SKULL_TOKEN"),
|
||||
ImVec2(32.0f, 32.0f), ImVec2(0, 0), ImVec2(1, 1), 2.0f, ImVec4(0, 0, 0, 0));
|
||||
ImGui::TableNextColumn();
|
||||
SplitsPopImageButtonStyle();
|
||||
ImGui::PushItemWidth(150.0f);
|
||||
|
||||
ImGui::BeginGroup();
|
||||
std::string MinusBTNName = " - ##Set Tokens";
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button(MinusBTNName.c_str()) && skullTokenCount > 0) {
|
||||
skullTokenCount--;
|
||||
}
|
||||
ImGui::SameLine();
|
||||
ImGui::SliderInt("##count", &skullTokenCount, 0, 100, "%d Tokens");
|
||||
std::string PlusBTNName = " + ##Set Tokens";
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button(PlusBTNName.c_str()) && skullTokenCount < 100) {
|
||||
skullTokenCount++;
|
||||
}
|
||||
ImGui::EndGroup();
|
||||
|
||||
ImGui::PopItemWidth();
|
||||
if (ImGui::Button("Set Tokens")) {
|
||||
auto findID = std::find_if(splitObjectList.begin(), splitObjectList.end(), [&](const SplitObject& obj) { return obj.splitID == ITEM_SKULL_TOKEN; });
|
||||
@ -442,6 +469,7 @@ void TimeSplitsPopUpContext() {
|
||||
ImGui::EndTable();
|
||||
} else {
|
||||
int rowIndex = 0;
|
||||
SplitsPushImageButtonStyle();
|
||||
for (auto item : popupList[popupID]) {
|
||||
auto findID = std::find_if(splitObjectList.begin(), splitObjectList.end(), [&](const SplitObject& obj) { return obj.splitID == item; });
|
||||
if (findID == splitObjectList.end()) {
|
||||
@ -468,7 +496,7 @@ void TimeSplitsPopUpContext() {
|
||||
if (popupID <= ITEM_SLINGSHOT && popupID != -1) {
|
||||
ImVec2 imageMin = ImGui::GetItemRectMin();
|
||||
ImVec2 imageMax = ImGui::GetItemRectMax();
|
||||
ImVec2 imageSize = ImVec2(imageMax.x - imageMin.x, imageMax.y - imageMin.y);
|
||||
//ImVec2 imageSize = ImVec2(imageMax.x - imageMin.x, imageMax.y - imageMin.y); UNUSED
|
||||
ImVec2 textPos = ImVec2(imageMax.x - ImGui::CalcTextSize("00").x - 5,
|
||||
imageMax.y - ImGui::CalcTextSize("00").y - 5);
|
||||
|
||||
@ -484,6 +512,7 @@ void TimeSplitsPopUpContext() {
|
||||
}
|
||||
rowIndex++;
|
||||
}
|
||||
SplitsPopImageButtonStyle();
|
||||
}
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
@ -610,10 +639,8 @@ void TimeSplitsDrawSplitsList() {
|
||||
ImGui::TableSetupColumn("Prev. Best");
|
||||
ImGui::TableHeadersRow();
|
||||
|
||||
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(1.0f, 1.0f, 1.0f, 0.0f));
|
||||
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(1.0f, 1.0f, 1.0f, 0.2f));
|
||||
ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(1.0f, 1.0f, 1.0f, 0.1f));
|
||||
|
||||
SplitsPushImageButtonStyle();
|
||||
for (auto& split : splitList) {
|
||||
ImGui::TableNextColumn();
|
||||
TimeSplitsSplitBestTimeDisplay(split);
|
||||
@ -648,10 +675,10 @@ void TimeSplitsDrawSplitsList() {
|
||||
|
||||
dragIndex++;
|
||||
}
|
||||
SplitsPopImageButtonStyle();
|
||||
|
||||
TimeSplitsPostDragAndDrop();
|
||||
|
||||
ImGui::PopStyleColor(3);
|
||||
ImGui::PopStyleVar(1);
|
||||
ImGui::EndTable();
|
||||
ImGui::EndChild();
|
||||
@ -677,7 +704,7 @@ void TimeSplitsDrawItemList(uint32_t type) {
|
||||
|
||||
ImGui::BeginChild("Item Child");
|
||||
ImGui::BeginTable("Item List", tableSize);
|
||||
for (int i = 0; i < tableSize; i++) {
|
||||
for (size_t i = 0; i < tableSize; i++) {
|
||||
if (i == 0) {
|
||||
ImGui::TableSetupColumn("Item Image", ImGuiTableColumnFlags_WidthFixed | ImGuiTableColumnFlags_NoHeaderLabel, 39.0f);
|
||||
} else {
|
||||
@ -689,15 +716,12 @@ void TimeSplitsDrawItemList(uint32_t type) {
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(1.0f, 1.0f, 1.0f, 0.0f));
|
||||
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(1.0f, 1.0f, 1.0f, 0.2f));
|
||||
ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(1.0f, 1.0f, 1.0f, 0.1f));
|
||||
|
||||
for (auto& split : splitObjectList) {
|
||||
if (split.splitType == type) {
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::PushID(split.splitID);
|
||||
TimeSplitsGetImageSize(split.splitID);
|
||||
SplitsPushImageButtonStyle();
|
||||
if (ImGui::ImageButton(Ship::Context::GetInstance()->GetWindow()->GetGui()->GetTextureByName(split.splitImage),
|
||||
imageSize, ImVec2(0, 0), ImVec2(1, 1), imagePadding, ImVec4(0, 0, 0, 0), split.splitTint)) {
|
||||
|
||||
@ -715,6 +739,7 @@ void TimeSplitsDrawItemList(uint32_t type) {
|
||||
}
|
||||
}
|
||||
}
|
||||
SplitsPopImageButtonStyle();
|
||||
|
||||
TimeSplitsPopUpContext();
|
||||
ImGui::PopID();
|
||||
@ -729,7 +754,6 @@ void TimeSplitsDrawItemList(uint32_t type) {
|
||||
|
||||
}
|
||||
}
|
||||
ImGui::PopStyleColor(3);
|
||||
ImGui::EndTable();
|
||||
ImGui::EndChild();
|
||||
}
|
||||
|
@ -84,6 +84,7 @@ Sail* Sail::Instance;
|
||||
#include "Enhancements/mods.h"
|
||||
#include "Enhancements/game-interactor/GameInteractor.h"
|
||||
#include "Enhancements/randomizer/draw.h"
|
||||
#include "Enhancements/custom-collectible/CustomCollectible.h"
|
||||
#include <libultraship/libultraship.h>
|
||||
|
||||
// Resource Types/Factories
|
||||
@ -703,6 +704,7 @@ extern "C" void VanillaItemTable_Init() {
|
||||
GET_ITEM(ITEM_NUT_UPGRADE_30, OBJECT_GI_NUTS, GID_NUTS, 0xA7, 0x80, CHEST_ANIM_SHORT, ITEM_CATEGORY_LESSER, MOD_NONE, GI_NUT_UPGRADE_30),
|
||||
GET_ITEM(ITEM_NUT_UPGRADE_40, OBJECT_GI_NUTS, GID_NUTS, 0xA8, 0x80, CHEST_ANIM_SHORT, ITEM_CATEGORY_LESSER, MOD_NONE, GI_NUT_UPGRADE_40),
|
||||
GET_ITEM(ITEM_BULLET_BAG_50, OBJECT_GI_DEKUPOUCH, GID_BULLET_BAG_50, 0x6C, 0x80, CHEST_ANIM_LONG, ITEM_CATEGORY_LESSER, MOD_NONE, GI_BULLET_BAG_50),
|
||||
GET_ITEM(ITEM_SHIP, OBJECT_UNSET_16E, GID_MAXIMUM, 0x00, 0x80, CHEST_ANIM_LONG, ITEM_CATEGORY_LESSER, MOD_NONE, GI_SHIP),
|
||||
GET_ITEM_NONE,
|
||||
GET_ITEM_NONE,
|
||||
GET_ITEM_NONE // GI_MAX - if you need to add to this table insert it before this entry.
|
||||
@ -874,6 +876,7 @@ std::unordered_map<ItemID, RandomizerGet> ItemIDtoRandomizerGetMap {
|
||||
{ ITEM_KOKIRI_EMERALD, RG_KOKIRI_EMERALD },
|
||||
{ ITEM_GORON_RUBY, RG_GORON_RUBY },
|
||||
{ ITEM_ZORA_SAPPHIRE, RG_ZORA_SAPPHIRE },
|
||||
{ ITEM_SWORD_MASTER, RG_MASTER_SWORD },
|
||||
};
|
||||
|
||||
extern "C" RandomizerGet RetrieveRandomizerGetFromItemID(ItemID itemID) {
|
||||
@ -1172,6 +1175,7 @@ extern "C" void InitOTR() {
|
||||
DebugConsole_Init();
|
||||
|
||||
InitMods();
|
||||
CustomCollectible::RegisterHooks();
|
||||
ActorDB::AddBuiltInCustomActors();
|
||||
// #region SOH [Randomizer] TODO: Remove these and refactor spoiler file handling for randomizer
|
||||
CVarClear(CVAR_GENERAL("RandomizerNewFileDropped"));
|
||||
@ -1932,7 +1936,7 @@ extern "C" u32 SpoilerFileExists(const char* spoilerFileName) {
|
||||
}
|
||||
|
||||
extern "C" u8 Randomizer_GetSettingValue(RandomizerSettingKey randoSettingKey) {
|
||||
return OTRGlobals::Instance->gRandoContext->GetOption(randoSettingKey).GetSelectedOptionIndex();
|
||||
return OTRGlobals::Instance->gRandoContext->GetOption(randoSettingKey).GetContextOptionIndex();
|
||||
}
|
||||
|
||||
extern "C" RandomizerCheck Randomizer_GetCheckFromActor(s16 actorId, s16 sceneNum, s16 actorParams) {
|
||||
@ -2152,7 +2156,6 @@ extern "C" int CustomMessage_RetrieveIfExists(PlayState* play) {
|
||||
} else if (textId >= TEXT_SHOP_ITEM_RANDOM_CONFIRM && textId <= TEXT_SHOP_ITEM_RANDOM_CONFIRM_END){
|
||||
RandomizerCheck rc = OTRGlobals::Instance->gRandomizer->GetCheckFromRandomizerInf((RandomizerInf)((textId - TEXT_SHOP_ITEM_RANDOM_CONFIRM) + RAND_INF_SHOP_ITEMS_KF_SHOP_ITEM_1));
|
||||
messageEntry = OTRGlobals::Instance->gRandomizer->GetMerchantMessage(rc, TEXT_SHOP_ITEM_RANDOM_CONFIRM);
|
||||
// textId: TEXT_SCRUB_RANDOM + (randomizerInf - RAND_INF_SCRUBS_PURCHASED_DODONGOS_CAVERN_DEKU_SCRUB_NEAR_BOMB_BAG_LEFT)
|
||||
} else if (textId == TEXT_SCRUB_RANDOM) {
|
||||
EnDns* enDns = (EnDns*)GET_PLAYER(play)->talkActor;
|
||||
RandomizerCheck rc = OTRGlobals::Instance->gRandomizer->GetCheckFromRandomizerInf((RandomizerInf)enDns->sohScrubIdentity.randomizerInf);
|
||||
|
@ -180,7 +180,7 @@ void SaveManager::LoadRandomizerVersion1() {
|
||||
int key, value;
|
||||
SaveManager::Instance->LoadData("sk" + std::to_string(i), key);
|
||||
SaveManager::Instance->LoadData("sv" + std::to_string(i), value);
|
||||
randoContext->GetOption(RandomizerSettingKey(key)).SetSelectedIndex(value);
|
||||
randoContext->GetOption(RandomizerSettingKey(key)).SetContextIndex(value);
|
||||
}
|
||||
|
||||
for (int i = 0; i < 50; i++) {
|
||||
@ -286,7 +286,7 @@ void SaveManager::LoadRandomizerVersion2() {
|
||||
SaveManager::Instance->LoadArray("randoSettings", RSK_MAX, [&](size_t i) {
|
||||
int value = 0;
|
||||
SaveManager::Instance->LoadData("", value);
|
||||
randoContext->GetOption(RandomizerSettingKey(i)).SetSelectedIndex(value);
|
||||
randoContext->GetOption(RandomizerSettingKey(i)).SetContextIndex(value);
|
||||
});
|
||||
|
||||
SaveManager::Instance->LoadArray("hintLocations", RH_ZR_OPEN_GROTTO_GOSSIP_STONE + 1, [&](size_t i) {
|
||||
@ -435,7 +435,7 @@ void SaveManager::LoadRandomizerVersion3() {
|
||||
SaveManager::Instance->LoadArray("randoSettings", RSK_MAX, [&](size_t i) {
|
||||
int value = 0;
|
||||
SaveManager::Instance->LoadData("", value);
|
||||
randoContext->GetOption(RandomizerSettingKey(i)).SetSelectedIndex(value);
|
||||
randoContext->GetOption(RandomizerSettingKey(i)).SetContextIndex(value);
|
||||
});
|
||||
|
||||
SaveManager::Instance->LoadArray("hintLocations", RH_MAX, [&](size_t i) {
|
||||
@ -464,7 +464,7 @@ void SaveManager::LoadRandomizerVersion3() {
|
||||
});
|
||||
|
||||
randoContext->GetTrials()->SkipAll();
|
||||
SaveManager::Instance->LoadArray("requiredTrials", randoContext->GetOption(RSK_TRIAL_COUNT).GetSelectedOptionIndex()+1, [&](size_t i) {
|
||||
SaveManager::Instance->LoadArray("requiredTrials", randoContext->GetOption(RSK_TRIAL_COUNT).GetContextOptionIndex() + 1, [&](size_t i) {
|
||||
size_t trialId;
|
||||
SaveManager::Instance->LoadData("", trialId);
|
||||
randoContext->GetTrial(trialId)->SetAsRequired();
|
||||
@ -513,7 +513,7 @@ void SaveManager::SaveRandomizer(SaveContext* saveContext, int sectionID, bool f
|
||||
SaveManager::Instance->SaveData("finalSeed", randoContext->GetSettings()->GetSeed());
|
||||
|
||||
SaveManager::Instance->SaveArray("randoSettings", RSK_MAX, [&](size_t i) {
|
||||
SaveManager::Instance->SaveData("", randoContext->GetOption((RandomizerSettingKey(i))).GetSelectedOptionIndex());
|
||||
SaveManager::Instance->SaveData("", randoContext->GetOption((RandomizerSettingKey(i))).GetContextOptionIndex());
|
||||
});
|
||||
|
||||
SaveManager::Instance->SaveArray("hintLocations", RH_MAX, [&](size_t i) {
|
||||
|
@ -39,6 +39,7 @@
|
||||
#include "Enhancements/debugger/MessageViewer.h"
|
||||
#include "soh/Notification/Notification.h"
|
||||
#include "soh/Enhancements/Holiday/Caladius.h"
|
||||
#include "soh/Enhancements/TimeDisplay/TimeDisplay.h"
|
||||
|
||||
bool isBetaQuestEnabled = false;
|
||||
|
||||
@ -138,6 +139,7 @@ namespace SohGui {
|
||||
std::shared_ptr<SohModalWindow> mModalWindow;
|
||||
std::shared_ptr<Notification::Window> mNotificationWindow;
|
||||
std::shared_ptr<CaladiusWindow> mCaladiusWindow;
|
||||
std::shared_ptr<TimeDisplayWindow> mTimeDisplayWindow;
|
||||
|
||||
void SetupGuiElements() {
|
||||
auto gui = Ship::Context::GetInstance()->GetWindow()->GetGui();
|
||||
@ -226,6 +228,8 @@ namespace SohGui {
|
||||
mCaladiusWindow = std::make_shared<CaladiusWindow>(CVAR_WINDOW("Holiday Cal"), "Holiday Cal");
|
||||
gui->AddGuiWindow(mCaladiusWindow);
|
||||
mCaladiusWindow->Show();
|
||||
mTimeDisplayWindow = std::make_shared<TimeDisplayWindow>(CVAR_WINDOW("TimeDisplayEnabled"), "Additional Timers");
|
||||
gui->AddGuiWindow(mTimeDisplayWindow);
|
||||
}
|
||||
|
||||
void Destroy() {
|
||||
@ -262,6 +266,7 @@ namespace SohGui {
|
||||
mTimeSplitWindow = nullptr;
|
||||
mCaladiusWindow = nullptr;
|
||||
mPlandomizerWindow = nullptr;
|
||||
mTimeDisplayWindow = nullptr;
|
||||
}
|
||||
|
||||
void RegisterPopup(std::string title, std::string message, std::string button1, std::string button2, std::function<void()> button1callback, std::function<void()> button2callback) {
|
||||
|
@ -45,6 +45,7 @@
|
||||
#include "Enhancements/timesplits/TimeSplits.h"
|
||||
#include "Enhancements/Holiday/Holiday.hpp"
|
||||
#include "Enhancements/randomizer/Plandomizer.h"
|
||||
#include "Enhancements/TimeDisplay/TimeDisplay.h"
|
||||
|
||||
// FA icons are kind of wonky, if they worked how I expected them to the "+ 2.0f" wouldn't be needed, but
|
||||
// they don't work how I expect them to so I added that because it looked good when I eyeballed it
|
||||
@ -84,6 +85,7 @@ static const char* imguiScaleOptions[4] = { "Small", "Normal", "Large", "X-Large
|
||||
static const char* chestStyleMatchesContentsOptions[4] = { "Disabled", "Both", "Texture Only", "Size Only" };
|
||||
static const char* skipGetItemAnimationOptions[3] = { "Disabled", "Junk Items", "All Items" };
|
||||
static const char* skipForcedDialogOptions[4] = { "None", "Navi Only", "NPCs Only", "All" };
|
||||
static const char* sleepingWaterfallOptions[3] = { "Always", "Once", "Never" };
|
||||
static const char* bunnyHoodOptions[3] = { "Disabled", "Faster Run & Longer Jump", "Faster Run" };
|
||||
static const char* mirroredWorldModes[9] = {
|
||||
"Disabled", "Always", "Random", "Random (Seeded)", "Dungeons",
|
||||
@ -124,7 +126,7 @@ static const char* imguiScaleOptions[4] = { "Small", "Normal", "Large", "X-Large
|
||||
CVAR_ENHANCEMENT("InjectItemCounts.HeartPiece"),
|
||||
CVAR_ENHANCEMENT("InjectItemCounts.HeartContainer"),
|
||||
};
|
||||
static const char* itemCountMessageOptions[sizeof(itemCountMessageCVars) / sizeof(const char*)] = {
|
||||
static const char* itemCountMessageOptions[ARRAY_COUNT(itemCountMessageCVars)] = {
|
||||
"Gold Skulltula Tokens",
|
||||
"Pieces of Heart",
|
||||
"Heart Containers",
|
||||
@ -584,9 +586,9 @@ void DrawSettingsMenu() {
|
||||
|
||||
ImGui::Text("Position");
|
||||
UIWidgets::EnhancementCombobox(CVAR_SETTING("Notifications.Position"), notificationPosition, 0);
|
||||
UIWidgets::EnhancementSliderFloat("Duration: %.0f seconds", "##NotificationDuration", CVAR_SETTING("Notifications.Duration"), 3.0f, 30.0f, "", 10.0f, false, false, false);
|
||||
UIWidgets::EnhancementSliderFloat("BG Opacity: %.1f %%", "##NotificaitonBgOpacity", CVAR_SETTING("Notifications.BgOpacity"), 0.0f, 1.0f, "", 0.5f, true, false, false);
|
||||
UIWidgets::EnhancementSliderFloat("Size: %.1f", "##NotificaitonSize", CVAR_SETTING("Notifications.Size"), 1.0f, 5.0f, "", 1.8f, false, false, false);
|
||||
UIWidgets::EnhancementSliderFloat("Duration: %.1f seconds", "##NotificationDuration", CVAR_SETTING("Notifications.Duration"), 3.0f, 30.0f, "", 10.0f, false, true, false);
|
||||
UIWidgets::EnhancementSliderFloat("BG Opacity: %.1f %%", "##NotificaitonBgOpacity", CVAR_SETTING("Notifications.BgOpacity"), 0.0f, 1.0f, "", 0.5f, true, true, false);
|
||||
UIWidgets::EnhancementSliderFloat("Size: %.1f", "##NotificaitonSize", CVAR_SETTING("Notifications.Size"), 1.0f, 20.0f, "", 1.8f, false, true, false);
|
||||
|
||||
UIWidgets::Spacer(0);
|
||||
|
||||
@ -607,6 +609,7 @@ extern std::shared_ptr<AudioEditor> mAudioEditorWindow;
|
||||
extern std::shared_ptr<CosmeticsEditorWindow> mCosmeticsEditorWindow;
|
||||
extern std::shared_ptr<GameplayStatsWindow> mGameplayStatsWindow;
|
||||
extern std::shared_ptr<TimeSplitWindow> mTimeSplitWindow;
|
||||
extern std::shared_ptr<TimeDisplayWindow> mTimeDisplayWindow;
|
||||
|
||||
void DrawEnhancementsMenu() {
|
||||
if (ImGui::BeginMenu("Enhancements"))
|
||||
@ -682,8 +685,8 @@ void DrawEnhancementsMenu() {
|
||||
UIWidgets::PaddedEnhancementCheckbox("Skip Owl Interactions", CVAR_ENHANCEMENT("TimeSavers.SkipOwlInteractions"), false, false, false, "", UIWidgets::CheckboxGraphics::Cross, IS_RANDO);
|
||||
UIWidgets::PaddedEnhancementCheckbox("Skip Misc Interactions", CVAR_ENHANCEMENT("TimeSavers.SkipMiscInteractions"), false, false, false, "", UIWidgets::CheckboxGraphics::Cross, IS_RANDO);
|
||||
UIWidgets::PaddedEnhancementCheckbox("Disable Title Card", CVAR_ENHANCEMENT("TimeSavers.DisableTitleCard"), false, false, false, "", UIWidgets::CheckboxGraphics::Cross, IS_RANDO);
|
||||
UIWidgets::PaddedEnhancementCheckbox("Skip Glitch-Aiding Cutscenes", CVAR_ENHANCEMENT("TimeSavers.SkipCutscene.GlitchAiding"), false, false, false, "", UIWidgets::CheckboxGraphics::Cross, 0);
|
||||
UIWidgets::Tooltip("Skip cutscenes that are associated with useful glitches, currently this is only the Fire Temple Darunia CS and Forest Temple Poe Sisters CS");
|
||||
UIWidgets::PaddedEnhancementCheckbox("Exclude Glitch-Aiding Cutscenes", CVAR_ENHANCEMENT("TimeSavers.SkipCutscene.GlitchAiding"), false, false, false, "", UIWidgets::CheckboxGraphics::Cross, 0);
|
||||
UIWidgets::Tooltip("Don't skip cutscenes that are associated with useful glitches, currently this is only the Fire Temple Darunia CS, Forest Temple Poe Sisters CS and the Box Skip One Point in Jabu");
|
||||
UIWidgets::PaddedEnhancementCheckbox("Skip Child Stealth", CVAR_ENHANCEMENT("TimeSavers.SkipChildStealth"), false, false, false, "", UIWidgets::CheckboxGraphics::Cross, false);
|
||||
UIWidgets::Tooltip("The crawlspace into Hyrule Castle goes straight to Zelda, skipping the guards.");
|
||||
UIWidgets::PaddedEnhancementCheckbox("Skip Tower Escape", CVAR_ENHANCEMENT("TimeSavers.SkipTowerEscape"), false, false, false, "", UIWidgets::CheckboxGraphics::Cross, false);
|
||||
@ -715,23 +718,15 @@ void DrawEnhancementsMenu() {
|
||||
UIWidgets::PaddedEnhancementSliderInt("Crawl speed %dx", "##CRAWLSPEED", CVAR_ENHANCEMENT("CrawlSpeed"), 1, 4, "", 1, true, false, true);
|
||||
UIWidgets::PaddedEnhancementCheckbox("Faster Heavy Block Lift", CVAR_ENHANCEMENT("FasterHeavyBlockLift"), false, false);
|
||||
UIWidgets::Tooltip("Speeds up lifting silver rocks and obelisks");
|
||||
UIWidgets::PaddedEnhancementCheckbox("Faster Rupee Accumulator", CVAR_ENHANCEMENT("TimeSavers.FasterRupeeAccumulator"), false, false);
|
||||
UIWidgets::PaddedEnhancementCheckbox("Skip Pickup Messages", CVAR_ENHANCEMENT("FastDrops"), true, false);
|
||||
UIWidgets::Tooltip("Skip pickup messages for new consumable items and bottle swipes");
|
||||
UIWidgets::PaddedEnhancementCheckbox("Fast Ocarina Playback", CVAR_ENHANCEMENT("FastOcarinaPlayback"), true, false);
|
||||
UIWidgets::Tooltip("Skip the part where the Ocarina playback is called when you play a song");
|
||||
bool forceSkipScarecrow = IS_RANDO && OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_SKIP_SCARECROWS_SONG);
|
||||
static const char* forceSkipScarecrowText = "This setting is forcefully enabled because a savefile\nwith \"Skip Scarecrow Song\" is loaded";
|
||||
UIWidgets::PaddedEnhancementCheckbox("Skip Scarecrow Song", CVAR_ENHANCEMENT("InstantScarecrow"), true, false,
|
||||
forceSkipScarecrow, forceSkipScarecrowText, UIWidgets::CheckboxGraphics::Checkmark);
|
||||
UIWidgets::Tooltip("Pierre appears when Ocarina is pulled out. Requires learning scarecrow song.");
|
||||
UIWidgets::PaddedEnhancementCheckbox("Skip Magic Arrow Equip Animation", CVAR_ENHANCEMENT("SkipArrowAnimation"), true, false);
|
||||
UIWidgets::PaddedEnhancementCheckbox("Skip save confirmation", CVAR_ENHANCEMENT("SkipSaveConfirmation"), true, false);
|
||||
UIWidgets::Tooltip("Skip the \"Game saved.\" confirmation screen");
|
||||
UIWidgets::PaddedEnhancementCheckbox("Faster Farore's Wind", CVAR_ENHANCEMENT("FastFarores"), true, false);
|
||||
UIWidgets::Tooltip("Greatly decreases cast time of Farore's Wind magic spell.");
|
||||
UIWidgets::PaddedEnhancementCheckbox("Skip water take breath animation", CVAR_ENHANCEMENT("SkipSwimDeepEndAnim"), true, false);
|
||||
UIWidgets::Tooltip("Skips Link's taking breath animation after coming up from water. This setting does not interfere with getting items from underwater.");
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
UIWidgets::Spacer(0);
|
||||
@ -798,6 +793,30 @@ void DrawEnhancementsMenu() {
|
||||
"- Not within range of Ocarina playing spots");
|
||||
UIWidgets::PaddedEnhancementCheckbox("Pause Warp", CVAR_ENHANCEMENT("PauseWarp"), true, false);
|
||||
UIWidgets::Tooltip("Selection of warp song in pause menu initiates warp. Disables song playback.");
|
||||
UIWidgets::PaddedEnhancementCheckbox("Skip water take breath animation", CVAR_ENHANCEMENT("SkipSwimDeepEndAnim"), true, false);
|
||||
UIWidgets::Tooltip("Skips Link's taking breath animation after coming up from water. This setting does not interfere with getting items from underwater.");
|
||||
bool forceSkipScarecrow = IS_RANDO && OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_SKIP_SCARECROWS_SONG);
|
||||
static const char* forceSkipScarecrowText = "This setting is forcefully enabled because a savefile\nwith \"Skip Scarecrow Song\" is loaded";
|
||||
UIWidgets::PaddedEnhancementCheckbox("Skip Scarecrow Song", CVAR_ENHANCEMENT("InstantScarecrow"), true, false,
|
||||
forceSkipScarecrow, forceSkipScarecrowText, UIWidgets::CheckboxGraphics::Checkmark);
|
||||
UIWidgets::Tooltip("Pierre appears when Ocarina is pulled out. Requires learning scarecrow song.");
|
||||
bool forceSleepingWaterfallEnhancement =
|
||||
IS_RANDO && OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_SLEEPING_WATERFALL) == RO_WATERFALL_OPEN;
|
||||
uint8_t forceSleepingWaterfallValue = OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_SLEEPING_WATERFALL) + 1;
|
||||
static const char* forceSleepingWaterfallText =
|
||||
"This setting is forcefully enabled because a randomizer savefile with \"Sleeping Waterfall: Open\" is loaded.";
|
||||
UIWidgets::PaddedText("Play Zelda's Lullaby to open Sleeping Waterfall", true, false);
|
||||
UIWidgets::EnhancementCombobox(CVAR_ENHANCEMENT("TimeSavers.SleepingWaterfall"),
|
||||
sleepingWaterfallOptions, 0, forceSleepingWaterfallEnhancement,
|
||||
forceSleepingWaterfallText, forceSleepingWaterfallValue);
|
||||
UIWidgets::Tooltip(
|
||||
"Always: Link must always play Zelda's Lullaby to open "
|
||||
"the waterfall entrance to Zora's Domain.\n"
|
||||
"Once: Link only needs to play Zelda's Lullaby once to "
|
||||
"open the waterfall; after that, it stays open permanently.\n"
|
||||
"Never: Link never needs to play Zelda's Lullaby to open the "
|
||||
"waterfall; he only needs to have learned it and have an ocarina."
|
||||
);
|
||||
|
||||
ImGui::EndTable();
|
||||
ImGui::EndMenu();
|
||||
@ -856,6 +875,17 @@ void DrawEnhancementsMenu() {
|
||||
"Toggling while inside the shop will not change prices or restock any SOLD OUTs");
|
||||
UIWidgets::PaddedEnhancementCheckbox("Aiming reticle for the bow/slingshot", CVAR_ENHANCEMENT("BowReticle"), true, false);
|
||||
UIWidgets::Tooltip("Aiming with a bow or slingshot will display a reticle as with the hookshot when the projectile is ready to fire.");
|
||||
if (UIWidgets::PaddedEnhancementCheckbox("Aim boomerang in first-person mode", CVAR_ENHANCEMENT("BoomerangFirstPerson"), true, false)) {
|
||||
if (!CVarGetInteger(CVAR_ENHANCEMENT("BoomerangFirstPerson"), 0)) {
|
||||
CVarSetInteger(CVAR_ENHANCEMENT("BoomerangReticle"), 0);
|
||||
}
|
||||
}
|
||||
UIWidgets::Tooltip(
|
||||
"Change aiming for the boomerang from third person to first person to see past Link's head");
|
||||
if (CVarGetInteger(CVAR_ENHANCEMENT("BoomerangFirstPerson"), 0)) {
|
||||
UIWidgets::PaddedEnhancementCheckbox("Aiming reticle for boomerang", CVAR_ENHANCEMENT("BoomerangReticle"), true, false);
|
||||
UIWidgets::Tooltip("Aiming with the boomerang will display a reticle as with the hookshot");
|
||||
}
|
||||
if (UIWidgets::PaddedEnhancementCheckbox("Allow strength equipment to be toggled", CVAR_ENHANCEMENT("ToggleStrength"), true, false)) {
|
||||
if (!CVarGetInteger(CVAR_ENHANCEMENT("ToggleStrength"), 0)) {
|
||||
CVarSetInteger(CVAR_ENHANCEMENT("StrengthDisabled"), 0);
|
||||
@ -868,7 +898,7 @@ void DrawEnhancementsMenu() {
|
||||
UIWidgets::Spacer(0);
|
||||
|
||||
if (ImGui::BeginMenu("Item Count Messages")) {
|
||||
int numOptions = sizeof(itemCountMessageCVars) / sizeof(const char*);
|
||||
int numOptions = ARRAY_COUNT(itemCountMessageCVars);
|
||||
bool allItemCountsChecked = std::all_of(itemCountMessageCVars, itemCountMessageCVars + numOptions,
|
||||
[](const char* cvar) { return CVarGetInteger(cvar, 0); });
|
||||
bool someItemCountsChecked = std::any_of(itemCountMessageCVars, itemCountMessageCVars + numOptions,
|
||||
@ -1270,6 +1300,9 @@ void DrawEnhancementsMenu() {
|
||||
UIWidgets::PaddedEnhancementCheckbox("Targetable Hookshot Reticle", CVAR_ENHANCEMENT("HookshotableReticle"), true, false);
|
||||
UIWidgets::Tooltip("Use a different color when aiming at hookshotable collision");
|
||||
|
||||
UIWidgets::PaddedEnhancementCheckbox("Faster Rupee Accumulator", CVAR_ENHANCEMENT("TimeSavers.FasterRupeeAccumulator"), true, false);
|
||||
UIWidgets::Tooltip("Causes your wallet to fill and empty faster when you gain or lose money.");
|
||||
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
|
||||
@ -1693,6 +1726,36 @@ void DrawEnhancementsMenu() {
|
||||
mTimeSplitWindow->ToggleVisibility();
|
||||
}
|
||||
}
|
||||
|
||||
if (mTimeDisplayWindow) {
|
||||
if (ImGui::Button(GetWindowButtonText("Additional Timers", CVarGetInteger(CVAR_WINDOW("TimeDisplayEnabled"), 0)).c_str(), ImVec2(-1.0f, 0.0f))) {
|
||||
mTimeDisplayWindow->ToggleVisibility();
|
||||
}
|
||||
}
|
||||
if (mTimeDisplayWindow->IsVisible()) {
|
||||
ImGui::SeparatorText("Timer Display Options");
|
||||
|
||||
if (!gPlayState) {
|
||||
ImGui::Text("Additional Timer options\n"
|
||||
"available when a file is\n"
|
||||
"loaded...");
|
||||
} else {
|
||||
if (UIWidgets::PaddedEnhancementSliderFloat("Font Scale: %.2fx", "##FontScale", CVAR_ENHANCEMENT("TimeDisplay.FontScale"),
|
||||
1.0f, 5.0f, "", 1.0f, false, true, false, true)) {
|
||||
TimeDisplayInitSettings();
|
||||
}
|
||||
if (UIWidgets::PaddedEnhancementCheckbox("Hide Background", CVAR_ENHANCEMENT("TimeDisplay.ShowWindowBG"),
|
||||
false, false)) {
|
||||
TimeDisplayInitSettings();
|
||||
}
|
||||
ImGui::Separator();
|
||||
for (auto& timer : timeDisplayList) {
|
||||
if (UIWidgets::PaddedEnhancementCheckbox(timer.timeLabel.c_str(), timer.timeEnable, false, false)) {
|
||||
TimeDisplayUpdateDisplayOptions();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
ImGui::PopStyleVar(3);
|
||||
ImGui::PopStyleColor(1);
|
||||
|
||||
|
@ -593,7 +593,7 @@ namespace UIWidgets {
|
||||
#if defined(__SWITCH__) || defined(__WIIU__)
|
||||
srand(time(NULL));
|
||||
#endif
|
||||
ImVec4 color = GetRandomValue(255);
|
||||
ImVec4 color = GetRandomValue();
|
||||
colors->x = color.x;
|
||||
colors->y = color.y;
|
||||
colors->z = color.z;
|
||||
|
@ -2,6 +2,7 @@
|
||||
#include "vt.h"
|
||||
|
||||
#include "overlays/actors/ovl_Arms_Hook/z_arms_hook.h"
|
||||
#include "overlays/actors/ovl_En_Arrow/z_en_arrow.h"
|
||||
#include "overlays/actors/ovl_En_Part/z_en_part.h"
|
||||
#include "objects/gameplay_keep/gameplay_keep.h"
|
||||
#include "objects/gameplay_dangeon_keep/gameplay_dangeon_keep.h"
|
||||
@ -1081,14 +1082,11 @@ void TitleCard_InitPlaceName(PlayState* play, TitleCardContext* titleCtx, void*
|
||||
}
|
||||
|
||||
void TitleCard_Update(PlayState* play, TitleCardContext* titleCtx) {
|
||||
const Color_RGB8 TitleCard_Colors_ori = {255,255,255};
|
||||
Color_RGB8 TitleCard_Colors = {255,255,255};
|
||||
if (titleCtx->isBossCard && CVarGetInteger(CVAR_COSMETIC("HUD.TitleCard.Boss.Changed"), 1) == 2) {
|
||||
TitleCard_Colors = CVarGetColor24(CVAR_COSMETIC("HUD.TitleCard.Boss.Value"), TitleCard_Colors_ori);
|
||||
} else if (!titleCtx->isBossCard && CVarGetInteger(CVAR_COSMETIC("HUD.TitleCard.Map.Changed"), 1) == 2) {
|
||||
TitleCard_Colors = CVarGetColor24(CVAR_COSMETIC("HUD.TitleCard.Map.Value"), TitleCard_Colors_ori);
|
||||
} else {
|
||||
TitleCard_Colors = TitleCard_Colors_ori;
|
||||
Color_RGB8 TitleCard_Colors = { 255, 255, 255 };
|
||||
if (titleCtx->isBossCard && CVarGetInteger(CVAR_COSMETIC("HUD.TitleCard.Boss.Changed"), 0) == 1) {
|
||||
TitleCard_Colors = CVarGetColor24(CVAR_COSMETIC("HUD.TitleCard.Boss.Value"), TitleCard_Colors);
|
||||
} else if (!titleCtx->isBossCard && CVarGetInteger(CVAR_COSMETIC("HUD.TitleCard.Map.Changed"), 0) == 1) {
|
||||
TitleCard_Colors = CVarGetColor24(CVAR_COSMETIC("HUD.TitleCard.Map.Value"), TitleCard_Colors);
|
||||
}
|
||||
|
||||
if (DECR(titleCtx->delayTimer) == 0) {
|
||||
@ -2350,8 +2348,14 @@ void Actor_DrawFaroresWindPointer(PlayState* play) {
|
||||
} else if (D_8015BC18 > 0.0f) {
|
||||
static Vec3f effectVel = { 0.0f, -0.05f, 0.0f };
|
||||
static Vec3f effectAccel = { 0.0f, -0.025f, 0.0f };
|
||||
static Color_RGBA8 effectPrimCol = { 255, 255, 255, 0 };
|
||||
static Color_RGBA8 effectEnvCol = { 100, 200, 0, 0 };
|
||||
Color_RGBA8 effectPrimCol = { 255, 255, 255, 0 };
|
||||
Color_RGBA8 effectEnvCol = { 100, 200, 0, 0 };
|
||||
if (CVarGetInteger(CVAR_COSMETIC("Magic.FaroresSecondary.Changed"), 0)) {
|
||||
effectEnvCol = CVarGetColor(CVAR_COSMETIC("Magic.FaroresSecondary.Value"), effectEnvCol);
|
||||
}
|
||||
if (CVarGetInteger(CVAR_COSMETIC("Magic.FaroresPrimary.Changed"), 0)) {
|
||||
effectPrimCol = CVarGetColor(CVAR_COSMETIC("Magic.FaroresPrimary.Value"), effectPrimCol);
|
||||
}
|
||||
Vec3f* curPos = &gSaveContext.respawn[RESPAWN_MODE_TOP].pos;
|
||||
Vec3f* nextPos = &gSaveContext.respawn[RESPAWN_MODE_DOWN].pos;
|
||||
f32 prevNum = D_8015BC18;
|
||||
@ -2446,8 +2450,16 @@ void Actor_DrawFaroresWindPointer(PlayState* play) {
|
||||
Matrix_Push();
|
||||
|
||||
gDPPipeSync(POLY_XLU_DISP++);
|
||||
gDPSetPrimColor(POLY_XLU_DISP++, 128, 128, 255, 255, 200, alpha);
|
||||
gDPSetEnvColor(POLY_XLU_DISP++, 100, 200, 0, 255);
|
||||
Color_RGB8 Spell_env = { 100, 200, 0 };
|
||||
Color_RGB8 Spell_col = { 255, 255, 200 };
|
||||
if (CVarGetInteger(CVAR_COSMETIC("Magic.FaroresSecondary.Changed"), 0)) {
|
||||
Spell_env = CVarGetColor24(CVAR_COSMETIC("Magic.FaroresSecondary.Value"), Spell_env);
|
||||
}
|
||||
if (CVarGetInteger(CVAR_COSMETIC("Magic.FaroresPrimary.Changed"), 0)) {
|
||||
Spell_col = CVarGetColor24(CVAR_COSMETIC("Magic.FaroresPrimary.Value"), Spell_col);
|
||||
}
|
||||
gDPSetPrimColor(POLY_XLU_DISP++, 128, 128, Spell_col.r, Spell_col.g, Spell_col.b, alpha);
|
||||
gDPSetEnvColor(POLY_XLU_DISP++, Spell_env.r, Spell_env.g, Spell_env.b, 255);
|
||||
|
||||
Matrix_RotateZ(((play->gameplayFrames * 1500) & 0xFFFF) * M_PI / 32768.0f, MTXMODE_APPLY);
|
||||
gSPMatrix(POLY_XLU_DISP++, MATRIX_NEWMTX(play->state.gfxCtx),
|
||||
@ -3854,8 +3866,14 @@ Actor* Actor_GetProjectileActor(PlayState* play, Actor* refActor, f32 radius) {
|
||||
// it can also be an arrow.
|
||||
// Luckily, the field at the same offset in the arrow actor is the x component of a vector
|
||||
// which will rarely ever be 0. So it's very unlikely for this bug to cause an issue.
|
||||
//
|
||||
// SoH [Port] We're making a change here, it doesn't technically fix the bug but makes it behave
|
||||
// more like hardware. Because of pointer size differences in SoH this was accessing a different
|
||||
// place in memory and causing issues with Dark link behavior, and probably other places too
|
||||
if ((Math_Vec3f_DistXYZ(&refActor->world.pos, &actor->world.pos) > radius) ||
|
||||
(((ArmsHook*)actor)->timer == 0)) {
|
||||
(actor->id == ACTOR_ARMS_HOOK && ((ArmsHook*)actor)->timer == 0) ||
|
||||
(actor->id == ACTOR_EN_ARROW && ((EnArrow*)actor)->unk_210.x == 0)
|
||||
) {
|
||||
actor = actor->next;
|
||||
} else {
|
||||
deltaX = Math_SinS(actor->world.rot.y) * (actor->speedXZ * 10.0f);
|
||||
|
@ -399,6 +399,9 @@ DrawItemTableEntry sDrawItemTable[] = {
|
||||
* Calls the corresponding draw function for the given draw ID
|
||||
*/
|
||||
void GetItem_Draw(PlayState* play, s16 drawId) {
|
||||
if (drawId < 0 || drawId >= GID_MAXIMUM) {
|
||||
return;
|
||||
}
|
||||
sDrawItemTable[drawId].drawFunc(play, drawId);
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,7 @@
|
||||
#include "global.h"
|
||||
#include "soh/OTRGlobals.h"
|
||||
#include "soh/Enhancements/game-interactor/GameInteractor.h"
|
||||
#include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h"
|
||||
|
||||
void GameOver_Init(PlayState* play) {
|
||||
play->gameOverCtx.state = GAMEOVER_INACTIVE;
|
||||
@ -34,7 +36,7 @@ void GameOver_Update(PlayState* play) {
|
||||
gSaveContext.eventInf[1] &= ~1;
|
||||
|
||||
// search inventory for spoiling items and revert if necessary
|
||||
if (!(IS_RANDO && Randomizer_GetSettingValue(RSK_SHUFFLE_ADULT_TRADE))) {
|
||||
if (GameInteractor_Should(VB_REVERT_SPOILING_ITEMS, true)) {
|
||||
for (i = 0; i < ARRAY_COUNT(gSpoilingItems); i++) {
|
||||
if (INV_CONTENT(ITEM_POCKET_EGG) == gSpoilingItems[i]) {
|
||||
INV_CONTENT(gSpoilingItemReverts[i]) = gSpoilingItemReverts[i];
|
||||
|
@ -7,6 +7,7 @@
|
||||
#include "textures/parameter_static/parameter_static.h"
|
||||
#include "textures/message_static/message_static.h"
|
||||
#include "textures/message_texture_static/message_texture_static.h"
|
||||
#include "soh/Enhancements/cosmetics/CosmeticsEditor.h"
|
||||
#include "soh/Enhancements/cosmetics/cosmeticsTypes.h"
|
||||
#include "soh/Enhancements/game-interactor/GameInteractor.h"
|
||||
#include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h"
|
||||
@ -367,6 +368,80 @@ void Message_FindCreditsMessage(PlayState* play, u16 textId) {
|
||||
}
|
||||
}
|
||||
|
||||
#pragma region [SoH] Cosmetics
|
||||
|
||||
#define MESSAGE_COSMETICS_HANDLE_COLOR(id) \
|
||||
if (CVarGetInteger(CVAR_COSMETIC("Message." id ".Changed"), 0)) { \
|
||||
Color_RGBA8 color = CVarGetColor(CVAR_COSMETIC("Message." id ".Value"), CosmeticsEditor_GetDefaultValue("Message." id)); \
|
||||
msgCtx->textColorR = color.r; \
|
||||
msgCtx->textColorG = color.g; \
|
||||
msgCtx->textColorB = color.b; \
|
||||
}
|
||||
|
||||
void Cosmetics_MaybeSetTextColor(MessageContext* msgCtx, u16 colorParameter) {
|
||||
switch (colorParameter) {
|
||||
case MSGCOL_RED:
|
||||
if (msgCtx->textBoxType == TEXTBOX_TYPE_WOODEN) {
|
||||
MESSAGE_COSMETICS_HANDLE_COLOR("Red.Wooden")
|
||||
} else {
|
||||
MESSAGE_COSMETICS_HANDLE_COLOR("Red.Normal")
|
||||
}
|
||||
break;
|
||||
case MSGCOL_ADJUSTABLE:
|
||||
if (msgCtx->textBoxType == TEXTBOX_TYPE_WOODEN) {
|
||||
MESSAGE_COSMETICS_HANDLE_COLOR("Adjustable.Wooden")
|
||||
} else {
|
||||
MESSAGE_COSMETICS_HANDLE_COLOR("Adjustable.Normal")
|
||||
}
|
||||
break;
|
||||
case MSGCOL_BLUE:
|
||||
if (msgCtx->textBoxType == TEXTBOX_TYPE_WOODEN) {
|
||||
MESSAGE_COSMETICS_HANDLE_COLOR("Blue.Wooden")
|
||||
} else {
|
||||
MESSAGE_COSMETICS_HANDLE_COLOR("Blue.Normal")
|
||||
}
|
||||
break;
|
||||
case MSGCOL_LIGHTBLUE:
|
||||
if (msgCtx->textBoxType == TEXTBOX_TYPE_WOODEN) {
|
||||
MESSAGE_COSMETICS_HANDLE_COLOR("LightBlue.Wooden")
|
||||
} else if (msgCtx->textBoxType == TEXTBOX_TYPE_NONE_NO_SHADOW) {
|
||||
MESSAGE_COSMETICS_HANDLE_COLOR("LightBlue.NoneNoShadow")
|
||||
} else {
|
||||
MESSAGE_COSMETICS_HANDLE_COLOR("LightBlue.Normal")
|
||||
}
|
||||
break;
|
||||
case MSGCOL_PURPLE:
|
||||
if (msgCtx->textBoxType == TEXTBOX_TYPE_WOODEN) {
|
||||
MESSAGE_COSMETICS_HANDLE_COLOR("Purple.Wooden")
|
||||
} else {
|
||||
MESSAGE_COSMETICS_HANDLE_COLOR("Purple.Normal")
|
||||
}
|
||||
break;
|
||||
case MSGCOL_YELLOW:
|
||||
if (msgCtx->textBoxType == TEXTBOX_TYPE_WOODEN) {
|
||||
MESSAGE_COSMETICS_HANDLE_COLOR("Yellow.Wooden")
|
||||
} else {
|
||||
MESSAGE_COSMETICS_HANDLE_COLOR("Yellow.Normal")
|
||||
}
|
||||
break;
|
||||
case MSGCOL_BLACK:
|
||||
MESSAGE_COSMETICS_HANDLE_COLOR("Black")
|
||||
break;
|
||||
case MSGCOL_DEFAULT:
|
||||
default:
|
||||
if (msgCtx->textBoxType == TEXTBOX_TYPE_NONE_NO_SHADOW) {
|
||||
MESSAGE_COSMETICS_HANDLE_COLOR("Default.NoneNoShadow")
|
||||
} else {
|
||||
MESSAGE_COSMETICS_HANDLE_COLOR("Default.Normal")
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#undef MESSAGE_COSMETICS_HANDLE_COLOR
|
||||
|
||||
#pragma endregion
|
||||
|
||||
void Message_SetTextColor(MessageContext* msgCtx, u16 colorParameter) {
|
||||
switch (colorParameter) {
|
||||
case MSGCOL_RED:
|
||||
@ -451,6 +526,7 @@ void Message_SetTextColor(MessageContext* msgCtx, u16 colorParameter) {
|
||||
}
|
||||
break;
|
||||
}
|
||||
Cosmetics_MaybeSetTextColor(msgCtx, colorParameter);
|
||||
}
|
||||
|
||||
void Message_DrawTextboxIcon(PlayState* play, Gfx** p, s16 x, s16 y) {
|
||||
@ -853,6 +929,8 @@ void Message_DrawText(PlayState* play, Gfx** gfxP) {
|
||||
msgCtx->textColorR = msgCtx->textColorG = msgCtx->textColorB = 255;
|
||||
}
|
||||
|
||||
Cosmetics_MaybeSetTextColor(msgCtx, MSGCOL_DEFAULT);
|
||||
|
||||
msgCtx->unk_E3D0 = 0;
|
||||
charTexIdx = 0;
|
||||
|
||||
|
@ -1891,6 +1891,12 @@ u8 Return_Item(u8 itemID, ModIndex modId, ItemID returnItem) {
|
||||
* @return u8
|
||||
*/
|
||||
u8 Item_Give(PlayState* play, u8 item) {
|
||||
// TODO: Add ShouldItemGive
|
||||
// if (!GameInteractor_ShouldItemGive(item) || item == ITEM_SHIP) {
|
||||
if (item == ITEM_SHIP) {
|
||||
return ITEM_NONE;
|
||||
}
|
||||
|
||||
//prevents getting sticks without the bag in case something got missed
|
||||
if (
|
||||
IS_RANDO &&
|
||||
@ -2486,6 +2492,11 @@ u8 Item_CheckObtainability(u8 item) {
|
||||
s16 slot = SLOT(item);
|
||||
s32 temp;
|
||||
|
||||
// SOH [Enhancements] Added to enable custom item gives
|
||||
if (item == ITEM_SHIP) {
|
||||
return ITEM_NONE;
|
||||
}
|
||||
|
||||
if (item >= ITEM_STICKS_5) {
|
||||
slot = SLOT(sExtraItemBases[item - ITEM_STICKS_5]);
|
||||
}
|
||||
@ -3388,6 +3399,11 @@ void Interface_UpdateMagicBar(PlayState* play) {
|
||||
|
||||
default:
|
||||
gSaveContext.magicState = MAGIC_STATE_IDLE;
|
||||
if (CVarGetInteger(CVAR_COSMETIC("Consumable.MagicBorder.Changed"), 0)) {
|
||||
sMagicBorder = CVarGetColor24(CVAR_COSMETIC("Consumable.MagicBorder.Value"), sMagicBorder_ori);
|
||||
} else {
|
||||
sMagicBorder = sMagicBorder_ori;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -3617,8 +3633,8 @@ void Interface_DrawEnemyHealthBar(TargetContext* targetCtx, PlayState* play) {
|
||||
s32 healthbar_offsetY = CVarGetInteger(CVAR_COSMETIC("HUD.EnemyHealthBar.PosY"), 0);
|
||||
s8 anchorType = CVarGetInteger(CVAR_COSMETIC("HUD.EnemyHealthBar.PosType"), ENEMYHEALTH_ANCHOR_ACTOR);
|
||||
|
||||
if (CVarGetInteger(CVAR_COSMETIC("HUD.EnemyHealthBar..Changed"), 0)) {
|
||||
healthbar_red = CVarGetColor(CVAR_COSMETIC("HUD.EnemyHealthBar..Value"), healthbar_red);
|
||||
if (CVarGetInteger(CVAR_COSMETIC("HUD.EnemyHealthBar.Changed"), 0)) {
|
||||
healthbar_red = CVarGetColor(CVAR_COSMETIC("HUD.EnemyHealthBar.Value"), healthbar_red);
|
||||
}
|
||||
if (CVarGetInteger(CVAR_COSMETIC("HUD.EnemyHealthBorder.Changed"), 0)) {
|
||||
healthbar_border = CVarGetColor(CVAR_COSMETIC("HUD.EnemyHealthBorder.Value"), healthbar_border);
|
||||
@ -5713,17 +5729,19 @@ void Interface_Draw(PlayState* play) {
|
||||
}
|
||||
|
||||
// Revert any spoiling trade quest items
|
||||
for (svar1 = 0; svar1 < ARRAY_COUNT(gSpoilingItems); svar1++) {
|
||||
if (INV_CONTENT(ITEM_TRADE_ADULT) == gSpoilingItems[svar1]) {
|
||||
gSaveContext.eventInf[0] &= 0x7F80;
|
||||
osSyncPrintf("EVENT_INF=%x\n", gSaveContext.eventInf[0]);
|
||||
play->nextEntranceIndex = spoilingItemEntrances[svar1];
|
||||
INV_CONTENT(gSpoilingItemReverts[svar1]) = gSpoilingItemReverts[svar1];
|
||||
if (GameInteractor_Should(VB_REVERT_SPOILING_ITEMS, true)) {
|
||||
for (svar1 = 0; svar1 < ARRAY_COUNT(gSpoilingItems); svar1++) {
|
||||
if (INV_CONTENT(ITEM_TRADE_ADULT) == gSpoilingItems[svar1]) {
|
||||
gSaveContext.eventInf[0] &= 0x7F80;
|
||||
osSyncPrintf("EVENT_INF=%x\n", gSaveContext.eventInf[0]);
|
||||
play->nextEntranceIndex = spoilingItemEntrances[svar1];
|
||||
INV_CONTENT(gSpoilingItemReverts[svar1]) = gSpoilingItemReverts[svar1];
|
||||
|
||||
for (svar2 = 1; svar2 < ARRAY_COUNT(gSaveContext.equips.buttonItems); svar2++) {
|
||||
if (gSaveContext.equips.buttonItems[svar2] == gSpoilingItems[svar1]) {
|
||||
gSaveContext.equips.buttonItems[svar2] = gSpoilingItemReverts[svar1];
|
||||
Interface_LoadItemIcon1(play, svar2);
|
||||
for (svar2 = 1; svar2 < ARRAY_COUNT(gSaveContext.equips.buttonItems); svar2++) {
|
||||
if (gSaveContext.equips.buttonItems[svar2] == gSpoilingItems[svar1]) {
|
||||
gSaveContext.equips.buttonItems[svar2] = gSpoilingItemReverts[svar1];
|
||||
Interface_LoadItemIcon1(play, svar2);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -6094,7 +6112,7 @@ void Interface_Draw(PlayState* play) {
|
||||
svar5 = CVarGetInteger(CVAR_COSMETIC("HUD.Timers.PosX"), 0)+204+X_Margins_Timer;
|
||||
} else if (CVarGetInteger(CVAR_COSMETIC("HUD.Timers.PosType"), 0) == 4) {//Hidden
|
||||
svar5 = -9999;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
OVERLAY_DISP =
|
||||
|
@ -888,6 +888,16 @@ s32 Player_HoldsSlingshot(Player* this) {
|
||||
return this->heldItemAction == PLAYER_IA_SLINGSHOT;
|
||||
}
|
||||
|
||||
// #region SOH [Enhancement]
|
||||
s32 Player_HoldsBoomerang(Player* this) {
|
||||
return this->heldItemAction == PLAYER_IA_BOOMERANG;
|
||||
}
|
||||
|
||||
s32 Player_AimsBoomerang(Player* this) {
|
||||
return Player_HoldsBoomerang(this) && (this->unk_834 != 0);
|
||||
}
|
||||
// #endregion
|
||||
|
||||
s32 func_8008F128(Player* this) {
|
||||
return Player_HoldsHookshot(this) && (this->heldActor == NULL);
|
||||
}
|
||||
@ -1798,6 +1808,9 @@ Vec3f sLeftRightFootLimbModelFootPos[] = {
|
||||
void Player_PostLimbDrawGameplay(PlayState* play, s32 limbIndex, Gfx** dList, Vec3s* rot, void* thisx) {
|
||||
Player* this = (Player*)thisx;
|
||||
|
||||
const Vec3s BoomerangViewAdult = { -31200, -9200, 17000 };
|
||||
const Vec3s BoomerangViewChild = { -31200, -8700, 17000 };
|
||||
|
||||
if (*dList != NULL) {
|
||||
Matrix_MultVec3f(&sZeroVec, D_80160000);
|
||||
}
|
||||
@ -2005,6 +2018,8 @@ void Player_PostLimbDrawGameplay(PlayState* play, s32 limbIndex, Gfx** dList, Ve
|
||||
play, this, ((this->heldItemAction == PLAYER_IA_HOOKSHOT) ? 38600.0f : 77600.0f) * CVarGetFloat(CVAR_CHEAT("HookshotReachMultiplier"), 1.0f));
|
||||
}
|
||||
}
|
||||
|
||||
// #region SOH [Enhancement]
|
||||
} else if (CVarGetInteger(CVAR_ENHANCEMENT("BowReticle"), 0) && (
|
||||
(this->heldItemAction == PLAYER_IA_BOW_FIRE) ||
|
||||
(this->heldItemAction == PLAYER_IA_BOW_ICE) ||
|
||||
@ -2023,6 +2038,21 @@ void Player_PostLimbDrawGameplay(PlayState* play, s32 limbIndex, Gfx** dList, Ve
|
||||
Player_DrawHookshotReticle(play, this, RETICLE_MAX);
|
||||
}
|
||||
}
|
||||
} else if (CVarGetInteger(CVAR_ENHANCEMENT("BoomerangReticle"), 0) && (this->heldItemAction == PLAYER_IA_BOOMERANG)) {
|
||||
if (Player_HoldsBoomerang(this)) {
|
||||
if (LINK_IS_ADULT) {
|
||||
Matrix_RotateZYX(BoomerangViewAdult.x, BoomerangViewAdult.y, BoomerangViewAdult.z,
|
||||
MTXMODE_APPLY);
|
||||
} else {
|
||||
Matrix_RotateZYX(BoomerangViewChild.x, BoomerangViewChild.y, BoomerangViewChild.z, MTXMODE_APPLY);
|
||||
}
|
||||
|
||||
if (Player_AimsBoomerang(this)) {
|
||||
Matrix_Translate(500.0f, 300.0f, 0.0f, MTXMODE_APPLY);
|
||||
Player_DrawHookshotReticle(play, this, RETICLE_MAX);
|
||||
}
|
||||
}
|
||||
// #endregion
|
||||
}
|
||||
|
||||
if ((this->unk_862 != 0) || ((func_8002DD6C(this) == 0) && (heldActor != NULL))) {
|
||||
|
@ -931,8 +931,21 @@ void EnMag_DrawInnerVanilla(Actor* thisx, PlayState* play, Gfx** gfxp) {
|
||||
gDPSetAlphaCompare(gfx++, G_AC_NONE);
|
||||
gDPSetCombineMode(gfx++, G_CC_MODULATEIA_PRIM, G_CC_MODULATEIA_PRIM);
|
||||
|
||||
gDPSetPrimColor(gfx++, 0, 0, (s16)this->copyrightAlpha, (s16)this->copyrightAlpha, (s16)this->copyrightAlpha,
|
||||
(s16)this->copyrightAlpha);
|
||||
if (CVarGetInteger(CVAR_COSMETIC("Title.Copyright.Changed"), 0)) {
|
||||
Color_RGBA8 copyrightColor = CVarGetColor(CVAR_COSMETIC("Title.Copyright.Value"), (Color_RGBA8){ 255, 255, 255, 255 });
|
||||
gDPSetPrimColor(
|
||||
gfx++,
|
||||
0,
|
||||
0,
|
||||
(s16)(((f32)copyrightColor.r / 255.0f) * this->copyrightAlpha),
|
||||
(s16)(((f32)copyrightColor.g / 255.0f) * this->copyrightAlpha),
|
||||
(s16)(((f32)copyrightColor.b / 255.0f) * this->copyrightAlpha),
|
||||
(s16)(((f32)copyrightColor.a / 255.0f) * this->copyrightAlpha)
|
||||
);
|
||||
} else {
|
||||
gDPSetPrimColor(gfx++, 0, 0, (s16)this->copyrightAlpha, (s16)this->copyrightAlpha, (s16)this->copyrightAlpha,
|
||||
(s16)this->copyrightAlpha);
|
||||
}
|
||||
|
||||
if ((s16)this->copyrightAlpha != 0) {
|
||||
gDPLoadTextureBlock(gfx++, copy_tex, G_IM_FMT_IA, G_IM_SIZ_8b, copy_width, 16, 0, G_TX_NOMIRROR | G_TX_CLAMP,
|
||||
|
@ -98,9 +98,8 @@ void func_80AACA40(EnMk* this, PlayState* play) {
|
||||
void func_80AACA94(EnMk* this, PlayState* play) {
|
||||
if (Actor_HasParent(&this->actor, play) != 0 || !GameInteractor_Should(VB_TRADE_FROG, true, this)) {
|
||||
this->actor.parent = NULL;
|
||||
this->actionFunc = func_80AACA40;
|
||||
Flags_SetRandomizerInf(RAND_INF_ADULT_TRADES_LH_TRADE_FROG);
|
||||
if (GameInteractor_Should(VB_TRADE_TIMER_EYEDROPS, true)) {
|
||||
if (GameInteractor_Should(VB_TRADE_TIMER_EYEDROPS, true, this)) {
|
||||
this->actionFunc = func_80AACA40;
|
||||
func_80088AA0(240);
|
||||
gSaveContext.eventInf[1] &= ~1;
|
||||
}
|
||||
|
@ -8,6 +8,7 @@
|
||||
#include "objects/object_ru1/object_ru1.h"
|
||||
#include "vt.h"
|
||||
#include "soh/ResourceManagerHelpers.h"
|
||||
#include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h"
|
||||
|
||||
#define FLAGS (ACTOR_FLAG_TARGETABLE | ACTOR_FLAG_UPDATE_WHILE_CULLED | ACTOR_FLAG_CAN_PRESS_SWITCH)
|
||||
|
||||
@ -763,14 +764,6 @@ void func_80AEC2C0(EnRu1* this, PlayState* play) {
|
||||
func_80AEC070(this, play, something);
|
||||
}
|
||||
|
||||
// Convenience function used so that Ruto always spawns in Jabu in rando, even after she's been kidnapped
|
||||
// Equivalent to !Flags_GetInfTable(INFTABLE_145) in vanilla
|
||||
bool shouldSpawnRuto() {
|
||||
// Flags_GetInfTable(INFTABLE_146) check is to prevent Ruto from spawning during the short period of time when
|
||||
// she's on the Zora's Sapphire pedestal but hasn't been kidnapped yet (would result in multiple Rutos otherwise)
|
||||
return !Flags_GetInfTable(INFTABLE_145) || (IS_RANDO && (Flags_GetInfTable(INFTABLE_146)));
|
||||
}
|
||||
|
||||
void func_80AEC320(EnRu1* this, PlayState* play) {
|
||||
s8 actorRoom;
|
||||
|
||||
@ -778,7 +771,10 @@ void func_80AEC320(EnRu1* this, PlayState* play) {
|
||||
func_80AEB264(this, &gRutoChildWait2Anim, 0, 0, 0);
|
||||
this->action = 7;
|
||||
EnRu1_SetMouthIndex(this, 1);
|
||||
} else if ((Flags_GetInfTable(INFTABLE_147)) && !Flags_GetInfTable(INFTABLE_140) && shouldSpawnRuto()) {
|
||||
} else if (
|
||||
Flags_GetInfTable(INFTABLE_147) && !Flags_GetInfTable(INFTABLE_140) &&
|
||||
GameInteractor_Should(VB_RUTO_BE_CONSIDERED_NOT_KIDNAPPED, !Flags_GetInfTable(INFTABLE_145), this)
|
||||
) {
|
||||
if (!func_80AEB020(this, play)) {
|
||||
func_80AEB264(this, &gRutoChildWait2Anim, 0, 0, 0);
|
||||
actorRoom = this->actor.room;
|
||||
@ -867,9 +863,9 @@ void func_80AEC780(EnRu1* this, PlayState* play) {
|
||||
s32 pad;
|
||||
Player* player = GET_PLAYER(play);
|
||||
|
||||
if ((func_80AEC5FC(this, play)) && (!Play_InCsMode(play)) &&
|
||||
if (GameInteractor_Should(VB_PLAY_CHILD_RUTO_INTRO, (func_80AEC5FC(this, play)) && (!Play_InCsMode(play)) &&
|
||||
(!(player->stateFlags1 & (PLAYER_STATE1_HANGING_OFF_LEDGE | PLAYER_STATE1_CLIMBING_LEDGE | PLAYER_STATE1_CLIMBING_LADDER))) &&
|
||||
(player->actor.bgCheckFlags & 1)) {
|
||||
(player->actor.bgCheckFlags & 1), this)) {
|
||||
|
||||
play->csCtx.segment = &D_80AF0880;
|
||||
gSaveContext.cutsceneTrigger = 1;
|
||||
@ -1183,8 +1179,11 @@ void func_80AED414(EnRu1* this, PlayState* play) {
|
||||
void func_80AED44C(EnRu1* this, PlayState* play) {
|
||||
s8 actorRoom;
|
||||
|
||||
if ((Flags_GetInfTable(INFTABLE_RUTO_IN_JJ_MEET_RUTO)) && shouldSpawnRuto() && !Flags_GetInfTable(INFTABLE_140) &&
|
||||
!Flags_GetInfTable(INFTABLE_147)) {
|
||||
if (
|
||||
Flags_GetInfTable(INFTABLE_RUTO_IN_JJ_MEET_RUTO) &&
|
||||
GameInteractor_Should(VB_RUTO_BE_CONSIDERED_NOT_KIDNAPPED, !Flags_GetInfTable(INFTABLE_145), this) &&
|
||||
!Flags_GetInfTable(INFTABLE_140) && !Flags_GetInfTable(INFTABLE_147)
|
||||
) {
|
||||
if (!func_80AEB020(this, play)) {
|
||||
func_80AEB264(this, &gRutoChildWait2Anim, 0, 0, 0);
|
||||
actorRoom = this->actor.room;
|
||||
@ -1550,8 +1549,8 @@ s32 func_80AEE394(EnRu1* this, PlayState* play) {
|
||||
colCtx = &play->colCtx;
|
||||
floorBgId = this->actor.floorBgId; // necessary match, can't move this out of this block unfortunately
|
||||
dynaPolyActor = DynaPoly_GetActor(colCtx, floorBgId);
|
||||
if (dynaPolyActor != NULL && dynaPolyActor->actor.id == ACTOR_BG_BDAN_OBJECTS &&
|
||||
dynaPolyActor->actor.params == 0 && !Player_InCsMode(play) && play->msgCtx.msgLength == 0) {
|
||||
if (GameInteractor_Should(VB_RUTO_RUN_TO_SAPPHIRE, dynaPolyActor != NULL && dynaPolyActor->actor.id == ACTOR_BG_BDAN_OBJECTS &&
|
||||
dynaPolyActor->actor.params == 0 && !Player_InCsMode(play) && play->msgCtx.msgLength == 0, this, dynaPolyActor)) {
|
||||
func_80AEE02C(this);
|
||||
play->csCtx.segment = &D_80AF10A4;
|
||||
gSaveContext.cutsceneTrigger = 1;
|
||||
@ -1611,7 +1610,7 @@ s32 func_80AEE6D0(EnRu1* this, PlayState* play) {
|
||||
s32 pad;
|
||||
s8 curRoomNum = play->roomCtx.curRoom.num;
|
||||
|
||||
if (!Flags_GetInfTable(INFTABLE_RUTO_IN_JJ_WANTS_TO_BE_TOSSED_TO_SAPPHIRE) && (func_80AEB124(play) != 0)) {
|
||||
if (GameInteractor_Should(VB_RUTO_WANT_TO_BE_TOSSED_TO_SAPPHIRE, !Flags_GetInfTable(INFTABLE_RUTO_IN_JJ_WANTS_TO_BE_TOSSED_TO_SAPPHIRE) && (func_80AEB124(play) != 0), this)) {
|
||||
if (!Player_InCsMode(play)) {
|
||||
Animation_Change(&this->skelAnime, &gRutoChildSeesSapphireAnim, 1.0f, 0,
|
||||
Animation_GetLastFrame(&gRutoChildSquirmAnim), ANIMMODE_LOOP, -8.0f);
|
||||
@ -2190,8 +2189,11 @@ void func_80AEFF40(EnRu1* this, PlayState* play) {
|
||||
void func_80AEFF94(EnRu1* this, PlayState* play) {
|
||||
s8 actorRoom;
|
||||
|
||||
if ((Flags_GetInfTable(INFTABLE_RUTO_IN_JJ_MEET_RUTO)) && (Flags_GetInfTable(INFTABLE_140)) && shouldSpawnRuto() &&
|
||||
(!(func_80AEB020(this, play)))) {
|
||||
if (
|
||||
Flags_GetInfTable(INFTABLE_RUTO_IN_JJ_MEET_RUTO) && Flags_GetInfTable(INFTABLE_140) &&
|
||||
GameInteractor_Should(VB_RUTO_BE_CONSIDERED_NOT_KIDNAPPED, !Flags_GetInfTable(INFTABLE_145), this) &&
|
||||
(!(func_80AEB020(this, play)))
|
||||
) {
|
||||
func_80AEB264(this, &gRutoChildWait2Anim, 0, 0, 0);
|
||||
actorRoom = this->actor.room;
|
||||
this->action = 22;
|
||||
|
@ -198,10 +198,14 @@ void MagicDark_DiamondDraw(Actor* thisx, PlayState* play) {
|
||||
MagicDark* this = (MagicDark*)thisx;
|
||||
s32 pad;
|
||||
u16 gameplayFrames = play->gameplayFrames;
|
||||
Color_RGB8 Spell_env_ori = {0, 100, 255};
|
||||
Color_RGB8 Spell_col_ori = {170, 255, 255};
|
||||
Color_RGB8 Spell_env = CVarGetColor24(CVAR_COSMETIC("Magic.NayrusSecondary.Value"), Spell_env_ori);
|
||||
Color_RGB8 Spell_col = CVarGetColor24(CVAR_COSMETIC("Magic.NayrusPrimary.Value"), Spell_col_ori);
|
||||
Color_RGB8 Spell_env = { 0, 100, 255 };
|
||||
Color_RGB8 Spell_col = { 170, 255, 255 };
|
||||
if (CVarGetInteger(CVAR_COSMETIC("Magic.NayrusSecondary.Changed"), 0)) {
|
||||
Spell_env = CVarGetColor24(CVAR_COSMETIC("Magic.NayrusSecondary.Value"), Spell_env);
|
||||
}
|
||||
if (CVarGetInteger(CVAR_COSMETIC("Magic.NayrusPrimary.Changed"), 0)) {
|
||||
Spell_col = CVarGetColor24(CVAR_COSMETIC("Magic.NayrusPrimary.Value"), Spell_col);
|
||||
}
|
||||
|
||||
OPEN_DISPS(play->state.gfxCtx);
|
||||
|
||||
@ -224,13 +228,8 @@ void MagicDark_DiamondDraw(Actor* thisx, PlayState* play) {
|
||||
Matrix_RotateY(this->actor.shape.rot.y * (M_PI / 0x8000), MTXMODE_APPLY);
|
||||
gSPMatrix(POLY_XLU_DISP++, MATRIX_NEWMTX(play->state.gfxCtx),
|
||||
G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW);
|
||||
if (CVarGetInteger(CVAR_COSMETIC("UseSpellsColors"),0)) {
|
||||
gDPSetPrimColor(POLY_XLU_DISP++, 0, 0x80, Spell_col.r, Spell_col.g, Spell_col.b, (s32)(this->primAlpha * 0.6f) & 0xFF);
|
||||
gDPSetEnvColor(POLY_XLU_DISP++, Spell_env.r, Spell_env.g, Spell_env.b, 128);
|
||||
} else {
|
||||
gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 170, 255, 255, (s32)(this->primAlpha * 0.6f) & 0xFF);
|
||||
gDPSetEnvColor(POLY_XLU_DISP++, 0, 100, 255, 128);
|
||||
}
|
||||
gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, Spell_col.r, Spell_col.g, Spell_col.b, (s32)(this->primAlpha * 0.6f) & 0xFF);
|
||||
gDPSetEnvColor(POLY_XLU_DISP++, Spell_env.r, Spell_env.g, Spell_env.b, 128);
|
||||
gSPDisplayList(POLY_XLU_DISP++, sDiamondMaterialDL);
|
||||
gSPDisplayList(POLY_XLU_DISP++,
|
||||
Gfx_TwoTexScroll(play->state.gfxCtx, 0, gameplayFrames * 2, gameplayFrames * -4, 32, 32, 1,
|
||||
@ -271,8 +270,18 @@ void MagicDark_OrbDraw(Actor* thisx, PlayState* play) {
|
||||
OPEN_DISPS(play->state.gfxCtx);
|
||||
|
||||
Gfx_SetupDL_25Xlu(play->state.gfxCtx);
|
||||
gDPSetPrimColor(POLY_XLU_DISP++, 0, 0x80, 170, 255, 255, 255);
|
||||
gDPSetEnvColor(POLY_XLU_DISP++, 0, 150, 255, 255);
|
||||
|
||||
Color_RGB8 Spell_env = { 0, 150, 255 };
|
||||
Color_RGB8 Spell_col = { 170, 255, 255 };
|
||||
if (CVarGetInteger(CVAR_COSMETIC("Magic.NayrusSecondary.Changed"), 0)) {
|
||||
Spell_env = CVarGetColor24(CVAR_COSMETIC("Magic.NayrusSecondary.Value"), Spell_env);
|
||||
}
|
||||
if (CVarGetInteger(CVAR_COSMETIC("Magic.NayrusPrimary.Changed"), 0)) {
|
||||
Spell_col = CVarGetColor24(CVAR_COSMETIC("Magic.NayrusPrimary.Value"), Spell_col);
|
||||
}
|
||||
|
||||
gDPSetPrimColor(POLY_XLU_DISP++, 0, 0x80, Spell_col.r, Spell_col.g, Spell_col.b, 255);
|
||||
gDPSetEnvColor(POLY_XLU_DISP++, Spell_env.r, Spell_env.g, Spell_env.b, 255);
|
||||
Matrix_Translate(pos.x, pos.y, pos.z, MTXMODE_NEW);
|
||||
Matrix_Scale(this->actor.scale.x, this->actor.scale.y, this->actor.scale.z, MTXMODE_APPLY);
|
||||
Matrix_Mult(&play->billboardMtxF, MTXMODE_APPLY);
|
||||
|
@ -217,10 +217,14 @@ void MagicFire_Draw(Actor* thisx, PlayState* play) {
|
||||
s32 pad2;
|
||||
s32 i;
|
||||
u8 alpha;
|
||||
Color_RGB8 Spell_env_ori = {255, 0, 0};
|
||||
Color_RGB8 Spell_col_ori = {255, 200, 0};
|
||||
Color_RGB8 Spell_env = CVarGetColor24(CVAR_COSMETIC("Magic.DinsSecondary.Value"), Spell_env_ori);
|
||||
Color_RGB8 Spell_col = CVarGetColor24(CVAR_COSMETIC("Magic.DinsPrimaryary.Value"), Spell_col_ori);
|
||||
Color_RGB8 Spell_env = { 255, 0, 0 };
|
||||
Color_RGB8 Spell_col = { 255, 200, 0 };
|
||||
if (CVarGetInteger(CVAR_COSMETIC("Magic.DinsSecondary.Changed"), 0)) {
|
||||
Spell_env = CVarGetColor24(CVAR_COSMETIC("Magic.DinsSecondary.Value"), Spell_env);
|
||||
}
|
||||
if (CVarGetInteger(CVAR_COSMETIC("Magic.DinsPrimaryary.Changed"), 0)) {
|
||||
Spell_col = CVarGetColor24(CVAR_COSMETIC("Magic.DinsPrimary.Value"), Spell_col);
|
||||
}
|
||||
|
||||
if (this->action > 0) {
|
||||
OPEN_DISPS(play->state.gfxCtx);
|
||||
@ -232,13 +236,8 @@ void MagicFire_Draw(Actor* thisx, PlayState* play) {
|
||||
gDPSetColorDither(POLY_XLU_DISP++, G_CD_DISABLE);
|
||||
gDPFillRectangle(POLY_XLU_DISP++, 0, 0, 319, 239);
|
||||
Gfx_SetupDL_25Xlu(play->state.gfxCtx);
|
||||
if (CVarGetInteger(CVAR_COSMETIC("UseSpellsColors"),0)) {
|
||||
gDPSetPrimColor(POLY_XLU_DISP++, 0, 0x80, Spell_col.r, Spell_col.g, Spell_col.b, (u8)(this->alphaMultiplier * 255));
|
||||
gDPSetEnvColor(POLY_XLU_DISP++, Spell_env.r, Spell_env.g, Spell_env.b, (u8)(this->alphaMultiplier * 255));
|
||||
} else {
|
||||
gDPSetPrimColor(POLY_XLU_DISP++, 0, 0x80, Spell_col_ori.r, Spell_col_ori.g, Spell_col_ori.b, (u8)(this->alphaMultiplier * 255));
|
||||
gDPSetEnvColor(POLY_XLU_DISP++, Spell_env_ori.r, Spell_env_ori.g, Spell_env_ori.b, (u8)(this->alphaMultiplier * 255));
|
||||
}
|
||||
gDPSetPrimColor(POLY_XLU_DISP++, 0, 0x80, Spell_col.r, Spell_col.g, Spell_col.b, (u8)(this->alphaMultiplier * 255));
|
||||
gDPSetEnvColor(POLY_XLU_DISP++, Spell_env.r, Spell_env.g, Spell_env.b, (u8)(this->alphaMultiplier * 255));
|
||||
Matrix_Scale(0.15f, 0.15f, 0.15f, MTXMODE_APPLY);
|
||||
gSPMatrix(POLY_XLU_DISP++, MATRIX_NEWMTX(play->state.gfxCtx),
|
||||
G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW);
|
||||
|
@ -3238,7 +3238,11 @@ s32 func_808358F0(Player* this, PlayState* play) {
|
||||
AnimationContext_SetCopyAll(play, this->skelAnime.limbCount, this->upperSkelAnime.jointTable,
|
||||
this->skelAnime.jointTable);
|
||||
} else {
|
||||
LinkAnimation_Update(play, &this->upperSkelAnime);
|
||||
// #region SOH [Enhancement]
|
||||
if (!CVarGetInteger(CVAR_ENHANCEMENT("BoomerangReticle"), 0)) {
|
||||
// #endregion
|
||||
LinkAnimation_Update(play, &this->upperSkelAnime);
|
||||
}
|
||||
}
|
||||
|
||||
func_80834EB8(this, play);
|
||||
@ -5825,7 +5829,13 @@ s32 func_8083AD4C(PlayState* play, Player* this) {
|
||||
|
||||
camMode = shouldUseBowCamera ? CAM_MODE_BOWARROW : CAM_MODE_SLINGSHOT;
|
||||
} else {
|
||||
camMode = CAM_MODE_BOOMERANG;
|
||||
// #region SOH [Enhancement]
|
||||
if (CVarGetInteger(CVAR_ENHANCEMENT("BoomerangFirstPerson"), 0)) {
|
||||
camMode = CAM_MODE_FIRSTPERSON;
|
||||
// #endregion
|
||||
} else {
|
||||
camMode = CAM_MODE_BOOMERANG;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
camMode = CAM_MODE_FIRSTPERSON;
|
||||
@ -7302,6 +7312,7 @@ s32 Player_ActionHandler_2(Player* this, PlayState* play) {
|
||||
interactedActor->id == ACTOR_EN_ITEM00 &&
|
||||
interactedActor->params != ITEM00_HEART_PIECE &&
|
||||
interactedActor->params != ITEM00_SMALL_KEY &&
|
||||
interactedActor->params != ITEM00_NONE &&
|
||||
interactedActor->params != ITEM00_SOH_GIVE_ITEM_ENTRY &&
|
||||
interactedActor->params != ITEM00_SOH_GIVE_ITEM_ENTRY_GI
|
||||
) ||
|
||||
@ -11488,7 +11499,13 @@ void Player_UpdateCamAndSeqModes(PlayState* play, Player* this) {
|
||||
camMode = CAM_MODE_TALK;
|
||||
} else if (this->stateFlags1 & PLAYER_STATE1_FRIENDLY_ACTOR_FOCUS) {
|
||||
if (this->stateFlags1 & PLAYER_STATE1_BOOMERANG_THROWN) {
|
||||
camMode = CAM_MODE_FOLLOWBOOMERANG;
|
||||
// #region SOH [Enhancement]
|
||||
if (CVarGetInteger(CVAR_ENHANCEMENT("BoomerangFirstPerson"), 0)) {
|
||||
camMode = CAM_MODE_TARGET;
|
||||
// #endregion
|
||||
} else {
|
||||
camMode = CAM_MODE_FOLLOWBOOMERANG;
|
||||
}
|
||||
} else {
|
||||
camMode = CAM_MODE_FOLLOWTARGET;
|
||||
}
|
||||
@ -11499,7 +11516,13 @@ void Player_UpdateCamAndSeqModes(PlayState* play, Player* this) {
|
||||
} else if (this->stateFlags1 & PLAYER_STATE1_CHARGING_SPIN_ATTACK) {
|
||||
camMode = CAM_MODE_CHARGE;
|
||||
} else if (this->stateFlags1 & PLAYER_STATE1_BOOMERANG_THROWN) {
|
||||
camMode = CAM_MODE_FOLLOWBOOMERANG;
|
||||
// #region SOH [Enhancement]
|
||||
if (CVarGetInteger(CVAR_ENHANCEMENT("BoomerangFirstPerson"), 0)) {
|
||||
camMode = CAM_MODE_TARGET;
|
||||
// #endregion
|
||||
} else {
|
||||
camMode = CAM_MODE_FOLLOWBOOMERANG;
|
||||
}
|
||||
Camera_SetParam(Play_GetCamera(play, 0), 8, this->boomerangActor);
|
||||
} else if (this->stateFlags1 & (PLAYER_STATE1_HANGING_OFF_LEDGE | PLAYER_STATE1_CLIMBING_LEDGE)) {
|
||||
if (Player_FriendlyLockOnOrParallel(this)) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user