Merge branch 'develop' into SplitGF

This commit is contained in:
Pepper0ni 2025-06-05 18:15:53 +01:00
commit 216b666f24
50 changed files with 2438 additions and 3625 deletions

View file

@ -12,7 +12,6 @@ extern "C"
#include "luslog.h"
#include <soh/Enhancements/item-tables/ItemTableTypes.h>
#include <soh/Enhancements/randomizer/randomizer_inf.h>
#if defined(INCLUDE_GAME_PRINTF) && defined(_DEBUG)
#define osSyncPrintf(fmt, ...) lusprintf(__FILE__, __LINE__, 0, fmt, ##__VA_ARGS__)

View file

@ -5,7 +5,6 @@
#include "z64math.h"
#include "z64audio.h"
#include "soh/Enhancements/randomizer/randomizerTypes.h"
#include "soh/Enhancements/randomizer/randomizer_inf.h"
#include "soh/Enhancements/gameplaystats.h"
#include "soh/Enhancements/randomizer/randomizer_entrance.h"
#include "soh/Enhancements/boss-rush/BossRushTypes.h"

View file

@ -0,0 +1,42 @@
#include <libultraship/bridge.h>
#include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h"
#include "soh/ShipInit.hpp"
#include "functions.h"
#include "macros.h"
#include "variables.h"
extern "C" PlayState* gPlayState;
static constexpr int32_t CVAR_RUPEE_DASH_DEFAULT = 0;
#define CVAR_RUPEE_DASH_NAME CVAR_ENHANCEMENT("RupeeDash")
#define CVAR_RUPEE_DASH_VALUE CVarGetInteger(CVAR_RUPEE_DASH_NAME, CVAR_RUPEE_DASH_DEFAULT)
static constexpr int32_t CVAR_RUPEE_DASH_INTERVAL_DEFAULT = 5;
#define CVAR_RUPEE_DASH_INTERVAL_NAME CVAR_ENHANCEMENT("RupeeDashInterval")
#define CVAR_RUPEE_DASH_INTERVAL_TIME \
CVarGetInteger(CVAR_RUPEE_DASH_INTERVAL_NAME, CVAR_RUPEE_DASH_INTERVAL_DEFAULT) * 20
void UpdateRupeeDash() {
// Initialize Timer
static uint16_t rupeeDashTimer = 0;
// Did time change by DashInterval?
if (rupeeDashTimer < CVAR_RUPEE_DASH_INTERVAL_TIME) {
rupeeDashTimer++;
return;
}
rupeeDashTimer = 0;
if (gSaveContext.rupees > 0) {
uint16_t walletSize = (CUR_UPG_VALUE(UPG_WALLET) + 1) * -1;
Rupees_ChangeBy(walletSize);
} else {
Health_ChangeBy(gPlayState, -16);
}
}
void RegisterRupeeDash() {
COND_HOOK(OnPlayerUpdate, CVAR_RUPEE_DASH_VALUE, UpdateRupeeDash);
}
static RegisterShipInitFunc initFunc_RupeeDash(RegisterRupeeDash, { CVAR_RUPEE_DASH_NAME });

View file

