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. // Grant item when picking up fairy.
COND_VB_SHOULD(VB_FAIRY_HEAL, shouldRegister, { COND_VB_SHOULD(VB_FAIRY_HEAL, shouldRegister, {
EnElf* enElf = va_arg(args, EnElf*); EnElf* enElf = va_arg(args, EnElf*);
const auto fairyIdentity = ObjectExtension::GetInstance().Get<FairyIdentity>(&enElf->actor); const auto fairyIdentity = ObjectExtension::GetInstance().Get<FairyIdentity>(&enElf->actor);
if (fairyIdentity == nullptr) { if (fairyIdentity != nullptr && fairyIdentity->randomizerInf != RAND_INF_MAX) {
return;
}
if (fairyIdentity != nullptr && fairyIdentity->randomizerInf && fairyIdentity->randomizerInf != RAND_INF_MAX) {
Flags_SetRandomizerInf(fairyIdentity->randomizerInf); 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 // Spawn fairies in fairy fountains
COND_VB_SHOULD(VB_SPAWN_FOUNTAIN_FAIRIES, shouldRegisterFountain, { COND_VB_SHOULD(VB_SPAWN_FOUNTAIN_FAIRIES, shouldRegisterFountain, {
Actor* actor = va_arg(args, Actor*); Actor* actor = va_arg(args, Actor*);

View File

@@ -51,11 +51,12 @@ ActorFunc drawFishing = NULL;
ActorFunc drawEnFish = NULL; ActorFunc drawEnFish = NULL;
Color_RGB8 fsPulseColor = { 30, 240, 200 }; Color_RGB8 fsPulseColor = { 30, 240, 200 };
static s16 fishGroupCounter = 0;
static bool enableAdvance = false;
namespace Rando { namespace Rando {
const FishIdentity Fishsanity::defaultIdentity = { RAND_INF_MAX, RC_UNKNOWN_CHECK }; const FishIdentity Fishsanity::defaultIdentity = { RAND_INF_MAX, RC_UNKNOWN_CHECK };
bool Fishsanity::fishsanityHelpersInit = false; bool Fishsanity::fishsanityHelpersInit = false;
s16 Fishsanity::fishGroupCounter = 0;
bool Fishsanity::enableAdvance = false;
std::unordered_map<RandomizerCheck, LinkAge> Fishsanity::pondFishAgeMap; std::unordered_map<RandomizerCheck, LinkAge> Fishsanity::pondFishAgeMap;
std::vector<RandomizerCheck> Fishsanity::childPondFish; std::vector<RandomizerCheck> Fishsanity::childPondFish;
std::vector<RandomizerCheck> Fishsanity::adultPondFish; std::vector<RandomizerCheck> Fishsanity::adultPondFish;
@@ -447,38 +448,6 @@ void Fishsanity::OnActorUpdateHandler(void* refActor) {
fishGroupCounter = 0; 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 } // namespace Rando
// C interface // 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() { void Rando::StaticData::RegisterFishLocations() {
static bool registered = false; static bool registered = false;
if (registered) if (registered)
@@ -634,4 +634,5 @@ void Rando::StaticData::RegisterFishLocations() {
// clang-format on // clang-format on
} }
static RegisterShipInitFunc registerShuffleFish(RegisterShuffleFish, { "IS_RANDO" });
static RegisterShipInitFunc initFunc(Rando::StaticData::RegisterFishLocations); static RegisterShipInitFunc initFunc(Rando::StaticData::RegisterFishLocations);

View File

@@ -137,28 +137,11 @@ class Fishsanity {
*/ */
static void OnActorInitHandler(void* refActor); static void OnActorInitHandler(void* refActor);
/**
* @brief PlayerUpdate hook handler for fishsanity
*/
static void OnPlayerUpdateHandler();
/** /**
* @brief ActorUpdate hook handler for fishsanity * @brief ActorUpdate hook handler for fishsanity
*/ */
static void OnActorUpdateHandler(void* refActor); 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: private:
/** /**
* @brief Initialize helper statics if they have not been initialized yet * @brief Initialize helper statics if they have not been initialized yet
@@ -184,9 +167,6 @@ class Fishsanity {
*/ */
static bool fishsanityHelpersInit; static bool fishsanityHelpersInit;
static s16 fishGroupCounter;
static bool enableAdvance;
///////////////////////////////////////////////////////// /////////////////////////////////////////////////////////
//// Helper data structures derived from static data //// //// Helper data structures derived from static data ////
///////////////////////////////////////////////////////// /////////////////////////////////////////////////////////

View File

@@ -6,7 +6,6 @@
#include "soh/Enhancements/item-tables/ItemTableManager.h" #include "soh/Enhancements/item-tables/ItemTableManager.h"
#include "soh/Enhancements/randomizer/randomizerTypes.h" #include "soh/Enhancements/randomizer/randomizerTypes.h"
#include "soh/Enhancements/randomizer/dungeon.h" #include "soh/Enhancements/randomizer/dungeon.h"
#include "soh/Enhancements/randomizer/fishsanity.h"
#include "soh/Enhancements/randomizer/static_data.h" #include "soh/Enhancements/randomizer/static_data.h"
#include "soh/Enhancements/game-interactor/GameInteractor.h" #include "soh/Enhancements/game-interactor/GameInteractor.h"
#include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h" #include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h"
@@ -2438,12 +2437,6 @@ void RandomizerRegisterHooks() {
static uint32_t onKaleidoUpdateHook = 0; static uint32_t onKaleidoUpdateHook = 0;
static uint32_t onCuccoOrChickenHatchHook = 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) { GameInteractor::Instance->RegisterGameHook<GameInteractor::OnLoadGame>([](int32_t fileNum) {
ShipInit::Init("IS_RANDO"); ShipInit::Init("IS_RANDO");
@@ -2470,13 +2463,6 @@ void RandomizerRegisterHooks() {
GameInteractor::Instance->UnregisterGameHook<GameInteractor::OnKaleidoscopeUpdate>(onKaleidoUpdateHook); GameInteractor::Instance->UnregisterGameHook<GameInteractor::OnKaleidoscopeUpdate>(onKaleidoUpdateHook);
GameInteractor::Instance->UnregisterGameHook<GameInteractor::OnCuccoOrChickenHatch>(onCuccoOrChickenHatchHook); 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; onFlagSetHook = 0;
onSceneFlagSetHook = 0; onSceneFlagSetHook = 0;
onPlayerUpdateForRCQueueHook = 0; onPlayerUpdateForRCQueueHook = 0;
@@ -2496,12 +2482,6 @@ void RandomizerRegisterHooks() {
onKaleidoUpdateHook = 0; onKaleidoUpdateHook = 0;
onCuccoOrChickenHatchHook = 0; onCuccoOrChickenHatchHook = 0;
fishsanityOnActorInitHook = 0;
fishsanityOnActorUpdateHook = 0;
fishsanityOnSceneInitHook = 0;
fishsanityOnVanillaBehaviorHook = 0;
fishsanityOnItemReceiveHook = 0;
if (!IS_RANDO) if (!IS_RANDO)
return; return;
@@ -2553,18 +2533,6 @@ void RandomizerRegisterHooks() {
if (RAND_GET_OPTION(RSK_FISHSANITY) != RO_FISHSANITY_OFF) { if (RAND_GET_OPTION(RSK_FISHSANITY) != RO_FISHSANITY_OFF) {
OTRGlobals::Instance->gRandoContext->GetFishsanity()->InitializeFromSave(); 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 //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 //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_COLOSSUS_GREAT_FAIRY_FOUNTAIN, []{return logic->HasExplosives();}),
Entrance(RR_SPIRIT_TEMPLE_ENTRYWAY, []{return true;}), Entrance(RR_SPIRIT_TEMPLE_ENTRYWAY, []{return true;}),
Entrance(RR_WASTELAND_NEAR_COLOSSUS, []{return true;}), Entrance(RR_WASTELAND_NEAR_COLOSSUS, []{return true;}),