Merge remote-tracking branch 'origin/develop' into actor-accessibility-experiments

This commit is contained in:
Demur Rumed 2025-06-12 20:26:14 +00:00
commit 14aa193d8c
36 changed files with 367 additions and 225 deletions

View file

@ -266,9 +266,6 @@ typedef struct Actor {
/* 0x134 */ ActorFunc draw; // Draw Routine. Called by `Actor_Draw` /* 0x134 */ ActorFunc draw; // Draw Routine. Called by `Actor_Draw`
/* 0x138 */ ActorResetFunc reset; /* 0x138 */ ActorResetFunc reset;
/* 0x13C */ char dbgPad[0x10]; // Padding that only exists in the debug rom /* 0x13C */ char dbgPad[0x10]; // Padding that only exists in the debug rom
// #region SOH [General]
/* */ u8 maximumHealth; // Max health value for use with health bars, set on actor init
// #endregion
} Actor; // size = 0x14C } Actor; // size = 0x14C
typedef enum { typedef enum {

View file

@ -0,0 +1,19 @@
#include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h"
#include "soh/ShipInit.hpp"
#include "soh/Enhancements/randomizer/context.h"
extern "C" {
extern PlayState* gPlayState;
#include "src/overlays/actors/ovl_En_Niw_Lady/z_en_niw_lady.h"
}
void RegisterCuccosToReturn() {
COND_VB_SHOULD(VB_SET_CUCCO_COUNT, CVarGetInteger(CVAR_ENHANCEMENT("CuccosToReturn"), 7) != 7, {
EnNiwLady* enNiwLady = va_arg(args, EnNiwLady*);
// Override starting Cucco count using setting value
enNiwLady->cuccosInPen = 7 - CVarGetInteger(CVAR_ENHANCEMENT("CuccosToReturn"), 7);
*should = false;
});
}
static RegisterShipInitFunc initFunc(RegisterCuccosToReturn, { CVAR_ENHANCEMENT("CuccosToReturn") });

View file

@ -110,6 +110,7 @@ void applyPreset(std::string presetName, std::vector<PresetSection> includeSecti
} }
} }
} }
ShipInit::InitAll();
} }
void DrawPresetSelector(std::vector<PresetSection> includeSections, std::string presetLoc, bool disabled) { void DrawPresetSelector(std::vector<PresetSection> includeSections, std::string presetLoc, bool disabled) {

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" #include "variables.h"
} }
#define RAND_GET_OPTION(option) Rando::Context::GetInstance()->GetOption(option).Get()
static bool sEnteredBlueWarp = false; static bool sEnteredBlueWarp = false;
/** /**

View file

@ -0,0 +1,31 @@
#include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h"
#include "soh/ShipInit.hpp"
extern "C" {
#include "src/overlays/actors/ovl_En_Wood02/z_en_wood02.h"
}
extern PlayState* gPlayState;
void RegisterTreesDropSticks() {
COND_VB_SHOULD(VB_TREE_DROP_COLLECTIBLE, CVarGetInteger(CVAR_ENHANCEMENT("TreesDropSticks"), 0), {
if (INV_CONTENT(ITEM_STICK) != ITEM_NONE) {
EnWood02* tree = va_arg(args, EnWood02*);
Vec3f dropsSpawnPt = tree->actor.world.pos;
dropsSpawnPt.y += 200.0f;
*should = false;
for (s32 numDrops = Rand_Next() % 4; numDrops > 0; numDrops--) {
Item_DropCollectible(gPlayState, &dropsSpawnPt, ITEM00_STICK);
}
}
});
COND_VB_SHOULD(VB_PREVENT_ADULT_STICK, CVarGetInteger(CVAR_ENHANCEMENT("TreesDropSticks"), 0), {
if (INV_CONTENT(ITEM_STICK) != ITEM_NONE) {
*should = false;
}
});
}
static RegisterShipInitFunc initFunc(RegisterTreesDropSticks, { CVAR_ENHANCEMENT("TreesDropSticks") });

View file

@ -13,15 +13,29 @@ extern "C" {
#include <z64.h> #include <z64.h>
} }
const char* enemyCVarList[] = { #define CVAR_ENEMY_RANDOMIZER_NAME CVAR_ENHANCEMENT("RandomizedEnemies")
CVAR_ENHANCEMENT("RandomizedEnemyList.Armos"), CVAR_ENHANCEMENT("RandomizedEnemyList.Arwing"), #define CVAR_ENEMY_RANDOMIZER_DEFAULT ENEMY_RANDOMIZER_OFF
CVAR_ENHANCEMENT("RandomizedEnemyList.BabyDodongo"), CVAR_ENHANCEMENT("RandomizedEnemyList.Bari"), #define CVAR_ENEMY_RANDOMIZER_VALUE CVarGetInteger(CVAR_ENEMY_RANDOMIZER_NAME, CVAR_ENEMY_RANDOMIZER_DEFAULT)
CVAR_ENHANCEMENT("RandomizedEnemyList.Beamos"), CVAR_ENHANCEMENT("RandomizedEnemyList.BigSkulltula"),
CVAR_ENHANCEMENT("RandomizedEnemyList.BigStalchild"), CVAR_ENHANCEMENT("RandomizedEnemyList.Biri"), typedef struct EnemyEntry {
CVAR_ENHANCEMENT("RandomizedEnemyList.BlackKnuckle"), CVAR_ENHANCEMENT("RandomizedEnemyList.BlueTektite"), int16_t id;
CVAR_ENHANCEMENT("RandomizedEnemyList.Bubble"), CVAR_ENHANCEMENT("RandomizedEnemyList.ClubMoblin"), int16_t params;
CVAR_ENHANCEMENT("RandomizedEnemyList.DarkLink"), CVAR_ENHANCEMENT("RandomizedEnemyList.Dinolfos"), } EnemyEntry;
CVAR_ENHANCEMENT("RandomizedEnemyList.Dodongo"), CVAR_ENHANCEMENT("RandomizedEnemyList.FireKeese"),
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.FloorTile"), CVAR_ENHANCEMENT("RandomizedEnemyList.Floormaster"),
CVAR_ENHANCEMENT("RandomizedEnemyList.FlyingPeahat"), CVAR_ENHANCEMENT("RandomizedEnemyList.FlyingPot"), CVAR_ENHANCEMENT("RandomizedEnemyList.FlyingPeahat"), CVAR_ENHANCEMENT("RandomizedEnemyList.FlyingPot"),
CVAR_ENHANCEMENT("RandomizedEnemyList.Freezard"), CVAR_ENHANCEMENT("RandomizedEnemyList.Gibdo"), 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.Keese"), CVAR_ENHANCEMENT("RandomizedEnemyList.LargeBaba"),
CVAR_ENHANCEMENT("RandomizedEnemyList.LikeLike"), CVAR_ENHANCEMENT("RandomizedEnemyList.Lizalfos"), CVAR_ENHANCEMENT("RandomizedEnemyList.LikeLike"), CVAR_ENHANCEMENT("RandomizedEnemyList.Lizalfos"),
CVAR_ENHANCEMENT("RandomizedEnemyList.MadScrub"), CVAR_ENHANCEMENT("RandomizedEnemyList.NormalWolfos"), CVAR_ENHANCEMENT("RandomizedEnemyList.MadScrub"), CVAR_ENHANCEMENT("RandomizedEnemyList.NormalWolfos"),
CVAR_ENHANCEMENT("RandomizedEnemyList.PeahatLarva"), CVAR_ENHANCEMENT("RandomizedEnemyList.Redead"), CVAR_ENHANCEMENT("RandomizedEnemyList.PeahatLarva"), /*CVAR_ENHANCEMENT("RandomizedEnemyList.Poe"),*/
CVAR_ENHANCEMENT("RandomizedEnemyList.RedTektite"), CVAR_ENHANCEMENT("RandomizedEnemyList.Shabom"), CVAR_ENHANCEMENT("RandomizedEnemyList.Redead"), CVAR_ENHANCEMENT("RandomizedEnemyList.RedTektite"),
CVAR_ENHANCEMENT("RandomizedEnemyList.ShellBlade"), CVAR_ENHANCEMENT("RandomizedEnemyList.Skulltula"), 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.SmallBaba"), CVAR_ENHANCEMENT("RandomizedEnemyList.SmallStalchild"),
CVAR_ENHANCEMENT("RandomizedEnemyList.Spike"), CVAR_ENHANCEMENT("RandomizedEnemyList.Stalfos"), CVAR_ENHANCEMENT("RandomizedEnemyList.SpearMoblin"), CVAR_ENHANCEMENT("RandomizedEnemyList.Spike"),
CVAR_ENHANCEMENT("RandomizedEnemyList.Stinger"), CVAR_ENHANCEMENT("RandomizedEnemyList.Tailparasan"), CVAR_ENHANCEMENT("RandomizedEnemyList.Stalfos"), CVAR_ENHANCEMENT("RandomizedEnemyList.Stinger"),
CVAR_ENHANCEMENT("RandomizedEnemyList.TorchSlug"), CVAR_ENHANCEMENT("RandomizedEnemyList.Wallmaster"), CVAR_ENHANCEMENT("RandomizedEnemyList.Tailparasan"), CVAR_ENHANCEMENT("RandomizedEnemyList.TorchSlug"),
CVAR_ENHANCEMENT("RandomizedEnemyList.WhiteKnuckle"), CVAR_ENHANCEMENT("RandomizedEnemyList.WhiteWolfos"), CVAR_ENHANCEMENT("RandomizedEnemyList.Wallmaster"), CVAR_ENHANCEMENT("RandomizedEnemyList.WhiteKnuckle"),
CVAR_ENHANCEMENT("RandomizedEnemyList.WitheredBaba"), CVAR_ENHANCEMENT("RandomizedEnemyList.WhiteWolfos"), CVAR_ENHANCEMENT("RandomizedEnemyList.WitheredBaba"),
}; };
const char* enemyNameList[] = { const char* enemyNameList[RANDOMIZED_ENEMY_SPAWN_TABLE_SIZE] = {
"Anubis",
"Armos", "Armos",
"Arwing", "Arwing",
"Baby Dodongo", "Baby Dodongo",
@ -58,6 +74,7 @@ const char* enemyNameList[] = {
"Dinolfos", "Dinolfos",
"Dodongo", "Dodongo",
"Fire Keese", "Fire Keese",
//"Flare Dancer",
"Floor Tile", "Floor Tile",
"Floormaster", "Floormaster",
"Flying Peahat", "Flying Peahat",
@ -75,13 +92,16 @@ const char* enemyNameList[] = {
"Mad Scrub", "Mad Scrub",
"Wolfos (Normal)", "Wolfos (Normal)",
"Peahat Larva", "Peahat Larva",
//"Poe",
"Redead", "Redead",
"Red Tektite", "Red Tektite",
"Shabom", "Shabom",
"Shell Blade", "Shell Blade",
"Skulltula", "Skulltula",
"Skull Kid",
"Small Deku Baba", "Small Deku Baba",
"Stalchild (Small)", "Stalchild (Small)",
"Spear Moblin",
"Spike", "Spike",
"Stalfos", "Stalfos",
"Stinger", "Stinger",
@ -94,6 +114,7 @@ const char* enemyNameList[] = {
}; };
static EnemyEntry randomizedEnemySpawnTable[RANDOMIZED_ENEMY_SPAWN_TABLE_SIZE] = { static EnemyEntry randomizedEnemySpawnTable[RANDOMIZED_ENEMY_SPAWN_TABLE_SIZE] = {
{ ACTOR_EN_ANUBICE_TAG, 1 }, // Anubis
{ ACTOR_EN_AM, -1 }, // Armos { ACTOR_EN_AM, -1 }, // Armos
{ ACTOR_EN_CLEAR_TAG, 1 }, // Arwing { ACTOR_EN_CLEAR_TAG, 1 }, // Arwing
{ ACTOR_EN_DODOJR, 0 }, // Baby Dodongo { ACTOR_EN_DODOJR, 0 }, // Baby Dodongo
@ -105,11 +126,13 @@ static EnemyEntry randomizedEnemySpawnTable[RANDOMIZED_ENEMY_SPAWN_TABLE_SIZE] =
{ ACTOR_EN_IK, 2 }, // Iron Knuckle (black, standing) { ACTOR_EN_IK, 2 }, // Iron Knuckle (black, standing)
{ ACTOR_EN_TITE, -2 }, // Tektite (blue) { ACTOR_EN_TITE, -2 }, // Tektite (blue)
{ ACTOR_EN_BB, -1 }, // Bubble (flying skull enemy) (blue) { ACTOR_EN_BB, -1 }, // Bubble (flying skull enemy) (blue)
{ ACTOR_EN_MB, 0 }, // Moblins (Club) { ACTOR_EN_MB, 0 }, // Club Moblin
{ ACTOR_EN_TORCH2, 0 }, // Dark Link { ACTOR_EN_TORCH2, 0 }, // Dark Link
{ ACTOR_EN_ZF, -2 }, // Dinolfos { ACTOR_EN_ZF, -2 }, // Dinolfos
{ ACTOR_EN_DODONGO, -1 }, // Dodongo { ACTOR_EN_DODONGO, -1 }, // Dodongo
{ ACTOR_EN_FIREFLY, 1 }, // Fire Keese { 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_YUKABYUN, 0 }, // Flying Floor Tile
{ ACTOR_EN_FLOORMAS, 0 }, // Floormaster { ACTOR_EN_FLOORMAS, 0 }, // Floormaster
{ ACTOR_EN_PEEHAT, -1 }, // Flying Peahat (big grounded, doesn't spawn larva) { ACTOR_EN_PEEHAT, -1 }, // Flying Peahat (big grounded, doesn't spawn larva)
@ -122,18 +145,30 @@ static EnemyEntry randomizedEnemySpawnTable[RANDOMIZED_ENEMY_SPAWN_TABLE_SIZE] =
{ ACTOR_EN_ST, 2 }, // Skulltula (invisible) { ACTOR_EN_ST, 2 }, // Skulltula (invisible)
{ ACTOR_EN_FIREFLY, 2 }, // Regular Keese { ACTOR_EN_FIREFLY, 2 }, // Regular Keese
{ ACTOR_EN_DEKUBABA, 1 }, // Deku Baba (large) { 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_RR, 0 }, // Like-Like
{ ACTOR_EN_ZF, -1 }, // Lizalfos { ACTOR_EN_ZF, -1 }, // Lizalfos
{ ACTOR_EN_DEKUNUTS, 768 }, // Mad Scrub (triple attack) (projectiles don't work) { ACTOR_EN_DEKUNUTS, 768 }, // Mad Scrub (triple attack) (projectiles don't work)
{ ACTOR_EN_WF, 0 }, // Wolfos (normal) { ACTOR_EN_WF, 0 }, // Wolfos (normal)
// 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 { 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_RD, 1 }, // Redead (standing)
{ ACTOR_EN_TITE, -1 }, // Tektite (red) { ACTOR_EN_TITE, -1 }, // Tektite (red)
{ ACTOR_EN_BUBBLE, 0 }, // Shabom (bubble) { ACTOR_EN_BUBBLE, 0 }, // Shabom (bubble)
{ ACTOR_EN_SB, 0 }, // Shell Blade { ACTOR_EN_SB, 0 }, // Shell Blade
{ ACTOR_EN_ST, 0 }, // Skulltula (normal) { ACTOR_EN_ST, 0 }, // Skulltula (normal)
{ ACTOR_EN_SKJ, 4159 }, // Skull Kid
{ ACTOR_EN_DEKUBABA, 0 }, // Deku Baba (small) { ACTOR_EN_DEKUBABA, 0 }, // Deku Baba (small)
{ ACTOR_EN_SKB, 1 }, // Stalchild (small) { ACTOR_EN_SKB, 1 }, // Stalchild (small)
{ ACTOR_EN_MB, -1 }, // Spear Moblin
{ ACTOR_EN_NY, 0 }, // Spike (rolling enemy) { ACTOR_EN_NY, 0 }, // Spike (rolling enemy)
{ ACTOR_EN_TEST, 2 }, // Stalfos { ACTOR_EN_TEST, 2 }, // Stalfos
{ ACTOR_EN_EIYER, 10 }, // Stinger (land) (One in formation, sink under floor and do not activate) { ACTOR_EN_EIYER, 10 }, // Stinger (land) (One in formation, sink under floor and do not activate)
@ -143,17 +178,10 @@ static EnemyEntry randomizedEnemySpawnTable[RANDOMIZED_ENEMY_SPAWN_TABLE_SIZE] =
{ ACTOR_EN_IK, 3 }, // Iron Knuckle (white, standing) { ACTOR_EN_IK, 3 }, // Iron Knuckle (white, standing)
{ ACTOR_EN_WF, 1 }, // Wolfos (white) { ACTOR_EN_WF, 1 }, // Wolfos (white)
{ ACTOR_EN_KAREBABA, 0 }, // Withered Deku Baba { 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)
}; };
static int enemiesToRandomize[] = { static int enemiesToRandomize[] = {
ACTOR_EN_ANUBICE_TAG, // Anubis
ACTOR_EN_FIREFLY, // Keese (including fire/ice) ACTOR_EN_FIREFLY, // Keese (including fire/ice)
ACTOR_EN_TEST, // Stalfos ACTOR_EN_TEST, // Stalfos
ACTOR_EN_TITE, // Tektite ACTOR_EN_TITE, // Tektite
@ -161,7 +189,7 @@ static int enemiesToRandomize[] = {
ACTOR_EN_OKUTA, // Octorok ACTOR_EN_OKUTA, // Octorok
ACTOR_EN_WALLMAS, // Wallmaster ACTOR_EN_WALLMAS, // Wallmaster
ACTOR_EN_DODONGO, // Dodongo ACTOR_EN_DODONGO, // Dodongo
// ACTOR_EN_REEBA, // Leever (reliant on spawner (z_e_encount1.c) // ACTOR_EN_REEBA, // Leever (reliant on spawner (z_en_encount1.c))
ACTOR_EN_PEEHAT, // Flying Peahat, big one spawning larva, larva ACTOR_EN_PEEHAT, // Flying Peahat, big one spawning larva, larva
ACTOR_EN_ZF, // Lizalfos, Dinolfos ACTOR_EN_ZF, // Lizalfos, Dinolfos
ACTOR_EN_GOMA, // Gohma Larva (normal, eggs, gohma eggs) ACTOR_EN_GOMA, // Gohma Larva (normal, eggs, gohma eggs)
@ -184,8 +212,7 @@ static int enemiesToRandomize[] = {
ACTOR_EN_FLOORMAS, // Floormaster ACTOR_EN_FLOORMAS, // Floormaster
ACTOR_EN_RD, // Redead, Gibdo ACTOR_EN_RD, // Redead, Gibdo
ACTOR_EN_SW, // Skullwalltula ACTOR_EN_SW, // Skullwalltula
// ACTOR_EN_FD, // Flare Dancer (can be randomized, but not randomized to, so keeping it in vanilla locations ACTOR_EN_FD, // Flare Dancer
// means it at least shows up in the game)
ACTOR_EN_SB, // Shell Blade ACTOR_EN_SB, // Shell Blade
ACTOR_EN_KAREBABA, // Withered Deku Baba ACTOR_EN_KAREBABA, // Withered Deku Baba
ACTOR_EN_RR, // Like-Like ACTOR_EN_RR, // Like-Like
@ -198,6 +225,7 @@ static int enemiesToRandomize[] = {
ACTOR_EN_WF, // Wolfos ACTOR_EN_WF, // Wolfos
ACTOR_EN_SKB, // Stalchild ACTOR_EN_SKB, // Stalchild
ACTOR_EN_CROW, // Guay 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, 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() { void GetSelectedEnemies() {
selectedEnemyList.clear(); 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)) { if (CVarGetInteger(CVAR_ENHANCEMENT("RandomizedEnemyList.All"), 0)) {
selectedEnemyList.push_back(randomizedEnemySpawnTable[i]); selectedEnemyList.push_back(randomizedEnemySpawnTable[i]);
} else if (CVarGetInteger(enemyCVarList[i], 1)) { } 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_SB:
case ACTOR_EN_NY: case ACTOR_EN_NY:
return (!(!isMQ && sceneNum == SCENE_WATER_TEMPLE && roomNum == 2)); return (!(!isMQ && sceneNum == SCENE_WATER_TEMPLE && roomNum == 2));
case ACTOR_EN_SKJ:
return !(sceneNum == SCENE_LOST_WOODS && LINK_IS_CHILD);
default: default:
return 1; 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) { bool IsEnemyAllowedToSpawn(int16_t sceneNum, int8_t roomNum, EnemyEntry enemy) {
uint32_t isMQ = ResourceMgr_IsSceneMasterQuest(sceneNum); uint32_t isMQ = ResourceMgr_IsSceneMasterQuest(sceneNum);
// Freezard - Child Link can only kill this with jump slash Deku Sticks or other equipment like bombs. // Freezard - Child Link can only kill this with jump slash Deku Sticks or other equipment like bombs.
// Beamos - Needs bombs. // Beamos - Needs bombs.
// Anubis - Needs fire.
// Shell Blade & Spike - Child Link can't kill these with sword or Deku Stick. // 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. // 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? // 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 || bool enemiesToExcludeClearRooms =
enemy.id == ACTOR_EN_NY || enemy.id == ACTOR_EN_CLEAR_TAG || enemy.id == ACTOR_EN_FZ || enemy.id == ACTOR_EN_VM || enemy.id == ACTOR_EN_SB || enemy.id == ACTOR_EN_NY ||
enemy.id == ACTOR_EN_WALLMAS || enemy.id == ACTOR_EN_TORCH2 || enemy.id == ACTOR_EN_CLEAR_TAG || enemy.id == ACTOR_EN_WALLMAS || enemy.id == ACTOR_EN_TORCH2 ||
enemy.id == ACTOR_EN_MB; (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. // Bari - Spawns 3 more enemies, potentially extremely difficult in timed rooms.
bool enemiesToExcludeTimedRooms = enemiesToExcludeClearRooms || enemy.id == ACTOR_EN_VALI; bool enemiesToExcludeTimedRooms = enemiesToExcludeClearRooms || enemy.id == ACTOR_EN_VALI;
@ -532,3 +562,16 @@ bool IsEnemyAllowedToSpawn(int16_t sceneNum, int8_t roomNum, EnemyEntry enemy) {
return 1; 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 #pragma once
#include <libultraship/bridge.h> #include <libultraship/libultra/types.h>
typedef struct EnemyEntry { #define RANDOMIZED_ENEMY_SPAWN_TABLE_SIZE 52
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);
extern const char* enemyCVarList[]; extern const char* enemyCVarList[];
extern const char* enemyNameList[]; extern const char* enemyNameList[];
extern void GetSelectedEnemies(); extern void GetSelectedEnemies();
#ifndef __cplusplus #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); int16_t* rotY, int16_t* rotZ, int16_t* params);
#endif #endif

View file

@ -20,6 +20,14 @@ typedef enum {
// - `int32_t` (entrance index) (promoted from `uint16_t` by va_arg) // - `int32_t` (entrance index) (promoted from `uint16_t` by va_arg)
VB_ALLOW_ENTRANCE_CS_FOR_EITHER_AGE, VB_ALLOW_ENTRANCE_CS_FOR_EITHER_AGE,
// #### `result`
// ```c
// sBgPoEventPuzzleState == 0xF
// ```
// #### `args`
// - None
VB_AMY_SOLVE,
// #### `result` // #### `result`
// ```c // ```c
// this->actor.textId == 0x401A // this->actor.textId == 0x401A
@ -1689,6 +1697,15 @@ typedef enum {
// - `*ObjTsubo` // - `*ObjTsubo`
VB_POT_SETUP_DRAW, VB_POT_SETUP_DRAW,
// #### `result`
// ```c
// dropId == ITEM00_STICK
// ```
// #### `args`
// - None
VB_PREVENT_ADULT_STICK,
// #### `result`
// #### `result` // #### `result`
// ```c // ```c
// true // true
@ -2065,6 +2082,13 @@ typedef enum {
VB_TRANSITION_TO_SAVE_SCREEN_ON_DEATH, VB_TRANSITION_TO_SAVE_SCREEN_ON_DEATH,
// #### `result` // #### `result`
// ```c
// true
// ```
// #### `args`
// - `*EnWood02`
VB_TREE_DROP_COLLECTIBLE,
// ```c // ```c
// true // true
// ``` // ```

View file

@ -9,8 +9,6 @@ extern PlayState* gPlayState;
#include "src/overlays/actors/ovl_En_Door/z_en_door.h" #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>; using SceneDoorParamsPair = std::pair<int, int>;
std::map<SceneDoorParamsPair, RandomizerInf> lookupTable = { std::map<SceneDoorParamsPair, RandomizerInf> lookupTable = {
// clang-format off // clang-format off

View file

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

View file

@ -15,8 +15,6 @@ extern "C" {
extern PlayState* gPlayState; extern PlayState* gPlayState;
} }
#define RAND_GET_OPTION(option) Rando::Context::GetInstance()->GetOption(option).Get()
extern void EnItem00_DrawRandomizedItem(EnItem00* enItem00, PlayState* play); extern void EnItem00_DrawRandomizedItem(EnItem00* enItem00, PlayState* play);
extern "C" void ObjKibako2_RandomizerDraw(Actor* thisx, 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) { uint8_t ObjKibako2_RandomizerHoldsItem(ObjKibako2* crateActor, PlayState* play) {
RandomizerCheck rc = crateActor->crateIdentity.randomizerCheck; RandomizerCheck rc = crateActor->crateIdentity.randomizerCheck;
uint8_t isDungeon = Rando::StaticData::GetLocation(rc)->IsDungeon(); 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 // Don't pull randomized item if crate isn't randomized or is already checked
if (!IS_RANDO || (crateSetting == RO_SHUFFLE_CRATES_OVERWORLD && isDungeon) || 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) { uint8_t ObjKibako_RandomizerHoldsItem(ObjKibako* smallCrateActor, PlayState* play) {
RandomizerCheck rc = smallCrateActor->smallCrateIdentity.randomizerCheck; RandomizerCheck rc = smallCrateActor->smallCrateIdentity.randomizerCheck;
uint8_t isDungeon = Rando::StaticData::GetLocation(rc)->IsDungeon(); 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 // Don't pull randomized item if crate isn't randomized or is already checked
if (!IS_RANDO || (crateSetting == RO_SHUFFLE_CRATES_OVERWORLD && isDungeon) || if (!IS_RANDO || (crateSetting == RO_SHUFFLE_CRATES_OVERWORLD && isDungeon) ||
@ -211,7 +209,7 @@ void ObjKibako_RandomizerSpawnCollectible(ObjKibako* smallCrateActor, PlayState*
void ObjKibako2_RandomizerInit(void* actorRef) { void ObjKibako2_RandomizerInit(void* actorRef) {
Actor* actor = static_cast<Actor*>(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 // 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 || if (actor->id != ACTOR_OBJ_KIBAKO2 ||

View file

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

View file

@ -11,8 +11,6 @@ extern "C" {
extern PlayState* gPlayState; extern PlayState* gPlayState;
} }
#define RAND_GET_OPTION(option) Rando::Context::GetInstance()->GetOption(option).Get()
extern void EnItem00_DrawRandomizedItem(EnItem00* enItem00, PlayState* play); extern void EnItem00_DrawRandomizedItem(EnItem00* enItem00, PlayState* play);
void DrawTypeOfGrass(EnKusa* grassActor, Gfx* bushDList, Gfx* grassDList, 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; RandomizerCheck rc = grassActor->grassIdentity.randomizerCheck;
uint8_t isDungeon = Rando::StaticData::GetLocation(rc)->IsDungeon(); 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 // Don't pull randomized item if grass isn't randomized or is already checked
if (!IS_RANDO || (grassSetting == RO_SHUFFLE_GRASS_OVERWORLD && isDungeon) || 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) { uint8_t ObjTsubo_RandomizerHoldsItem(ObjTsubo* potActor, PlayState* play) {
RandomizerCheck rc = potActor->potIdentity.randomizerCheck; RandomizerCheck rc = potActor->potIdentity.randomizerCheck;
uint8_t isDungeon = Rando::StaticData::GetLocation(rc)->IsDungeon(); 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 // Don't pull randomized item if pot isn't randomized or is already checked
if (!IS_RANDO || (potSetting == RO_SHUFFLE_POTS_OVERWORLD && isDungeon) || 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 // 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) { if (id == VB_LOCK_BOSS_DOOR) {
DoorShutter* doorActor = va_arg(args, DoorShutter*); 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 && if (gPlayState->sceneNum == SCENE_GANONS_TOWER && doorActor->dyna.actor.world.pos.y == 800 &&
(shufflePotSetting == RO_SHUFFLE_POTS_DUNGEONS || shufflePotSetting == RO_SHUFFLE_POTS_ALL)) { (shufflePotSetting == RO_SHUFFLE_POTS_DUNGEONS || shufflePotSetting == RO_SHUFFLE_POTS_ALL)) {
*should = false; *should = false;

View file

@ -14,6 +14,8 @@
#include <map> #include <map>
#include <nlohmann/json.hpp> #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 * @brief Singleton for storing and accessing dynamic Randomizer-related data
* *

View file

@ -15,10 +15,6 @@ extern SaveContext gSaveContext;
extern PlayState* gPlayState; 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 * @brief Parallel list of pond fish checks for both ages
*/ */
@ -488,15 +484,15 @@ void Fishsanity::OnItemReceiveHandler(GetItemEntry itemEntry) {
// C interface // C interface
extern "C" { extern "C" {
bool Randomizer_GetPondFishShuffled() { bool Randomizer_GetPondFishShuffled() {
return FSi->GetPondFishShuffled(); return Rando::Context::GetInstance()->GetFishsanity()->GetPondFishShuffled();
} }
bool Randomizer_GetOverworldFishShuffled() { bool Randomizer_GetOverworldFishShuffled() {
return FSi->GetOverworldFishShuffled(); return Rando::Context::GetInstance()->GetFishsanity()->GetOverworldFishShuffled();
} }
bool Randomizer_IsAdultPond() { bool Randomizer_IsAdultPond() {
return FSi->IsAdultPond(); return Rando::Context::GetInstance()->GetFishsanity()->IsAdultPond();
} }
void Fishsanity_DrawEffShadow(Actor* actor, Lights* lights, PlayState* play) { 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); extern void EnGe1_SetAnimationIdle(EnGe1* enGe1);
} }
#define RAND_GET_OPTION(option) Rando::Context::GetInstance()->GetOption(option).Get()
bool LocMatchesQuest(Rando::Location loc) { bool LocMatchesQuest(Rando::Location loc) {
if (loc.GetQuest() == RCQUEST_BOTH) { if (loc.GetQuest() == RCQUEST_BOTH) {
return true; return true;
@ -927,13 +925,6 @@ void RandomizerOnVanillaBehaviorHandler(GIVanillaBehavior id, bool* should, va_l
*should = Flags_GetRandomizerInf(RAND_INF_LEARNED_EPONA_SONG); *should = Flags_GetRandomizerInf(RAND_INF_LEARNED_EPONA_SONG);
break; break;
} }
case VB_SET_CUCCO_COUNT: {
EnNiwLady* enNiwLady = va_arg(args, EnNiwLady*);
// Override starting Cucco count using setting value
enNiwLady->cuccosInPen = 7 - RAND_GET_OPTION(RSK_CUCCO_COUNT);
*should = false;
break;
}
case VB_KING_ZORA_THANK_CHILD: { case VB_KING_ZORA_THANK_CHILD: {
// Allow turning in Ruto's letter even if you have already rescued her // Allow turning in Ruto's letter even if you have already rescued her
if (!Flags_GetEventChkInf(EVENTCHKINF_KING_ZORA_MOVED)) { if (!Flags_GetEventChkInf(EVENTCHKINF_KING_ZORA_MOVED)) {
@ -2144,7 +2135,7 @@ void RandomizerOnActorUpdateHandler(void* refActor) {
shutterDoor->unk_16E = 0; shutterDoor->unk_16E = 0;
} }
} else if (actor->id == ACTOR_DOOR_GERUDO) { } else if (actor->id == ACTOR_DOOR_GERUDO) {
DoorGerudo* gerudoDoor = (DoorGerudo*)actor; DoorGerudo* gerudoDoor = reinterpret_cast<DoorGerudo*>(actor);
gerudoDoor->actionFunc = func_8099485C; gerudoDoor->actionFunc = func_8099485C;
gerudoDoor->dyna.actor.world.pos.y = gerudoDoor->dyna.actor.home.pos.y + 200.0f; gerudoDoor->dyna.actor.world.pos.y = gerudoDoor->dyna.actor.home.pos.y + 200.0f;
} }

View file

@ -595,7 +595,6 @@ void Settings::CreateOptionDescriptions() {
"\n" "\n"
"Greg as Wildcard - Greg does not change logic, Greg helps obtain GBK, max number of " "Greg as Wildcard - Greg does not change logic, Greg helps obtain GBK, max number of "
"rewards on slider does not change."; "rewards on slider does not change.";
mOptionDescriptions[RSK_CUCCO_COUNT] = "The amount of cuccos needed to claim the reward from Anju the Cucco Lady.";
mOptionDescriptions[RSK_BIG_POE_COUNT] = "The Poe collector will give a reward for turning in this many Big Poes."; mOptionDescriptions[RSK_BIG_POE_COUNT] = "The Poe collector will give a reward for turning in this many Big Poes.";
mOptionDescriptions[RSK_SKIP_CHILD_STEALTH] = mOptionDescriptions[RSK_SKIP_CHILD_STEALTH] =
"The crawlspace into Hyrule Castle goes straight to Zelda, skipping the guards."; "The crawlspace into Hyrule Castle goes straight to Zelda, skipping the guards.";

View file

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

View file

@ -5805,7 +5805,6 @@ typedef enum {
RSK_STARTING_NUTS, RSK_STARTING_NUTS,
RSK_FULL_WALLETS, RSK_FULL_WALLETS,
RSK_SHUFFLE_CHEST_MINIGAME, RSK_SHUFFLE_CHEST_MINIGAME,
RSK_CUCCO_COUNT,
RSK_BIG_POE_COUNT, RSK_BIG_POE_COUNT,
RSK_SKIP_EPONA_RACE, RSK_SKIP_EPONA_RACE,
RSK_COMPLETE_MASK_QUEST, RSK_COMPLETE_MASK_QUEST,

View file

@ -269,7 +269,6 @@ void Settings::CreateOptions() {
OPT_BOOL(RSK_SKIP_EPONA_RACE, "Skip Epona Race", {"Don't Skip", "Skip"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("SkipEponaRace"), mOptionDescriptions[RSK_SKIP_EPONA_RACE], WidgetType::Checkbox, RO_GENERIC_DONT_SKIP); OPT_BOOL(RSK_SKIP_EPONA_RACE, "Skip Epona Race", {"Don't Skip", "Skip"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("SkipEponaRace"), mOptionDescriptions[RSK_SKIP_EPONA_RACE], WidgetType::Checkbox, RO_GENERIC_DONT_SKIP);
OPT_BOOL(RSK_SKIP_SCARECROWS_SONG, "Skip Scarecrow's Song", CVAR_RANDOMIZER_SETTING("SkipScarecrowsSong"), mOptionDescriptions[RSK_SKIP_SCARECROWS_SONG]); OPT_BOOL(RSK_SKIP_SCARECROWS_SONG, "Skip Scarecrow's Song", CVAR_RANDOMIZER_SETTING("SkipScarecrowsSong"), mOptionDescriptions[RSK_SKIP_SCARECROWS_SONG]);
OPT_U8(RSK_BIG_POE_COUNT, "Big Poe Target Count", {NumOpts(0, 10)}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("BigPoeTargetCount"), mOptionDescriptions[RSK_BIG_POE_COUNT], WidgetType::Slider, 10); OPT_U8(RSK_BIG_POE_COUNT, "Big Poe Target Count", {NumOpts(0, 10)}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("BigPoeTargetCount"), mOptionDescriptions[RSK_BIG_POE_COUNT], WidgetType::Slider, 10);
OPT_U8(RSK_CUCCO_COUNT, "Cuccos to return", {NumOpts(0, 7)}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("CuccosToReturn"), mOptionDescriptions[RSK_CUCCO_COUNT], WidgetType::Slider, 7);
OPT_BOOL(RSK_COMPLETE_MASK_QUEST, "Complete Mask Quest", CVAR_RANDOMIZER_SETTING("CompleteMaskQuest"), mOptionDescriptions[RSK_COMPLETE_MASK_QUEST]); OPT_BOOL(RSK_COMPLETE_MASK_QUEST, "Complete Mask Quest", CVAR_RANDOMIZER_SETTING("CompleteMaskQuest"), mOptionDescriptions[RSK_COMPLETE_MASK_QUEST]);
OPT_U8(RSK_GOSSIP_STONE_HINTS, "Gossip Stone Hints", {"No Hints", "Need Nothing", "Mask of Truth", "Stone of Agony"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("GossipStoneHints"), mOptionDescriptions[RSK_GOSSIP_STONE_HINTS], WidgetType::Combobox, RO_GOSSIP_STONES_NEED_NOTHING, false, IMFLAG_NONE); OPT_U8(RSK_GOSSIP_STONE_HINTS, "Gossip Stone Hints", {"No Hints", "Need Nothing", "Mask of Truth", "Stone of Agony"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("GossipStoneHints"), mOptionDescriptions[RSK_GOSSIP_STONE_HINTS], WidgetType::Combobox, RO_GOSSIP_STONES_NEED_NOTHING, false, IMFLAG_NONE);
OPT_U8(RSK_HINT_CLARITY, "Hint Clarity", {"Obscure", "Ambiguous", "Clear"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("HintClarity"), mOptionDescriptions[RSK_HINT_CLARITY], WidgetType::Combobox, RO_HINT_CLARITY_CLEAR, true, IMFLAG_INDENT); OPT_U8(RSK_HINT_CLARITY, "Hint Clarity", {"Obscure", "Ambiguous", "Clear"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("HintClarity"), mOptionDescriptions[RSK_HINT_CLARITY], WidgetType::Combobox, RO_HINT_CLARITY_CLEAR, true, IMFLAG_INDENT);
@ -1316,8 +1315,8 @@ void Settings::CreateOptions() {
WidgetContainerType::TABLE); WidgetContainerType::TABLE);
mOptionGroups[RSG_TIMESAVERS_IMGUI] = OptionGroup::SubGroup( mOptionGroups[RSG_TIMESAVERS_IMGUI] = OptionGroup::SubGroup(
"Timesavers", "Timesavers",
{ &mOptions[RSK_CUCCO_COUNT], &mOptions[RSK_BIG_POE_COUNT], &mOptions[RSK_SKIP_CHILD_ZELDA], { &mOptions[RSK_BIG_POE_COUNT], &mOptions[RSK_SKIP_CHILD_ZELDA], &mOptions[RSK_SKIP_EPONA_RACE],
&mOptions[RSK_SKIP_EPONA_RACE], &mOptions[RSK_COMPLETE_MASK_QUEST], &mOptions[RSK_SKIP_SCARECROWS_SONG] }, &mOptions[RSK_COMPLETE_MASK_QUEST], &mOptions[RSK_SKIP_SCARECROWS_SONG] },
WidgetContainerType::COLUMN); WidgetContainerType::COLUMN);
mOptionGroups[RSG_ITEM_POOL_HINTS_IMGUI] = OptionGroup::SubGroup("", mOptionGroups[RSG_ITEM_POOL_HINTS_IMGUI] = OptionGroup::SubGroup("",
{ {
@ -1582,7 +1581,6 @@ void Settings::CreateOptions() {
&mOptions[RSK_SKIP_EPONA_RACE], &mOptions[RSK_SKIP_EPONA_RACE],
&mOptions[RSK_SKIP_SCARECROWS_SONG], &mOptions[RSK_SKIP_SCARECROWS_SONG],
&mOptions[RSK_BIG_POE_COUNT], &mOptions[RSK_BIG_POE_COUNT],
&mOptions[RSK_CUCCO_COUNT],
&mOptions[RSK_COMPLETE_MASK_QUEST], &mOptions[RSK_COMPLETE_MASK_QUEST],
}); });
mOptionGroups[RSG_MISC] = OptionGroup("Miscellaneous Settings", mOptionGroups[RSG_MISC] = OptionGroup("Miscellaneous Settings",
@ -2878,11 +2876,8 @@ void Context::FinalizeSettings(const std::set<RandomizerCheck>& excludedLocation
if (mOptions[RSK_FISHSANITY].IsNot(RO_FISHSANITY_HYRULE_LOACH)) { if (mOptions[RSK_FISHSANITY].IsNot(RO_FISHSANITY_HYRULE_LOACH)) {
mOptions[RSK_LOACH_HINT].Set(RO_GENERIC_OFF); mOptions[RSK_LOACH_HINT].Set(RO_GENERIC_OFF);
} }
}
if (mOptions[RSK_CUCCO_COUNT].Is(0)) {
mOptions[RSK_CHICKENS_HINT].Set(RO_GENERIC_OFF);
}
}
void Settings::ParseJson(nlohmann::json spoilerFileJson) { void Settings::ParseJson(nlohmann::json spoilerFileJson) {
mContext->SetSeedString(spoilerFileJson["seed"].get<std::string>()); mContext->SetSeedString(spoilerFileJson["seed"].get<std::string>());
mContext->SetSeed(spoilerFileJson["finalSeed"].get<uint32_t>()); mContext->SetSeed(spoilerFileJson["finalSeed"].get<uint32_t>());

View file

@ -28,6 +28,7 @@ extern "C" {
#include "src/overlays/actors/ovl_En_Daiku/z_en_daiku.h" #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_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_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_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_Bg_Dy_Yoseizo/z_bg_dy_yoseizo.h"
#include "src/overlays/actors/ovl_En_Dnt_Demo/z_en_dnt_demo.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); 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) { void EnMa1_EndTeachSong(EnMa1* enMa1, PlayState* play) {
if (Message_GetState(&gPlayState->msgCtx) == TEXT_STATE_CLOSING) { if (Message_GetState(&gPlayState->msgCtx) == TEXT_STATE_CLOSING) {
Flags_SetRandomizerInf(RAND_INF_LEARNED_EPONA_SONG); Flags_SetRandomizerInf(RAND_INF_LEARNED_EPONA_SONG);
@ -1386,3 +1385,15 @@ void TimeSaverRegisterHooks() {
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnItemReceive>(TimeSaverOnItemReceiveHandler); 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

@ -147,28 +147,25 @@ void RegisterOnInterfaceUpdateHook() {
timer = gSaveContext.subTimerSeconds; timer = gSaveContext.subTimerSeconds;
} }
if (timer > 0) { if (timer > 0 && timer % (timer < 60 ? 10 : 30) == 0 && timer != prevTimer) {
if (timer > prevTimer || (timer % 30 == 0 && prevTimer != timer)) {
uint32_t minutes = timer / 60; uint32_t minutes = timer / 60;
uint32_t seconds = timer % 60; uint32_t seconds = timer % 60;
char* announceBuf = ttsAnnounceBuf; char* announceBuf = ttsAnnounceBuf;
char arg[8]; // at least big enough where no s8 string will overflow char arg[8]; // at least big enough where no s8 string will overflow
if (minutes > 0) { if (minutes > 0) {
snprintf(arg, sizeof(arg), "%d", minutes); snprintf(arg, sizeof(arg), "%d", minutes);
auto translation = GetParameritizedText((minutes > 1) ? "minutes_plural" : "minutes_singular", auto translation =
TEXT_BANK_MISC, arg); GetParameritizedText((minutes > 1) ? "minutes_plural" : "minutes_singular", TEXT_BANK_MISC, arg);
announceBuf += snprintf(announceBuf, sizeof(ttsAnnounceBuf), "%s ", translation.c_str()); announceBuf += snprintf(announceBuf, sizeof(ttsAnnounceBuf), "%s ", translation.c_str());
} }
if (seconds > 0) { if (seconds > 0) {
snprintf(arg, sizeof(arg), "%d", seconds); snprintf(arg, sizeof(arg), "%d", seconds);
auto translation = GetParameritizedText((seconds > 1) ? "seconds_plural" : "seconds_singular", auto translation =
TEXT_BANK_MISC, arg); GetParameritizedText((seconds > 1) ? "seconds_plural" : "seconds_singular", TEXT_BANK_MISC, arg);
announceBuf += snprintf(announceBuf, sizeof(ttsAnnounceBuf), "%s", translation.c_str()); announceBuf += snprintf(announceBuf, sizeof(ttsAnnounceBuf), "%s", translation.c_str());
} }
assert(announceBuf < ttsAnnounceBuf + sizeof(ttsAnnounceBuf)); assert(announceBuf < ttsAnnounceBuf + sizeof(ttsAnnounceBuf));
SpeechSynthesizer::Instance->Speak(ttsAnnounceBuf, GetLanguageCode()); SpeechSynthesizer::Instance->Speak(ttsAnnounceBuf, GetLanguageCode());
prevTimer = timer;
}
} }
prevTimer = timer; prevTimer = timer;

View file

@ -0,0 +1,29 @@
#include "ActorMaximumHealth.h"
#include "soh/ObjectExtension/ObjectExtension.h"
#include "soh/ShipInit.hpp"
#include "soh/Enhancements/game-interactor/GameInteractor.h"
struct ActorMaximumHealth {
u8 maximumHealth = 0;
};
static ObjectExtension::Register<ActorMaximumHealth> ActorMaximumHealthRegister;
u8 GetActorMaximumHealth(const Actor* actor) {
const ActorMaximumHealth* maxHealth = ObjectExtension::GetInstance().Get<ActorMaximumHealth>(actor);
return maxHealth != nullptr ? maxHealth->maximumHealth : ActorMaximumHealth{}.maximumHealth;
}
void SetActorMaximumHealth(const Actor* actor, u8 maximumHealth) {
ObjectExtension::GetInstance().Set<ActorMaximumHealth>(actor, ActorMaximumHealth{ maximumHealth });
}
static void ActorMaximumHealth_Register() {
COND_HOOK(OnActorInit, true, [](void* ptr) {
Actor* actor = static_cast<Actor*>(ptr);
if (actor->category == ACTORCAT_ENEMY) {
SetActorMaximumHealth(actor, actor->colChkInfo.health);
}
});
}
RegisterShipInitFunc actorMaximumHealthInit(ActorMaximumHealth_Register);

View file

@ -0,0 +1,17 @@
#ifndef ACTOR_MAXIMUM_HEALTH_H
#define ACTOR_MAXIMUM_HEALTH_H
#ifdef __cplusplus
extern "C" {
#include "z64actor.h"
#endif
// Max health value for use with health bars, set on actor init
u8 GetActorMaximumHealth(const Actor* actor);
void SetActorMaximumHealth(const Actor* actor, u8 maximumHealth);
#ifdef __cplusplus
}
#endif
#endif // ACTOR_MAXIMUM_HEALTH_H

View file

@ -1138,6 +1138,10 @@ void SohMenu::AddMenuEnhancements() {
.CVar(CVAR_ENHANCEMENT("CuccoStayDurationMult")) .CVar(CVAR_ENHANCEMENT("CuccoStayDurationMult"))
.Options(IntSliderOptions().Min(1).Max(5).DefaultValue(1).Format("%dx").Tooltip( .Options(IntSliderOptions().Min(1).Max(5).DefaultValue(1).Format("%dx").Tooltip(
"Cuccos will stay in place longer after putting them down, by a multiple of the value of the slider.")); "Cuccos will stay in place longer after putting them down, by a multiple of the value of the slider."));
AddWidget(path, "Cuccos Needed By Anju: %d", WIDGET_CVAR_SLIDER_INT)
.CVar(CVAR_ENHANCEMENT("CuccosToReturn"))
.Options(IntSliderOptions().Min(0).Max(7).DefaultValue(7).Format("%d").Tooltip(
"The amount of cuccos needed to receive bottle from Anju the Cucco Lady."));
path.column = SECTION_COLUMN_3; path.column = SECTION_COLUMN_3;
AddWidget(path, "Enemies", WIDGET_SEPARATOR_TEXT); AddWidget(path, "Enemies", WIDGET_SEPARATOR_TEXT);
@ -1312,6 +1316,11 @@ void SohMenu::AddMenuEnhancements() {
.Format("%d notes") .Format("%d notes")
.Tooltip("Adjust the number of notes you need to play to end the third round.")); .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; path.column = SECTION_COLUMN_3;
AddWidget(path, "Fishing", WIDGET_SEPARATOR_TEXT); AddWidget(path, "Fishing", WIDGET_SEPARATOR_TEXT);
AddWidget(path, "Customize Behavior##Fishing", WIDGET_CVAR_CHECKBOX) AddWidget(path, "Customize Behavior##Fishing", WIDGET_CVAR_CHECKBOX)
@ -1581,9 +1590,6 @@ void SohMenu::AddMenuEnhancements() {
.Options(CheckboxOptions().Tooltip( .Options(CheckboxOptions().Tooltip(
"Keese and Guay no longer target you and simply ignore you as if you were wearing the " "Keese and Guay no longer target you and simply ignore you as if you were wearing the "
"Skull Mask.")); "Skull Mask."));
AddWidget(path, "No Dampe Fire", WIDGET_CVAR_CHECKBOX)
.CVar(CVAR_CHEAT("NoDampeFire"))
.Options(CheckboxOptions().Tooltip("Dampe won't drop fireballs during race."));
AddWidget(path, "Glitch Aids", WIDGET_SEPARATOR_TEXT); AddWidget(path, "Glitch Aids", WIDGET_SEPARATOR_TEXT);
AddWidget(path, "Easy Frame Advancing with Pause", WIDGET_CVAR_CHECKBOX) AddWidget(path, "Easy Frame Advancing with Pause", WIDGET_CVAR_CHECKBOX)

View file

@ -1261,11 +1261,6 @@ void Actor_Init(Actor* actor, PlayState* play) {
actor->init = NULL; actor->init = NULL;
GameInteractor_ExecuteOnActorInit(actor); GameInteractor_ExecuteOnActorInit(actor);
// For enemy health bar we need to know the max health during init
if (actor->category == ACTORCAT_ENEMY) {
actor->maximumHealth = actor->colChkInfo.health;
}
} }
} }
@ -2633,11 +2628,6 @@ void Actor_UpdateAll(PlayState* play, ActorContext* actorCtx) {
actor->init = NULL; actor->init = NULL;
GameInteractor_ExecuteOnActorInit(actor); GameInteractor_ExecuteOnActorInit(actor);
// For enemy health bar we need to know the max health during init
if (actor->category == ACTORCAT_ENEMY) {
actor->maximumHealth = actor->colChkInfo.health;
}
} }
actor = actor->next; actor = actor->next;
} else if (!Object_IsLoaded(&play->objectCtx, actor->objBankIndex)) { } else if (!Object_IsLoaded(&play->objectCtx, actor->objBankIndex)) {

View file

@ -4,8 +4,8 @@
#include "overlays/effects/ovl_Effect_Ss_Dead_Sound/z_eff_ss_dead_sound.h" #include "overlays/effects/ovl_Effect_Ss_Dead_Sound/z_eff_ss_dead_sound.h"
#include "textures/icon_item_static/icon_item_static.h" #include "textures/icon_item_static/icon_item_static.h"
#include "soh/Enhancements/game-interactor/GameInteractor.h" #include "soh/Enhancements/game-interactor/GameInteractor.h"
#include "soh/OTRGlobals.h"
#include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h" #include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h"
#include "soh/OTRGlobals.h"
#define FLAGS 0 #define FLAGS 0
@ -1536,7 +1536,7 @@ s16 func_8001F404(s16 dropId) {
if (LINK_IS_ADULT) { if (LINK_IS_ADULT) {
if (dropId == ITEM00_SEEDS) { if (dropId == ITEM00_SEEDS) {
dropId = ITEM00_ARROWS_SMALL; dropId = ITEM00_ARROWS_SMALL;
} else if ((dropId == ITEM00_STICK) && !(CVarGetInteger(CVAR_ENHANCEMENT("TreesDropSticks"), 0))) { } else if (GameInteractor_Should(VB_PREVENT_ADULT_STICK, dropId == ITEM00_STICK)) {
dropId = ITEM00_RUPEE_GREEN; dropId = ITEM00_RUPEE_GREEN;
} }
} else { } else {

View file

@ -24,6 +24,7 @@
#include "soh/OTRGlobals.h" #include "soh/OTRGlobals.h"
#include "soh/ResourceManagerHelpers.h" #include "soh/ResourceManagerHelpers.h"
#include "soh/Enhancements/gameplaystats.h" #include "soh/Enhancements/gameplaystats.h"
#include "soh/ObjectExtension/ActorMaximumHealth.h"
#include "message_data_static.h" #include "message_data_static.h"
extern MessageTableEntry* sNesMessageEntryTablePtr; extern MessageTableEntry* sNesMessageEntryTablePtr;
@ -3643,7 +3644,7 @@ void Interface_DrawEnemyHealthBar(TargetContext* targetCtx, PlayState* play) {
f32 scaleY = -0.75f; f32 scaleY = -0.75f;
f32 scaledHeight = -texHeight * scaleY; f32 scaledHeight = -texHeight * scaleY;
f32 halfBarWidth = endTexWidth + ((f32)healthbar_fillWidth / 2); f32 halfBarWidth = endTexWidth + ((f32)healthbar_fillWidth / 2);
s16 healthBarFill = ((f32)actor->colChkInfo.health / actor->maximumHealth) * healthbar_fillWidth; s16 healthBarFill = ((f32)actor->colChkInfo.health / GetActorMaximumHealth(actor)) * healthbar_fillWidth;
if (anchorType == ENEMYHEALTH_ANCHOR_ACTOR) { if (anchorType == ENEMYHEALTH_ANCHOR_ACTOR) {
// Get actor projected position // Get actor projected position

View file

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

View file

@ -2,6 +2,7 @@
#include "objects/object_fz/object_fz.h" #include "objects/object_fz/object_fz.h"
#include "soh/frame_interpolation.h" #include "soh/frame_interpolation.h"
#include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h" #include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h"
#include "soh/ObjectExtension/ActorMaximumHealth.h"
#define FLAGS \ #define FLAGS \
(ACTOR_FLAG_ATTENTION_ENABLED | ACTOR_FLAG_HOSTILE | ACTOR_FLAG_UPDATE_CULLING_DISABLED | \ (ACTOR_FLAG_ATTENTION_ENABLED | ACTOR_FLAG_HOSTILE | ACTOR_FLAG_UPDATE_CULLING_DISABLED | \
@ -725,7 +726,7 @@ void EnFz_Draw(Actor* thisx, PlayState* play) {
// displayLists, so we need to recompute the index based on the scaled health (using the maximum health value) and // displayLists, so we need to recompute the index based on the scaled health (using the maximum health value) and
// clamp the final result for safety. // clamp the final result for safety.
if (CVarGetInteger(CVAR_ENHANCEMENT("EnemySizeScalesHealth"), 0)) { if (CVarGetInteger(CVAR_ENHANCEMENT("EnemySizeScalesHealth"), 0)) {
u8 scaledHealth = (u8)(((f32)this->actor.colChkInfo.health / this->actor.maximumHealth) * 6); u8 scaledHealth = (u8)(((f32)this->actor.colChkInfo.health / GetActorMaximumHealth(this)) * 6);
index = (6 - scaledHealth) >> 1; index = (6 - scaledHealth) >> 1;
index = CLAMP(index, 0, 2); index = CLAMP(index, 0, 2);
} }

View file

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

View file

@ -6,6 +6,7 @@
#include "z_en_wood02.h" #include "z_en_wood02.h"
#include "objects/object_wood02/object_wood02.h" #include "objects/object_wood02/object_wood02.h"
#include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h"
#define FLAGS 0 #define FLAGS 0
@ -327,7 +328,6 @@ void EnWood02_Update(Actor* thisx, PlayState* play2) {
Vec3f dropsSpawnPt; Vec3f dropsSpawnPt;
s32 i; s32 i;
s32 leavesParams; s32 leavesParams;
s32 numDrops;
// Despawn extra trees in a group if out of range // Despawn extra trees in a group if out of range
if ((this->spawnType == WOOD_SPAWN_SPAWNED) && (this->actor.parent != NULL)) { if ((this->spawnType == WOOD_SPAWN_SPAWNED) && (this->actor.parent != NULL)) {
@ -358,12 +358,7 @@ void EnWood02_Update(Actor* thisx, PlayState* play2) {
dropsSpawnPt.y += 200.0f; dropsSpawnPt.y += 200.0f;
if ((this->unk_14C >= 0) && (this->unk_14C < 0x64)) { if ((this->unk_14C >= 0) && (this->unk_14C < 0x64)) {
if (CVarGetInteger(CVAR_ENHANCEMENT("TreesDropSticks"), 0) && INV_CONTENT(ITEM_STICK) != ITEM_NONE) { if (GameInteractor_Should(VB_TREE_DROP_COLLECTIBLE, true, this)) {
numDrops = Rand_ZeroOne() * 4;
for (i = 0; i < numDrops; ++i) {
Item_DropCollectible(play, &dropsSpawnPt, ITEM00_STICK);
}
} else {
Item_DropCollectibleRandom(play, &this->actor, &dropsSpawnPt, this->unk_14C << 4); Item_DropCollectibleRandom(play, &this->actor, &dropsSpawnPt, this->unk_14C << 4);
} }
} else if (this->actor.home.rot.z != 0) { } else if (this->actor.home.rot.z != 0) {

View file

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