@ -193,23 +193,22 @@ void ParsePreset(nlohmann::json& json, std::string name) {
}
void LoadPresets() {
if (!fs::exists(presetFolder)) {
return;
}
if (!presets.empty()) {
presets.clear();
}
for (auto const& preset : fs::directory_iterator(presetFolder)) {
std::ifstream ifs(preset.path());
if (fs::exists(presetFolder)) {
for (auto const& preset : fs::directory_iterator(presetFolder)) {
std::ifstream ifs(preset.path());
auto json = nlohmann::json::parse(ifs);
if (!json.contains("presetName")) {
spdlog::error(fmt::format("Attempted to load file {} as a preset, but was not a preset file.",
preset.path().filename().string()));
} else {
ParsePreset(json, preset.path().filename().stem().string());
auto json = nlohmann::json::parse(ifs);
if (!json.contains("presetName")) {
spdlog::error(fmt::format("Attempted to load file {} as a preset, but was not a preset file.",
preset.path().filename().string()));
} else {
ParsePreset(json, preset.path().filename().stem().string());
}
ifs.close();
}
ifs.close();
}
auto initData = std::make_shared<Ship::ResourceInitData>();
initData->Format = RESOURCE_FORMAT_BINARY;

View file

@ -0,0 +1,25 @@
#include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h"
#include "soh/ShipInit.hpp"
extern "C" {
#include "src/overlays/actors/ovl_En_Ice_Hono/z_en_ice_hono.h"
void EnIceHono_CapturableFlame(EnIceHono* thisx, PlayState* play);
u32 EnIceHono_InBottleRange(EnIceHono* thisx, PlayState* play);
}
extern PlayState* gPlayState;
void OnEnIceHonoUpdate(void* actor) {
EnIceHono* thisx = (EnIceHono*)actor;
if (thisx->actionFunc != EnIceHono_CapturableFlame && EnIceHono_InBottleRange(thisx, gPlayState)) {
// GI_MAX in this case allows the player to catch the actor in a bottle
Actor_OfferGetItem(&thisx->actor, gPlayState, GI_MAX, 60.0f, 100.0f);
}
}
void RegisterRebottleBlueFire() {
COND_ID_HOOK(OnActorUpdate, ACTOR_EN_ICE_HONO, CVarGetInteger(CVAR_ENHANCEMENT("RebottleBlueFire"), 0),
OnEnIceHonoUpdate);
}
static RegisterShipInitFunc initFunc(RegisterRebottleBlueFire, { CVAR_ENHANCEMENT("RebottleBlueFire") });

View file

@ -0,0 +1,8 @@
#include "soh/Enhancements/game-interactor/GameInteractor.h"
#include "soh/ShipInit.hpp"
void RegisterSkipAmyPuzzle() {
COND_VB_SHOULD(VB_AMY_SOLVE, CVarGetInteger(CVAR_ENHANCEMENT("SkipAmyPuzzle"), 0), { *should = true; });
}
static RegisterShipInitFunc initFunc(RegisterSkipAmyPuzzle, { CVAR_ENHANCEMENT("SkipAmyPuzzle") });

View file

@ -10,8 +10,6 @@ extern "C" {
#include "variables.h"
}
#define RAND_GET_OPTION(option) Rando::Context::GetInstance()->GetOption(option).Get()
static bool sEnteredBlueWarp = false;
/**

View file

@ -7,6 +7,7 @@
#include "soh/Enhancements/nametag.h"
#include "soh/ShipInit.hpp"
#include <algorithm>
#include <array>
#include <bit>
#include <map>
@ -45,13 +46,6 @@ typedef struct {
Vec3s rot;
} ActorInfo;
typedef enum {
LIST,
TARGET,
HELD,
INTERACT,
} RetrievalMethod;
std::array<const char*, 12> acMapping = {
"Switch", "Background (Prop type 1)",
"Player", "Bomb",
@ -873,37 +867,14 @@ void ActorViewer_AddTagForAllActors() {
void ActorViewerWindow::DrawElement() {
ImGui::BeginDisabled(CVarGetInteger(CVAR_SETTING("DisableChanges"), 0));
static Actor* display;
static Actor empty{};
static Actor* fetch = NULL;
static ActorInfo newActor = { 0, 0, { 0, 0, 0 }, { 0, 0, 0 } };
static bool needs_reset = false;
static ImU16 one = 1;
static int actor;
static int category = 0;
static RetrievalMethod rm;
static std::string filler = "Please select";
static std::vector<Actor*> list;
static u16 lastSceneId = 0;
static std::string searchString = "";
static s16 currentSelectedInDropdown;
static std::vector<u16> actors;
static s16 currentSelectedInDropdown = -1;
static std::vector<u16> actorSearchResults;
if (gPlayState != nullptr) {
needs_reset = lastSceneId != gPlayState->sceneNum;
if (needs_reset) {
display = &empty;
fetch = nullptr;
actor = category = 0;
filler = "Please Select";
list.clear();
needs_reset = false;
searchString = "";
currentSelectedInDropdown = -1;
actors.clear();
}
lastSceneId = gPlayState->sceneNum;
if (ImGui::BeginChild("options", ImVec2(0, 0), ImGuiChildFlags_Border | ImGuiChildFlags_AutoResizeY)) {
bool toggled = false;
bool optionChange = false;
@ -967,21 +938,19 @@ void ActorViewerWindow::DrawElement() {
ImGui::EndCombo();
}
if (display == nullptr) {
filler = "Please select";
}
if (ImGui::BeginCombo("Actor", filler.c_str())) {
if (gPlayState != nullptr && lastSceneId != gPlayState->sceneNum) {
PopulateActorDropdown(category, list);
lastSceneId = gPlayState->sceneNum;
}
for (int i = 0; i < list.size(); i++) {
std::string label = std::to_string(i) + ": " + ActorDB::Instance->RetrieveEntry(list[i]->id).name;
std::string description = GetActorDescription(list[i]->id);
if (description != "")
label += " (" + description + ")";
if (ImGui::Selectable(label.c_str())) {
rm = LIST;
if (ImGui::Selectable(label.c_str(), list[i] == display)) {
display = list[i];
actor = i;
filler = label;
break;
}
@ -992,88 +961,76 @@ void ActorViewerWindow::DrawElement() {
PushStyleHeader(THEME_COLOR);
if (ImGui::TreeNode("Selected Actor")) {
DrawGroupWithBorder(
[&]() {
ImGui::Text("Name: %s", ActorDB::Instance->RetrieveEntry(display->id).name.c_str());
ImGui::Text("Description: %s", GetActorDescription(display->id).c_str());
ImGui::Text("Category: %s", acMapping[display->category]);
ImGui::Text("ID: %d", display->id);
ImGui::Text("Parameters: %d", display->params);
ImGui::Text("Actor List Index: %d", GetActorListIndex(display));
},
"Selected Actor");
ImGui::SameLine();
ImGui::PushItemWidth(ImGui::GetFontSize() * 6);
if (display != nullptr) {
DrawGroupWithBorder(
[&]() {
ImGui::Text("Name: %s", ActorDB::Instance->RetrieveEntry(display->id).name.c_str());
ImGui::Text("Description: %s", GetActorDescription(display->id).c_str());
ImGui::Text("Category: %s", acMapping[display->category]);
ImGui::Text("ID: %d", display->id);
ImGui::Text("Parameters: %d", display->params);
ImGui::Text("Actor List Index: %d", GetActorListIndex(display));
},
"Selected Actor");
ImGui::SameLine();
ImGui::PushItemWidth(ImGui::GetFontSize() * 6);
DrawGroupWithBorder(
[&]() {
ImGui::PushItemWidth(ImGui::GetFontSize() * 6);
DrawGroupWithBorder(
[&]() {
ImGui::PushItemWidth(ImGui::GetFontSize() * 6);
PushStyleInput(THEME_COLOR);
ImGui::Text("Actor Position");
ImGui::InputScalar("X##CurPos", ImGuiDataType_Float, &display->world.pos.x);
ImGui::InputScalar("Y##CurPos", ImGuiDataType_Float, &display->world.pos.y);
ImGui::InputScalar("Z##CurPos", ImGuiDataType_Float, &display->world.pos.z);
ImGui::PopItemWidth();
PopStyleInput();
},
"Actor Position");
ImGui::SameLine();
DrawGroupWithBorder(
[&]() {
PushStyleInput(THEME_COLOR);
ImGui::PushItemWidth(ImGui::GetFontSize() * 6);
ImGui::Text("Actor Rotation");
ImGui::InputScalar("X##CurRot", ImGuiDataType_S16, &display->world.rot.x);
ImGui::InputScalar("Y##CurRot", ImGuiDataType_S16, &display->world.rot.y);
ImGui::InputScalar("Z##CurRot", ImGuiDataType_S16, &display->world.rot.z);
ImGui::PopItemWidth();
PopStyleInput();
},
"Actor Rotation");
if (display->category == ACTORCAT_BOSS || display->category == ACTORCAT_ENEMY) {
PushStyleInput(THEME_COLOR);
ImGui::Text("Actor Position");
ImGui::InputScalar("X##CurPos", ImGuiDataType_Float, &display->world.pos.x);
ImGui::InputScalar("Y##CurPos", ImGuiDataType_Float, &display->world.pos.y);
ImGui::InputScalar("Z##CurPos", ImGuiDataType_Float, &display->world.pos.z);
ImGui::PopItemWidth();
ImGui::InputScalar("Enemy Health", ImGuiDataType_U8, &display->colChkInfo.health);
PopStyleInput();
},
"Actor Position");
ImGui::SameLine();
DrawGroupWithBorder(
[&]() {
PushStyleInput(THEME_COLOR);
ImGui::PushItemWidth(ImGui::GetFontSize() * 6);
ImGui::Text("Actor Rotation");
ImGui::InputScalar("X##CurRot", ImGuiDataType_S16, &display->world.rot.x);
ImGui::InputScalar("Y##CurRot", ImGuiDataType_S16, &display->world.rot.y);
ImGui::InputScalar("Z##CurRot", ImGuiDataType_S16, &display->world.rot.z);
ImGui::PopItemWidth();
PopStyleInput();
},
"Actor Rotation");
if (display->category == ACTORCAT_BOSS || display->category == ACTORCAT_ENEMY) {
PushStyleInput(THEME_COLOR);
ImGui::InputScalar("Enemy Health", ImGuiDataType_U8, &display->colChkInfo.health);
PopStyleInput();
UIWidgets::InsertHelpHoverText("Some actors might not use this!");
}
DrawGroupWithBorder(
[&]() {
ImGui::Text("flags");
UIWidgets::DrawFlagArray32("flags", display->flags);
},
"flags");
ImGui::SameLine();
DrawGroupWithBorder(
[&]() {
ImGui::Text("bgCheckFlags");
UIWidgets::DrawFlagArray16("bgCheckFlags", display->bgCheckFlags);
},
"bgCheckFlags");
if (Button("Refresh", ButtonOptions().Color(THEME_COLOR))) {
PopulateActorDropdown(category, list);
switch (rm) {
case INTERACT:
case HELD:
case TARGET:
display = fetch;
break;
case LIST:
display = list[actor];
break;
default:
break;
UIWidgets::InsertHelpHoverText("Some actors might not use this!");
}
}
if (Button("Go to Actor", ButtonOptions().Color(THEME_COLOR))) {
Player* player = GET_PLAYER(gPlayState);
Math_Vec3f_Copy(&player->actor.world.pos, &display->world.pos);
Math_Vec3f_Copy(&player->actor.home.pos, &player->actor.world.pos);
DrawGroupWithBorder(
[&]() {
ImGui::Text("flags");
UIWidgets::DrawFlagArray32("flags", display->flags);
},
"flags");
ImGui::SameLine();
DrawGroupWithBorder(
[&]() {
ImGui::Text("bgCheckFlags");
UIWidgets::DrawFlagArray16("bgCheckFlags", display->bgCheckFlags);
},
"bgCheckFlags");
if (Button("Go to Actor", ButtonOptions().Color(THEME_COLOR))) {
Player* player = GET_PLAYER(gPlayState);
Math_Vec3f_Copy(&player->actor.world.pos, &display->world.pos);
Math_Vec3f_Copy(&player->actor.home.pos, &player->actor.world.pos);
}
} else {
ImGui::Text("Select an actor to display information.");
}
if (Button("Fetch from Target",
@ -1081,34 +1038,28 @@ void ActorViewerWindow::DrawElement() {
.Color(THEME_COLOR)
.Tooltip("Grabs actor with target arrow above it. You might need C-Up for enemies"))) {
Player* player = GET_PLAYER(gPlayState);
fetch = player->talkActor;
if (fetch != NULL) {
display = fetch;
category = fetch->category;
if (player->talkActor != NULL) {
display = player->talkActor;
category = display->category;
PopulateActorDropdown(category, list);
rm = TARGET;
}
}
if (Button("Fetch from Held",
ButtonOptions().Color(THEME_COLOR).Tooltip("Grabs actor that Link is holding"))) {
Player* player = GET_PLAYER(gPlayState);
fetch = player->heldActor;
if (fetch != NULL) {
display = fetch;
category = fetch->category;
if (player->heldActor != NULL) {
display = player->heldActor;
category = display->category;
PopulateActorDropdown(category, list);
rm = HELD;
}
}
if (Button("Fetch from Interaction",
ButtonOptions().Color(THEME_COLOR).Tooltip("Grabs actor from \"interaction range\""))) {
Player* player = GET_PLAYER(gPlayState);
fetch = player->interactRangeActor;
if (fetch != NULL) {
display = fetch;
category = fetch->category;
if (player->interactRangeActor != NULL) {
display = player->interactRangeActor;
category = display->category;
PopulateActorDropdown(category, list);
rm = INTERACT;
}
}
@ -1119,21 +1070,22 @@ void ActorViewerWindow::DrawElement() {
// ImGui::PushItemWidth(ImGui::GetFontSize() * 10);
if (InputString("Search Actor", &searchString, InputOptions().Color(THEME_COLOR))) {
actors = GetActorsWithDescriptionContainingString(searchString);
actorSearchResults = GetActorsWithDescriptionContainingString(searchString);
currentSelectedInDropdown = -1;
}
if (!SohUtils::IsStringEmpty(searchString) && !actors.empty()) {
std::string preview = currentSelectedInDropdown == -1
? "Please Select"
: ActorDB::Instance->RetrieveEntry(actors[currentSelectedInDropdown]).desc;
if (!SohUtils::IsStringEmpty(searchString) && !actorSearchResults.empty()) {
std::string preview =
currentSelectedInDropdown == -1
? "Please Select"
: ActorDB::Instance->RetrieveEntry(actorSearchResults[currentSelectedInDropdown]).desc;
PushStyleCombobox(THEME_COLOR);
if (ImGui::BeginCombo("Results", preview.c_str())) {
for (u8 i = 0; i < actors.size(); i++) {
if (ImGui::Selectable(ActorDB::Instance->RetrieveEntry(actors[i]).desc.c_str(),
for (u8 i = 0; i < actorSearchResults.size(); i++) {
if (ImGui::Selectable(ActorDB::Instance->RetrieveEntry(actorSearchResults[i]).desc.c_str(),
i == currentSelectedInDropdown)) {
currentSelectedInDropdown = i;
newActor.id = actors[i];
newActor.id = actorSearchResults[i];
}
}
ImGui::EndCombo();
@ -1237,20 +1189,40 @@ void ActorViewerWindow::DrawElement() {
PopStyleHeader();
} else {
ImGui::Text("Global Context needed for actor info!");
if (needs_reset) {
fetch = nullptr;
actor = category = 0;
filler = "Please Select";
list.clear();
needs_reset = false;
searchString = "";
currentSelectedInDropdown = -1;
actors.clear();
}
}
ImGui::EndDisabled();
}
void ActorViewerWindow::InitElement() {
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnActorSpawn>([this](void* refActor) {
Actor* actor = static_cast<Actor*>(refActor);
// Reload actor list if the new actor belongs to the selected category
if (category == actor->category) {
PopulateActorDropdown(actor->category, list);
}
});
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnActorDestroy>([this](void* refActor) {
Actor* actor = static_cast<Actor*>(refActor);
// If the actor belongs to the selected category, we need to manually remove it, as it has not been removed from
// the global actor array yet
if (category == actor->category) {
list.erase(std::remove(list.begin(), list.end(), actor), list.end());
}
if (display == actor) {
display = nullptr;
}
});
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnSceneInit>([this](int16_t sceneNum) {
display = nullptr;
category = ACTORCAT_SWITCH;
list.clear();
});
}
void ActorViewer_RegisterNameTagHooks() {
COND_HOOK(OnActorInit, CVAR_ACTOR_NAME_TAGS_ENABLED,
[](void* actor) { ActorViewer_AddTagForActor(static_cast<Actor*>(actor)); });

View file

@ -2,11 +2,20 @@
#include <libultraship/libultraship.h>
#include "z64actor.h"
#include <vector>
class ActorViewerWindow final : public Ship::GuiWindow {
public:
using GuiWindow::GuiWindow;
void DrawElement() override;
void InitElement() override{};
void InitElement() override;
void UpdateElement() override{};
private:
Actor* display = nullptr;
int category = ACTORCAT_SWITCH;
std::vector<Actor*> list;
};

View file

@ -554,6 +554,10 @@ void DrawFlagTableArray16(const FlagTable& flagTable, uint16_t row, uint16_t& fl
uint32_t bitMask = 1 << flagIndex;
ImVec4 themeColor = ColorValues.at(THEME_COLOR);
ImVec4 colorDark = { themeColor.x * 0.4f, themeColor.y * 0.4f, themeColor.z * 0.4f, themeColor.z };
ImVec4& color = themeColor;
if (!hasDescription) {
color = colorDark;
}
PushStyleCheckbox(hasDescription ? themeColor : colorDark);
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(4.0f, 3.0f));
bool flag = (flags & bitMask) != 0;

File diff suppressed because it is too large Load diff

View file

@ -13,15 +13,29 @@ extern "C" {
#include <z64.h>
}
const char* enemyCVarList[] = {
CVAR_ENHANCEMENT("RandomizedEnemyList.Armos"), CVAR_ENHANCEMENT("RandomizedEnemyList.Arwing"),
CVAR_ENHANCEMENT("RandomizedEnemyList.BabyDodongo"), CVAR_ENHANCEMENT("RandomizedEnemyList.Bari"),
CVAR_ENHANCEMENT("RandomizedEnemyList.Beamos"), CVAR_ENHANCEMENT("RandomizedEnemyList.BigSkulltula"),
CVAR_ENHANCEMENT("RandomizedEnemyList.BigStalchild"), CVAR_ENHANCEMENT("RandomizedEnemyList.Biri"),
CVAR_ENHANCEMENT("RandomizedEnemyList.BlackKnuckle"), CVAR_ENHANCEMENT("RandomizedEnemyList.BlueTektite"),
CVAR_ENHANCEMENT("RandomizedEnemyList.Bubble"), CVAR_ENHANCEMENT("RandomizedEnemyList.ClubMoblin"),
CVAR_ENHANCEMENT("RandomizedEnemyList.DarkLink"), CVAR_ENHANCEMENT("RandomizedEnemyList.Dinolfos"),
CVAR_ENHANCEMENT("RandomizedEnemyList.Dodongo"), CVAR_ENHANCEMENT("RandomizedEnemyList.FireKeese"),
#define CVAR_ENEMY_RANDOMIZER_NAME CVAR_ENHANCEMENT("RandomizedEnemies")
#define CVAR_ENEMY_RANDOMIZER_DEFAULT ENEMY_RANDOMIZER_OFF
#define CVAR_ENEMY_RANDOMIZER_VALUE CVarGetInteger(CVAR_ENEMY_RANDOMIZER_NAME, CVAR_ENEMY_RANDOMIZER_DEFAULT)
typedef struct EnemyEntry {
int16_t id;
int16_t params;
} EnemyEntry;
bool IsEnemyFoundToRandomize(int16_t sceneNum, int8_t roomNum, int16_t actorId, int16_t params, float posX);
bool IsEnemyAllowedToSpawn(int16_t sceneNum, int8_t roomNum, EnemyEntry enemy);
EnemyEntry GetRandomizedEnemyEntry(uint32_t seed);
const char* enemyCVarList[RANDOMIZED_ENEMY_SPAWN_TABLE_SIZE] = {
CVAR_ENHANCEMENT("RandomizedEnemyList.Anubis"), CVAR_ENHANCEMENT("RandomizedEnemyList.Armos"),
CVAR_ENHANCEMENT("RandomizedEnemyList.Arwing"), CVAR_ENHANCEMENT("RandomizedEnemyList.BabyDodongo"),
CVAR_ENHANCEMENT("RandomizedEnemyList.Bari"), CVAR_ENHANCEMENT("RandomizedEnemyList.Beamos"),
CVAR_ENHANCEMENT("RandomizedEnemyList.BigSkulltula"), CVAR_ENHANCEMENT("RandomizedEnemyList.BigStalchild"),
CVAR_ENHANCEMENT("RandomizedEnemyList.Biri"), CVAR_ENHANCEMENT("RandomizedEnemyList.BlackKnuckle"),
CVAR_ENHANCEMENT("RandomizedEnemyList.BlueTektite"), CVAR_ENHANCEMENT("RandomizedEnemyList.Bubble"),
CVAR_ENHANCEMENT("RandomizedEnemyList.ClubMoblin"), CVAR_ENHANCEMENT("RandomizedEnemyList.DarkLink"),
CVAR_ENHANCEMENT("RandomizedEnemyList.Dinolfos"), CVAR_ENHANCEMENT("RandomizedEnemyList.Dodongo"),
CVAR_ENHANCEMENT("RandomizedEnemyList.FireKeese"), /*CVAR_ENHANCEMENT("RandomizedEnemyList.FlareDancer"),*/
CVAR_ENHANCEMENT("RandomizedEnemyList.FloorTile"), CVAR_ENHANCEMENT("RandomizedEnemyList.Floormaster"),
CVAR_ENHANCEMENT("RandomizedEnemyList.FlyingPeahat"), CVAR_ENHANCEMENT("RandomizedEnemyList.FlyingPot"),
CVAR_ENHANCEMENT("RandomizedEnemyList.Freezard"), CVAR_ENHANCEMENT("RandomizedEnemyList.Gibdo"),
@ -30,18 +44,20 @@ const char* enemyCVarList[] = {
CVAR_ENHANCEMENT("RandomizedEnemyList.Keese"), CVAR_ENHANCEMENT("RandomizedEnemyList.LargeBaba"),
CVAR_ENHANCEMENT("RandomizedEnemyList.LikeLike"), CVAR_ENHANCEMENT("RandomizedEnemyList.Lizalfos"),
CVAR_ENHANCEMENT("RandomizedEnemyList.MadScrub"), CVAR_ENHANCEMENT("RandomizedEnemyList.NormalWolfos"),
CVAR_ENHANCEMENT("RandomizedEnemyList.PeahatLarva"), CVAR_ENHANCEMENT("RandomizedEnemyList.Redead"),
CVAR_ENHANCEMENT("RandomizedEnemyList.RedTektite"), CVAR_ENHANCEMENT("RandomizedEnemyList.Shabom"),
CVAR_ENHANCEMENT("RandomizedEnemyList.ShellBlade"), CVAR_ENHANCEMENT("RandomizedEnemyList.Skulltula"),
CVAR_ENHANCEMENT("RandomizedEnemyList.PeahatLarva"), /*CVAR_ENHANCEMENT("RandomizedEnemyList.Poe"),*/
CVAR_ENHANCEMENT("RandomizedEnemyList.Redead"), CVAR_ENHANCEMENT("RandomizedEnemyList.RedTektite"),
CVAR_ENHANCEMENT("RandomizedEnemyList.Shabom"), CVAR_ENHANCEMENT("RandomizedEnemyList.ShellBlade"),
CVAR_ENHANCEMENT("RandomizedEnemyList.Skulltula"), CVAR_ENHANCEMENT("RandomizedEnemyList.SkullKid"),
CVAR_ENHANCEMENT("RandomizedEnemyList.SmallBaba"), CVAR_ENHANCEMENT("RandomizedEnemyList.SmallStalchild"),
CVAR_ENHANCEMENT("RandomizedEnemyList.Spike"), CVAR_ENHANCEMENT("RandomizedEnemyList.Stalfos"),
CVAR_ENHANCEMENT("RandomizedEnemyList.Stinger"), CVAR_ENHANCEMENT("RandomizedEnemyList.Tailparasan"),
CVAR_ENHANCEMENT("RandomizedEnemyList.TorchSlug"), CVAR_ENHANCEMENT("RandomizedEnemyList.Wallmaster"),
CVAR_ENHANCEMENT("RandomizedEnemyList.WhiteKnuckle"), CVAR_ENHANCEMENT("RandomizedEnemyList.WhiteWolfos"),
CVAR_ENHANCEMENT("RandomizedEnemyList.WitheredBaba"),
CVAR_ENHANCEMENT("RandomizedEnemyList.SpearMoblin"), CVAR_ENHANCEMENT("RandomizedEnemyList.Spike"),
CVAR_ENHANCEMENT("RandomizedEnemyList.Stalfos"), CVAR_ENHANCEMENT("RandomizedEnemyList.Stinger"),
CVAR_ENHANCEMENT("RandomizedEnemyList.Tailparasan"), CVAR_ENHANCEMENT("RandomizedEnemyList.TorchSlug"),
CVAR_ENHANCEMENT("RandomizedEnemyList.Wallmaster"), CVAR_ENHANCEMENT("RandomizedEnemyList.WhiteKnuckle"),
CVAR_ENHANCEMENT("RandomizedEnemyList.WhiteWolfos"), CVAR_ENHANCEMENT("RandomizedEnemyList.WitheredBaba"),
};
const char* enemyNameList[] = {
const char* enemyNameList[RANDOMIZED_ENEMY_SPAWN_TABLE_SIZE] = {
"Anubis",
"Armos",
"Arwing",
"Baby Dodongo",
@ -58,6 +74,7 @@ const char* enemyNameList[] = {
"Dinolfos",
"Dodongo",
"Fire Keese",
//"Flare Dancer",
"Floor Tile",
"Floormaster",
"Flying Peahat",
@ -75,13 +92,16 @@ const char* enemyNameList[] = {
"Mad Scrub",
"Wolfos (Normal)",
"Peahat Larva",
//"Poe",
"Redead",
"Red Tektite",
"Shabom",
"Shell Blade",
"Skulltula",
"Skull Kid",
"Small Deku Baba",
"Stalchild (Small)",
"Spear Moblin",
"Spike",
"Stalfos",
"Stinger",
@ -94,98 +114,105 @@ const char* enemyNameList[] = {
};
static EnemyEntry randomizedEnemySpawnTable[RANDOMIZED_ENEMY_SPAWN_TABLE_SIZE] = {
{ ACTOR_EN_AM, -1 }, // Armos
{ ACTOR_EN_CLEAR_TAG, 1 }, // Arwing
{ ACTOR_EN_DODOJR, 0 }, // Baby Dodongo
{ ACTOR_EN_VALI, -1 }, // Bari (big jellyfish)
{ ACTOR_EN_VM, 1280 }, // Beamos
{ ACTOR_EN_ST, 1 }, // Skulltula (big)
{ ACTOR_EN_SKB, 20 }, // Stalchild (big)
{ ACTOR_EN_BILI, 0 }, // Biri (jellyfish)
{ ACTOR_EN_IK, 2 }, // Iron Knuckle (black, standing)
{ ACTOR_EN_TITE, -2 }, // Tektite (blue)
{ ACTOR_EN_BB, -1 }, // Bubble (flying skull enemy) (blue)
{ ACTOR_EN_MB, 0 }, // Moblins (Club)
{ ACTOR_EN_TORCH2, 0 }, // Dark Link
{ ACTOR_EN_ZF, -2 }, // Dinolfos
{ ACTOR_EN_DODONGO, -1 }, // Dodongo
{ ACTOR_EN_FIREFLY, 1 }, // Fire Keese
{ ACTOR_EN_YUKABYUN, 0 }, // Flying Floor Tile
{ ACTOR_EN_FLOORMAS, 0 }, // Floormaster
{ ACTOR_EN_PEEHAT, -1 }, // Flying Peahat (big grounded, doesn't spawn larva)
{ ACTOR_EN_TUBO_TRAP, 0 }, // Flying pot
{ ACTOR_EN_FZ, 0 }, // Freezard
{ ACTOR_EN_RD, 32766 }, // Gibdo (standing)
{ ACTOR_EN_GOMA, 7 }, // Gohma Larva (Non-Gohma rooms)
{ ACTOR_EN_CROW, 0 }, // Guay
{ ACTOR_EN_FIREFLY, 4 }, // Ice Keese
{ ACTOR_EN_ST, 2 }, // Skulltula (invisible)
{ ACTOR_EN_FIREFLY, 2 }, // Regular Keese
{ ACTOR_EN_DEKUBABA, 1 }, // Deku Baba (large)
{ ACTOR_EN_ANUBICE_TAG, 1 }, // Anubis
{ ACTOR_EN_AM, -1 }, // Armos
{ ACTOR_EN_CLEAR_TAG, 1 }, // Arwing
{ ACTOR_EN_DODOJR, 0 }, // Baby Dodongo
{ ACTOR_EN_VALI, -1 }, // Bari (big jellyfish)
{ ACTOR_EN_VM, 1280 }, // Beamos
{ ACTOR_EN_ST, 1 }, // Skulltula (big)
{ ACTOR_EN_SKB, 20 }, // Stalchild (big)
{ ACTOR_EN_BILI, 0 }, // Biri (jellyfish)
{ ACTOR_EN_IK, 2 }, // Iron Knuckle (black, standing)
{ ACTOR_EN_TITE, -2 }, // Tektite (blue)
{ ACTOR_EN_BB, -1 }, // Bubble (flying skull enemy) (blue)
{ ACTOR_EN_MB, 0 }, // Club Moblin
{ ACTOR_EN_TORCH2, 0 }, // Dark Link
{ ACTOR_EN_ZF, -2 }, // Dinolfos
{ ACTOR_EN_DODONGO, -1 }, // Dodongo
{ ACTOR_EN_FIREFLY, 1 }, // Fire Keese
// { ACTOR_EN_FD, 0 }, // Flare Dancer (possible cause of crashes because of spawning flame actors on
// sloped ground)
{ ACTOR_EN_YUKABYUN, 0 }, // Flying Floor Tile
{ ACTOR_EN_FLOORMAS, 0 }, // Floormaster
{ ACTOR_EN_PEEHAT, -1 }, // Flying Peahat (big grounded, doesn't spawn larva)
{ ACTOR_EN_TUBO_TRAP, 0 }, // Flying pot
{ ACTOR_EN_FZ, 0 }, // Freezard
{ ACTOR_EN_RD, 32766 }, // Gibdo (standing)
{ ACTOR_EN_GOMA, 7 }, // Gohma Larva (Non-Gohma rooms)
{ ACTOR_EN_CROW, 0 }, // Guay
{ ACTOR_EN_FIREFLY, 4 }, // Ice Keese
{ ACTOR_EN_ST, 2 }, // Skulltula (invisible)
{ ACTOR_EN_FIREFLY, 2 }, // Regular Keese
{ ACTOR_EN_DEKUBABA, 1 }, // Deku Baba (large)
// Doesn't work (reliant on surface and also normally used in tandem with a leever spawner, kills itself too quickly
// otherwise) { ACTOR_EN_REEBA, 0 }, // Leever
{ ACTOR_EN_RR, 0 }, // Like-Like
{ ACTOR_EN_ZF, -1 }, // Lizalfos
{ ACTOR_EN_DEKUNUTS, 768 }, // Mad Scrub (triple attack) (projectiles don't work)
{ ACTOR_EN_WF, 0 }, // Wolfos (normal)
{ ACTOR_EN_PEEHAT, 1 }, // Flying Peahat Larva
{ ACTOR_EN_RD, 1 }, // Redead (standing)
{ ACTOR_EN_TITE, -1 }, // Tektite (red)
{ ACTOR_EN_BUBBLE, 0 }, // Shabom (bubble)
{ ACTOR_EN_SB, 0 }, // Shell Blade
{ ACTOR_EN_ST, 0 }, // Skulltula (normal)
{ ACTOR_EN_DEKUBABA, 0 }, // Deku Baba (small)
{ ACTOR_EN_SKB, 1 }, // Stalchild (small)
{ ACTOR_EN_NY, 0 }, // Spike (rolling enemy)
{ ACTOR_EN_TEST, 2 }, // Stalfos
{ ACTOR_EN_EIYER, 10 }, // Stinger (land) (One in formation, sink under floor and do not activate)
{ ACTOR_EN_TP, -1 }, // Electric Tailpasaran
{ ACTOR_EN_BW, 0 }, // Torch Slug
{ ACTOR_EN_WALLMAS, 1 }, // Wallmaster
{ ACTOR_EN_IK, 3 }, // Iron Knuckle (white, standing)
{ ACTOR_EN_WF, 1 }, // Wolfos (white)
{ ACTOR_EN_KAREBABA, 0 }, // Withered Deku Baba
// Doesn't work {ACTOR_EN_POH, 0}, // Poe (Seems to rely on other objects?)
// Doesn't work {ACTOR_EN_POH, 2}, // Poe (composer Sharp) (Seems to rely on other objects?)
// Doesn't work {ACTOR_EN_POH, 3}, // Poe (composer Flat) (Seems to rely on other objects?)
// Doesn't work {ACTOR_EN_OKUTA, 0}, // Octorok (actor directly uses water box collision to handle hiding/popping
// up) Doesn't work {ACTOR_EN_REEBA, 0}, // Leever (reliant on surface and also normally used in tandem with a
// leever spawner, kills itself too quickly otherwise) Kinda doesn't work { ACTOR_EN_FD, 0 }, // Flare Dancer (jumps
// out of bounds a lot, and possible cause of crashes because of spawning a ton of flame actors)
// Doesn't work (actor directly uses water box collision to handle hiding/popping up)
// { ACTOR_EN_OKUTA, 0 }, // Octorok
{ ACTOR_EN_PEEHAT, 1 }, // Flying Peahat Larva
// Doesn't work (Seems to rely on other objects?)
// { ACTOR_EN_POH, 0 }, // Poe
// Doesn't work (Seems to rely on other objects?)
// { ACTOR_EN_POH, 2 }, // Poe (composer Sharp)
// Doesn't work (Seems to rely on other objects?)
// { ACTOR_EN_POH, 3 }, // Poe (composer Flat)
{ ACTOR_EN_RD, 1 }, // Redead (standing)
{ ACTOR_EN_TITE, -1 }, // Tektite (red)
{ ACTOR_EN_BUBBLE, 0 }, // Shabom (bubble)
{ ACTOR_EN_SB, 0 }, // Shell Blade
{ ACTOR_EN_ST, 0 }, // Skulltula (normal)
{ ACTOR_EN_SKJ, 4159 }, // Skull Kid
{ ACTOR_EN_DEKUBABA, 0 }, // Deku Baba (small)
{ ACTOR_EN_SKB, 1 }, // Stalchild (small)
{ ACTOR_EN_MB, -1 }, // Spear Moblin
{ ACTOR_EN_NY, 0 }, // Spike (rolling enemy)
{ ACTOR_EN_TEST, 2 }, // Stalfos
{ ACTOR_EN_EIYER, 10 }, // Stinger (land) (One in formation, sink under floor and do not activate)
{ ACTOR_EN_TP, -1 }, // Electric Tailpasaran
{ ACTOR_EN_BW, 0 }, // Torch Slug
{ ACTOR_EN_WALLMAS, 1 }, // Wallmaster
{ ACTOR_EN_IK, 3 }, // Iron Knuckle (white, standing)
{ ACTOR_EN_WF, 1 }, // Wolfos (white)
{ ACTOR_EN_KAREBABA, 0 }, // Withered Deku Baba
};
static int enemiesToRandomize[] = {
ACTOR_EN_FIREFLY, // Keese (including fire/ice)
ACTOR_EN_TEST, // Stalfos
ACTOR_EN_TITE, // Tektite
ACTOR_EN_POH, // Poe (normal, blue rupee, composers)
ACTOR_EN_OKUTA, // Octorok
ACTOR_EN_WALLMAS, // Wallmaster
ACTOR_EN_DODONGO, // Dodongo
// ACTOR_EN_REEBA, // Leever (reliant on spawner (z_e_encount1.c)
ACTOR_EN_PEEHAT, // Flying Peahat, big one spawning larva, larva
ACTOR_EN_ZF, // Lizalfos, Dinolfos
ACTOR_EN_GOMA, // Gohma Larva (normal, eggs, gohma eggs)
ACTOR_EN_BUBBLE, // Shabom (bubble)
ACTOR_EN_DODOJR, // Baby Dodongo
ACTOR_EN_TORCH2, // Dark Link
ACTOR_EN_BILI, // Biri (small jellyfish)
ACTOR_EN_TP, // Electric Tailpasaran
ACTOR_EN_ST, // Skulltula (normal, big, invisible)
ACTOR_EN_BW, // Torch Slug
ACTOR_EN_EIYER, // Stinger (land)
ACTOR_EN_MB, // Moblins (Club, spear)
ACTOR_EN_DEKUBABA, // Deku Baba (small, large)
ACTOR_EN_AM, // Armos (enemy variant)
ACTOR_EN_DEKUNUTS, // Mad Scrub (single attack, triple attack)
ACTOR_EN_VALI, // Bari (big jellyfish) (spawns very high up)
ACTOR_EN_BB, // Bubble (flying skull enemy) (all colors)
ACTOR_EN_YUKABYUN, // Flying Floor Tile
ACTOR_EN_VM, // Beamos
ACTOR_EN_FLOORMAS, // Floormaster
ACTOR_EN_RD, // Redead, Gibdo
ACTOR_EN_SW, // Skullwalltula
// ACTOR_EN_FD, // Flare Dancer (can be randomized, but not randomized to, so keeping it in vanilla locations
// means it at least shows up in the game)
ACTOR_EN_ANUBICE_TAG, // Anubis
ACTOR_EN_FIREFLY, // Keese (including fire/ice)
ACTOR_EN_TEST, // Stalfos
ACTOR_EN_TITE, // Tektite
ACTOR_EN_POH, // Poe (normal, blue rupee, composers)
ACTOR_EN_OKUTA, // Octorok
ACTOR_EN_WALLMAS, // Wallmaster
ACTOR_EN_DODONGO, // Dodongo
// ACTOR_EN_REEBA, // Leever (reliant on spawner (z_en_encount1.c))
ACTOR_EN_PEEHAT, // Flying Peahat, big one spawning larva, larva
ACTOR_EN_ZF, // Lizalfos, Dinolfos
ACTOR_EN_GOMA, // Gohma Larva (normal, eggs, gohma eggs)
ACTOR_EN_BUBBLE, // Shabom (bubble)
ACTOR_EN_DODOJR, // Baby Dodongo
ACTOR_EN_TORCH2, // Dark Link
ACTOR_EN_BILI, // Biri (small jellyfish)
ACTOR_EN_TP, // Electric Tailpasaran
ACTOR_EN_ST, // Skulltula (normal, big, invisible)
ACTOR_EN_BW, // Torch Slug
ACTOR_EN_EIYER, // Stinger (land)
ACTOR_EN_MB, // Moblins (Club, spear)
ACTOR_EN_DEKUBABA, // Deku Baba (small, large)
ACTOR_EN_AM, // Armos (enemy variant)
ACTOR_EN_DEKUNUTS, // Mad Scrub (single attack, triple attack)
ACTOR_EN_VALI, // Bari (big jellyfish) (spawns very high up)
ACTOR_EN_BB, // Bubble (flying skull enemy) (all colors)
ACTOR_EN_YUKABYUN, // Flying Floor Tile
ACTOR_EN_VM, // Beamos
ACTOR_EN_FLOORMAS, // Floormaster
ACTOR_EN_RD, // Redead, Gibdo
ACTOR_EN_SW, // Skullwalltula
ACTOR_EN_FD, // Flare Dancer
ACTOR_EN_SB, // Shell Blade
ACTOR_EN_KAREBABA, // Withered Deku Baba
ACTOR_EN_RR, // Like-Like
@ -198,6 +225,7 @@ static int enemiesToRandomize[] = {
ACTOR_EN_WF, // Wolfos
ACTOR_EN_SKB, // Stalchild
ACTOR_EN_CROW, // Guay
ACTOR_EN_SKJ, // Skull Kid
};
extern "C" uint8_t GetRandomizedEnemy(PlayState* play, int16_t* actorId, f32* posX, f32* posY, f32* posZ, int16_t* rotX,
@ -322,7 +350,7 @@ static std::vector<EnemyEntry> selectedEnemyList;
void GetSelectedEnemies() {
selectedEnemyList.clear();
for (int i = 0; i < 49; i++) {
for (int i = 0; i < RANDOMIZED_ENEMY_SPAWN_TABLE_SIZE; i++) {
if (CVarGetInteger(CVAR_ENHANCEMENT("RandomizedEnemyList.All"), 0)) {
selectedEnemyList.push_back(randomizedEnemySpawnTable[i]);
} else if (CVarGetInteger(enemyCVarList[i], 1)) {
@ -408,6 +436,8 @@ bool IsEnemyFoundToRandomize(int16_t sceneNum, int8_t roomNum, int16_t actorId,
case ACTOR_EN_SB:
case ACTOR_EN_NY:
return (!(!isMQ && sceneNum == SCENE_WATER_TEMPLE && roomNum == 2));
case ACTOR_EN_SKJ:
return !(sceneNum == SCENE_LOST_WOODS && LINK_IS_CHILD);
default:
return 1;
}
@ -419,19 +449,19 @@ bool IsEnemyFoundToRandomize(int16_t sceneNum, int8_t roomNum, int16_t actorId,
}
bool IsEnemyAllowedToSpawn(int16_t sceneNum, int8_t roomNum, EnemyEntry enemy) {
uint32_t isMQ = ResourceMgr_IsSceneMasterQuest(sceneNum);
// Freezard - Child Link can only kill this with jump slash Deku Sticks or other equipment like bombs.
// Beamos - Needs bombs.
// Anubis - Needs fire.
// Shell Blade & Spike - Child Link can't kill these with sword or Deku Stick.
// Arwing & Dark Link - Both go out of bounds way too easily, softlocking the player.
// Flare dancer, Arwing & Dark Link - Both go out of bounds way too easily, softlocking the player.
// Wallmaster - Not easily visible, often makes players think they're softlocked and that there's no enemies left.
// Club Moblin - Many issues with them falling or placing out of bounds. Maybe fixable in the future?
bool enemiesToExcludeClearRooms = enemy.id == ACTOR_EN_FZ || enemy.id == ACTOR_EN_VM || enemy.id == ACTOR_EN_SB ||
enemy.id == ACTOR_EN_NY || enemy.id == ACTOR_EN_CLEAR_TAG ||
enemy.id == ACTOR_EN_WALLMAS || enemy.id == ACTOR_EN_TORCH2 ||
enemy.id == ACTOR_EN_MB;
bool enemiesToExcludeClearRooms =
enemy.id == ACTOR_EN_FZ || enemy.id == ACTOR_EN_VM || enemy.id == ACTOR_EN_SB || enemy.id == ACTOR_EN_NY ||
enemy.id == ACTOR_EN_CLEAR_TAG || enemy.id == ACTOR_EN_WALLMAS || enemy.id == ACTOR_EN_TORCH2 ||
(enemy.id == ACTOR_EN_MB && enemy.params == 0) || enemy.id == ACTOR_EN_FD || enemy.id == ACTOR_EN_ANUBICE_TAG;
// Bari - Spawns 3 more enemies, potentially extremely difficult in timed rooms.
bool enemiesToExcludeTimedRooms = enemiesToExcludeClearRooms || enemy.id == ACTOR_EN_VALI;
@ -532,3 +562,16 @@ bool IsEnemyAllowedToSpawn(int16_t sceneNum, int8_t roomNum, EnemyEntry enemy) {
return 1;
}
}
void FixClubMoblinScale(void* ptr) {
Actor* actor = (Actor*)ptr;
if (actor->params == -1) {
Actor_SetScale(actor, 0.014f);
}
}
void RegisterEnemyRandomizer() {
COND_ID_HOOK(OnActorInit, ACTOR_EN_MB, CVAR_ENEMY_RANDOMIZER_VALUE, FixClubMoblinScale);
}
static RegisterShipInitFunc initFunc(RegisterEnemyRandomizer, { CVAR_ENEMY_RANDOMIZER_NAME });

View file

@ -1,23 +1,16 @@
#pragma once
#include <libultraship/bridge.h>
#include <libultraship/libultra/types.h>
typedef struct EnemyEntry {
int16_t id;
int16_t params;
} EnemyEntry;
#define RANDOMIZED_ENEMY_SPAWN_TABLE_SIZE 49
bool IsEnemyFoundToRandomize(int16_t sceneNum, int8_t roomNum, int16_t actorId, int16_t params, float posX);
bool IsEnemyAllowedToSpawn(int16_t sceneNum, int8_t roomNum, EnemyEntry enemy);
EnemyEntry GetRandomizedEnemyEntry(uint32_t seed);
#define RANDOMIZED_ENEMY_SPAWN_TABLE_SIZE 52
extern const char* enemyCVarList[];
extern const char* enemyNameList[];
extern void GetSelectedEnemies();
#ifndef __cplusplus
uint8_t GetRandomizedEnemy(PlayState* play, int16_t* actorId, f32* posX, f32* posY, f32* posZ, int16_t* rotX,
struct PlayState;
uint8_t GetRandomizedEnemy(struct PlayState* play, int16_t* actorId, f32* posX, f32* posY, f32* posZ, int16_t* rotX,
int16_t* rotY, int16_t* rotZ, int16_t* params);
#endif

View file

@ -27,6 +27,7 @@ DEFINE_HOOK(OnOcarinaSongAction, ());
DEFINE_HOOK(OnCuccoOrChickenHatch, ());
DEFINE_HOOK(OnShopSlotChange, (uint8_t cursorIndex, int16_t price));
DEFINE_HOOK(OnActorInit, (void* actor));
DEFINE_HOOK(OnActorSpawn, (void* actor));
DEFINE_HOOK(OnActorUpdate, (void* actor));
DEFINE_HOOK(OnActorKill, (void* actor));
DEFINE_HOOK(OnActorDestroy, (void* actor));

View file

@ -108,6 +108,13 @@ void GameInteractor_ExecuteOnActorInit(void* actor) {
GameInteractor::Instance->ExecuteHooksForFilter<GameInteractor::OnActorInit>(actor);
}
void GameInteractor_ExecuteOnActorSpawn(void* actor) {
GameInteractor::Instance->ExecuteHooks<GameInteractor::OnActorSpawn>(actor);
GameInteractor::Instance->ExecuteHooksForID<GameInteractor::OnActorSpawn>(((Actor*)actor)->id, actor);
GameInteractor::Instance->ExecuteHooksForPtr<GameInteractor::OnActorSpawn>((uintptr_t)actor, actor);
GameInteractor::Instance->ExecuteHooksForFilter<GameInteractor::OnActorSpawn>(actor);
}
void GameInteractor_ExecuteOnActorUpdate(void* actor) {
GameInteractor::Instance->ExecuteHooks<GameInteractor::OnActorUpdate>(actor);
GameInteractor::Instance->ExecuteHooksForID<GameInteractor::OnActorUpdate>(((Actor*)actor)->id, actor);

View file

@ -29,6 +29,7 @@ void GameInteractor_ExecuteOnSetDoAction(uint16_t action);
void GameInteractor_ExecuteOnOcarinaSongAction();
void GameInteractor_ExecuteOnCuccoOrChickenHatch();
void GameInteractor_ExecuteOnActorInit(void* actor);
void GameInteractor_ExecuteOnActorSpawn(void* actor);
void GameInteractor_ExecuteOnActorUpdate(void* actor);
void GameInteractor_ExecuteOnActorKill(void* actor);
void GameInteractor_ExecuteOnActorDestroy(void* actor);

View file

@ -20,6 +20,14 @@ typedef enum {
// - `int32_t` (entrance index) (promoted from `uint16_t` by va_arg)
VB_ALLOW_ENTRANCE_CS_FOR_EITHER_AGE,
// #### `result`
// ```c
// sBgPoEventPuzzleState == 0xF
// ```
// #### `args`
// - None
VB_AMY_SOLVE,
// #### `result`
// ```c
// this->actor.textId == 0x401A

View file

@ -134,31 +134,6 @@ void RegisterOcarinaTimeTravel() {
});
}
void RegisterRupeeDash() {
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnPlayerUpdate>([]() {
if (!CVarGetInteger(CVAR_ENHANCEMENT("RupeeDash"), 0)) {
return;
}
// Initialize Timer
static uint16_t rupeeDashTimer = 0;
uint16_t rdmTime = CVarGetInteger(CVAR_ENHANCEMENT("RupeeDashInterval"), 5) * 20;
// Did time change by DashInterval?
if (rupeeDashTimer >= rdmTime) {
rupeeDashTimer = 0;
if (gSaveContext.rupees > 0) {
uint16_t walletSize = (CUR_UPG_VALUE(UPG_WALLET) + 1) * -1;
Rupees_ChangeBy(walletSize);
} else {
Health_ChangeBy(gPlayState, -16);
}
} else {
rupeeDashTimer++;
}
});
}
static bool hasAffectedHealth = false;
void UpdatePermanentHeartLossState() {
if (!GameInteractor::IsSaveLoaded())
@ -982,7 +957,6 @@ void InitMods() {
TimeSavers_Register();
RegisterTTS();
RegisterOcarinaTimeTravel();
RegisterRupeeDash();
RegisterPermanentHeartLoss();
RegisterDeleteFileOnDeath();
RegisterHyperBosses();

View file

@ -1015,7 +1015,8 @@ static void RandomizeOwnDungeon(const Rando::DungeonInfo* dungeon) {
// filter out locations that may be required to have songs placed at them
dungeonLocations = FilterFromPool(dungeonLocations, [ctx](const auto loc) {
if (ctx->GetOption(RSK_SHUFFLE_SONGS).Is(RO_SONG_SHUFFLE_SONG_LOCATIONS)) {
if (ctx->GetOption(RSK_SHUFFLE_SONGS).Is(RO_SONG_SHUFFLE_SONG_LOCATIONS) ||
ctx->GetOption(RSK_SHUFFLE_SONGS).Is(RO_SONG_SHUFFLE_OFF)) {
return !(Rando::StaticData::GetLocation(loc)->GetRCType() == RCTYPE_SONG_LOCATION);
}
if (ctx->GetOption(RSK_SHUFFLE_SONGS).Is(RO_SONG_SHUFFLE_DUNGEON_REWARDS)) {
@ -1344,8 +1345,8 @@ int Fill() {
StartPerformanceTimer(PT_LIMITED_CHECKS);
// Then Place songs if song shuffle is set to specific locations
if (ctx->GetOption(RSK_SHUFFLE_SONGS).IsNot(RO_SONG_SHUFFLE_ANYWHERE)) {
if (ctx->GetOption(RSK_SHUFFLE_SONGS).IsNot(RO_SONG_SHUFFLE_ANYWHERE) &&
ctx->GetOption(RSK_SHUFFLE_SONGS).IsNot(RO_SONG_SHUFFLE_OFF)) {
// Get each song
std::vector<RandomizerGet> songs = FilterAndEraseFromPool(ItemPool, [](const auto i) {
return Rando::StaticData::RetrieveItem(i).GetItemType() == ITEMTYPE_SONG;

View file

@ -1133,11 +1133,26 @@ void GenerateItemPool() {
}
}
// add extra songs only if song shuffle is anywhere
AddItemsToPool(ItemPool, songList);
if (ctx->GetOption(RSK_SHUFFLE_SONGS).Is(RO_SONG_SHUFFLE_ANYWHERE) &&
ctx->GetOption(RSK_ITEM_POOL).Is(RO_ITEM_POOL_PLENTIFUL)) {
AddItemsToPool(PendingJunkPool, songList);
if (ctx->GetOption(RSK_SHUFFLE_SONGS).IsNot(RO_SONG_SHUFFLE_OFF)) {
AddItemsToPool(ItemPool, songList);
// add extra songs only if song shuffle is anywhere
if (ctx->GetOption(RSK_SHUFFLE_SONGS).Is(RO_SONG_SHUFFLE_ANYWHERE) &&
ctx->GetOption(RSK_ITEM_POOL).Is(RO_ITEM_POOL_PLENTIFUL)) {
AddItemsToPool(PendingJunkPool, songList);
}
} else {
ctx->PlaceItemInLocation(RC_SHEIK_IN_FOREST, RG_MINUET_OF_FOREST, false, true);
ctx->PlaceItemInLocation(RC_SHEIK_IN_CRATER, RG_BOLERO_OF_FIRE, false, true);
ctx->PlaceItemInLocation(RC_SHEIK_IN_ICE_CAVERN, RG_SERENADE_OF_WATER, false, true);
ctx->PlaceItemInLocation(RC_SHEIK_AT_COLOSSUS, RG_REQUIEM_OF_SPIRIT, false, true);
ctx->PlaceItemInLocation(RC_SHEIK_IN_KAKARIKO, RG_NOCTURNE_OF_SHADOW, false, true);
ctx->PlaceItemInLocation(RC_SHEIK_AT_TEMPLE, RG_PRELUDE_OF_LIGHT, false, true);
ctx->PlaceItemInLocation(RC_SONG_FROM_IMPA, RG_ZELDAS_LULLABY, false, true);
ctx->PlaceItemInLocation(RC_SONG_FROM_MALON, RG_EPONAS_SONG, false, true);
ctx->PlaceItemInLocation(RC_SONG_FROM_SARIA, RG_SARIAS_SONG, false, true);
ctx->PlaceItemInLocation(RC_SONG_FROM_ROYAL_FAMILYS_TOMB, RG_SUNS_SONG, false, true);
ctx->PlaceItemInLocation(RC_SONG_FROM_OCARINA_OF_TIME, RG_SONG_OF_TIME, false, true);
ctx->PlaceItemInLocation(RC_SONG_FROM_WINDMILL, RG_SONG_OF_STORMS, false, true);
}
/*For item pool generation, dungeon items are either placed in their vanilla

View file

@ -9,8 +9,6 @@ extern PlayState* gPlayState;
#include "src/overlays/actors/ovl_En_Door/z_en_door.h"
}
#define RAND_GET_OPTION(option) Rando::Context::GetInstance()->GetOption(option).Get()
using SceneDoorParamsPair = std::pair<int, int>;
std::map<SceneDoorParamsPair, RandomizerInf> lookupTable = {
// clang-format off

View file

@ -11,8 +11,7 @@ extern void EnItem00_DrawRandomizedItem(EnItem00* enItem00, PlayState* play);
void ObjComb_RandomizerChooseItemDrop(ObjComb* objComb, PlayState* play) {
s16 params = objComb->actor.params & 0x1F;
if (Rando::Context::GetInstance()->GetOption(RSK_SHUFFLE_BEEHIVES).Get() &&
!Flags_GetRandomizerInf(objComb->beehiveIdentity.randomizerInf)) {
if (RAND_GET_OPTION(RSK_SHUFFLE_BEEHIVES) && !Flags_GetRandomizerInf(objComb->beehiveIdentity.randomizerInf)) {
EnItem00* item00 = (EnItem00*)Item_DropCollectible2(play, &objComb->actor.world.pos, ITEM00_SOH_DUMMY);
item00->randoInf = objComb->beehiveIdentity.randomizerInf;
item00->itemEntry =
@ -41,8 +40,7 @@ void ObjComb_RandomizerWait(ObjComb* objComb, PlayState* play) {
s32 dmgFlags;
objComb->unk_1B0 -= 50;
if (Rando::Context::GetInstance()->GetOption(RSK_SHUFFLE_BEEHIVES).Get() &&
!Flags_GetRandomizerInf(objComb->beehiveIdentity.randomizerInf)) {
if (RAND_GET_OPTION(RSK_SHUFFLE_BEEHIVES) && !Flags_GetRandomizerInf(objComb->beehiveIdentity.randomizerInf)) {
if (objComb->unk_1B0 <= -5000) {
objComb->unk_1B0 = 1500;
}
@ -85,7 +83,7 @@ void ObjComb_RandomizerUpdate(void* actor) {
}
void RegisterShuffleBeehives() {
bool shouldRegister = IS_RANDO && Rando::Context::GetInstance()->GetOption(RSK_SHUFFLE_BEEHIVES).Get();
bool shouldRegister = IS_RANDO && RAND_GET_OPTION(RSK_SHUFFLE_BEEHIVES);
COND_ID_HOOK(OnActorInit, ACTOR_OBJ_COMB, shouldRegister, ObjComb_RandomizerInit);
COND_ID_HOOK(OnActorUpdate, ACTOR_OBJ_COMB, shouldRegister, ObjComb_RandomizerUpdate);

View file

@ -34,7 +34,7 @@ void EnCow_MoveForRandomizer(EnCow* enCow, PlayState* play) {
}
void RegisterShuffleCows() {
bool shouldRegister = IS_RANDO && Rando::Context::GetInstance()->GetOption(RSK_SHUFFLE_COWS).Get();
bool shouldRegister = IS_RANDO && RAND_GET_OPTION(RSK_SHUFFLE_COWS);
COND_VB_SHOULD(VB_GIVE_ITEM_FROM_COW, shouldRegister, {
EnCow* enCow = va_arg(args, EnCow*);

View file

@ -15,8 +15,6 @@ extern "C" {
extern PlayState* gPlayState;
}
#define RAND_GET_OPTION(option) Rando::Context::GetInstance()->GetOption(option).Get()
extern void EnItem00_DrawRandomizedItem(EnItem00* enItem00, PlayState* play);
extern "C" void ObjKibako2_RandomizerDraw(Actor* thisx, PlayState* play) {
@ -158,7 +156,7 @@ extern "C" void ObjKibako_RandomizerDraw(Actor* thisx, PlayState* play) {
uint8_t ObjKibako2_RandomizerHoldsItem(ObjKibako2* crateActor, PlayState* play) {
RandomizerCheck rc = crateActor->crateIdentity.randomizerCheck;
uint8_t isDungeon = Rando::StaticData::GetLocation(rc)->IsDungeon();
uint8_t crateSetting = Rando::Context::GetInstance()->GetOption(RSK_SHUFFLE_CRATES).Get();
uint8_t crateSetting = RAND_GET_OPTION(RSK_SHUFFLE_CRATES);
// Don't pull randomized item if crate isn't randomized or is already checked
if (!IS_RANDO || (crateSetting == RO_SHUFFLE_CRATES_OVERWORLD && isDungeon) ||
@ -174,7 +172,7 @@ uint8_t ObjKibako2_RandomizerHoldsItem(ObjKibako2* crateActor, PlayState* play)
uint8_t ObjKibako_RandomizerHoldsItem(ObjKibako* smallCrateActor, PlayState* play) {
RandomizerCheck rc = smallCrateActor->smallCrateIdentity.randomizerCheck;
uint8_t isDungeon = Rando::StaticData::GetLocation(rc)->IsDungeon();
uint8_t crateSetting = Rando::Context::GetInstance()->GetOption(RSK_SHUFFLE_CRATES).Get();
uint8_t crateSetting = RAND_GET_OPTION(RSK_SHUFFLE_CRATES);
// Don't pull randomized item if crate isn't randomized or is already checked
if (!IS_RANDO || (crateSetting == RO_SHUFFLE_CRATES_OVERWORLD && isDungeon) ||
@ -211,7 +209,7 @@ void ObjKibako_RandomizerSpawnCollectible(ObjKibako* smallCrateActor, PlayState*
void ObjKibako2_RandomizerInit(void* actorRef) {
Actor* actor = static_cast<Actor*>(actorRef);
uint8_t logicSetting = Rando::Context::GetInstance()->GetOption(RSK_LOGIC_RULES).Get();
uint8_t logicSetting = RAND_GET_OPTION(RSK_LOGIC_RULES);
// don't shuffle two OOB crates in GF and don't shuffle child GV/GF crates when not in no logic
if (actor->id != ACTOR_OBJ_KIBAKO2 ||

View file

@ -2,7 +2,7 @@
#include <z64.h>
#include "soh/Enhancements/item-tables/ItemTableTypes.h"
#include "randomizer_inf.h"
#include "randomizerTypes.h"
typedef struct FairyIdentity {
RandomizerInf randomizerInf;

View file

@ -23,7 +23,7 @@ void ShuffleFreestanding_OnVanillaBehaviorHandler(GIVanillaBehavior id, bool* sh
Rando::Location* loc =
OTRGlobals::Instance->gRandomizer->GetCheckObjectFromActor(item00->actor.id, gPlayState->sceneNum, params);
uint8_t isDungeon = loc->IsDungeon();
uint8_t freestandingSetting = Rando::Context::GetInstance()->GetOption(RSK_SHUFFLE_FREESTANDING).Get();
uint8_t freestandingSetting = RAND_GET_OPTION(RSK_SHUFFLE_FREESTANDING);
RandomizerCheck randomizerCheck = loc->GetRandomizerCheck();
bool checkObtained = Rando::Context::GetInstance()->GetItemLocation(randomizerCheck)->HasObtained();

View file

@ -11,8 +11,6 @@ extern "C" {
extern PlayState* gPlayState;
}
#define RAND_GET_OPTION(option) Rando::Context::GetInstance()->GetOption(option).Get()
extern void EnItem00_DrawRandomizedItem(EnItem00* enItem00, PlayState* play);
void DrawTypeOfGrass(EnKusa* grassActor, Gfx* bushDList, Gfx* grassDList, PlayState* play) {
@ -96,7 +94,7 @@ uint8_t EnKusa_RandomizerHoldsItem(EnKusa* grassActor, PlayState* play) {
RandomizerCheck rc = grassActor->grassIdentity.randomizerCheck;
uint8_t isDungeon = Rando::StaticData::GetLocation(rc)->IsDungeon();
uint8_t grassSetting = Rando::Context::GetInstance()->GetOption(RSK_SHUFFLE_GRASS).Get();
uint8_t grassSetting = RAND_GET_OPTION(RSK_SHUFFLE_GRASS);
// Don't pull randomized item if grass isn't randomized or is already checked
if (!IS_RANDO || (grassSetting == RO_SHUFFLE_GRASS_OVERWORLD && isDungeon) ||

View file

@ -27,7 +27,7 @@ extern "C" void ObjTsubo_RandomizerDraw(Actor* thisx, PlayState* play) {
uint8_t ObjTsubo_RandomizerHoldsItem(ObjTsubo* potActor, PlayState* play) {
RandomizerCheck rc = potActor->potIdentity.randomizerCheck;
uint8_t isDungeon = Rando::StaticData::GetLocation(rc)->IsDungeon();
uint8_t potSetting = Rando::Context::GetInstance()->GetOption(RSK_SHUFFLE_POTS).Get();
uint8_t potSetting = RAND_GET_OPTION(RSK_SHUFFLE_POTS);
// Don't pull randomized item if pot isn't randomized or is already checked
if (!IS_RANDO || (potSetting == RO_SHUFFLE_POTS_OVERWORLD && isDungeon) ||
@ -85,7 +85,7 @@ void ShufflePots_OnVanillaBehaviorHandler(GIVanillaBehavior id, bool* should, va
// Unlock early Ganon's Boss Key doors to allow access to the pots there when pots are shuffled in dungeon
if (id == VB_LOCK_BOSS_DOOR) {
DoorShutter* doorActor = va_arg(args, DoorShutter*);
uint8_t shufflePotSetting = Rando::Context::GetInstance()->GetOption(RSK_SHUFFLE_POTS).Get();
uint8_t shufflePotSetting = RAND_GET_OPTION(RSK_SHUFFLE_POTS);
if (gPlayState->sceneNum == SCENE_GANONS_TOWER && doorActor->dyna.actor.world.pos.y == 800 &&
(shufflePotSetting == RO_SHUFFLE_POTS_DUNGEONS || shufflePotSetting == RO_SHUFFLE_POTS_ALL)) {
*should = false;

View file

@ -0,0 +1,26 @@
#include "soh/ShipInit.hpp"
#include "location.h"
#include "static_data.h"
void Rando::StaticData::RegisterSongLocations() {
static bool registered = false;
if (registered)
return;
registered = true;
// clang-format off
locationTable[RC_SHEIK_IN_FOREST] = Location::Base(RC_SHEIK_IN_FOREST, RCQUEST_BOTH, RCTYPE_SONG_LOCATION, ACTOR_ID_MAX, SCENE_SACRED_FOREST_MEADOW, 0x00, "Sheik in Forest", "Sheik in Forest", RHT_SHEIK_IN_FOREST, RG_MINUET_OF_FOREST, SpoilerCollectionCheck::EventChkInf(EVENTCHKINF_LEARNED_MINUET_OF_FOREST), true);
locationTable[RC_SHEIK_IN_CRATER] = Location::Base(RC_SHEIK_IN_CRATER, RCQUEST_BOTH, RCTYPE_SONG_LOCATION, ACTOR_ID_MAX, SCENE_DEATH_MOUNTAIN_CRATER, 0x00, "Sheik in Crater", "Sheik in Crater", RHT_SHEIK_IN_CRATER, RG_BOLERO_OF_FIRE, SpoilerCollectionCheck::EventChkInf(EVENTCHKINF_LEARNED_BOLERO_OF_FIRE), true);
locationTable[RC_SHEIK_IN_ICE_CAVERN] = Location::Base(RC_SHEIK_IN_ICE_CAVERN, RCQUEST_BOTH, RCTYPE_SONG_LOCATION, ACTOR_ID_MAX, SCENE_ICE_CAVERN, 0x00, "Sheik in Ice Cavern", "Sheik in Ice Cavern", RHT_SHEIK_IN_ICE_CAVERN, RG_SERENADE_OF_WATER, SpoilerCollectionCheck::EventChkInf(EVENTCHKINF_LEARNED_SERENADE_OF_WATER), true);
locationTable[RC_SHEIK_AT_COLOSSUS] = Location::Base(RC_SHEIK_AT_COLOSSUS, RCQUEST_BOTH, RCTYPE_SONG_LOCATION, ACTOR_ID_MAX, SCENE_DESERT_COLOSSUS, 0x00, "Sheik at Colossus", "Sheik at Colossus", RHT_SHEIK_AT_COLOSSUS, RG_REQUIEM_OF_SPIRIT, SpoilerCollectionCheck::EventChkInf(EVENTCHKINF_LEARNED_REQUIEM_OF_SPIRIT), true);
locationTable[RC_SHEIK_IN_KAKARIKO] = Location::Base(RC_SHEIK_IN_KAKARIKO, RCQUEST_BOTH, RCTYPE_SONG_LOCATION, ACTOR_ID_MAX, SCENE_KAKARIKO_VILLAGE, 0x00, "Sheik in Kakariko", "Sheik in Kakariko", RHT_SHEIK_IN_KAKARIKO, RG_NOCTURNE_OF_SHADOW, SpoilerCollectionCheck::EventChkInf(EVENTCHKINF_BONGO_BONGO_ESCAPED_FROM_WELL), true);
locationTable[RC_SHEIK_AT_TEMPLE] = Location::Base(RC_SHEIK_AT_TEMPLE, RCQUEST_BOTH, RCTYPE_SONG_LOCATION, ACTOR_ID_MAX, SCENE_TEMPLE_OF_TIME, 0x00, "Sheik at Temple", "Sheik at Temple", RHT_SHEIK_AT_TEMPLE, RG_PRELUDE_OF_LIGHT, SpoilerCollectionCheck::EventChkInf(EVENTCHKINF_LEARNED_PRELUDE_OF_LIGHT), true);
locationTable[RC_SONG_FROM_IMPA] = Location::Base(RC_SONG_FROM_IMPA, RCQUEST_BOTH, RCTYPE_SONG_LOCATION, RCAREA_HYRULE_CASTLE, ACTOR_ID_MAX, SCENE_ID_MAX, 0x00, "Song from Impa", "Song from Impa", RHT_SONG_FROM_IMPA, RG_ZELDAS_LULLABY, SpoilerCollectionCheck::EventChkInf(EVENTCHKINF_LEARNED_ZELDAS_LULLABY), true);
locationTable[RC_SONG_FROM_MALON] = Location::Base(RC_SONG_FROM_MALON, RCQUEST_BOTH, RCTYPE_SONG_LOCATION, ACTOR_ID_MAX, SCENE_LON_LON_RANCH, 0x00, "Song from Malon", "Song from Malon", RHT_SONG_FROM_MALON, RG_EPONAS_SONG, SpoilerCollectionCheck::RandomizerInf(RAND_INF_LEARNED_EPONA_SONG), true);
locationTable[RC_SONG_FROM_SARIA] = Location::Base(RC_SONG_FROM_SARIA, RCQUEST_BOTH, RCTYPE_SONG_LOCATION, ACTOR_ID_MAX, SCENE_SACRED_FOREST_MEADOW, 0x00, "Song from Saria", "Song from Saria", RHT_SONG_FROM_SARIA, RG_SARIAS_SONG, SpoilerCollectionCheck::EventChkInf(EVENTCHKINF_LEARNED_SARIAS_SONG), true);
locationTable[RC_SONG_FROM_ROYAL_FAMILYS_TOMB] = Location::Base(RC_SONG_FROM_ROYAL_FAMILYS_TOMB, RCQUEST_BOTH, RCTYPE_SONG_LOCATION, ACTOR_ID_MAX, SCENE_ROYAL_FAMILYS_TOMB, 0x00, "Song from Royal Family's Tomb", "Song from Royal Family's Tomb", RHT_SONG_FROM_ROYAL_FAMILYS_TOMB, RG_SUNS_SONG, SpoilerCollectionCheck::EventChkInf(EVENTCHKINF_LEARNED_SUNS_SONG), true);
locationTable[RC_SONG_FROM_OCARINA_OF_TIME] = Location::Base(RC_SONG_FROM_OCARINA_OF_TIME, RCQUEST_BOTH, RCTYPE_SONG_LOCATION, ACTOR_ID_MAX, SCENE_HYRULE_FIELD, 0x00, "Song from Ocarina of Time", "Song from Ocarina of Time", RHT_SONG_FROM_OCARINA_OF_TIME, RG_SONG_OF_TIME, SpoilerCollectionCheck::EventChkInf(EVENTCHKINF_LEARNED_SONG_OF_TIME), true);
locationTable[RC_SONG_FROM_WINDMILL] = Location::Base(RC_SONG_FROM_WINDMILL, RCQUEST_BOTH, RCTYPE_SONG_LOCATION, RCAREA_KAKARIKO_VILLAGE, ACTOR_ID_MAX, SCENE_WINDMILL_AND_DAMPES_GRAVE, 0x00, "Song from Windmill", "Song from Windmill", RHT_SONG_FROM_WINDMILL, RG_SONG_OF_STORMS, SpoilerCollectionCheck::EventChkInf(EVENTCHKINF_LEARNED_SONG_OF_STORMS), true);
// clang-format-on
}
static RegisterShipInitFunc initSongLocations(Rando::StaticData::RegisterSongLocations);

View file

@ -179,6 +179,7 @@ void Context::GenerateLocationPool() {
location.GetRandomizerCheck() == RC_LW_DEKU_SCRUB_NEAR_BRIDGE ||
location.GetRandomizerCheck() == RC_HF_DEKU_SCRUB_GROTTO)) ||
(location.GetRCType() == RCTYPE_ADULT_TRADE && mOptions[RSK_SHUFFLE_ADULT_TRADE].Is(RO_GENERIC_OFF)) ||
(location.GetRCType() == RCTYPE_SONG_LOCATION && mOptions[RSK_SHUFFLE_SONGS].Is(RO_SONG_SHUFFLE_OFF)) ||
(location.GetRCType() == RCTYPE_COW && mOptions[RSK_SHUFFLE_COWS].Is(RO_GENERIC_OFF)) ||
(location.GetRandomizerCheck() == RC_LH_HYRULE_LOACH &&
mOptions[RSK_FISHSANITY].IsNot(RO_FISHSANITY_HYRULE_LOACH)) ||

View file

@ -14,6 +14,8 @@
#include <map>
#include <nlohmann/json.hpp>
#define RAND_GET_OPTION(option) Rando::Context::GetInstance()->GetOption(option).Get()
/**
* @brief Singleton for storing and accessing dynamic Randomizer-related data
*

View file

@ -15,10 +15,6 @@ extern SaveContext gSaveContext;
extern PlayState* gPlayState;
}
#define FSi OTRGlobals::Instance->gRandoContext->GetFishsanity()
#define RAND_GET_OPTION(option) Rando::Context::GetInstance()->GetOption(option).Get()
/**
* @brief Parallel list of pond fish checks for both ages
*/
@ -488,15 +484,15 @@ void Fishsanity::OnItemReceiveHandler(GetItemEntry itemEntry) {
// C interface
extern "C" {
bool Randomizer_GetPondFishShuffled() {
return FSi->GetPondFishShuffled();
return Rando::Context::GetInstance()->GetFishsanity()->GetPondFishShuffled();
}
bool Randomizer_GetOverworldFishShuffled() {
return FSi->GetOverworldFishShuffled();
return Rando::Context::GetInstance()->GetFishsanity()->GetOverworldFishShuffled();
}
bool Randomizer_IsAdultPond() {
return FSi->IsAdultPond();
return Rando::Context::GetInstance()->GetFishsanity()->IsAdultPond();
}
void Fishsanity_DrawEffShadow(Actor* actor, Lights* lights, PlayState* play) {

View file

@ -71,8 +71,6 @@ extern void EnGe1_Wait_Archery(EnGe1* enGe1, PlayState* play);
extern void EnGe1_SetAnimationIdle(EnGe1* enGe1);
}
#define RAND_GET_OPTION(option) Rando::Context::GetInstance()->GetOption(option).Get()
bool LocMatchesQuest(Rando::Location loc) {
if (loc.GetQuest() == RCQUEST_BOTH) {
return true;
@ -2144,7 +2142,7 @@ void RandomizerOnActorUpdateHandler(void* refActor) {
shutterDoor->unk_16E = 0;
}
} else if (actor->id == ACTOR_DOOR_GERUDO) {
DoorGerudo* gerudoDoor = (DoorGerudo*)actor;
DoorGerudo* gerudoDoor = reinterpret_cast<DoorGerudo*>(actor);
gerudoDoor->actionFunc = func_8099485C;
gerudoDoor->dyna.actor.world.pos.y = gerudoDoor->dyna.actor.home.pos.y + 200.0f;
}

View file

@ -828,20 +828,6 @@ void Rando::StaticData::InitLocationTable() {
locationTable[RC_DMC_GREAT_FAIRY_REWARD] = Location::Base(RC_DMC_GREAT_FAIRY_REWARD, RCQUEST_BOTH, RCTYPE_STANDARD, RCAREA_DEATH_MOUNTAIN_CRATER, ACTOR_BG_DY_YOSEIZO, SCENE_GREAT_FAIRYS_FOUNTAIN_MAGIC, 2, "Great Fairy Reward", RHT_DMC_GREAT_FAIRY_REWARD, RG_PROGRESSIVE_MAGIC_METER, SpoilerCollectionCheck::RandomizerInf(RAND_INF_DMC_GREAT_FAIRY_REWARD), true);
locationTable[RC_OGC_GREAT_FAIRY_REWARD] = Location::Base(RC_OGC_GREAT_FAIRY_REWARD, RCQUEST_BOTH, RCTYPE_STANDARD, RCAREA_HYRULE_CASTLE, ACTOR_BG_DY_YOSEIZO, SCENE_GREAT_FAIRYS_FOUNTAIN_MAGIC, 3, "OGC Great Fairy Reward", "OGC Great Fairy Reward", RHT_OGC_GREAT_FAIRY_REWARD, RG_DOUBLE_DEFENSE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_OGC_GREAT_FAIRY_REWARD), true);
// Songs
locationTable[RC_SHEIK_IN_FOREST] = Location::Base(RC_SHEIK_IN_FOREST, RCQUEST_BOTH, RCTYPE_SONG_LOCATION, ACTOR_ID_MAX, SCENE_SACRED_FOREST_MEADOW, 0x00, "Sheik in Forest", "Sheik in Forest", RHT_SHEIK_IN_FOREST, RG_MINUET_OF_FOREST, SpoilerCollectionCheck::EventChkInf(EVENTCHKINF_LEARNED_MINUET_OF_FOREST), true);
locationTable[RC_SHEIK_IN_CRATER] = Location::Base(RC_SHEIK_IN_CRATER, RCQUEST_BOTH, RCTYPE_SONG_LOCATION, ACTOR_ID_MAX, SCENE_DEATH_MOUNTAIN_CRATER, 0x00, "Sheik in Crater", "Sheik in Crater", RHT_SHEIK_IN_CRATER, RG_BOLERO_OF_FIRE, SpoilerCollectionCheck::EventChkInf(EVENTCHKINF_LEARNED_BOLERO_OF_FIRE), true);
locationTable[RC_SHEIK_IN_ICE_CAVERN] = Location::Base(RC_SHEIK_IN_ICE_CAVERN, RCQUEST_BOTH, RCTYPE_SONG_LOCATION, ACTOR_ID_MAX, SCENE_ICE_CAVERN, 0x00, "Sheik in Ice Cavern", "Sheik in Ice Cavern", RHT_SHEIK_IN_ICE_CAVERN, RG_SERENADE_OF_WATER, SpoilerCollectionCheck::EventChkInf(EVENTCHKINF_LEARNED_SERENADE_OF_WATER), true);
locationTable[RC_SHEIK_AT_COLOSSUS] = Location::Base(RC_SHEIK_AT_COLOSSUS, RCQUEST_BOTH, RCTYPE_SONG_LOCATION, ACTOR_ID_MAX, SCENE_DESERT_COLOSSUS, 0x00, "Sheik at Colossus", "Sheik at Colossus", RHT_SHEIK_AT_COLOSSUS, RG_REQUIEM_OF_SPIRIT, SpoilerCollectionCheck::EventChkInf(EVENTCHKINF_LEARNED_REQUIEM_OF_SPIRIT), true);
locationTable[RC_SHEIK_IN_KAKARIKO] = Location::Base(RC_SHEIK_IN_KAKARIKO, RCQUEST_BOTH, RCTYPE_SONG_LOCATION, ACTOR_ID_MAX, SCENE_KAKARIKO_VILLAGE, 0x00, "Sheik in Kakariko", "Sheik in Kakariko", RHT_SHEIK_IN_KAKARIKO, RG_NOCTURNE_OF_SHADOW, SpoilerCollectionCheck::EventChkInf(EVENTCHKINF_BONGO_BONGO_ESCAPED_FROM_WELL), true);
locationTable[RC_SHEIK_AT_TEMPLE] = Location::Base(RC_SHEIK_AT_TEMPLE, RCQUEST_BOTH, RCTYPE_SONG_LOCATION, ACTOR_ID_MAX, SCENE_TEMPLE_OF_TIME, 0x00, "Sheik at Temple", "Sheik at Temple", RHT_SHEIK_AT_TEMPLE, RG_PRELUDE_OF_LIGHT, SpoilerCollectionCheck::EventChkInf(EVENTCHKINF_LEARNED_PRELUDE_OF_LIGHT), true);
locationTable[RC_SONG_FROM_IMPA] = Location::Base(RC_SONG_FROM_IMPA, RCQUEST_BOTH, RCTYPE_SONG_LOCATION, RCAREA_HYRULE_CASTLE, ACTOR_ID_MAX, SCENE_ID_MAX, 0x00, "Song from Impa", "Song from Impa", RHT_SONG_FROM_IMPA, RG_ZELDAS_LULLABY, SpoilerCollectionCheck::EventChkInf(EVENTCHKINF_LEARNED_ZELDAS_LULLABY), true);
locationTable[RC_SONG_FROM_MALON] = Location::Base(RC_SONG_FROM_MALON, RCQUEST_BOTH, RCTYPE_SONG_LOCATION, ACTOR_ID_MAX, SCENE_LON_LON_RANCH, 0x00, "Song from Malon", "Song from Malon", RHT_SONG_FROM_MALON, RG_EPONAS_SONG, SpoilerCollectionCheck::RandomizerInf(RAND_INF_LEARNED_EPONA_SONG), true);
locationTable[RC_SONG_FROM_SARIA] = Location::Base(RC_SONG_FROM_SARIA, RCQUEST_BOTH, RCTYPE_SONG_LOCATION, ACTOR_ID_MAX, SCENE_SACRED_FOREST_MEADOW, 0x00, "Song from Saria", "Song from Saria", RHT_SONG_FROM_SARIA, RG_SARIAS_SONG, SpoilerCollectionCheck::EventChkInf(EVENTCHKINF_LEARNED_SARIAS_SONG), true);
locationTable[RC_SONG_FROM_ROYAL_FAMILYS_TOMB] = Location::Base(RC_SONG_FROM_ROYAL_FAMILYS_TOMB, RCQUEST_BOTH, RCTYPE_SONG_LOCATION, ACTOR_ID_MAX, SCENE_ROYAL_FAMILYS_TOMB, 0x00, "Song from Royal Family's Tomb", "Song from Royal Family's Tomb", RHT_SONG_FROM_ROYAL_FAMILYS_TOMB, RG_SUNS_SONG, SpoilerCollectionCheck::EventChkInf(EVENTCHKINF_LEARNED_SUNS_SONG), true);
locationTable[RC_SONG_FROM_OCARINA_OF_TIME] = Location::Base(RC_SONG_FROM_OCARINA_OF_TIME, RCQUEST_BOTH, RCTYPE_SONG_LOCATION, ACTOR_ID_MAX, SCENE_HYRULE_FIELD, 0x00, "Song from Ocarina of Time", "Song from Ocarina of Time", RHT_SONG_FROM_OCARINA_OF_TIME, RG_SONG_OF_TIME, SpoilerCollectionCheck::EventChkInf(EVENTCHKINF_LEARNED_SONG_OF_TIME), true);
locationTable[RC_SONG_FROM_WINDMILL] = Location::Base(RC_SONG_FROM_WINDMILL, RCQUEST_BOTH, RCTYPE_SONG_LOCATION, RCAREA_KAKARIKO_VILLAGE, ACTOR_ID_MAX, SCENE_WINDMILL_AND_DAMPES_GRAVE, 0x00, "Song from Windmill", "Song from Windmill", RHT_SONG_FROM_WINDMILL, RG_SONG_OF_STORMS, SpoilerCollectionCheck::EventChkInf(EVENTCHKINF_LEARNED_SONG_OF_STORMS), true);
/*-------------------------------
--- SHOPS ---
8 6 2 4

View file

@ -196,6 +196,8 @@ void Settings::CreateOptionDescriptions() {
mOptionDescriptions[RSK_MIX_INTERIOR_ENTRANCES] = "Interior entrances will be part of the mixed pool.";
mOptionDescriptions[RSK_MIX_GROTTO_ENTRANCES] = "Grotto entrances will be part of the mixed pool.";
mOptionDescriptions[RSK_SHUFFLE_SONGS] =
"Off - Songs will appear at their vanilla locations.\n"
"\n"
"Song locations - Songs will only appear at locations that normally teach songs.\n"
"\n"
"Dungeon rewards - Songs appear after beating a major dungeon boss.\n"

View file

@ -4393,7 +4393,7 @@ CustomMessage Randomizer::GetFishingPondOwnerMessage(u16 originalTextId) {
"fischen!",
"Désolé, mais l'étang est fermé.&J'ai perdu ma bonne %rCanne à Pêche%w...&Impossible de pêcher sans elle!");
if (Rando::Context::GetInstance()->GetOption(RSK_FISHING_POLE_HINT)) {
if (GetRandoSettingValue(RSK_FISHING_POLE_HINT)) {
messageEntry = messageEntry + CustomMessage(ctx->GetHint(RH_FISHING_POLE)->GetHintMessage());
}

View file

@ -2,7 +2,6 @@
#include <stdint.h>
#include "z64item.h"
#include "randomizer_inf.h"
#define MAX_TRICK_NAME_SIZE 50
@ -214,6 +213,15 @@ typedef enum {
LOGIC_MAX
} LogicVal;
#define DEFINE_RAND_INF(enum) enum,
typedef enum {
#include "randomizer_inf.h"
RAND_INF_MAX,
} RandomizerInf;
#undef DEFINE_RAND_INF
typedef enum {
RA_NONE,
RA_LINKS_POCKET,
@ -6166,6 +6174,7 @@ typedef enum {
// Song shuffle Settings (Song locations, Dungeon rewards, anywhere)
typedef enum {
RO_SONG_SHUFFLE_OFF,
RO_SONG_SHUFFLE_SONG_LOCATIONS,
RO_SONG_SHUFFLE_DUNGEON_REWARDS,
RO_SONG_SHUFFLE_ANYWHERE,

View file

@ -137,8 +137,10 @@ void RandomizerCheckObjects::UpdateImGuiVisibility() {
CVarGetInteger(CVAR_RANDOMIZER_SETTING("ShuffleMerchants"), RO_SHUFFLE_MERCHANTS_OFF) !=
RO_SHUFFLE_MERCHANTS_OFF) &&
(location.GetRCType() != RCTYPE_SONG_LOCATION ||
CVarGetInteger(CVAR_RANDOMIZER_SETTING("ShuffleSongs"), RO_SONG_SHUFFLE_SONG_LOCATIONS) !=
RO_SONG_SHUFFLE_SONG_LOCATIONS) && // song locations
(CVarGetInteger(CVAR_RANDOMIZER_SETTING("ShuffleSongs"), RO_SONG_SHUFFLE_SONG_LOCATIONS) !=
RO_SONG_SHUFFLE_SONG_LOCATIONS &&
CVarGetInteger(CVAR_RANDOMIZER_SETTING("ShuffleSongs"), RO_SONG_SHUFFLE_SONG_LOCATIONS) !=
RO_SONG_SHUFFLE_OFF)) && // song locations
((location.GetRCType() != RCTYPE_BOSS_HEART_OR_OTHER_REWARD &&
location.GetRandomizerCheck() != RC_SONG_FROM_IMPA &&
location.GetRandomizerCheck() != RC_SHEIK_IN_ICE_CAVERN) ||

View file

@ -51,6 +51,7 @@ bool showBeans;
bool showScrubs;
bool showMajorScrubs;
bool showMerchants;
bool showSongs;
bool showBeehives;
bool showCows;
bool showOverworldFreestanding;
@ -1306,6 +1307,9 @@ void LoadSettings() {
OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_SHUFFLE_MERCHANTS) ==
RO_SHUFFLE_MERCHANTS_ALL
: true;
showSongs = IS_RANDO
? OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_SHUFFLE_SONGS) != RO_SONG_SHUFFLE_OFF
: false;
showBeehives = IS_RANDO
? OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_SHUFFLE_BEEHIVES) == RO_GENERIC_YES
: false;
@ -1516,6 +1520,7 @@ bool IsCheckShuffled(RandomizerCheck rc) {
(showMajorScrubs && (rc == RC_LW_DEKU_SCRUB_NEAR_BRIDGE || // The 3 scrubs that are always randomized
rc == RC_HF_DEKU_SCRUB_GROTTO || rc == RC_LW_DEKU_SCRUB_GROTTO_FRONT))) &&
(loc->GetRCType() != RCTYPE_MERCHANT || showMerchants) &&
(loc->GetRCType() != RCTYPE_SONG_LOCATION || showSongs) &&
(loc->GetRCType() != RCTYPE_BEEHIVE || showBeehives) &&
(loc->GetRCType() != RCTYPE_OCARINA || showOcarinas) &&
(loc->GetRCType() != RCTYPE_SKULL_TOKEN || alwaysShowGS ||

File diff suppressed because it is too large Load diff

View file

@ -177,7 +177,7 @@ void Settings::CreateOptions() {
OPT_U8(RSK_MQ_GANONS_CASTLE, "Ganon's Castle Quest", {"Vanilla", "Master Quest", "Random"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("MQDungeonsGanonsCastle"), "", WidgetType::Combobox, RO_MQ_SET_VANILLA);
OPT_U8(RSK_SHUFFLE_DUNGEON_REWARDS, "Shuffle Dungeon Rewards", {"Vanilla", "End of Dungeons", "Any Dungeon", "Overworld", "Anywhere"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("ShuffleDungeonReward"), mOptionDescriptions[RSK_SHUFFLE_DUNGEON_REWARDS], WidgetType::Combobox, RO_DUNGEON_REWARDS_END_OF_DUNGEON);
OPT_U8(RSK_LINKS_POCKET, "Link's Pocket", {"Dungeon Reward", "Advancement", "Anything", "Nothing"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("LinksPocket"), "", WidgetType::Combobox, RO_LINKS_POCKET_DUNGEON_REWARD);
OPT_U8(RSK_SHUFFLE_SONGS, "Shuffle Songs", {"Song Locations", "Dungeon Rewards", "Anywhere"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("ShuffleSongs"), mOptionDescriptions[RSK_SHUFFLE_SONGS], WidgetType::Combobox, RO_SONG_SHUFFLE_SONG_LOCATIONS);
OPT_U8(RSK_SHUFFLE_SONGS, "Shuffle Songs", {"Off", "Song Locations", "Dungeon Rewards", "Anywhere"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("ShuffleSongs"), mOptionDescriptions[RSK_SHUFFLE_SONGS], WidgetType::Combobox, RO_SONG_SHUFFLE_SONG_LOCATIONS);
OPT_U8(RSK_SHOPSANITY, "Shop Shuffle", {"Off", "Specific Count", "Random"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("Shopsanity"), mOptionDescriptions[RSK_SHOPSANITY], WidgetType::Combobox, RO_SHOPSANITY_OFF);
OPT_U8(RSK_SHOPSANITY_COUNT, "Shops Item Count", {NumOpts(0, 7/*8*/)}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("ShopsanityCount"), mOptionDescriptions[RSK_SHOPSANITY_COUNT], WidgetType::Slider, 0, false, IMFLAG_NONE);
OPT_U8(RSK_SHOPSANITY_PRICES, "Shops Prices", {"Vanilla", "Cheap Balanced", "Balanced", "Fixed", "Range", "Set By Wallet"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("ShopsanityPrices"), mOptionDescriptions[RSK_SHOPSANITY_PRICES], WidgetType::Combobox, RO_PRICE_VANILLA, false, IMFLAG_NONE);

View file

@ -49,6 +49,7 @@ class StaticData {
static std::vector<RandomizerCheck> GetPondFishLocations();
static std::vector<RandomizerCheck> GetOverworldFishLocations();
static std::vector<RandomizerCheck> GetOverworldFairyLocations();
static void RegisterSongLocations();
static void RegisterBeehiveLocations();
static void RegisterCowLocations();
static void RegisterFishLocations();

View file

@ -28,6 +28,7 @@ extern "C" {
#include "src/overlays/actors/ovl_En_Daiku/z_en_daiku.h"
#include "src/overlays/actors/ovl_Bg_Spot02_Objects/z_bg_spot02_objects.h"
#include "src/overlays/actors/ovl_Bg_Spot03_Taki/z_bg_spot03_taki.h"
#include "src/overlays/actors/ovl_Bg_Spot06_Objects/z_bg_spot06_objects.h"
#include "src/overlays/actors/ovl_Bg_Hidan_Kousi/z_bg_hidan_kousi.h"
#include "src/overlays/actors/ovl_Bg_Dy_Yoseizo/z_bg_dy_yoseizo.h"
#include "src/overlays/actors/ovl_En_Dnt_Demo/z_en_dnt_demo.h"
@ -49,8 +50,6 @@ extern void EnRu2_SetEncounterSwitchFlag(EnRu2* enRu2, PlayState* play);
extern void EnDaiku_EscapeSuccess(EnDaiku* enDaiku, PlayState* play);
}
#define RAND_GET_OPTION(option) Rando::Context::GetInstance()->GetOption(option).Get()
void EnMa1_EndTeachSong(EnMa1* enMa1, PlayState* play) {
if (Message_GetState(&gPlayState->msgCtx) == TEXT_STATE_CLOSING) {
Flags_SetRandomizerInf(RAND_INF_LEARNED_EPONA_SONG);
@ -1386,3 +1385,15 @@ void TimeSaverRegisterHooks() {
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnItemReceive>(TimeSaverOnItemReceiveHandler);
});
}
void RegisterSkipWaterTempleGateDelay() {
COND_ID_HOOK(OnActorUpdate, ACTOR_BG_SPOT06_OBJECTS,
CVarGetInteger(CVAR_ENHANCEMENT("TimeSavers.SkipMiscInteractions"), IS_RANDO), [](void* actor) {
BgSpot06Objects* spot06 = static_cast<BgSpot06Objects*>(actor);
if (spot06->dyna.actor.params == 0) {
spot06->timer = 0;
}
})
}
static RegisterShipInitFunc skipWaterTempleGateDelay(RegisterSkipWaterTempleGateDelay);

View file

@ -3,6 +3,7 @@
#ifdef __cplusplus
#include <string>
#include <vector>
#include <set>
#include <unordered_map>

View file

@ -792,6 +792,11 @@ void SohMenu::AddMenuEnhancements() {
.CVar(CVAR_ENHANCEMENT("FastFarores"))
.Options(CheckboxOptions().Tooltip("Greatly decreases cast time of Farore's Wind magic spell."));
AddWidget(path, "Bottles", WIDGET_SEPARATOR_TEXT);
AddWidget(path, "Rebottle Blue Fire", WIDGET_CVAR_CHECKBOX)
.CVar(CVAR_ENHANCEMENT("RebottleBlueFire"))
.Options(CheckboxOptions().Tooltip("Blue Fire dropped from bottle can be bottled."));
// Fixes
path.sidebarName = "Fixes";
AddSidebarEntry("Enhancements", path.sidebarName, 3);
@ -1307,6 +1312,11 @@ void SohMenu::AddMenuEnhancements() {
.Format("%d notes")
.Tooltip("Adjust the number of notes you need to play to end the third round."));
AddWidget(path, "Forest Temple", WIDGET_SEPARATOR_TEXT);
AddWidget(path, "Solve Amy's Puzzle", WIDGET_CVAR_CHECKBOX)
.CVar(CVAR_ENHANCEMENT("SkipAmyPuzzle"))
.Options(CheckboxOptions().Tooltip("Amy's block pushing puzzle instantly solved."));
path.column = SECTION_COLUMN_3;
AddWidget(path, "Fishing", WIDGET_SEPARATOR_TEXT);
AddWidget(path, "Customize Behavior##Fishing", WIDGET_CVAR_CHECKBOX)

View file

@ -3401,6 +3401,8 @@ Actor* Actor_Spawn(ActorContext* actorCtx, PlayState* play, s16 actorId, f32 pos
Actor_Init(actor, play);
gSegments[6] = temp;
GameInteractor_ExecuteOnActorSpawn(actor);
return actor;
}

View file

@ -6,6 +6,8 @@
#include "z_bg_po_event.h"
#include "objects/object_po_sisters/object_po_sisters.h"
#include "soh/Enhancements/game-interactor/GameInteractor.h"
#include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h"
#define FLAGS 0
@ -333,7 +335,7 @@ void BgPoEvent_BlockIdle(BgPoEvent* this, PlayState* play) {
Player* player = GET_PLAYER(play);
Actor* amy;
if (sBgPoEventPuzzleState == 0xF) {
if (GameInteractor_Should(VB_AMY_SOLVE, sBgPoEventPuzzleState == 0xF)) {
this->actionFunc = BgPoEvent_BlockSolved;
if ((this->type == 0) && (this->index == 0)) {
amy = Actor_Spawn(&play->actorCtx, play, ACTOR_EN_PO_SISTERS, this->dyna.actor.world.pos.x + 30.0f,

View file

@ -154,17 +154,6 @@ void EnFu_WaitChild(EnFu* this, PlayState* play) {
}
}
void GivePlayerRandoRewardSongOfStorms(EnFu* windmillGuy, PlayState* play, RandomizerCheck check) {
if (windmillGuy->actor.parent != NULL && windmillGuy->actor.parent->id == GET_PLAYER(play)->actor.id &&
!Flags_GetTreasure(play, 0x1F)) {
Flags_SetTreasure(play, 0x1F);
windmillGuy->actionFunc = func_80A1DBD4;
} else if (!Flags_GetTreasure(play, 0x1F)) {
GetItemEntry getItemEntry = Randomizer_GetItemFromKnownCheck(check, RG_SONG_OF_STORMS);
GiveItemEntryFromActor(&windmillGuy->actor, play, getItemEntry, 10000.0f, 100.0f);
}
}
void func_80A1DB60(EnFu* this, PlayState* play) {
if (play->csCtx.state == CS_STATE_IDLE) {
this->actionFunc = EnFu_WaitAdult;

View file

@ -1,6 +1,7 @@
#include "z_en_skj.h"
#include "overlays/actors/ovl_En_Skjneedle/z_en_skjneedle.h"
#include "objects/object_skj/object_skj.h"
#include "soh/Enhancements/enhancementTypes.h"
#include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h"
#include "soh/OTRGlobals.h"
#include "soh/ResourceManagerHelpers.h"
@ -404,7 +405,9 @@ void EnSkj_Init(Actor* thisx, PlayState* play2) {
default:
this->actor.params = type;
if (((this->actor.params != 0) && (this->actor.params != 1)) && (this->actor.params != 2)) {
if (INV_CONTENT(ITEM_TRADE_ADULT) < ITEM_SAW) {
if (INV_CONTENT(ITEM_TRADE_ADULT) < ITEM_SAW &&
CVarGetInteger(CVAR_ENHANCEMENT("RandomizedEnemies"), ENEMY_RANDOMIZER_OFF) ==
ENEMY_RANDOMIZER_OFF) {
Actor_Kill(&this->actor);
return;
}

View file

@ -1374,7 +1374,8 @@ void FileChoose_UpdateQuestMenu(GameState* thisx) {
} else {
defaultName = &emptyNameNES;
}
} else { // GAME_REGION_NTSC
this->charPage = FS_CHAR_PAGE_HIRA; // Default to Hiragana Keyboard
} else { // GAME_REGION_NTSC
defaultName = CVarGetInteger(CVAR_ENHANCEMENT("LinkDefaultName"), 0) ? &linkNameNES : &emptyNameNES;
}
memcpy(Save_GetSaveMetaInfo(this->buttonIndex)->playerName, defaultName, 8);
@ -1575,7 +1576,8 @@ void FileChoose_UpdateRandomizerMenu(GameState* thisx) {
} else {
defaultName = &emptyNameNES;
}
} else { // GAME_REGION_NTSC
this->charPage = FS_CHAR_PAGE_HIRA; // Default to Hiragana Keyboard
} else { // GAME_REGION_NTSC
defaultName = CVarGetInteger(CVAR_ENHANCEMENT("LinkDefaultName"), 0) ? &linkNameNES : &emptyNameNES;
}
memcpy(Save_GetSaveMetaInfo(this->buttonIndex)->playerName, defaultName, 8);