Fairysanity: allow using bottle (#6021)

Based on fishsanity, which is refactored with hooks

Also open up logic catching fairies at oasis if player has bottle
This commit is contained in:
Philip Dubé
2025-12-27 11:15:31 +00:00
committed by GitHub
parent 1c21608a95
commit 9c24ccec1a
5 changed files with 47 additions and 93 deletions

View File

@@ -98,17 +98,22 @@ void RegisterShuffleFairies() {
// Grant item when picking up fairy.
COND_VB_SHOULD(VB_FAIRY_HEAL, shouldRegister, {
EnElf* enElf = va_arg(args, EnElf*);
const auto fairyIdentity = ObjectExtension::GetInstance().Get<FairyIdentity>(&enElf->actor);
if (fairyIdentity == nullptr) {
return;
}
if (fairyIdentity != nullptr && fairyIdentity->randomizerInf && fairyIdentity->randomizerInf != RAND_INF_MAX) {
if (fairyIdentity != nullptr && fairyIdentity->randomizerInf != RAND_INF_MAX) {
Flags_SetRandomizerInf(fairyIdentity->randomizerInf);
}
});
COND_VB_SHOULD(VB_BOTTLE_ACTOR, shouldRegister, {
Actor* actor = va_arg(args, Actor*);
const auto fairyIdentity = ObjectExtension::GetInstance().Get<FairyIdentity>(actor);
if (fairyIdentity != nullptr && fairyIdentity->randomizerInf != RAND_INF_MAX) {
Flags_SetRandomizerInf(fairyIdentity->randomizerInf);
actor->parent = &GET_PLAYER(gPlayState)->actor;
*should = false;
}
});
// Spawn fairies in fairy fountains
COND_VB_SHOULD(VB_SPAWN_FOUNTAIN_FAIRIES, shouldRegisterFountain, {
Actor* actor = va_arg(args, Actor*);

View File

@@ -51,11 +51,12 @@ ActorFunc drawFishing = NULL;
ActorFunc drawEnFish = NULL;
Color_RGB8 fsPulseColor = { 30, 240, 200 };
static s16 fishGroupCounter = 0;
static bool enableAdvance = false;
namespace Rando {
const FishIdentity Fishsanity::defaultIdentity = { RAND_INF_MAX, RC_UNKNOWN_CHECK };
bool Fishsanity::fishsanityHelpersInit = false;
s16 Fishsanity::fishGroupCounter = 0;
bool Fishsanity::enableAdvance = false;
std::unordered_map<RandomizerCheck, LinkAge> Fishsanity::pondFishAgeMap;
std::vector<RandomizerCheck> Fishsanity::childPondFish;
std::vector<RandomizerCheck> Fishsanity::adultPondFish;
@@ -447,38 +448,6 @@ void Fishsanity::OnActorUpdateHandler(void* refActor) {
fishGroupCounter = 0;
}
}
void Fishsanity::OnSceneInitHandler(int16_t sceneNum) {
if (sceneNum == SCENE_ZORAS_DOMAIN) {
fishGroupCounter = 0;
}
}
void Fishsanity::OnVanillaBehaviorHandler(GIVanillaBehavior id, bool* should, va_list originalArgs) {
va_list args;
va_copy(args, originalArgs);
Actor* actor = va_arg(args, Actor*);
auto fs = OTRGlobals::Instance->gRandoContext->GetFishsanity();
va_end(args);
if (id == VB_BOTTLE_ACTOR && actor->id == ACTOR_EN_FISH && fs->GetOverworldFishShuffled()) {
FishIdentity fish = OTRGlobals::Instance->gRandomizer->IdentifyFish(gPlayState->sceneNum, actor->params);
if (fish.randomizerCheck != RC_UNKNOWN_CHECK && !Flags_GetRandomizerInf(fish.randomizerInf)) {
Flags_SetRandomizerInf(fish.randomizerInf);
actor->parent = &GET_PLAYER(gPlayState)->actor;
*should = false;
}
}
}
void Fishsanity::OnItemReceiveHandler(GetItemEntry itemEntry) {
if (enableAdvance) {
enableAdvance = false;
OTRGlobals::Instance->gRandoContext->GetFishsanity()->AdvancePond();
}
}
} // namespace Rando
// C interface
@@ -575,6 +544,37 @@ void Fishsanity_CloseGreyscaleColor(PlayState* play) {
}
}
void RegisterShuffleFish() {
bool shouldRegister = IS_RANDO && RAND_GET_OPTION(RSK_FISHSANITY) != RO_FISHSANITY_OFF;
COND_HOOK(OnSceneInit, shouldRegister, [](int16_t sceneNum) {
if (sceneNum == SCENE_ZORAS_DOMAIN) {
fishGroupCounter = 0;
}
});
COND_HOOK(OnActorInit, shouldRegister, Rando::Fishsanity::OnActorInitHandler);
COND_HOOK(OnActorUpdate, shouldRegister, Rando::Fishsanity::OnActorUpdateHandler);
COND_HOOK(OnItemReceive, shouldRegister, [](GetItemEntry itemEntry) {
if (enableAdvance) {
enableAdvance = false;
OTRGlobals::Instance->gRandoContext->GetFishsanity()->AdvancePond();
}
});
COND_VB_SHOULD(VB_BOTTLE_ACTOR, shouldRegister, {
Actor* actor = va_arg(args, Actor*);
auto fs = OTRGlobals::Instance->gRandoContext->GetFishsanity();
if (actor->id == ACTOR_EN_FISH && fs->GetOverworldFishShuffled()) {
FishIdentity fish = OTRGlobals::Instance->gRandomizer->IdentifyFish(gPlayState->sceneNum, actor->params);
if (fish.randomizerCheck != RC_UNKNOWN_CHECK && !Flags_GetRandomizerInf(fish.randomizerInf)) {
Flags_SetRandomizerInf(fish.randomizerInf);
actor->parent = &GET_PLAYER(gPlayState)->actor;
*should = false;
}
}
});
}
void Rando::StaticData::RegisterFishLocations() {
static bool registered = false;
if (registered)
@@ -634,4 +634,5 @@ void Rando::StaticData::RegisterFishLocations() {
// clang-format on
}
static RegisterShipInitFunc registerShuffleFish(RegisterShuffleFish, { "IS_RANDO" });
static RegisterShipInitFunc initFunc(Rando::StaticData::RegisterFishLocations);

View File

@@ -137,28 +137,11 @@ class Fishsanity {
*/
static void OnActorInitHandler(void* refActor);
/**
* @brief PlayerUpdate hook handler for fishsanity
*/
static void OnPlayerUpdateHandler();
/**
* @brief ActorUpdate hook handler for fishsanity
*/
static void OnActorUpdateHandler(void* refActor);
/**
* @brief SceneInit hook handler for fishsanity
*/
static void OnSceneInitHandler(int16_t sceneNum);
/**
* @brief VB hook handler for fishsanity
*/
static void OnVanillaBehaviorHandler(GIVanillaBehavior id, bool* should, va_list originalArgs);
static void OnItemReceiveHandler(GetItemEntry itemEntry);
private:
/**
* @brief Initialize helper statics if they have not been initialized yet
@@ -184,9 +167,6 @@ class Fishsanity {
*/
static bool fishsanityHelpersInit;
static s16 fishGroupCounter;
static bool enableAdvance;
/////////////////////////////////////////////////////////
//// Helper data structures derived from static data ////
/////////////////////////////////////////////////////////

View File

@@ -6,7 +6,6 @@
#include "soh/Enhancements/item-tables/ItemTableManager.h"
#include "soh/Enhancements/randomizer/randomizerTypes.h"
#include "soh/Enhancements/randomizer/dungeon.h"
#include "soh/Enhancements/randomizer/fishsanity.h"
#include "soh/Enhancements/randomizer/static_data.h"
#include "soh/Enhancements/game-interactor/GameInteractor.h"
#include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h"
@@ -2438,12 +2437,6 @@ void RandomizerRegisterHooks() {
static uint32_t onKaleidoUpdateHook = 0;
static uint32_t onCuccoOrChickenHatchHook = 0;
static uint32_t fishsanityOnActorInitHook = 0;
static uint32_t fishsanityOnActorUpdateHook = 0;
static uint32_t fishsanityOnSceneInitHook = 0;
static uint32_t fishsanityOnVanillaBehaviorHook = 0;
static uint32_t fishsanityOnItemReceiveHook = 0;
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnLoadGame>([](int32_t fileNum) {
ShipInit::Init("IS_RANDO");
@@ -2470,13 +2463,6 @@ void RandomizerRegisterHooks() {
GameInteractor::Instance->UnregisterGameHook<GameInteractor::OnKaleidoscopeUpdate>(onKaleidoUpdateHook);
GameInteractor::Instance->UnregisterGameHook<GameInteractor::OnCuccoOrChickenHatch>(onCuccoOrChickenHatchHook);
GameInteractor::Instance->UnregisterGameHook<GameInteractor::OnActorInit>(fishsanityOnActorInitHook);
GameInteractor::Instance->UnregisterGameHook<GameInteractor::OnActorUpdate>(fishsanityOnActorUpdateHook);
GameInteractor::Instance->UnregisterGameHook<GameInteractor::OnSceneInit>(fishsanityOnSceneInitHook);
GameInteractor::Instance->UnregisterGameHook<GameInteractor::OnVanillaBehavior>(
fishsanityOnVanillaBehaviorHook);
GameInteractor::Instance->UnregisterGameHook<GameInteractor::OnItemReceive>(fishsanityOnItemReceiveHook);
onFlagSetHook = 0;
onSceneFlagSetHook = 0;
onPlayerUpdateForRCQueueHook = 0;
@@ -2496,12 +2482,6 @@ void RandomizerRegisterHooks() {
onKaleidoUpdateHook = 0;
onCuccoOrChickenHatchHook = 0;
fishsanityOnActorInitHook = 0;
fishsanityOnActorUpdateHook = 0;
fishsanityOnSceneInitHook = 0;
fishsanityOnVanillaBehaviorHook = 0;
fishsanityOnItemReceiveHook = 0;
if (!IS_RANDO)
return;
@@ -2553,18 +2533,6 @@ void RandomizerRegisterHooks() {
if (RAND_GET_OPTION(RSK_FISHSANITY) != RO_FISHSANITY_OFF) {
OTRGlobals::Instance->gRandoContext->GetFishsanity()->InitializeFromSave();
fishsanityOnActorInitHook = GameInteractor::Instance->RegisterGameHook<GameInteractor::OnActorInit>(
Rando::Fishsanity::OnActorInitHandler);
fishsanityOnActorUpdateHook = GameInteractor::Instance->RegisterGameHook<GameInteractor::OnActorUpdate>(
Rando::Fishsanity::OnActorUpdateHandler);
fishsanityOnSceneInitHook = GameInteractor::Instance->RegisterGameHook<GameInteractor::OnSceneInit>(
Rando::Fishsanity::OnSceneInitHandler);
fishsanityOnVanillaBehaviorHook =
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnVanillaBehavior>(
Rando::Fishsanity::OnVanillaBehaviorHandler);
fishsanityOnItemReceiveHook = GameInteractor::Instance->RegisterGameHook<GameInteractor::OnItemReceive>(
Rando::Fishsanity::OnItemReceiveHandler);
}
});
}

View File

@@ -23,7 +23,7 @@ void RegionTable_Init_DesertColossus() {
}, {
//Exits
//You can kinda get the fairies without entering the water, but it relies on them cooperating and leevers are jerks. should be a trick
Entrance(RR_DESERT_COLOSSUS_OASIS, []{return logic->CanUse(RG_SONG_OF_STORMS) && (logic->HasItem(RG_BRONZE_SCALE) || logic->CanUse(RG_IRON_BOOTS));}),
Entrance(RR_DESERT_COLOSSUS_OASIS, []{return logic->CanUse(RG_SONG_OF_STORMS) && (logic->HasItem(RG_BRONZE_SCALE) || logic->CanUse(RG_IRON_BOOTS) || logic->CanUse(RG_EMPTY_BOTTLE));}),
Entrance(RR_COLOSSUS_GREAT_FAIRY_FOUNTAIN, []{return logic->HasExplosives();}),
Entrance(RR_SPIRIT_TEMPLE_ENTRYWAY, []{return true;}),
Entrance(RR_WASTELAND_NEAR_COLOSSUS, []{return true;}),