Refactor Fishsanity (#4268)

* Move Fishsanity hooks out of mods.cpp

* Assign fish check flags.

* Clean up location_list for fish

* Prevent fishing from giving double items.

* Remove no-longer-used mPendingFish

* Override draw function for fishing

This allows the draw functions in the source
overlay to match the decomp.

* Override draw function for EnFish

* Overwrite grotto fish params based on respawn data

This allows the randomizer to identify them
automatically without any special logic.

The catch (pun not intended) is that grotto fish don't respawn,
and they were previously identified for such by a params value of 1,
so the logic to take care of that needed to be duplicated.
Thankfully it wasn't very much.

* Add a VB for catching actors in bottles.

* Clean up remaining code after conversion to VB

This breaks fast FastDrops for bottle pickups, though
readding it shouldn't be too hard with the VB hook.

* Remove fishsanityParams from Fishing

It was previously used to track exactly which fish would be
released after a catch, but since both candidate fish
would've been caught, they both wouldn't give checks
anyways.

* Update soh/soh/Enhancements/randomizer/hook_handlers.cpp

Co-authored-by: Pepe20129 <72659707+Pepe20129@users.noreply.github.com>

* Re-add FastDrops for bottle pickups.

While this does diverge from the vanilla decomp, I'm uncertain of
the order that hooks are run, so I put it back into z_player.c
just to be safe. A future commit can do a more proper implementation
using VB hooks.

* Move initialisation of fishsanity hooks into hook_handlers

* Change location constructor to take RandomizerInf instead of uint8_t

This shouldn't have an effect as-is, but other changes can add
additional randomizer flags that can end up pushing fishsanity check
flags out of the range of a uint8_t, causing the cast to overflow
and not be stored correctly. With this change, it could still overflow
when writing to the flag field of Location, but said field is unused
and the parameter is really only for setting the flag for the
SpoilerCollectionCheck.

* Render uncaught overworld fish as randomized item

* Fix windows build by zeroing unused field

* Fix scene parameter type

This resolves a build error on Mac and Windows, but Linux
instead buries it in the sea of warnings, meaning I can't
see it until it fails CI.

---------

Co-authored-by: Pepe20129 <72659707+Pepe20129@users.noreply.github.com>
This commit is contained in:
Angelo Bulfone
2024-10-02 09:37:15 -07:00
committed by GitHub
parent e36dcefe54
commit 89ca2149b9
13 changed files with 306 additions and 359 deletions

View File

@@ -7,7 +7,6 @@
#include "z_en_fish.h"
#include "objects/gameplay_keep/gameplay_keep.h"
#include "vt.h"
#include "soh/Enhancements/randomizer/fishsanity.h"
#define FLAGS 0
@@ -37,7 +36,6 @@ void EnFish_Unique_SwimIdle(EnFish* this, PlayState* play);
static Actor* D_80A17010 = NULL;
static f32 D_80A17014 = 0.0f;
static f32 D_80A17018 = 0.0f;
static Color_RGBA16 fsPulseColor = { 30, 240, 200 };
static ColliderJntSphElementInit sJntSphElementsInit[1] = {
{
@@ -762,31 +760,10 @@ void EnFish_Update(Actor* thisx, PlayState* play) {
}
}
// #region SOH [Randomizer]
s32 EnFish_FishsanityOverrideLimbDraw(PlayState* play, s32 limbIndex, Gfx** dList, Vec3f* pos, Vec3s* rot,
void* thisx) {
EnFish* this = (EnFish*)thisx;
Fishsanity_OpenGreyscaleColor(play, &fsPulseColor, ABS(this->actor.params) * 20);
return 0;
}
void EnFish_FishPostLimbDraw(PlayState* play, s32 limbIndex, Gfx** dList, Vec3s* rot, void* thisx) {
Fishsanity_CloseGreyscaleColor(play);
}
// #endregion
void EnFish_Draw(Actor* thisx, PlayState* play) {
EnFish* this = (EnFish*)thisx;
Gfx_SetupDL_25Opa(play->state.gfxCtx);
// #region SOH [Randomizer]
// Modify drawing for uncollected fish, having a shadowDraw implies this is being given uncollected FX
if (IS_RANDO && Randomizer_GetOverworldFishShuffled() && this->actor.shape.shadowDraw != NULL) {
SkelAnime_DrawSkeletonOpa(play, &this->skelAnime, EnFish_FishsanityOverrideLimbDraw, EnFish_FishPostLimbDraw, this);
Collider_UpdateSpheres(0, &this->collider);
return;
}
// #endregion
SkelAnime_DrawSkeletonOpa(play, &this->skelAnime, NULL, NULL, NULL);
Collider_UpdateSpheres(0, &this->collider);
}

View File

@@ -15,8 +15,6 @@
#define FLAGS ACTOR_FLAG_UPDATE_WHILE_CULLED
#define WATER_SURFACE_Y(play) play->colCtx.colHeader->waterBoxes->ySurface
#define IS_FISHSANITY (IS_RANDO && Randomizer_GetPondFishShuffled())
#define FISHID(params) (Randomizer_IdentifyFish(play->sceneNum, params))
bool getShouldSpawnLoaches();
void Fishing_Init(Actor* thisx, PlayState* play);
@@ -433,7 +431,6 @@ static f32 sFishGroupAngle3;
static FishingEffect sFishingEffects[FISHING_EFFECT_COUNT];
static Vec3f sStreamSoundProjectedPos;
static s16 sFishOnHandParams;
static Color_RGBA16 fsPulseColor = { 30, 240, 200 };
u8 AllHyruleLoaches() {
return CVarGetInteger(CVAR_ENHANCEMENT("CustomizeFishing"), 0) && CVarGetInteger(CVAR_ENHANCEMENT("AllHyruleLoaches"), 0);
@@ -3998,9 +3995,6 @@ void Fishing_UpdateFish(Actor* thisx, PlayState* play2) {
sFishOnHandLength = this->fishLength;
sFishOnHandIsLoach = (this->isLoach || AllHyruleLoaches());
sLureCaughtWith = sLureEquipped;
if (IS_FISHSANITY) {
sFishOnHandParams = this->fishsanityParams;
}
Actor_Kill(&this->actor);
} else if (getShouldConfirmKeep() && (this->isLoach == 0 && !AllHyruleLoaches()) && (sFishOnHandIsLoach == 0) &&
((s16)this->fishLength < (s16)sFishOnHandLength)) {
@@ -4015,11 +4009,6 @@ void Fishing_UpdateFish(Actor* thisx, PlayState* play2) {
sLureCaughtWith = sLureEquipped;
this->fishLength = lengthTemp;
this->isLoach = loachTemp;
if (IS_FISHSANITY) {
s16 paramsTemp = sFishOnHandParams;
sFishOnHandParams = this->fishsanityParams;
this->fishsanityParams = paramsTemp;
}
}
}
if (this->keepState == 0) {
@@ -4040,11 +4029,6 @@ void Fishing_UpdateFish(Actor* thisx, PlayState* play2) {
sLureCaughtWith = sLureEquipped;
this->fishLength = temp1;
this->isLoach = temp2;
if (IS_FISHSANITY) {
s16 paramsTemp = sFishOnHandParams;
sFishOnHandParams = this->fishsanityParams;
this->fishsanityParams = paramsTemp;
}
}
sRodCastState = 0;
}
@@ -4310,13 +4294,6 @@ s32 Fishing_FishOverrideLimbDraw(PlayState* play, s32 limbIndex, Gfx** dList, Ve
void* thisx) {
Fishing* this = (Fishing*)thisx;
// #region SOH [Randomizer]
// A fish having a shadowDraw implies that it is being given uncollected FX
if (IS_FISHSANITY && this->actor.shape.shadowDraw != NULL) {
Fishsanity_OpenGreyscaleColor(play, &fsPulseColor, (this->actor.params - 100) * 20);
}
// #endregion
if (limbIndex == 0xD) {
rot->z -= this->fishLimbDRotZDelta - 11000;
} else if ((limbIndex == 2) || (limbIndex == 3)) {
@@ -4339,13 +4316,6 @@ s32 Fishing_FishOverrideLimbDraw(PlayState* play, s32 limbIndex, Gfx** dList, Ve
void Fishing_FishPostLimbDraw(PlayState* play, s32 limbIndex, Gfx** dList, Vec3s* rot, void* thisx) {
Fishing* this = (Fishing*)thisx;
// #region SOH [Randomizer]
// A fish having a shadowDraw implies that it is being given uncollected FX
if (IS_FISHSANITY && this->actor.shape.shadowDraw != NULL) {
Fishsanity_CloseGreyscaleColor(play);
}
// #endregion
if (limbIndex == 0xD) {
Matrix_MultVec3f(&sFishMouthOffset, &this->fishMouthPos);
}
@@ -4355,13 +4325,6 @@ s32 Fishing_LoachOverrideLimbDraw(PlayState* play, s32 limbIndex, Gfx** dList, V
void* thisx) {
Fishing* this = (Fishing*)thisx;
// #region SOH [Randomizer]
// A fish having a shadowDraw implies that it is being given uncollected FX
if (IS_FISHSANITY && this->actor.shape.shadowDraw != NULL) {
Fishsanity_OpenGreyscaleColor(play, &fsPulseColor, (this->actor.params - 100) * 20);
}
// #endregion
if (limbIndex == 3) {
rot->y += this->loachRotYDelta[0];
} else if (limbIndex == 4) {
@@ -4377,13 +4340,6 @@ void Fishing_LoachPostLimbDraw(PlayState* play, s32 limbIndex, Gfx** dList, Vec3
static Vec3f sLoachMouthOffset = { 500.0f, 500.0f, 0.0f };
Fishing* this = (Fishing*)thisx;
// #region SOH [Randomizer]
// A fish having a shadowDraw implies that it is being given uncollected FX
if (IS_FISHSANITY && this->actor.shape.shadowDraw != NULL) {
Fishsanity_CloseGreyscaleColor(play);
}
// #endregion
if (limbIndex == 0xB) {
Matrix_MultVec3f(&sLoachMouthOffset, &this->fishMouthPos);
}

View File

@@ -57,7 +57,6 @@ typedef struct Fishing {
/* 0x0220 */ LightInfo lightInfo;
/* 0x0230 */ ColliderJntSph collider;
/* 0x0250 */ ColliderJntSphElement colliderElements[12];
/* */ s16 fishsanityParams;
} Fishing; // size = 0x0550
#define EN_FISH_OWNER 1 // param for owner of pond. default if params<100

View File

@@ -29,7 +29,6 @@
#include "soh/Enhancements/enhancementTypes.h"
#include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h"
#include "soh/Enhancements/randomizer/randomizer_grotto.h"
#include "soh/Enhancements/randomizer/fishsanity.h"
#include "soh/frame_interpolation.h"
#include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h"
@@ -13955,33 +13954,9 @@ void Player_Action_8084ECA4(Player* this, PlayState* play) {
func_8083721C(this);
// TODO: Rework the bottle rando code in vanilla behavior overhaul
if (LinkAnimation_Update(play, &this->skelAnime)) {
if (this->av1.actionVar1 != 0) {
if (IS_RANDO && this->av1.actionVar1 < 0) {
rc = this->av2.actionVar2;
// Award rando item; this should only give us GI_NONE if something went wrong during the catch setup
gi = Randomizer_GetItemFromKnownCheck(rc, GI_NONE);
temp = Randomizer_GetRandomizerInfFromCheck(rc);
// Either we can't give an item, we can't tell if we've gotten the check, or we have definitely gotten the check
if (gi.getItemId == GI_NONE || temp == RAND_INF_MAX || Flags_GetRandomizerInf(temp)) {
this->av1.actionVar1 = 0;
if (this->interactRangeActor != NULL)
this->interactRangeActor->parent = NULL;
}
// Item get cutscene hasn't played yet
else if((this->interactRangeActor == NULL && !(this->stateFlags1 & PLAYER_STATE1_ITEM_OVER_HEAD)) || !Actor_HasParent(this->interactRangeActor, play)) {
// Can't guarantee that whatever we "caught" is actually going to still exist
if (GiveItemEntryWithoutActor(play, gi)) {
// have to set this flag manually to prevent interactRangeActor from being wiped out
this->stateFlags1 |= PLAYER_STATE1_ITEM_OVER_HEAD;
this->pendingFlag.flagID = temp;
this->pendingFlag.flagType = FLAG_RANDOMIZER_INF;
Flags_SetRandomizerInf(temp);
}
}
} else if (this->av2.actionVar2 == 0) {
if (this->av2.actionVar2 == 0) {
if (CVarGetInteger(CVAR_ENHANCEMENT("FastDrops"), 0)) {
this->av1.actionVar1 = 0;
} else {
@@ -14017,42 +13992,15 @@ void Player_Action_8084ECA4(Player* this, PlayState* play) {
}
}
if (i < 4) {
if (GameInteractor_Should(VB_BOTTLE_ACTOR, i < 4, this->interactRangeActor)) {
this->av1.actionVar1 = i + 1;
this->av2.actionVar2 = 0;
this->interactRangeActor->parent = &this->actor;
// TODO: this should probably be refactored a bit, maybe rehome some of this to rando instead
if (IS_RANDO) {
// Check if fishsanity applies for this actor
if (Randomizer_GetOverworldFishShuffled()) {
fish = Randomizer_IdentifyFish(play->sceneNum, this->interactRangeActor->params);
if (fish.randomizerCheck != RC_UNKNOWN_CHECK && !Flags_GetRandomizerInf(fish.randomizerInf)) {
gi = Randomizer_GetItemFromKnownCheck(fish.randomizerCheck, GI_FISH);
rc = fish.randomizerCheck;
// check if the item is a bottle item anyway
catchInfo = NULL;
for (j = 0; j < 4; j++) {
if (D_80854A04[j].itemId == gi.itemId) {
catchInfo = &D_80854A04[j];
break;
}
}
}
}
}
// Vanilla behavior/rando gave a bottle item
if (!IS_RANDO || catchInfo != NULL) {
Player_UpdateBottleHeld(play, this, catchInfo->itemId, ABS(catchInfo->itemAction));
if (!CVarGetInteger(CVAR_ENHANCEMENT("FastDrops"), 0)) {
this->stateFlags1 |= PLAYER_STATE1_IN_ITEM_CS | PLAYER_STATE1_IN_CUTSCENE;
Player_AnimPlayOnceAdjusted(play, this, sp24->unk_04);
func_80835EA4(play, 4);
}
} else if (IS_RANDO && gi.itemId != ITEM_NONE) {
// Non-bottle item found from rando, flag for special behavior
this->av1.actionVar1 = -1;
this->av2.actionVar2 = rc;
Player_UpdateBottleHeld(play, this, catchInfo->itemId, ABS(catchInfo->itemAction));
if (!CVarGetInteger(CVAR_ENHANCEMENT("FastDrops"), 0)) {
this->stateFlags1 |= PLAYER_STATE1_IN_ITEM_CS | PLAYER_STATE1_IN_CUTSCENE;
Player_AnimPlayOnceAdjusted(play, this, sp24->unk_04);
func_80835EA4(play, 4);
}
}
}