mirror of
https://github.com/HarbourMasters/Shipwright
synced 2026-05-23 15:02:21 -04:00
Merge branch 'develop' into aManchipelago
This commit is contained in:
@@ -1044,6 +1044,7 @@ VecSph* OLib_Vec3fToVecSph(VecSph* dest, Vec3f* vec);
|
||||
VecSph* OLib_Vec3fToVecSphGeo(VecSph* arg0, Vec3f* arg1);
|
||||
VecSph* OLib_Vec3fDiffToVecSphGeo(VecSph* arg0, Vec3f* a, Vec3f* b);
|
||||
Vec3f* OLib_Vec3fDiffRad(Vec3f* dest, Vec3f* a, Vec3f* b);
|
||||
void OnePointCutscene_SetCsCamPoints(Camera* camera, s16 actionParameters, s16 initTimer, CutsceneCameraPoint* atPoints, CutsceneCameraPoint* eyePoints);
|
||||
s16 OnePointCutscene_Init(PlayState* play, s16 csId, s16 timer, Actor* actor, s16 camIdx);
|
||||
s16 OnePointCutscene_EndCutscene(PlayState* play, s16 camIdx);
|
||||
s32 OnePointCutscene_Attention(PlayState* play, Actor* actor);
|
||||
@@ -1102,6 +1103,7 @@ void FrameAdvance_Init(FrameAdvanceContext* frameAdvCtx);
|
||||
s32 FrameAdvance_Update(FrameAdvanceContext* frameAdvCtx, Input* input);
|
||||
u8 PlayerGrounded(Player* player);
|
||||
void Player_SetBootData(PlayState* play, Player* player);
|
||||
void Player_StartAnimMovement(PlayState* play, Player* player, s32 flags);
|
||||
s32 Player_InBlockingCsMode(PlayState* play, Player* player);
|
||||
s32 Player_TryCsAction(PlayState* play, Actor* actor, s32 csAction);
|
||||
s32 Player_InCsMode(PlayState* play);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#include <libultraship/libultraship.h>
|
||||
|
||||
class TimeDisplayWindow : public Ship::GuiWindow {
|
||||
class TimeDisplayWindow final : public Ship::GuiWindow {
|
||||
public:
|
||||
using GuiWindow::GuiWindow;
|
||||
|
||||
|
||||
@@ -0,0 +1,110 @@
|
||||
#include <libultraship/bridge.h>
|
||||
#include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h"
|
||||
#include "soh/ShipInit.hpp"
|
||||
#include "global.h"
|
||||
|
||||
extern "C" {
|
||||
#include "functions.h"
|
||||
#include "objects/gameplay_keep/gameplay_keep.h"
|
||||
extern PlayState* gPlayState;
|
||||
}
|
||||
|
||||
#define CVAR_CRAWL_SPEED_NAME CVAR_ENHANCEMENT("CrawlSpeed")
|
||||
#define CVAR_CRAWL_SPEED_DEFAULT 1
|
||||
#define CVAR_CRAWL_SPEED_VALUE CVarGetInteger(CVAR_CRAWL_SPEED_NAME, CVAR_CRAWL_SPEED_DEFAULT)
|
||||
#define CVAR_GLITCH_AIDING_NAME CVAR_ENHANCEMENT("GlitchAidingCrawlspaces")
|
||||
#define CVAR_GLITCH_AIDING_DEFAULT 0
|
||||
#define CVAR_GLITCH_AIDING_VALUE CVarGetInteger(CVAR_GLITCH_AIDING_NAME, CVAR_GLITCH_AIDING_DEFAULT)
|
||||
|
||||
extern "C" void ExitCrawlspace(Player* player, PlayState* play) {
|
||||
LinkAnimationHeader* animExit = (LinkAnimationHeader*)gPlayerAnim_link_child_tunnel_end;
|
||||
LinkAnimationHeader* animEnter = (LinkAnimationHeader*)gPlayerAnim_link_child_tunnel_start;
|
||||
|
||||
if (player->linearVelocity > 0.0f) {
|
||||
// Leaving a crawlspace forwards
|
||||
player->actor.shape.rot.y = player->actor.wallYaw + 0x8000;
|
||||
LinkAnimation_Change(play, &player->skelAnime, animExit, ((CVAR_CRAWL_SPEED_VALUE + 1.0f) / 2.0f), 0.0f,
|
||||
Animation_GetLastFrame(animExit), ANIMMODE_ONCE, 0.0f);
|
||||
Player_StartAnimMovement(play, player, 0x9D);
|
||||
OnePointCutscene_Init(play, 9601, 999, NULL, MAIN_CAM);
|
||||
} else {
|
||||
// Leaving a crawlspace backwards
|
||||
player->actor.shape.rot.y = player->actor.wallYaw;
|
||||
LinkAnimation_Change(play, &player->skelAnime, animEnter, -1.0f * ((CVAR_CRAWL_SPEED_VALUE + 1.0f) / 2.0f),
|
||||
Animation_GetLastFrame(animEnter), 0.0f, ANIMMODE_ONCE, 0.0f);
|
||||
Player_StartAnimMovement(play, player, 0x9D);
|
||||
OnePointCutscene_Init(play, 9602, 999, NULL, MAIN_CAM);
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" void ExitCrawlspaceCS(PlayState* play, Camera* csCam, int16_t actionParameters, int16_t initTimer,
|
||||
CutsceneCameraPoint* atPoints, CutsceneCameraPoint* eyePoints) {
|
||||
s16 camCrawlTemp = CVAR_CRAWL_SPEED_VALUE;
|
||||
s16 camCrawlTimer = initTimer / camCrawlTemp;
|
||||
|
||||
OnePointCutscene_SetCsCamPoints(csCam, actionParameters | 0x1000, camCrawlTimer, atPoints, eyePoints);
|
||||
}
|
||||
|
||||
extern "C" void EnterCrawlspace(Player* player, PlayState* play) {
|
||||
LinkAnimationHeader* anim = (LinkAnimationHeader*)gPlayerAnim_link_child_tunnel_start;
|
||||
|
||||
LinkAnimation_Change(play, &player->skelAnime, anim, ((CVAR_CRAWL_SPEED_VALUE + 1.0f) / 2.0f), 0.0f,
|
||||
Animation_GetLastFrame(anim), ANIMMODE_ONCE, 0.0f);
|
||||
}
|
||||
|
||||
extern "C" void IncreaseCrawlSpeed(Player* player, PlayState* play) {
|
||||
Input* sControlInput = &play->state.input[0];
|
||||
player->linearVelocity = sControlInput->rel.stick_y * 0.03f * CVAR_CRAWL_SPEED_VALUE;
|
||||
}
|
||||
|
||||
void CrawlSpeed_Register() {
|
||||
bool shouldRegister = CVAR_CRAWL_SPEED_VALUE > 1;
|
||||
|
||||
COND_VB_SHOULD(VB_CRAWL_SPEED_EXIT, shouldRegister, {
|
||||
Player* player = GET_PLAYER(gPlayState);
|
||||
bool excludeWellBackroom = (player->actor.world.pos.x > 950.0f) && (player->actor.world.pos.x < 1025.0f) &&
|
||||
(player->actor.world.pos.z > -1510.0f) && (player->actor.world.pos.z < -1490.0f) &&
|
||||
gPlayState->sceneNum == SCENE_BOTTOM_OF_THE_WELL;
|
||||
bool excludeGlitchAiding = CVAR_GLITCH_AIDING_VALUE;
|
||||
if (excludeGlitchAiding && excludeWellBackroom) {
|
||||
*should = true;
|
||||
} else {
|
||||
ExitCrawlspace(player, gPlayState);
|
||||
*should = false;
|
||||
}
|
||||
});
|
||||
|
||||
COND_VB_SHOULD(VB_CRAWL_SPEED_EXIT_CS, shouldRegister, {
|
||||
Player* player = GET_PLAYER(gPlayState);
|
||||
Camera* csCam = va_arg(args, Camera*);
|
||||
s16 csId = va_arg(args, s16);
|
||||
s16 actionParameters = va_arg(args, s16);
|
||||
s16 initTimer = va_arg(args, s16);
|
||||
CutsceneCameraPoint* atPoints = va_arg(args, CutsceneCameraPoint*);
|
||||
CutsceneCameraPoint* eyePoints = va_arg(args, CutsceneCameraPoint*);
|
||||
bool excludeWellBackroom = (player->actor.world.pos.x > 950.0f) && (player->actor.world.pos.x < 1025.0f) &&
|
||||
(player->actor.world.pos.z > -1510.0f) && (player->actor.world.pos.z < -1490.0f) &&
|
||||
gPlayState->sceneNum == SCENE_BOTTOM_OF_THE_WELL;
|
||||
bool excludeGlitchAiding = CVAR_GLITCH_AIDING_VALUE;
|
||||
if (excludeGlitchAiding && excludeWellBackroom) {
|
||||
*should = true;
|
||||
} else {
|
||||
ExitCrawlspaceCS(gPlayState, csCam, actionParameters, initTimer, atPoints, eyePoints);
|
||||
*should = false;
|
||||
}
|
||||
});
|
||||
|
||||
COND_VB_SHOULD(VB_CRAWL_SPEED_ENTER, shouldRegister, {
|
||||
Player* player = GET_PLAYER(gPlayState);
|
||||
EnterCrawlspace(player, gPlayState);
|
||||
*should = false;
|
||||
});
|
||||
|
||||
COND_VB_SHOULD(VB_CRAWL_SPEED_INCREASE, shouldRegister, {
|
||||
Player* player = GET_PLAYER(gPlayState);
|
||||
IncreaseCrawlSpeed(player, gPlayState);
|
||||
*should = false;
|
||||
});
|
||||
}
|
||||
|
||||
static RegisterShipInitFunc initSpeed(CrawlSpeed_Register, { CVAR_CRAWL_SPEED_NAME });
|
||||
@@ -7,7 +7,7 @@
|
||||
#include <imgui.h>
|
||||
#include "AudioCollection.h"
|
||||
|
||||
class AudioEditor : public Ship::GuiWindow {
|
||||
class AudioEditor final : public Ship::GuiWindow {
|
||||
public:
|
||||
using GuiWindow::GuiWindow;
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ typedef enum {
|
||||
STICK_MODE_ALWAYS_HIDDEN,
|
||||
} StickMode;
|
||||
|
||||
class InputViewer : public Ship::GuiWindow {
|
||||
class InputViewer final : public Ship::GuiWindow {
|
||||
public:
|
||||
using GuiWindow::GuiWindow;
|
||||
|
||||
@@ -33,7 +33,7 @@ class InputViewer : public Ship::GuiWindow {
|
||||
void RenderButton(std::string btn, std::string btnOutline, int state, ImVec2 size, int outlineMode);
|
||||
};
|
||||
|
||||
class InputViewerSettingsWindow : public Ship::GuiWindow {
|
||||
class InputViewerSettingsWindow final : public Ship::GuiWindow {
|
||||
public:
|
||||
using GuiWindow::GuiWindow;
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ typedef struct {
|
||||
N64ButtonMask defaultBtn;
|
||||
} CustomButtonMap;
|
||||
|
||||
class SohInputEditorWindow : public Ship::GuiWindow {
|
||||
class SohInputEditorWindow final : public Ship::GuiWindow {
|
||||
public:
|
||||
using GuiWindow::GuiWindow;
|
||||
~SohInputEditorWindow();
|
||||
|
||||
@@ -60,7 +60,7 @@ void CosmeticsEditor_ResetAll();
|
||||
void CosmeticsEditor_ResetGroup(CosmeticGroup group);
|
||||
void ApplyOrResetCustomGfxPatches(bool manualChange = true);
|
||||
|
||||
class CosmeticsEditorWindow : public Ship::GuiWindow {
|
||||
class CosmeticsEditorWindow final : public Ship::GuiWindow {
|
||||
public:
|
||||
using GuiWindow::GuiWindow;
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ void MessageDebug_DisplayCustomMessage(const char* customMessage);
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
||||
class MessageViewer : public Ship::GuiWindow {
|
||||
class MessageViewer final : public Ship::GuiWindow {
|
||||
public:
|
||||
static inline const char* TABLE_ID = "MessageViewer";
|
||||
using GuiWindow::GuiWindow;
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
#include <libultraship/libultraship.h>
|
||||
|
||||
class SohStatsWindow : public Ship::GuiWindow {
|
||||
class SohStatsWindow final : public Ship::GuiWindow {
|
||||
public:
|
||||
using GuiWindow::GuiWindow;
|
||||
~SohStatsWindow(){};
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
#include <libultraship/libultraship.h>
|
||||
|
||||
class ActorViewerWindow : public Ship::GuiWindow {
|
||||
class ActorViewerWindow final : public Ship::GuiWindow {
|
||||
public:
|
||||
using GuiWindow::GuiWindow;
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ extern "C" {
|
||||
extern PlayState* gPlayState;
|
||||
}
|
||||
|
||||
typedef enum ColRenderSetting { ColRenderDisabled, ColRenderSolid, ColRenderTransparent } ColRenderSetting;
|
||||
enum ColRenderSetting { ColRenderDisabled, ColRenderSolid, ColRenderTransparent };
|
||||
|
||||
static std::unordered_map<int32_t, const char*> ColRenderSettingNames = {
|
||||
{ ColRenderDisabled, "Disabled" },
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
typedef enum { COLVIEW_DISABLED, COLVIEW_SOLID, COLVIEW_TRANSPARENT } ColViewerRenderSetting;
|
||||
|
||||
#ifdef __cplusplus
|
||||
class ColViewerWindow : public Ship::GuiWindow {
|
||||
class ColViewerWindow final : public Ship::GuiWindow {
|
||||
public:
|
||||
using GuiWindow::GuiWindow;
|
||||
|
||||
|
||||
@@ -106,7 +106,7 @@ char z2ASCII(int code) {
|
||||
return char(ret);
|
||||
}
|
||||
|
||||
typedef enum MagicLevel { MAGIC_LEVEL_NONE, MAGIC_LEVEL_SINGLE, MAGIC_LEVEL_DOUBLE };
|
||||
enum MagicLevel { MAGIC_LEVEL_NONE, MAGIC_LEVEL_SINGLE, MAGIC_LEVEL_DOUBLE };
|
||||
|
||||
std::unordered_map<int8_t, const char*> magicLevelMap = {
|
||||
{ MAGIC_LEVEL_NONE, "None" },
|
||||
@@ -114,7 +114,7 @@ std::unordered_map<int8_t, const char*> magicLevelMap = {
|
||||
{ MAGIC_LEVEL_DOUBLE, "Double" },
|
||||
};
|
||||
|
||||
typedef enum AudioOutput {
|
||||
enum AudioOutput {
|
||||
AUDIO_STEREO,
|
||||
AUDIO_MONO,
|
||||
AUDIO_HEADSET,
|
||||
@@ -128,7 +128,7 @@ std::unordered_map<uint8_t, const char*> audioMap = {
|
||||
{ AUDIO_SURROUND, "Surround" },
|
||||
};
|
||||
|
||||
typedef enum ZTarget {
|
||||
enum ZTarget {
|
||||
Z_TARGET_SWITCH,
|
||||
Z_TARGET_HOLD,
|
||||
};
|
||||
|
||||
@@ -1770,7 +1770,7 @@ const std::vector<std::string> state3 = {
|
||||
"Travelling to Hook Target",
|
||||
};
|
||||
|
||||
class SaveEditorWindow : public Ship::GuiWindow {
|
||||
class SaveEditorWindow final : public Ship::GuiWindow {
|
||||
public:
|
||||
using GuiWindow::GuiWindow;
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
#include <libultraship/libultraship.h>
|
||||
|
||||
class DLViewerWindow : public Ship::GuiWindow {
|
||||
class DLViewerWindow final : public Ship::GuiWindow {
|
||||
public:
|
||||
using GuiWindow::GuiWindow;
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#include <libultraship/libultraship.h>
|
||||
|
||||
class HookDebuggerWindow : public Ship::GuiWindow {
|
||||
class HookDebuggerWindow final : public Ship::GuiWindow {
|
||||
public:
|
||||
using GuiWindow::GuiWindow;
|
||||
|
||||
|
||||
@@ -33,7 +33,7 @@ typedef struct {
|
||||
uint32_t y;
|
||||
} ValueTableElement;
|
||||
|
||||
class ValueViewerWindow : public Ship::GuiWindow {
|
||||
class ValueViewerWindow final : public Ship::GuiWindow {
|
||||
public:
|
||||
using GuiWindow::GuiWindow;
|
||||
|
||||
|
||||
@@ -22,6 +22,7 @@ DEFINE_HOOK(OnFlagSet, (int16_t flagType, int16_t flag));
|
||||
DEFINE_HOOK(OnFlagUnset, (int16_t flagType, int16_t flag));
|
||||
DEFINE_HOOK(OnSceneSpawnActors, ());
|
||||
DEFINE_HOOK(OnPlayerUpdate, ());
|
||||
DEFINE_HOOK(OnSetDoAction, (uint16_t action));
|
||||
DEFINE_HOOK(OnOcarinaSongAction, ());
|
||||
DEFINE_HOOK(OnCuccoOrChickenHatch, ());
|
||||
DEFINE_HOOK(OnShopSlotChange, (uint8_t cursorIndex, int16_t price));
|
||||
|
||||
@@ -85,6 +85,10 @@ void GameInteractor_ExecuteOnPlayerUpdate() {
|
||||
GameInteractor::Instance->ExecuteHooks<GameInteractor::OnPlayerUpdate>();
|
||||
}
|
||||
|
||||
void GameInteractor_ExecuteOnSetDoAction(uint16_t action) {
|
||||
GameInteractor::Instance->ExecuteHooks<GameInteractor::OnSetDoAction>(action);
|
||||
}
|
||||
|
||||
void GameInteractor_ExecuteOnOcarinaSongAction() {
|
||||
GameInteractor::Instance->ExecuteHooks<GameInteractor::OnOcarinaSongAction>();
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@ void GameInteractor_ExecuteOnFlagSet(int16_t flagType, int16_t flag);
|
||||
void GameInteractor_ExecuteOnFlagUnset(int16_t flagType, int16_t flag);
|
||||
void GameInteractor_ExecuteOnSceneSpawnActors();
|
||||
void GameInteractor_ExecuteOnPlayerUpdate();
|
||||
void GameInteractor_ExecuteOnSetDoAction(uint16_t action);
|
||||
void GameInteractor_ExecuteOnOcarinaSongAction();
|
||||
void GameInteractor_ExecuteOnCuccoOrChickenHatch();
|
||||
void GameInteractor_ExecuteOnActorInit(void* actor);
|
||||
|
||||
@@ -294,6 +294,43 @@ typedef enum {
|
||||
// - `*ObjKibako2`
|
||||
VB_CRATE_SETUP_DRAW,
|
||||
|
||||
// #### `result`
|
||||
// ```c
|
||||
// true
|
||||
// ```
|
||||
// #### `args`
|
||||
// - None
|
||||
VB_CRAWL_SPEED_ENTER,
|
||||
|
||||
// #### `result`
|
||||
// ```c
|
||||
// true
|
||||
// ```
|
||||
// #### `args`
|
||||
// - None
|
||||
VB_CRAWL_SPEED_EXIT,
|
||||
|
||||
// #### `result`
|
||||
// ```c
|
||||
// true
|
||||
// ```
|
||||
// #### `args`
|
||||
// - `*Camera`
|
||||
// - 'int16_t' (csId)
|
||||
// - 'int16_t' (actionParameters)
|
||||
// - 'int16_t' (initTimer)
|
||||
// - 'CutsceneCameraPoint*' (atPoints)
|
||||
// - 'CutsceneCameraPoint*' (eyePoints)
|
||||
VB_CRAWL_SPEED_EXIT_CS,
|
||||
|
||||
// #### `result`
|
||||
// ```c
|
||||
// true
|
||||
// ```
|
||||
// #### `args`
|
||||
// - None
|
||||
VB_CRAWL_SPEED_INCREASE,
|
||||
|
||||
// #### `result`
|
||||
// ```c
|
||||
// !Flags_GetItemGetInf(ITEMGETINF_1C)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#include <libultraship/libultraship.h>
|
||||
|
||||
class GameplayStatsWindow : public Ship::GuiWindow {
|
||||
class GameplayStatsWindow final : public Ship::GuiWindow {
|
||||
public:
|
||||
using GuiWindow::GuiWindow;
|
||||
|
||||
|
||||
@@ -219,18 +219,18 @@ void ProcessExits(Region* region, GetAccessibleLocationsStruct& gals, Randomizer
|
||||
}
|
||||
ValidateSphereZero(gals);
|
||||
}
|
||||
// If the exit is accessible and hasn't been added yet, add it to the pool
|
||||
// RANDOTODO do we want to add the region after the loop now, considering we
|
||||
// are processing the new region immediately. Maybe a reverse for loop in ProcessRegion?
|
||||
if (!exitRegion->addedToPool) {
|
||||
exitRegion->addedToPool = true;
|
||||
gals.regionPool.push_back(exit.GetConnectedRegionKey());
|
||||
}
|
||||
|
||||
// process the region we just expanded to, to reduce looping
|
||||
ProcessRegion(exitRegion, gals, ignore, stopOnBeatable, addToPlaythrough);
|
||||
}
|
||||
|
||||
// If the exit is accessible and hasn't been added yet, add it to the pool
|
||||
// RANDOTODO do we want to add the region after the loop now, considering we
|
||||
// are processing the new region immediately. Maybe a reverse for loop in ProcessRegion?
|
||||
if (!exitRegion->addedToPool && exit.ConditionsMet()) {
|
||||
exitRegion->addedToPool = true;
|
||||
gals.regionPool.push_back(exit.GetConnectedRegionKey());
|
||||
}
|
||||
|
||||
if (addToPlaythrough) {
|
||||
// RANDOTODO Should this match the regular spheres?
|
||||
// Add shuffled entrances to the entrance playthrough
|
||||
@@ -327,13 +327,12 @@ bool IsBeatableWithout(RandomizerCheck excludedCheck, bool replaceItem,
|
||||
auto ctx = Rando::Context::GetInstance();
|
||||
RandomizerGet copy = ctx->GetItemLocation(excludedCheck)->GetPlacedRandomizerGet(); // Copy out item
|
||||
ctx->GetItemLocation(excludedCheck)->SetPlacedItem(RG_NONE); // Write in empty item
|
||||
ctx->playthroughBeatable = false;
|
||||
logic->Reset();
|
||||
CheckBeatable(ignore);
|
||||
bool result = CheckBeatable(ignore);
|
||||
if (replaceItem) {
|
||||
ctx->GetItemLocation(excludedCheck)->SetPlacedItem(copy); // Immediately put item back
|
||||
}
|
||||
return ctx->playthroughBeatable;
|
||||
return result;
|
||||
}
|
||||
|
||||
// Reset non-Logic-class logic, and optionally apply the initial inventory
|
||||
@@ -581,6 +580,7 @@ void GeneratePlaythrough() {
|
||||
// return if the seed is currently beatable or not
|
||||
bool CheckBeatable(RandomizerGet ignore /* = RG_NONE*/) {
|
||||
auto ctx = Rando::Context::GetInstance();
|
||||
ctx->playthroughBeatable = false;
|
||||
GetAccessibleLocationsStruct gals(0);
|
||||
ResetLogic(ctx, gals, true);
|
||||
do {
|
||||
@@ -929,10 +929,8 @@ static void AssumedFill(const std::vector<RandomizerGet>& items, const std::vect
|
||||
// If ALR is off, then we check beatability after placing the item.
|
||||
// If the game is beatable, then we can stop placing items with logic.
|
||||
if (!ctx->GetOption(RSK_ALL_LOCATIONS_REACHABLE)) {
|
||||
ctx->playthroughBeatable = false;
|
||||
logic->Reset();
|
||||
CheckBeatable();
|
||||
if (ctx->playthroughBeatable) {
|
||||
if (CheckBeatable()) {
|
||||
SPDLOG_DEBUG("Game beatable, now placing items randomly. " + std::to_string(itemsToPlace.size()) +
|
||||
" major items remaining.\n\n");
|
||||
FastFill(itemsToPlace, GetEmptyLocations(allowedLocations), true);
|
||||
|
||||
@@ -2358,7 +2358,7 @@ void StaticData::HintTable_Init() {
|
||||
{QM_YELLOW, QM_GREEN}));
|
||||
|
||||
hintTextTable[RHT_BIGGORON_HINT] = HintText(CustomMessage("Arrrrrre you here to claim my finest #[[1]]#? Shoooooow me your #Claim Check#.",
|
||||
/*german*/ "Arrrrr, bist du hier, um mein feinsten #[[1]]# zu beanspruchen? Zeig mir deinen #Zertifikat#!"
|
||||
/*german*/ "Arrrrr, bist du hier, um mein feinsten #[[1]]# zu beanspruchen? Zeig mir deinen #Zertifikat#!",
|
||||
/*french*/ "Eeeeeees-tu ici pour réclaaaaaamer mon plus beau #[[1]]#? Moooooontre-moi ton #Certificat#.",
|
||||
{QM_GREEN, QM_RED}));
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ extern "C" {
|
||||
#include "soh/Enhancements/randomizer/item.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
class PlandomizerWindow : public Ship::GuiWindow {
|
||||
class PlandomizerWindow final : public Ship::GuiWindow {
|
||||
public:
|
||||
using GuiWindow::GuiWindow;
|
||||
|
||||
|
||||
@@ -535,6 +535,8 @@ void Rando::StaticData::RegisterCrateLocations() {
|
||||
locationTable[RC_SPIRIT_TEMPLE_BEFORE_CHILD_CLIMB_SMALL_CRATE_2] = Location::SmallCrate(RC_SPIRIT_TEMPLE_BEFORE_CHILD_CLIMB_SMALL_CRATE_2, RCQUEST_VANILLA, RCAREA_SPIRIT_TEMPLE, SCENE_SPIRIT_TEMPLE, TWO_ACTOR_PARAMS(-1151, -939), "Before Child Climb Small Crate 2", RHT_CRATE_SPIRIT_TEMPLE, RG_DEKU_NUTS_5, SpoilerCollectionCheck::RandomizerInf(RAND_INF_SPIRIT_TEMPLE_BEFORE_CHILD_CLIMB_SMALL_CRATE_2));
|
||||
locationTable[RC_JABU_JABUS_BELLY_MQ_TRIPLE_HALLWAY_SMALL_CRATE_1] = Location::SmallCrate(RC_JABU_JABUS_BELLY_MQ_TRIPLE_HALLWAY_SMALL_CRATE_1, RCQUEST_MQ, RCAREA_JABU_JABUS_BELLY, SCENE_JABU_JABU, TWO_ACTOR_PARAMS(-512, -4592), "MQ Triple Hallway Small Crate 1", RHT_CRATE_JABU_JABU, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_JABU_JABUS_BELLY_MQ_TRIPLE_HALLWAY_SMALL_CRATE_1));
|
||||
locationTable[RC_JABU_JABUS_BELLY_MQ_TRIPLE_HALLWAY_SMALL_CRATE_2] = Location::SmallCrate(RC_JABU_JABUS_BELLY_MQ_TRIPLE_HALLWAY_SMALL_CRATE_2, RCQUEST_MQ, RCAREA_JABU_JABUS_BELLY, SCENE_JABU_JABU, TWO_ACTOR_PARAMS(-666, -4671), "MQ Triple Hallway Small Crate 2", RHT_CRATE_JABU_JABU, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_JABU_JABUS_BELLY_MQ_TRIPLE_HALLWAY_SMALL_CRATE_2));
|
||||
locationTable[RC_JABU_JABUS_BELLY_MQ_JIGGLIES_SMALL_CRATE_1] = Location::SmallCrate(RC_JABU_JABUS_BELLY_MQ_JIGGLIES_SMALL_CRATE_1, RCQUEST_MQ, RCAREA_JABU_JABUS_BELLY, SCENE_JABU_JABU, TWO_ACTOR_PARAMS(-1381, -2115), "MQ Jigglies Small Crate 1", RHT_CRATE_JABU_JABU, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_JABU_JABUS_BELLY_MQ_JIGGLIES_SMALL_CRATE_1));
|
||||
locationTable[RC_JABU_JABUS_BELLY_MQ_JIGGLIES_SMALL_CRATE_2] = Location::SmallCrate(RC_JABU_JABUS_BELLY_MQ_JIGGLIES_SMALL_CRATE_2, RCQUEST_MQ, RCAREA_JABU_JABUS_BELLY, SCENE_JABU_JABU, TWO_ACTOR_PARAMS(-1341, -2116), "MQ Jigglies Small Crate 2", RHT_CRATE_JABU_JABU, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_JABU_JABUS_BELLY_MQ_JIGGLIES_SMALL_CRATE_2));
|
||||
locationTable[RC_FOREST_TEMPLE_MQ_FROZEN_EYE_SWITCH_SMALL_CRATE_1] = Location::SmallCrate(RC_FOREST_TEMPLE_MQ_FROZEN_EYE_SWITCH_SMALL_CRATE_1, RCQUEST_MQ, RCAREA_FOREST_TEMPLE, SCENE_FOREST_TEMPLE, TWO_ACTOR_PARAMS(1599, -1322), "MQ Frozen Eye Switch Small Crate 1", RHT_CRATE_FOREST_TEMPLE, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_FOREST_TEMPLE_MQ_FROZEN_EYE_SWITCH_SMALL_CRATE_1));
|
||||
locationTable[RC_FOREST_TEMPLE_MQ_FROZEN_EYE_SWITCH_SMALL_CRATE_2] = Location::SmallCrate(RC_FOREST_TEMPLE_MQ_FROZEN_EYE_SWITCH_SMALL_CRATE_2, RCQUEST_MQ, RCAREA_FOREST_TEMPLE, SCENE_FOREST_TEMPLE, TWO_ACTOR_PARAMS(2364, -873), "MQ Frozen Eye Switch Small Crate 2", RHT_CRATE_FOREST_TEMPLE, RG_ARROWS_5, SpoilerCollectionCheck::RandomizerInf(RAND_INF_FOREST_TEMPLE_MQ_FROZEN_EYE_SWITCH_SMALL_CRATE_2));
|
||||
locationTable[RC_FOREST_TEMPLE_MQ_FROZEN_EYE_SWITCH_SMALL_CRATE_3] = Location::SmallCrate(RC_FOREST_TEMPLE_MQ_FROZEN_EYE_SWITCH_SMALL_CRATE_3, RCQUEST_MQ, RCAREA_FOREST_TEMPLE, SCENE_FOREST_TEMPLE, TWO_ACTOR_PARAMS(2312, -874), "MQ Frozen Eye Switch Small Crate 3", RHT_CRATE_FOREST_TEMPLE, RG_ARROWS_5, SpoilerCollectionCheck::RandomizerInf(RAND_INF_FOREST_TEMPLE_MQ_FROZEN_EYE_SWITCH_SMALL_CRATE_3));
|
||||
|
||||
@@ -497,7 +497,7 @@ void Rando::StaticData::RegisterGrassLocations() {
|
||||
locationTable[RC_JABU_JABUS_BELLY_MQ_BASEMENT_GRASS_1] = Location::Grass(RC_JABU_JABUS_BELLY_MQ_BASEMENT_GRASS_1, RCQUEST_MQ, RCAREA_JABU_JABUS_BELLY, SCENE_JABU_JABU, TWO_ACTOR_PARAMS(-91, -2815), "MQ Basement Grass 1", RHT_JABU_JABUS_BELLY_GRASS, RG_BLUE_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_JABU_JABUS_BELLY_MQ_BASEMENT_GRASS_1));
|
||||
locationTable[RC_JABU_JABUS_BELLY_MQ_BASEMENT_GRASS_2] = Location::Grass(RC_JABU_JABUS_BELLY_MQ_BASEMENT_GRASS_2, RCQUEST_MQ, RCAREA_JABU_JABUS_BELLY, SCENE_JABU_JABU, TWO_ACTOR_PARAMS(231, -3575), "MQ Basement Grass 2", RHT_JABU_JABUS_BELLY_GRASS, RG_BLUE_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_JABU_JABUS_BELLY_MQ_BASEMENT_GRASS_2));
|
||||
locationTable[RC_JABU_JABUS_BELLY_MQ_BASEMENT_GRASS_3] = Location::Grass(RC_JABU_JABUS_BELLY_MQ_BASEMENT_GRASS_3, RCQUEST_MQ, RCAREA_JABU_JABUS_BELLY, SCENE_JABU_JABU, TWO_ACTOR_PARAMS(305, -3481), "MQ Basement Grass 3", RHT_JABU_JABUS_BELLY_GRASS, RG_BLUE_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_JABU_JABUS_BELLY_MQ_BASEMENT_GRASS_3));
|
||||
locationTable[RC_JABU_JABUS_BELLY_MQ_WIGGLERS_GRASS] = Location::Grass(RC_JABU_JABUS_BELLY_MQ_WIGGLERS_GRASS, RCQUEST_MQ, RCAREA_JABU_JABUS_BELLY, SCENE_JABU_JABU, TWO_ACTOR_PARAMS(-1089, -1489), "MQ Wigglers Grass", RHT_JABU_JABUS_BELLY_GRASS, RG_BLUE_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_JABU_JABUS_BELLY_MQ_WIGGLERS_GRASS));
|
||||
locationTable[RC_JABU_JABUS_BELLY_MQ_JIGGLIES_GRASS] = Location::Grass(RC_JABU_JABUS_BELLY_MQ_JIGGLIES_GRASS, RCQUEST_MQ, RCAREA_JABU_JABUS_BELLY, SCENE_JABU_JABU, TWO_ACTOR_PARAMS(-1089, -1489), "MQ Jigglies Grass", RHT_JABU_JABUS_BELLY_GRASS, RG_BLUE_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_JABU_JABUS_BELLY_MQ_JIGGLIES_GRASS));
|
||||
locationTable[RC_JABU_JABUS_BELLY_MQ_FALLING_LIKE_LIKE_GRASS] = Location::Grass(RC_JABU_JABUS_BELLY_MQ_FALLING_LIKE_LIKE_GRASS, RCQUEST_MQ, RCAREA_JABU_JABUS_BELLY, SCENE_JABU_JABU, TWO_ACTOR_PARAMS(652, -5687), "MQ Like Like Grass", RHT_JABU_JABUS_BELLY_GRASS, RG_BLUE_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_JABU_JABUS_BELLY_MQ_FALLING_LIKE_LIKE_GRASS));
|
||||
locationTable[RC_JABU_JABUS_BELLY_MQ_BASEMENT_BOOMERANG_GRASS] = Location::Grass(RC_JABU_JABUS_BELLY_MQ_BASEMENT_BOOMERANG_GRASS, RCQUEST_MQ, RCAREA_JABU_JABUS_BELLY, SCENE_JABU_JABU, TWO_ACTOR_PARAMS(1228, -2647), "MQ Basement Boomerang Grass", RHT_JABU_JABUS_BELLY_GRASS, RG_BLUE_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_JABU_JABUS_BELLY_MQ_BASEMENT_BOOMERANG_GRASS));
|
||||
locationTable[RC_JABU_JABUS_BELLY_MQ_AFTER_BIG_OCTO_GRASS_1] = Location::Grass(RC_JABU_JABUS_BELLY_MQ_AFTER_BIG_OCTO_GRASS_1, RCQUEST_MQ, RCAREA_JABU_JABUS_BELLY, SCENE_JABU_JABU, TWO_ACTOR_PARAMS(-1360, -3606), "MQ After Big Octo Grass 1", RHT_JABU_JABUS_BELLY_GRASS, RG_BLUE_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_JABU_JABUS_BELLY_MQ_AFTER_BIG_OCTO_GRASS_1));
|
||||
|
||||
@@ -1489,62 +1489,62 @@ int EntranceShuffler::ShuffleAllEntrances() {
|
||||
// If a boss room is inside a boss door, make the blue warp go outside the dungeon's entrance
|
||||
std::map<std::string, Entrance*> bossExits = {
|
||||
{ EntranceNameByRegions(RR_DEKU_TREE_BOSS_ROOM, RR_DEKU_TREE_BOSS_ENTRYWAY),
|
||||
GetEntrance(EntranceNameByRegions(RR_DEKU_TREE_ENTRYWAY, RR_KF_OUTSIDE_DEKU_TREE)) },
|
||||
GetEntrance(RR_DEKU_TREE_ENTRYWAY, RR_KF_OUTSIDE_DEKU_TREE) },
|
||||
{ EntranceNameByRegions(RR_DODONGOS_CAVERN_BOSS_ROOM, RR_DODONGOS_CAVERN_BOSS_ENTRYWAY),
|
||||
GetEntrance(EntranceNameByRegions(RR_DODONGOS_CAVERN_ENTRYWAY, RR_DEATH_MOUNTAIN_TRAIL)) },
|
||||
GetEntrance(RR_DODONGOS_CAVERN_ENTRYWAY, RR_DEATH_MOUNTAIN_TRAIL) },
|
||||
{ EntranceNameByRegions(RR_JABU_JABUS_BELLY_BOSS_ROOM, RR_JABU_JABUS_BELLY_BOSS_ENTRYWAY),
|
||||
GetEntrance(EntranceNameByRegions(RR_JABU_JABUS_BELLY_ENTRYWAY, RR_ZORAS_FOUNTAIN)) },
|
||||
GetEntrance(RR_JABU_JABUS_BELLY_ENTRYWAY, RR_ZORAS_FOUNTAIN) },
|
||||
{ EntranceNameByRegions(RR_FOREST_TEMPLE_BOSS_ROOM, RR_FOREST_TEMPLE_BOSS_ENTRYWAY),
|
||||
GetEntrance(EntranceNameByRegions(RR_FOREST_TEMPLE_ENTRYWAY, RR_SACRED_FOREST_MEADOW)) },
|
||||
GetEntrance(RR_FOREST_TEMPLE_ENTRYWAY, RR_SACRED_FOREST_MEADOW) },
|
||||
{ EntranceNameByRegions(RR_FIRE_TEMPLE_BOSS_ROOM, RR_FIRE_TEMPLE_BOSS_ENTRYWAY),
|
||||
GetEntrance(EntranceNameByRegions(RR_FIRE_TEMPLE_ENTRYWAY, RR_DMC_CENTRAL_LOCAL)) },
|
||||
GetEntrance(RR_FIRE_TEMPLE_ENTRYWAY, RR_DMC_CENTRAL_LOCAL) },
|
||||
{ EntranceNameByRegions(RR_WATER_TEMPLE_BOSS_ROOM, RR_WATER_TEMPLE_BOSS_ENTRYWAY),
|
||||
GetEntrance(EntranceNameByRegions(RR_WATER_TEMPLE_ENTRYWAY, RR_LAKE_HYLIA)) },
|
||||
GetEntrance(RR_WATER_TEMPLE_ENTRYWAY, RR_LAKE_HYLIA) },
|
||||
{ EntranceNameByRegions(RR_SPIRIT_TEMPLE_BOSS_ROOM, RR_SPIRIT_TEMPLE_BOSS_ENTRYWAY),
|
||||
GetEntrance(EntranceNameByRegions(RR_SPIRIT_TEMPLE_ENTRYWAY, RR_DESERT_COLOSSUS_OUTSIDE_TEMPLE)) },
|
||||
GetEntrance(RR_SPIRIT_TEMPLE_ENTRYWAY, RR_DESERT_COLOSSUS_OUTSIDE_TEMPLE) },
|
||||
{ EntranceNameByRegions(RR_SHADOW_TEMPLE_BOSS_ROOM, RR_SHADOW_TEMPLE_BOSS_ENTRYWAY),
|
||||
GetEntrance(EntranceNameByRegions(RR_SHADOW_TEMPLE_ENTRYWAY, RR_GRAVEYARD_WARP_PAD_REGION)) },
|
||||
GetEntrance(RR_SHADOW_TEMPLE_ENTRYWAY, RR_GRAVEYARD_WARP_PAD_REGION) },
|
||||
};
|
||||
|
||||
// If a boss room is inside a dungeon entrance (or inside a dungeon which is inside a dungeon entrance), make
|
||||
// the blue warp go to that dungeon's blue warp target
|
||||
std::map<std::string, Entrance*> dungeonExits = {
|
||||
{ EntranceNameByRegions(RR_DEKU_TREE_ENTRYWAY, RR_KF_OUTSIDE_DEKU_TREE),
|
||||
GetEntrance(EntranceNameByRegions(RR_DEKU_TREE_BOSS_ROOM, RR_KF_OUTSIDE_DEKU_TREE)) },
|
||||
GetEntrance(RR_DEKU_TREE_BOSS_ROOM, RR_KF_OUTSIDE_DEKU_TREE) },
|
||||
{ EntranceNameByRegions(RR_DODONGOS_CAVERN_ENTRYWAY, RR_DEATH_MOUNTAIN_TRAIL),
|
||||
GetEntrance(EntranceNameByRegions(RR_DODONGOS_CAVERN_BOSS_ROOM, RR_DEATH_MOUNTAIN_TRAIL)) },
|
||||
GetEntrance(RR_DODONGOS_CAVERN_BOSS_ROOM, RR_DEATH_MOUNTAIN_TRAIL) },
|
||||
{ EntranceNameByRegions(RR_JABU_JABUS_BELLY_ENTRYWAY, RR_ZORAS_FOUNTAIN),
|
||||
GetEntrance(EntranceNameByRegions(RR_JABU_JABUS_BELLY_BOSS_ROOM, RR_ZORAS_FOUNTAIN)) },
|
||||
GetEntrance(RR_JABU_JABUS_BELLY_BOSS_ROOM, RR_ZORAS_FOUNTAIN) },
|
||||
{ EntranceNameByRegions(RR_FOREST_TEMPLE_ENTRYWAY, RR_SACRED_FOREST_MEADOW),
|
||||
GetEntrance(EntranceNameByRegions(RR_FOREST_TEMPLE_BOSS_ROOM, RR_SACRED_FOREST_MEADOW)) },
|
||||
GetEntrance(RR_FOREST_TEMPLE_BOSS_ROOM, RR_SACRED_FOREST_MEADOW) },
|
||||
{ EntranceNameByRegions(RR_FIRE_TEMPLE_ENTRYWAY, RR_DMC_CENTRAL_LOCAL),
|
||||
GetEntrance(EntranceNameByRegions(RR_FIRE_TEMPLE_BOSS_ROOM, RR_DMC_CENTRAL_LOCAL)) },
|
||||
GetEntrance(RR_FIRE_TEMPLE_BOSS_ROOM, RR_DMC_CENTRAL_LOCAL) },
|
||||
{ EntranceNameByRegions(RR_WATER_TEMPLE_ENTRYWAY, RR_LAKE_HYLIA),
|
||||
GetEntrance(EntranceNameByRegions(RR_WATER_TEMPLE_BOSS_ROOM, RR_LAKE_HYLIA)) },
|
||||
GetEntrance(RR_WATER_TEMPLE_BOSS_ROOM, RR_LAKE_HYLIA) },
|
||||
{ EntranceNameByRegions(RR_SPIRIT_TEMPLE_ENTRYWAY, RR_DESERT_COLOSSUS_OUTSIDE_TEMPLE),
|
||||
GetEntrance(EntranceNameByRegions(RR_SPIRIT_TEMPLE_BOSS_ROOM, RR_DESERT_COLOSSUS)) },
|
||||
GetEntrance(RR_SPIRIT_TEMPLE_BOSS_ROOM, RR_DESERT_COLOSSUS) },
|
||||
{ EntranceNameByRegions(RR_SHADOW_TEMPLE_ENTRYWAY, RR_GRAVEYARD_WARP_PAD_REGION),
|
||||
GetEntrance(EntranceNameByRegions(RR_SHADOW_TEMPLE_BOSS_ROOM, RR_GRAVEYARD_WARP_PAD_REGION)) },
|
||||
GetEntrance(RR_SHADOW_TEMPLE_BOSS_ROOM, RR_GRAVEYARD_WARP_PAD_REGION) },
|
||||
};
|
||||
|
||||
// Pair <BlueWarp exit, BossRoom reverse exit>
|
||||
std::vector<EntrancePair> bossRoomExitPairs = {
|
||||
{ GetEntrance(EntranceNameByRegions(RR_DEKU_TREE_BOSS_ROOM, RR_KF_OUTSIDE_DEKU_TREE)),
|
||||
GetEntrance(EntranceNameByRegions(RR_DEKU_TREE_BOSS_ROOM, RR_DEKU_TREE_BOSS_ENTRYWAY)) },
|
||||
{ GetEntrance(EntranceNameByRegions(RR_DODONGOS_CAVERN_BOSS_ROOM, RR_DEATH_MOUNTAIN_TRAIL)),
|
||||
GetEntrance(EntranceNameByRegions(RR_DODONGOS_CAVERN_BOSS_ROOM, RR_DODONGOS_CAVERN_BOSS_ENTRYWAY)) },
|
||||
{ GetEntrance(EntranceNameByRegions(RR_JABU_JABUS_BELLY_BOSS_ROOM, RR_ZORAS_FOUNTAIN)),
|
||||
GetEntrance(EntranceNameByRegions(RR_JABU_JABUS_BELLY_BOSS_ROOM, RR_JABU_JABUS_BELLY_BOSS_ENTRYWAY)) },
|
||||
{ GetEntrance(EntranceNameByRegions(RR_FOREST_TEMPLE_BOSS_ROOM, RR_SACRED_FOREST_MEADOW)),
|
||||
GetEntrance(EntranceNameByRegions(RR_FOREST_TEMPLE_BOSS_ROOM, RR_FOREST_TEMPLE_BOSS_ENTRYWAY)) },
|
||||
{ GetEntrance(EntranceNameByRegions(RR_FIRE_TEMPLE_BOSS_ROOM, RR_DMC_CENTRAL_LOCAL)),
|
||||
GetEntrance(EntranceNameByRegions(RR_FIRE_TEMPLE_BOSS_ROOM, RR_FIRE_TEMPLE_BOSS_ENTRYWAY)) },
|
||||
{ GetEntrance(EntranceNameByRegions(RR_WATER_TEMPLE_BOSS_ROOM, RR_LAKE_HYLIA)),
|
||||
GetEntrance(EntranceNameByRegions(RR_WATER_TEMPLE_BOSS_ROOM, RR_WATER_TEMPLE_BOSS_ENTRYWAY)) },
|
||||
{ GetEntrance(EntranceNameByRegions(RR_SPIRIT_TEMPLE_BOSS_ROOM, RR_DESERT_COLOSSUS)),
|
||||
GetEntrance(EntranceNameByRegions(RR_SPIRIT_TEMPLE_BOSS_ROOM, RR_SPIRIT_TEMPLE_BOSS_ENTRYWAY)) },
|
||||
{ GetEntrance(EntranceNameByRegions(RR_SHADOW_TEMPLE_BOSS_ROOM, RR_GRAVEYARD_WARP_PAD_REGION)),
|
||||
GetEntrance(EntranceNameByRegions(RR_SHADOW_TEMPLE_BOSS_ROOM, RR_SHADOW_TEMPLE_BOSS_ENTRYWAY)) },
|
||||
{ GetEntrance(RR_DEKU_TREE_BOSS_ROOM, RR_KF_OUTSIDE_DEKU_TREE),
|
||||
GetEntrance(RR_DEKU_TREE_BOSS_ROOM, RR_DEKU_TREE_BOSS_ENTRYWAY) },
|
||||
{ GetEntrance(RR_DODONGOS_CAVERN_BOSS_ROOM, RR_DEATH_MOUNTAIN_TRAIL),
|
||||
GetEntrance(RR_DODONGOS_CAVERN_BOSS_ROOM, RR_DODONGOS_CAVERN_BOSS_ENTRYWAY) },
|
||||
{ GetEntrance(RR_JABU_JABUS_BELLY_BOSS_ROOM, RR_ZORAS_FOUNTAIN),
|
||||
GetEntrance(RR_JABU_JABUS_BELLY_BOSS_ROOM, RR_JABU_JABUS_BELLY_BOSS_ENTRYWAY) },
|
||||
{ GetEntrance(RR_FOREST_TEMPLE_BOSS_ROOM, RR_SACRED_FOREST_MEADOW),
|
||||
GetEntrance(RR_FOREST_TEMPLE_BOSS_ROOM, RR_FOREST_TEMPLE_BOSS_ENTRYWAY) },
|
||||
{ GetEntrance(RR_FIRE_TEMPLE_BOSS_ROOM, RR_DMC_CENTRAL_LOCAL),
|
||||
GetEntrance(RR_FIRE_TEMPLE_BOSS_ROOM, RR_FIRE_TEMPLE_BOSS_ENTRYWAY) },
|
||||
{ GetEntrance(RR_WATER_TEMPLE_BOSS_ROOM, RR_LAKE_HYLIA),
|
||||
GetEntrance(RR_WATER_TEMPLE_BOSS_ROOM, RR_WATER_TEMPLE_BOSS_ENTRYWAY) },
|
||||
{ GetEntrance(RR_SPIRIT_TEMPLE_BOSS_ROOM, RR_DESERT_COLOSSUS),
|
||||
GetEntrance(RR_SPIRIT_TEMPLE_BOSS_ROOM, RR_SPIRIT_TEMPLE_BOSS_ENTRYWAY) },
|
||||
{ GetEntrance(RR_SHADOW_TEMPLE_BOSS_ROOM, RR_GRAVEYARD_WARP_PAD_REGION),
|
||||
GetEntrance(RR_SHADOW_TEMPLE_BOSS_ROOM, RR_SHADOW_TEMPLE_BOSS_ENTRYWAY) },
|
||||
};
|
||||
|
||||
for (EntrancePair pair : bossRoomExitPairs) {
|
||||
|
||||
@@ -286,12 +286,12 @@ void Rando::StaticData::InitItemTable() {
|
||||
itemTable[RG_TREASURE_GAME_HEART] = Item(RG_TREASURE_GAME_HEART, Text{ "Piece of Heart (WINNER)", "Quart de Coeur (Chasse-aux-Trésors)", "Herzstück (Schatztruhenminispiel)" }, ITEMTYPE_ITEM, GI_HEART_PIECE_WIN, true, LOGIC_PIECE_OF_HEART, RHT_TREASURE_GAME_HEART, ITEM_HEART_PIECE_2, OBJECT_GI_HEARTS, GID_HEART_PIECE, 0xFA, 0x80, CHEST_ANIM_LONG, ITEM_CATEGORY_LESSER, MOD_NONE);
|
||||
itemTable[RG_TREASURE_GAME_GREEN_RUPEE] = Item(RG_TREASURE_GAME_GREEN_RUPEE, Text{ "Green Rupee (LOSER)", "Rubis Vert (Chasse-aux-Trésors)", "Grüner Rubin (Schatztruhenminispiel)" }, ITEMTYPE_ITEM, GI_RUPEE_GREEN_LOSE, false, LOGIC_NONE, RHT_TREASURE_GAME_GREEN_RUPEE, ITEM_RUPEE_GREEN, OBJECT_GI_RUPY, GID_RUPEE_GREEN, 0xF4, 0x00, CHEST_ANIM_SHORT, ITEM_CATEGORY_MAJOR, MOD_NONE);
|
||||
// Shop
|
||||
itemTable[RG_BUY_DEKU_NUTS_5] = Item(RG_BUY_DEKU_NUTS_5, Text{ "Buy Deku Nut (5)", "Acheter: Noix Mojo (5)", "Deku-Nuß kaufen (5)" }, ITEMTYPE_SHOP, GI_NUTS_5_2, true, LOGIC_NUTS, RHT_DEKU_NUTS_5, ITEM_NUTS_5, OBJECT_GI_NUTS, GID_NUTS, 0x34, 0x0C, CHEST_ANIM_SHORT, ITEM_CATEGORY_JUNK, MOD_NONE, false, 15);
|
||||
itemTable[RG_BUY_DEKU_NUTS_5] = Item(RG_BUY_DEKU_NUTS_5, Text{ "Buy Deku Nut (5)", "Acheter: Noix Mojo (5)", "Deku-Nuß kaufen (5)" }, ITEMTYPE_SHOP, GI_NUTS_5_2, true, LOGIC_BUY_NUTS, RHT_DEKU_NUTS_5, ITEM_NUTS_5, OBJECT_GI_NUTS, GID_NUTS, 0x34, 0x0C, CHEST_ANIM_SHORT, ITEM_CATEGORY_JUNK, MOD_NONE, false, 15);
|
||||
itemTable[RG_BUY_ARROWS_30] = Item(RG_BUY_ARROWS_30, Text{ "Buy Arrows (30)", "Acheter: Flèches (30)", "Pfeile kaufen (30)" }, ITEMTYPE_SHOP, GI_ARROWS_MEDIUM, true, LOGIC_BUY_ARROW, RHT_ARROWS_30, ITEM_ARROWS_MEDIUM, OBJECT_GI_ARROW, GID_ARROWS_MEDIUM, 0xE6, 0x49, CHEST_ANIM_SHORT, ITEM_CATEGORY_JUNK, MOD_NONE, false, 60);
|
||||
itemTable[RG_BUY_ARROWS_50] = Item(RG_BUY_ARROWS_50, Text{ "Buy Arrows (50)", "Acheter: Flèches (50)", "Pfeile kaufen (50)" }, ITEMTYPE_SHOP, GI_ARROWS_LARGE, true, LOGIC_BUY_ARROW, RHT_ARROWS_30, ITEM_ARROWS_LARGE, OBJECT_GI_ARROW, GID_ARROWS_LARGE, 0xE6, 0x4A, CHEST_ANIM_SHORT, ITEM_CATEGORY_JUNK, MOD_NONE, false, 90);
|
||||
itemTable[RG_BUY_BOMBS_525] = Item(RG_BUY_BOMBS_525, Text{ "Buy Bombs (5) [25]", "Acheter: Bombes (5) [25]", "Bomben kaufen (5) [25]" }, ITEMTYPE_SHOP, GI_BOMBS_5, true, LOGIC_BUY_BOMB, RHT_BOMBS_5, ITEM_BOMBS_5, OBJECT_GI_BOMB_1, GID_BOMB, 0x32, 0x59, CHEST_ANIM_SHORT, ITEM_CATEGORY_JUNK, MOD_NONE, false, 25);
|
||||
itemTable[RG_BUY_DEKU_NUTS_10] = Item(RG_BUY_DEKU_NUTS_10, Text{ "Buy Deku Nut (10)", "Acheter: Noix Mojo (10)", "Deku-Nuß kaufen (10)" }, ITEMTYPE_SHOP, GI_NUTS_10, true, LOGIC_NUTS, RHT_DEKU_NUTS_10, ITEM_NUTS_10, OBJECT_GI_NUTS, GID_NUTS, 0x34, 0x0C, CHEST_ANIM_SHORT, ITEM_CATEGORY_JUNK, MOD_NONE, false, 30);
|
||||
itemTable[RG_BUY_DEKU_STICK_1] = Item(RG_BUY_DEKU_STICK_1, Text{ "Buy Deku Stick (1)", "Acheter: Bâton Mojo (1)", "Deku-Stab kaufen (1)" }, ITEMTYPE_SHOP, GI_STICKS_1, true, LOGIC_STICKS, RHT_DEKU_STICK_1, ITEM_STICK, OBJECT_GI_STICK, GID_STICK, 0x37, 0x0D, CHEST_ANIM_SHORT, ITEM_CATEGORY_JUNK, MOD_NONE, false, 10);
|
||||
itemTable[RG_BUY_DEKU_NUTS_10] = Item(RG_BUY_DEKU_NUTS_10, Text{ "Buy Deku Nut (10)", "Acheter: Noix Mojo (10)", "Deku-Nuß kaufen (10)" }, ITEMTYPE_SHOP, GI_NUTS_10, true, LOGIC_BUY_NUTS, RHT_DEKU_NUTS_10, ITEM_NUTS_10, OBJECT_GI_NUTS, GID_NUTS, 0x34, 0x0C, CHEST_ANIM_SHORT, ITEM_CATEGORY_JUNK, MOD_NONE, false, 30);
|
||||
itemTable[RG_BUY_DEKU_STICK_1] = Item(RG_BUY_DEKU_STICK_1, Text{ "Buy Deku Stick (1)", "Acheter: Bâton Mojo (1)", "Deku-Stab kaufen (1)" }, ITEMTYPE_SHOP, GI_STICKS_1, true, LOGIC_BUY_STICKS, RHT_DEKU_STICK_1, ITEM_STICK, OBJECT_GI_STICK, GID_STICK, 0x37, 0x0D, CHEST_ANIM_SHORT, ITEM_CATEGORY_JUNK, MOD_NONE, false, 10);
|
||||
itemTable[RG_BUY_BOMBS_10] = Item(RG_BUY_BOMBS_10, Text{ "Buy Bombs (10)", "Acheter: Bombes (10)", "Bomben kaufen (10)" }, ITEMTYPE_SHOP, GI_BOMBS_10, true, LOGIC_BUY_BOMB, RHT_BOMBS_10, ITEM_BOMBS_10, OBJECT_GI_BOMB_1, GID_BOMB, 0x32, 0x59, CHEST_ANIM_SHORT, ITEM_CATEGORY_JUNK, MOD_NONE, false, 50);
|
||||
itemTable[RG_BUY_FISH] = Item(RG_BUY_FISH, Text{ "Buy Fish", "Acheter: Poisson", "Fisch kaufen" }, ITEMTYPE_SHOP, GI_FISH, true, LOGIC_FISH_ACCESS, RHT_BOTTLE_WITH_FISH, ITEM_FISH, OBJECT_GI_FISH, GID_FISH, 0x47, 0x80, CHEST_ANIM_LONG, ITEM_CATEGORY_JUNK, MOD_NONE, false, 200);
|
||||
itemTable[RG_BUY_RED_POTION_30] = Item(RG_BUY_RED_POTION_30, Text{ "Buy Red Potion [30]", "Acheter: Potion Rouge [30]", "Rotes Elixier kaufen [30]" }, ITEMTYPE_SHOP, GI_POTION_RED, false, LOGIC_NONE, RHT_BOTTLE_WITH_RED_POTION, ITEM_POTION_RED, OBJECT_GI_LIQUID, GID_POTION_RED, 0x43, 0x80, CHEST_ANIM_LONG, ITEM_CATEGORY_JUNK, MOD_NONE, false, 30);
|
||||
|
||||
@@ -627,13 +627,10 @@ std::vector<Rando::Entrance*> GetShuffleableEntrances(Rando::EntranceType type,
|
||||
return entrancesToShuffle;
|
||||
}
|
||||
|
||||
// Get the specific entrance by name
|
||||
Rando::Entrance* GetEntrance(const std::string name) {
|
||||
for (RandomizerRegion region : Regions::GetAllRegions()) {
|
||||
for (auto& exit : RegionTable(region)->exits) {
|
||||
if (exit.GetName() == name) {
|
||||
return &exit;
|
||||
}
|
||||
Rando::Entrance* GetEntrance(RandomizerRegion source, RandomizerRegion destination) {
|
||||
for (auto& exit : RegionTable(source)->exits) {
|
||||
if (exit.GetConnectedRegionKey() == destination) {
|
||||
return &exit;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -350,7 +350,7 @@ extern void DumpWorldGraph(std::string str);
|
||||
void RegionTable_Init();
|
||||
Region* RegionTable(const RandomizerRegion regionKey);
|
||||
std::vector<Rando::Entrance*> GetShuffleableEntrances(Rando::EntranceType type, bool onlyPrimary = true);
|
||||
Rando::Entrance* GetEntrance(const std::string name);
|
||||
Rando::Entrance* GetEntrance(RandomizerRegion source, RandomizerRegion destination);
|
||||
|
||||
// Overworld
|
||||
void RegionTable_Init_KokiriForest();
|
||||
|
||||
@@ -312,9 +312,11 @@ void RegionTable_Init_JabuJabusBelly() {
|
||||
}, {
|
||||
//Locations
|
||||
LOCATION(RC_JABU_JABUS_BELLY_MQ_COW, logic->CanUse(RG_EPONAS_SONG) && logic->CanUse(RG_FAIRY_SLINGSHOT)),
|
||||
LOCATION(RC_JABU_JABUS_BELLY_MQ_WIGGLERS_GRASS, logic->CanCutShrubs()),
|
||||
LOCATION(RC_JABU_JABUS_BELLY_MQ_JIGGLIES_GRASS, logic->CanCutShrubs()),
|
||||
LOCATION(RC_JABU_JABUS_BELLY_MQ_AFTER_BIG_OCTO_GRASS_1, logic->CanCutShrubs()),
|
||||
LOCATION(RC_JABU_JABUS_BELLY_MQ_AFTER_BIG_OCTO_GRASS_2, logic->CanCutShrubs()),
|
||||
LOCATION(RC_JABU_JABUS_BELLY_MQ_JIGGLIES_SMALL_CRATE_1, logic->CanUse(RG_FAIRY_SLINGSHOT) && logic->CanBreakSmallCrates()),
|
||||
LOCATION(RC_JABU_JABUS_BELLY_MQ_JIGGLIES_SMALL_CRATE_2, logic->CanUse(RG_FAIRY_SLINGSHOT) && logic->CanBreakSmallCrates()),
|
||||
}, {
|
||||
//Exits
|
||||
Entrance(RR_JABU_JABUS_BELLY_MQ_LIFT_ROOM, []{return logic->CanUse(RG_BOOMERANG) && logic->CanUse(RG_FAIRY_SLINGSHOT);}),
|
||||
|
||||
@@ -322,11 +322,9 @@ bool Logic::CanUse(RandomizerGet itemName) {
|
||||
case RG_KOKIRI_SWORD:
|
||||
return IsChild; // || KokiriSwordAsAdult;
|
||||
case RG_NUTS:
|
||||
return (NutPot || NutCrate || DekuBabaNuts) &&
|
||||
AmmoCanDrop; // RANDOTODO BuyNuts currently mixed in with Nuts, should be seperate as BuyNuts are
|
||||
// also a Nuts source
|
||||
return ((NutPot || NutCrate || DekuBabaNuts) && AmmoCanDrop) || GetInLogic(LOGIC_BUY_NUTS);
|
||||
case RG_STICKS:
|
||||
return IsChild /* || StickAsAdult;*/ && (StickPot || DekuBabaSticks);
|
||||
return IsChild /* || StickAsAdult;*/ && (StickPot || DekuBabaSticks || GetInLogic(LOGIC_BUY_STICKS));
|
||||
case RG_DEKU_SHIELD:
|
||||
return IsChild; // || DekuShieldAsAdult;
|
||||
case RG_PROGRESSIVE_BOMB_BAG:
|
||||
|
||||
@@ -1551,7 +1551,7 @@ std::map<RandomizerCheck, RandomizerInf> rcToRandomizerInf = {
|
||||
{ RC_JABU_JABUS_BELLY_MQ_BASEMENT_GRASS_1, RAND_INF_JABU_JABUS_BELLY_MQ_BASEMENT_GRASS_1 },
|
||||
{ RC_JABU_JABUS_BELLY_MQ_BASEMENT_GRASS_2, RAND_INF_JABU_JABUS_BELLY_MQ_BASEMENT_GRASS_2 },
|
||||
{ RC_JABU_JABUS_BELLY_MQ_BASEMENT_GRASS_3, RAND_INF_JABU_JABUS_BELLY_MQ_BASEMENT_GRASS_3 },
|
||||
{ RC_JABU_JABUS_BELLY_MQ_WIGGLERS_GRASS, RAND_INF_JABU_JABUS_BELLY_MQ_WIGGLERS_GRASS },
|
||||
{ RC_JABU_JABUS_BELLY_MQ_JIGGLIES_GRASS, RAND_INF_JABU_JABUS_BELLY_MQ_JIGGLIES_GRASS },
|
||||
{ RC_JABU_JABUS_BELLY_MQ_AFTER_BIG_OCTO_GRASS_1, RAND_INF_JABU_JABUS_BELLY_MQ_AFTER_BIG_OCTO_GRASS_1 },
|
||||
{ RC_JABU_JABUS_BELLY_MQ_AFTER_BIG_OCTO_GRASS_2, RAND_INF_JABU_JABUS_BELLY_MQ_AFTER_BIG_OCTO_GRASS_2 },
|
||||
{ RC_JABU_JABUS_BELLY_MQ_FALLING_LIKE_LIKE_GRASS, RAND_INF_JABU_JABUS_BELLY_MQ_FALLING_LIKE_LIKE_GRASS },
|
||||
@@ -3022,6 +3022,14 @@ std::map<RandomizerCheck, RandomizerInf> rcToRandomizerInf = {
|
||||
RC_JABU_JABUS_BELLY_MQ_TRIPLE_HALLWAY_SMALL_CRATE_2,
|
||||
RAND_INF_JABU_JABUS_BELLY_MQ_TRIPLE_HALLWAY_SMALL_CRATE_2,
|
||||
},
|
||||
{
|
||||
RC_JABU_JABUS_BELLY_MQ_JIGGLIES_SMALL_CRATE_1,
|
||||
RAND_INF_JABU_JABUS_BELLY_MQ_JIGGLIES_SMALL_CRATE_1,
|
||||
},
|
||||
{
|
||||
RC_JABU_JABUS_BELLY_MQ_JIGGLIES_SMALL_CRATE_2,
|
||||
RAND_INF_JABU_JABUS_BELLY_MQ_JIGGLIES_SMALL_CRATE_2,
|
||||
},
|
||||
{
|
||||
RC_FOREST_TEMPLE_MQ_FROZEN_EYE_SWITCH_SMALL_CRATE_1,
|
||||
RAND_INF_FOREST_TEMPLE_MQ_FROZEN_EYE_SWITCH_SMALL_CRATE_1,
|
||||
|
||||
@@ -184,10 +184,10 @@ typedef enum {
|
||||
LOGIC_GREG,
|
||||
LOGIC_PIECE_OF_HEART,
|
||||
LOGIC_HEART_CONTAINER,
|
||||
LOGIC_NUTS,
|
||||
LOGIC_BUY_NUTS,
|
||||
LOGIC_BUY_ARROW,
|
||||
LOGIC_BUY_BOMB,
|
||||
LOGIC_STICKS,
|
||||
LOGIC_BUY_STICKS,
|
||||
LOGIC_FISH_ACCESS,
|
||||
LOGIC_BUY_MAGIC_POTION,
|
||||
LOGIC_BUY_BOMBCHUS,
|
||||
@@ -2576,6 +2576,8 @@ typedef enum {
|
||||
// MQ Dungeon Small Crates
|
||||
RC_JABU_JABUS_BELLY_MQ_TRIPLE_HALLWAY_SMALL_CRATE_1,
|
||||
RC_JABU_JABUS_BELLY_MQ_TRIPLE_HALLWAY_SMALL_CRATE_2,
|
||||
RC_JABU_JABUS_BELLY_MQ_JIGGLIES_SMALL_CRATE_1,
|
||||
RC_JABU_JABUS_BELLY_MQ_JIGGLIES_SMALL_CRATE_2,
|
||||
RC_FOREST_TEMPLE_MQ_FROZEN_EYE_SWITCH_SMALL_CRATE_1,
|
||||
RC_FOREST_TEMPLE_MQ_FROZEN_EYE_SWITCH_SMALL_CRATE_2,
|
||||
RC_FOREST_TEMPLE_MQ_FROZEN_EYE_SWITCH_SMALL_CRATE_3,
|
||||
@@ -3448,7 +3450,7 @@ typedef enum {
|
||||
RC_JABU_JABUS_BELLY_MQ_BASEMENT_GRASS_1,
|
||||
RC_JABU_JABUS_BELLY_MQ_BASEMENT_GRASS_2,
|
||||
RC_JABU_JABUS_BELLY_MQ_BASEMENT_GRASS_3,
|
||||
RC_JABU_JABUS_BELLY_MQ_WIGGLERS_GRASS,
|
||||
RC_JABU_JABUS_BELLY_MQ_JIGGLIES_GRASS,
|
||||
RC_JABU_JABUS_BELLY_MQ_AFTER_BIG_OCTO_GRASS_1,
|
||||
RC_JABU_JABUS_BELLY_MQ_AFTER_BIG_OCTO_GRASS_2,
|
||||
RC_JABU_JABUS_BELLY_MQ_FALLING_LIKE_LIKE_GRASS,
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
|
||||
namespace CheckTracker {
|
||||
|
||||
class CheckTrackerSettingsWindow : public Ship::GuiWindow {
|
||||
class CheckTrackerSettingsWindow final : public Ship::GuiWindow {
|
||||
public:
|
||||
using GuiWindow::GuiWindow;
|
||||
~CheckTrackerSettingsWindow(){};
|
||||
@@ -19,7 +19,7 @@ class CheckTrackerSettingsWindow : public Ship::GuiWindow {
|
||||
void UpdateElement() override{};
|
||||
};
|
||||
|
||||
class CheckTrackerWindow : public Ship::GuiWindow {
|
||||
class CheckTrackerWindow final : public Ship::GuiWindow {
|
||||
public:
|
||||
using GuiWindow::GuiWindow;
|
||||
void Draw() override;
|
||||
|
||||
@@ -91,7 +91,7 @@ s16 GetLastEntranceOverride();
|
||||
s16 GetCurrentGrottoId();
|
||||
const EntranceData* GetEntranceData(s16);
|
||||
|
||||
class EntranceTrackerSettingsWindow : public Ship::GuiWindow {
|
||||
class EntranceTrackerSettingsWindow final : public Ship::GuiWindow {
|
||||
public:
|
||||
using GuiWindow::GuiWindow;
|
||||
|
||||
@@ -101,7 +101,7 @@ class EntranceTrackerSettingsWindow : public Ship::GuiWindow {
|
||||
void UpdateElement() override{};
|
||||
};
|
||||
|
||||
class EntranceTrackerWindow : public Ship::GuiWindow {
|
||||
class EntranceTrackerWindow final : public Ship::GuiWindow {
|
||||
public:
|
||||
using GuiWindow::GuiWindow;
|
||||
void Draw() override;
|
||||
|
||||
@@ -973,6 +973,8 @@ typedef enum {
|
||||
|
||||
RAND_INF_JABU_JABUS_BELLY_MQ_TRIPLE_HALLWAY_SMALL_CRATE_1,
|
||||
RAND_INF_JABU_JABUS_BELLY_MQ_TRIPLE_HALLWAY_SMALL_CRATE_2,
|
||||
RAND_INF_JABU_JABUS_BELLY_MQ_JIGGLIES_SMALL_CRATE_1,
|
||||
RAND_INF_JABU_JABUS_BELLY_MQ_JIGGLIES_SMALL_CRATE_2,
|
||||
RAND_INF_FOREST_TEMPLE_MQ_FROZEN_EYE_SWITCH_SMALL_CRATE_1,
|
||||
RAND_INF_FOREST_TEMPLE_MQ_FROZEN_EYE_SWITCH_SMALL_CRATE_2,
|
||||
RAND_INF_FOREST_TEMPLE_MQ_FROZEN_EYE_SWITCH_SMALL_CRATE_3,
|
||||
@@ -1931,7 +1933,7 @@ typedef enum {
|
||||
RAND_INF_JABU_JABUS_BELLY_MQ_BASEMENT_GRASS_1,
|
||||
RAND_INF_JABU_JABUS_BELLY_MQ_BASEMENT_GRASS_2,
|
||||
RAND_INF_JABU_JABUS_BELLY_MQ_BASEMENT_GRASS_3,
|
||||
RAND_INF_JABU_JABUS_BELLY_MQ_WIGGLERS_GRASS,
|
||||
RAND_INF_JABU_JABUS_BELLY_MQ_JIGGLIES_GRASS,
|
||||
RAND_INF_JABU_JABUS_BELLY_MQ_AFTER_BIG_OCTO_GRASS_1,
|
||||
RAND_INF_JABU_JABUS_BELLY_MQ_AFTER_BIG_OCTO_GRASS_2,
|
||||
RAND_INF_JABU_JABUS_BELLY_MQ_FALLING_LIKE_LIKE_GRASS,
|
||||
|
||||
@@ -30,7 +30,7 @@ typedef struct ItemTrackerDungeon {
|
||||
std::vector<uint32_t> items;
|
||||
} ItemTrackerDungeon;
|
||||
|
||||
class ItemTrackerSettingsWindow : public Ship::GuiWindow {
|
||||
class ItemTrackerSettingsWindow final : public Ship::GuiWindow {
|
||||
public:
|
||||
using GuiWindow::GuiWindow;
|
||||
|
||||
@@ -40,7 +40,7 @@ class ItemTrackerSettingsWindow : public Ship::GuiWindow {
|
||||
void UpdateElement() override{};
|
||||
};
|
||||
|
||||
class ItemTrackerWindow : public Ship::GuiWindow {
|
||||
class ItemTrackerWindow final : public Ship::GuiWindow {
|
||||
public:
|
||||
using GuiWindow::GuiWindow;
|
||||
void Draw() override;
|
||||
|
||||
@@ -6,7 +6,7 @@ namespace Rando {
|
||||
class Settings;
|
||||
}
|
||||
|
||||
class RandomizerSettingsWindow : public Ship::GuiWindow {
|
||||
class RandomizerSettingsWindow final : public Ship::GuiWindow {
|
||||
public:
|
||||
using GuiWindow::GuiWindow;
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ extern "C" {
|
||||
#include <libultraship/libultraship.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
class TimeSplitWindow : public Ship::GuiWindow {
|
||||
class TimeSplitWindow final : public Ship::GuiWindow {
|
||||
public:
|
||||
using GuiWindow::GuiWindow;
|
||||
|
||||
@@ -39,7 +39,7 @@ typedef enum SplitStatus {
|
||||
SPLIT_STATUS_ACTIVE,
|
||||
SPLIT_STATUS_INACTIVE,
|
||||
SPLIT_STATUS_COLLECTED,
|
||||
SPLIT_STATUS_SKIPPED
|
||||
SPLIT_STATUS_SKIPPED,
|
||||
} SplitStatus;
|
||||
|
||||
typedef enum SplitType {
|
||||
|
||||
@@ -95,10 +95,8 @@ const char* GetLanguageCode() {
|
||||
switch (CVarGetInteger(CVAR_SETTING("Languages"), 0)) {
|
||||
case LANGUAGE_FRA:
|
||||
return "fr-FR";
|
||||
break;
|
||||
case LANGUAGE_GER:
|
||||
return "de-DE";
|
||||
break;
|
||||
}
|
||||
|
||||
return "en-US";
|
||||
@@ -1148,6 +1146,44 @@ void RegisterOnSetGameLanguageHook() {
|
||||
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnSetGameLanguage>([]() { InitTTSBank(); });
|
||||
}
|
||||
|
||||
void RegisterOnSetDoAction() {
|
||||
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnSetDoAction>([](uint16_t action) {
|
||||
if (CVarGetInteger(CVAR_SETTING("A11yTTS"), 0)) {
|
||||
uint8_t language = CVarGetInteger(CVAR_SETTING("Languages"), 0);
|
||||
const char* text;
|
||||
switch (action) {
|
||||
case DO_ACTION_CHECK:
|
||||
text = language == LANGUAGE_FRA ? "voir" : language == LANGUAGE_GER ? "lesen" : "check";
|
||||
break;
|
||||
case DO_ACTION_ENTER:
|
||||
text = language == LANGUAGE_FRA ? "entrer" : language == LANGUAGE_GER ? "kriechen" : "enter";
|
||||
break;
|
||||
case DO_ACTION_OPEN:
|
||||
text = language == LANGUAGE_FRA ? "ouvrir" : language == LANGUAGE_GER ? "öffnen" : "open";
|
||||
break;
|
||||
case DO_ACTION_CLIMB:
|
||||
text = language == LANGUAGE_FRA ? "monter" : language == LANGUAGE_GER ? "hinauf" : "climb";
|
||||
break;
|
||||
case DO_ACTION_SPEAK:
|
||||
text = language == LANGUAGE_FRA ? "parler" : language == LANGUAGE_GER ? "reden" : "speak";
|
||||
break;
|
||||
case DO_ACTION_GRAB:
|
||||
text = language == LANGUAGE_FRA ? "action" : language == LANGUAGE_GER ? "aktion" : "grab";
|
||||
break;
|
||||
case DO_ACTION_DOWN: {
|
||||
Player* player = GET_PLAYER(gPlayState);
|
||||
if (player == NULL || !(player->stateFlags1 & PLAYER_STATE1_ON_HORSE))
|
||||
return;
|
||||
text = language == LANGUAGE_FRA ? "descendre" : language == LANGUAGE_GER ? "herab" : "down";
|
||||
} break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
SpeechSynthesizer::Instance->Speak(text, GetLanguageCode());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void RegisterTTSModHooks() {
|
||||
RegisterOnSetGameLanguageHook();
|
||||
RegisterOnDialogMessageHook();
|
||||
@@ -1156,6 +1192,7 @@ void RegisterTTSModHooks() {
|
||||
RegisterOnInterfaceUpdateHook();
|
||||
RegisterOnKaleidoscopeUpdateHook();
|
||||
RegisterOnUpdateMainMenuSelection();
|
||||
RegisterOnSetDoAction();
|
||||
}
|
||||
|
||||
void RegisterTTS() {
|
||||
|
||||
@@ -19,7 +19,7 @@ struct Options {
|
||||
float remainingTime = 0.0f; // Seconds
|
||||
};
|
||||
|
||||
class Window : public Ship::GuiWindow {
|
||||
class Window final : public Ship::GuiWindow {
|
||||
public:
|
||||
using GuiWindow::GuiWindow;
|
||||
|
||||
|
||||
+6
-20
@@ -1740,20 +1740,6 @@ std::wstring StringToU16(const std::string& s) {
|
||||
return utf16;
|
||||
}
|
||||
|
||||
int CopyStringToCharBuffer(const std::string& inputStr, char* buffer, const int maxBufferSize) {
|
||||
if (!inputStr.empty()) {
|
||||
// Prevent potential horrible overflow due to implicit conversion of maxBufferSize to an unsigned. Prevents
|
||||
// negatives.
|
||||
memset(buffer, 0, std::max<int>(0, maxBufferSize));
|
||||
// Gaurentee that this value will be greater than 0, regardless of passed variables.
|
||||
const int copiedCharLen = std::min<int>(std::max<int>(0, maxBufferSize - 1), inputStr.length());
|
||||
memcpy(buffer, inputStr.c_str(), copiedCharLen);
|
||||
return copiedCharLen;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
extern "C" void OTRGfxPrint(const char* str, void* printer, void (*printImpl)(void*, char)) {
|
||||
const std::vector<uint32_t> hira1 = {
|
||||
u'を', u'ぁ', u'ぃ', u'ぅ', u'ぇ', u'ぉ', u'ゃ', u'ゅ', u'ょ', u'っ', u'-', u'あ', u'い',
|
||||
@@ -1985,7 +1971,7 @@ extern "C" void* getN64WeirdFrame(s32 i) {
|
||||
return &weirdFrameBytes[i + sizeof(n64WeirdFrames)];
|
||||
}
|
||||
|
||||
extern "C" int GetEquipNowMessage(char* buffer, char* src, const int maxBufferSize) {
|
||||
extern "C" size_t GetEquipNowMessage(char* buffer, char* src, const size_t maxBufferSize) {
|
||||
CustomMessage customMessage("\x04\x1A\x08"
|
||||
"Would you like to equip it now?"
|
||||
"\x09&&"
|
||||
@@ -2026,7 +2012,7 @@ extern "C" int GetEquipNowMessage(char* buffer, char* src, const int maxBufferSi
|
||||
|
||||
if (!str.empty()) {
|
||||
memset(buffer, 0, maxBufferSize);
|
||||
const int copiedCharLen = std::min<int>(maxBufferSize - 1, str.length());
|
||||
const size_t copiedCharLen = std::min<size_t>(maxBufferSize - 1, str.length());
|
||||
memcpy(buffer, str.c_str(), copiedCharLen);
|
||||
return copiedCharLen;
|
||||
}
|
||||
@@ -2168,7 +2154,7 @@ extern "C" int CustomMessage_RetrieveIfExists(PlayState* play) {
|
||||
uint16_t textId = msgCtx->textId;
|
||||
Font* font = &msgCtx->font;
|
||||
char* buffer = font->msgBuf;
|
||||
const int maxBufferSize = sizeof(font->msgBuf);
|
||||
const size_t maxBufferSize = sizeof(font->msgBuf);
|
||||
CustomMessage messageEntry;
|
||||
s16 actorParams = 0;
|
||||
if (IS_RANDO) {
|
||||
@@ -2479,14 +2465,14 @@ extern "C" int CustomMessage_RetrieveIfExists(PlayState* play) {
|
||||
switch (gSaveContext.language) {
|
||||
case LANGUAGE_FRA:
|
||||
return msgCtx->msgLength = font->msgLength =
|
||||
CopyStringToCharBuffer(messageEntry.GetFrench(MF_RAW), buffer, maxBufferSize);
|
||||
SohUtils::CopyStringToCharBuffer(buffer, messageEntry.GetFrench(MF_RAW), maxBufferSize);
|
||||
case LANGUAGE_GER:
|
||||
return msgCtx->msgLength = font->msgLength =
|
||||
CopyStringToCharBuffer(messageEntry.GetGerman(MF_RAW), buffer, maxBufferSize);
|
||||
SohUtils::CopyStringToCharBuffer(buffer, messageEntry.GetGerman(MF_RAW), maxBufferSize);
|
||||
case LANGUAGE_ENG:
|
||||
default:
|
||||
return msgCtx->msgLength = font->msgLength =
|
||||
CopyStringToCharBuffer(messageEntry.GetEnglish(MF_RAW), buffer, maxBufferSize);
|
||||
SohUtils::CopyStringToCharBuffer(buffer, messageEntry.GetEnglish(MF_RAW), maxBufferSize);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -121,7 +121,7 @@ int Controller_ShouldRumble(size_t slot);
|
||||
void Controller_BlockGameInput();
|
||||
void Controller_UnblockGameInput();
|
||||
void* getN64WeirdFrame(s32 i);
|
||||
int GetEquipNowMessage(char* buffer, char* src, const int maxBufferSize);
|
||||
size_t GetEquipNowMessage(char* buffer, char* src, const size_t maxBufferSize);
|
||||
u32 SpoilerFileExists(const char* spoilerFileName);
|
||||
Sprite* GetSeedTexture(uint8_t index);
|
||||
uint8_t GetSeedIconIndex(uint8_t index);
|
||||
|
||||
@@ -382,6 +382,11 @@ void SohMenu::AddMenuEnhancements() {
|
||||
AddWidget(path, "Crawl Speed %dx", WIDGET_CVAR_SLIDER_INT)
|
||||
.CVar(CVAR_ENHANCEMENT("CrawlSpeed"))
|
||||
.Options(IntSliderOptions().Min(1).Max(4).DefaultValue(1).Format("%dx"));
|
||||
AddWidget(path, "Exclude Glitch-Aiding Crawlspaces", WIDGET_CVAR_CHECKBOX)
|
||||
.CVar(CVAR_ENHANCEMENT("GlitchAidingCrawlspaces"))
|
||||
.PreFunc([](WidgetInfo& info) { info.isHidden = CVarGetInteger(CVAR_ENHANCEMENT("CrawlSpeed"), 0) == 1; })
|
||||
.Options(CheckboxOptions().Tooltip("Don't increase crawl speed when exiting glitch-useful crawlspaces."
|
||||
"Currently it is only the BOTW crawlspace to locked door"));
|
||||
AddWidget(path, "King Zora Speed: %.2fx", WIDGET_CVAR_SLIDER_FLOAT)
|
||||
.CVar(CVAR_ENHANCEMENT("MweepSpeed"))
|
||||
.Options(FloatSliderOptions().Min(0.1f).Max(5.0f).DefaultValue(1.0f).Format("%.2fx"));
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
#include "window/gui/GuiMenuBar.h"
|
||||
#include "window/gui/GuiElement.h"
|
||||
|
||||
class SohModalWindow : public Ship::GuiWindow {
|
||||
class SohModalWindow final : public Ship::GuiWindow {
|
||||
public:
|
||||
using GuiWindow::GuiWindow;
|
||||
void Draw() override;
|
||||
|
||||
+8
-12
@@ -364,8 +364,10 @@ const std::string& SohUtils::GetRandomizerCheckAreaPrefix(int32_t rcarea) {
|
||||
}
|
||||
|
||||
void SohUtils::CopyStringToCharArray(char* destination, std::string source, size_t size) {
|
||||
strncpy(destination, source.c_str(), size - 1);
|
||||
destination[size - 1] = '\0';
|
||||
if (size > 0) {
|
||||
strncpy(destination, source.c_str(), size - 1);
|
||||
destination[size - 1] = '\0';
|
||||
}
|
||||
}
|
||||
|
||||
std::string SohUtils::Sanitize(std::string stringValue) {
|
||||
@@ -388,12 +390,9 @@ std::string SohUtils::Sanitize(std::string stringValue) {
|
||||
}
|
||||
|
||||
size_t SohUtils::CopyStringToCharBuffer(char* buffer, const std::string& source, const size_t maxBufferSize) {
|
||||
if (!source.empty()) {
|
||||
// Prevent potential horrible overflow due to implicit conversion of maxBufferSize to an unsigned. Prevents
|
||||
// negatives.
|
||||
memset(buffer, 0, std::max<size_t>(0, maxBufferSize));
|
||||
// Gaurentee that this value will be greater than 0, regardless of passed variables.
|
||||
const size_t copiedCharLen = std::min<size_t>(std::max<size_t>(0, maxBufferSize - 1), source.length());
|
||||
if (!source.empty() && maxBufferSize > 0) {
|
||||
memset(buffer, 0, maxBufferSize);
|
||||
const size_t copiedCharLen = std::min<size_t>(maxBufferSize - 1, source.length());
|
||||
memcpy(buffer, source.c_str(), copiedCharLen);
|
||||
return copiedCharLen;
|
||||
}
|
||||
@@ -408,10 +407,7 @@ bool SohUtils::IsStringEmpty(std::string str) {
|
||||
std::string::size_type end = str.find_last_not_of(' ');
|
||||
|
||||
// Check if the string is empty after stripping spaces
|
||||
if (start == std::string::npos || end == std::string::npos)
|
||||
return true; // The string is empty
|
||||
else
|
||||
return false; // The string is not empty
|
||||
return start == std::string::npos || end == std::string::npos;
|
||||
}
|
||||
|
||||
uint32_t SohUtils::Hash(std::string str) {
|
||||
|
||||
@@ -71,12 +71,6 @@ s32 OnePointCutscene_SetInfo(PlayState* play, s16 camIdx, s16 csId, Actor* actor
|
||||
f32 tempRand;
|
||||
Unique9OnePointCs* csInfo = ONEPOINT_CS_INFO(csCam);
|
||||
|
||||
// #region SOH [Enhancement]
|
||||
// the default is 90, lower values necessary to prevent camera swing as animation speeds up
|
||||
s16 camCrawlTemp = CVarGetInteger(CVAR_ENHANCEMENT("CrawlSpeed"), 1);
|
||||
s16 camCrawlTimer = D_8012042C / camCrawlTemp;
|
||||
// #endregion
|
||||
|
||||
switch (csId) {
|
||||
case 1020:
|
||||
if (timer < 20) {
|
||||
@@ -336,26 +330,19 @@ s32 OnePointCutscene_SetInfo(PlayState* play, s16 camIdx, s16 csId, Actor* actor
|
||||
case 9601:
|
||||
Play_CameraChangeSetting(play, camIdx, CAM_SET_CS_3);
|
||||
Play_CameraChangeSetting(play, MAIN_CAM, mainCam->prevSetting);
|
||||
if (CVarGetInteger(CVAR_ENHANCEMENT("CrawlSpeed"), 1) > 1) {
|
||||
OnePointCutscene_SetCsCamPoints(csCam, D_80120430 | 0x1000, camCrawlTimer, D_80120308, D_80120398);
|
||||
} else {
|
||||
if (GameInteractor_Should(VB_CRAWL_SPEED_EXIT_CS, true, csCam, csId, D_80120430, D_8012042C, D_80120308,
|
||||
D_80120398)) {
|
||||
OnePointCutscene_SetCsCamPoints(csCam, D_80120430 | 0x1000, D_8012042C, D_80120308, D_80120398);
|
||||
}
|
||||
break;
|
||||
case 9602:
|
||||
// #region SOH [Enhancement]
|
||||
if (CVarGetInteger(CVAR_ENHANCEMENT("CrawlSpeed"), 1) > 1) {
|
||||
Play_CameraChangeSetting(play, camIdx, CAM_SET_CS_3);
|
||||
Play_CameraChangeSetting(play, MAIN_CAM, mainCam->prevSetting);
|
||||
OnePointCutscene_SetCsCamPoints(csCam, D_80120430 | 0x1000, camCrawlTimer, D_80120308, D_80120434);
|
||||
break;
|
||||
// #endregion
|
||||
} else {
|
||||
Play_CameraChangeSetting(play, camIdx, CAM_SET_CS_3);
|
||||
Play_CameraChangeSetting(play, MAIN_CAM, mainCam->prevSetting);
|
||||
Play_CameraChangeSetting(play, camIdx, CAM_SET_CS_3);
|
||||
Play_CameraChangeSetting(play, MAIN_CAM, mainCam->prevSetting);
|
||||
if (GameInteractor_Should(VB_CRAWL_SPEED_EXIT_CS, true, csCam, csId, D_80120430, D_8012042C, D_80120308,
|
||||
D_80120434)) {
|
||||
OnePointCutscene_SetCsCamPoints(csCam, D_80120430 | 0x1000, D_8012042C, D_80120308, D_80120434);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 4175:
|
||||
csInfo->keyFrames = D_8012147C;
|
||||
csInfo->keyFrameCnt = 4;
|
||||
|
||||
@@ -2791,6 +2791,7 @@ void Interface_SetDoAction(PlayState* play, u16 action) {
|
||||
PauseContext* pauseCtx = &play->pauseCtx;
|
||||
|
||||
if (interfaceCtx->unk_1F0 != action) {
|
||||
GameInteractor_ExecuteOnSetDoAction(action);
|
||||
interfaceCtx->unk_1F0 = action;
|
||||
interfaceCtx->unk_1EC = 1;
|
||||
interfaceCtx->unk_1F4 = 0.0f;
|
||||
|
||||
@@ -7679,19 +7679,10 @@ s32 Player_TryEnteringCrawlspace(Player* this, PlayState* play, u32 interactWall
|
||||
this->actor.world.pos.z = zVertex1 + (distToInteractWall * wallPolyNormZ);
|
||||
func_80832224(this);
|
||||
this->actor.prevPos = this->actor.world.pos;
|
||||
// #region SOH [Enhancement]
|
||||
if (CVarGetInteger(CVAR_ENHANCEMENT("CrawlSpeed"), 1) > 1) {
|
||||
// increase animation speed when entering a tunnel
|
||||
LinkAnimation_Change(play, &this->skelAnime, &gPlayerAnim_link_child_tunnel_start,
|
||||
((CVarGetInteger(CVAR_ENHANCEMENT("CrawlSpeed"), 1) + 1.0f) / 2.0f), 0.0f,
|
||||
Animation_GetLastFrame(&gPlayerAnim_link_child_tunnel_start), ANIMMODE_ONCE,
|
||||
0.0f);
|
||||
Player_StartAnimMovement(play, this, 0x9D);
|
||||
// #endregion
|
||||
} else {
|
||||
if (GameInteractor_Should(VB_CRAWL_SPEED_ENTER, true)) {
|
||||
Player_AnimPlayOnce(play, this, &gPlayerAnim_link_child_tunnel_start);
|
||||
Player_StartAnimMovement(play, this, 0x9D);
|
||||
}
|
||||
Player_StartAnimMovement(play, this, 0x9D);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -7773,36 +7764,16 @@ s32 Player_TryLeavingCrawlspace(Player* this, PlayState* play) {
|
||||
if (ABS(yawToWall) > 0x4000) {
|
||||
Player_SetupAction(play, this, Player_Action_8084C81C, 0);
|
||||
|
||||
if (this->linearVelocity > 0.0f) {
|
||||
// Leaving a crawlspace forwards
|
||||
this->actor.shape.rot.y = this->actor.wallYaw + 0x8000;
|
||||
// #region SOH [Enhancement]
|
||||
if (CVarGetInteger(CVAR_ENHANCEMENT("CrawlSpeed"), 1) > 1) {
|
||||
LinkAnimation_Change(play, &this->skelAnime, &gPlayerAnim_link_child_tunnel_end,
|
||||
((CVarGetInteger(CVAR_ENHANCEMENT("CrawlSpeed"), 1) + 1.0f) / 2.0f), 0.0f,
|
||||
Animation_GetLastFrame(&gPlayerAnim_link_child_tunnel_end), ANIMMODE_ONCE,
|
||||
0.0f);
|
||||
Player_StartAnimMovement(play, this, 0x9D);
|
||||
OnePointCutscene_Init(play, 9601, 999, NULL, MAIN_CAM);
|
||||
// #endregion
|
||||
} else {
|
||||
if (GameInteractor_Should(VB_CRAWL_SPEED_EXIT, true)) {
|
||||
if (this->linearVelocity > 0.0f) {
|
||||
// Leaving a crawlspace forwards
|
||||
this->actor.shape.rot.y = this->actor.wallYaw + 0x8000;
|
||||
Player_AnimPlayOnce(play, this, &gPlayerAnim_link_child_tunnel_end);
|
||||
Player_StartAnimMovement(play, this, 0x9D);
|
||||
OnePointCutscene_Init(play, 9601, 999, NULL, MAIN_CAM);
|
||||
}
|
||||
} else {
|
||||
// Leaving a crawlspace backwards
|
||||
this->actor.shape.rot.y = this->actor.wallYaw;
|
||||
// #region SOH [Enhancement]
|
||||
if (CVarGetInteger(CVAR_ENHANCEMENT("CrawlSpeed"), 1) > 1) {
|
||||
LinkAnimation_Change(play, &this->skelAnime, &gPlayerAnim_link_child_tunnel_start,
|
||||
-1.0f * ((CVarGetInteger(CVAR_ENHANCEMENT("CrawlSpeed"), 1) + 1.0f) / 2.0f),
|
||||
Animation_GetLastFrame(&gPlayerAnim_link_child_tunnel_start), 0.0f,
|
||||
ANIMMODE_ONCE, 0.0f);
|
||||
Player_StartAnimMovement(play, this, 0x9D);
|
||||
OnePointCutscene_Init(play, 9602, 999, NULL, MAIN_CAM);
|
||||
// #endregion
|
||||
} else {
|
||||
// Leaving a crawlspace backwards
|
||||
this->actor.shape.rot.y = this->actor.wallYaw;
|
||||
LinkAnimation_Change(play, &this->skelAnime, &gPlayerAnim_link_child_tunnel_start, -1.0f,
|
||||
Animation_GetLastFrame(&gPlayerAnim_link_child_tunnel_start), 0.0f,
|
||||
ANIMMODE_ONCE, 0.0f);
|
||||
@@ -13587,12 +13558,7 @@ void Player_Action_8084C760(Player* this, PlayState* play) {
|
||||
|
||||
// player speed in a tunnel
|
||||
if (!Player_TryLeavingCrawlspace(this, play)) {
|
||||
// #region SOH [Enhancement]
|
||||
if (CVarGetInteger(CVAR_ENHANCEMENT("CrawlSpeed"), 1) > 1) {
|
||||
this->linearVelocity =
|
||||
sControlInput->rel.stick_y * 0.03f * CVarGetInteger(CVAR_ENHANCEMENT("CrawlSpeed"), 1);
|
||||
// #endregion
|
||||
} else {
|
||||
if (GameInteractor_Should(VB_CRAWL_SPEED_INCREASE, true)) {
|
||||
this->linearVelocity = sControlInput->rel.stick_y * 0.03f;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user