From 6e27cd7107185afd0d5a76b323c1210ebc7938ad Mon Sep 17 00:00:00 2001 From: nclok1405 <155463060+nclok1405@users.noreply.github.com> Date: Wed, 31 Dec 2025 04:45:19 +0900 Subject: [PATCH] Add Gerudo Fighter to Enemy Randomizer (#6005) Being captured by random fighter behaves like being captured by a wall master --- soh/soh/Enhancements/enemyrandomizer.cpp | 52 ++++++++++++++++++- soh/soh/Enhancements/enemyrandomizer.h | 2 +- .../vanilla-behavior/GIVanillaBehavior.h | 24 +++++++++ .../overlays/actors/ovl_En_GeldB/z_en_geldb.c | 35 ++++++++----- 4 files changed, 97 insertions(+), 16 deletions(-) diff --git a/soh/soh/Enhancements/enemyrandomizer.cpp b/soh/soh/Enhancements/enemyrandomizer.cpp index 2913e06db4..52d35fbc59 100644 --- a/soh/soh/Enhancements/enemyrandomizer.cpp +++ b/soh/soh/Enhancements/enemyrandomizer.cpp @@ -12,6 +12,9 @@ extern "C" { #include #include "src/overlays/actors/ovl_En_Rr/z_en_rr.h" +#include "src/overlays/actors/ovl_En_GeldB/z_en_geldb.h" + +extern PlayState* gPlayState; } #define CVAR_ENEMY_RANDOMIZER_NAME CVAR_ENHANCEMENT("RandomizedEnemies") @@ -51,6 +54,7 @@ const char* enemyCVarList[RANDOMIZED_ENEMY_SPAWN_TABLE_SIZE] = { CVAR_ENHANCEMENT("RandomizedEnemyList.FlyingPeahat"), CVAR_ENHANCEMENT("RandomizedEnemyList.FlyingPot"), CVAR_ENHANCEMENT("RandomizedEnemyList.Freezard"), + CVAR_ENHANCEMENT("RandomizedEnemyList.GerudoFighter"), CVAR_ENHANCEMENT("RandomizedEnemyList.Gibdo"), CVAR_ENHANCEMENT("RandomizedEnemyList.GohmaLarva"), CVAR_ENHANCEMENT("RandomizedEnemyList.Guay"), @@ -114,6 +118,7 @@ const char* enemyNameList[RANDOMIZED_ENEMY_SPAWN_TABLE_SIZE] = { "Flying Peahat", "Flying Pot", "Freezard", + "Gerudo Fighter", "Gibdo", "Gohma Larva", "Guay", @@ -178,6 +183,7 @@ static EnemyEntry randomizedEnemySpawnTable[RANDOMIZED_ENEMY_SPAWN_TABLE_SIZE] = { ACTOR_EN_PEEHAT, -1 }, // Flying Peahat (big grounded, doesn't spawn larva) { ACTOR_EN_TUBO_TRAP, 0 }, // Flying pot { ACTOR_EN_FZ, 0 }, // Freezard + { ACTOR_EN_GELDB, 0 }, // Gerudo Fighter { ACTOR_EN_RD, 32766 }, // Gibdo (standing) { ACTOR_EN_GOMA, 7 }, // Gohma Larva (Non-Gohma rooms) { ACTOR_EN_CROW, 0 }, // Guay @@ -617,6 +623,14 @@ void FixClubMoblinScale(void* ptr) { } } +static void OnGerudoFighterDefeat(void* refActor) { + EnGeldB* enGeldB = reinterpret_cast(refActor); + + if (enGeldB->keyFlag == 0) { + Item_DropCollectibleRandom(gPlayState, &enGeldB->actor, &enGeldB->actor.world.pos, 0xC0); + } +} + void RegisterEnemyRandomizer() { COND_ID_HOOK(OnActorInit, ACTOR_EN_MB, CVAR_ENEMY_RANDOMIZER_VALUE, FixClubMoblinScale); @@ -675,6 +689,42 @@ void RegisterEnemyRandomizer() { *should = false; } }); + + // Allow Random Gerudo Fighters (contain no keys) to spawn without any switch flags + COND_VB_SHOULD(VB_GERUDO_FIGHTER_CONTINUE_WAITING, CVAR_ENEMY_RANDOMIZER_VALUE != CVAR_ENEMY_RANDOMIZER_DEFAULT, { + EnGeldB* enGeldB = va_arg(args, EnGeldB*); + + if (enGeldB->keyFlag == 0) { + if (!enGeldB->invisible || enGeldB->actor.xzDistToPlayer <= 300.0f) { + *should = false; + } + } + }); + + // Don't play Miniboss music for Random Gerudo Fighters + COND_VB_SHOULD(VB_GERUDO_FIGHTER_PLAY_MINIBOSS_MUSIC, CVAR_ENEMY_RANDOMIZER_VALUE != CVAR_ENEMY_RANDOMIZER_DEFAULT, + { + EnGeldB* enGeldB = va_arg(args, EnGeldB*); + + if (enGeldB->keyFlag == 0) { + *should = false; + } + }); + + // If Random Gerudo Fighters knock Link down, void him out like Wallmasters + COND_VB_SHOULD(VB_GERUDO_FIGHTER_THROW_LINK_TO_JAIL, CVAR_ENEMY_RANDOMIZER_VALUE != CVAR_ENEMY_RANDOMIZER_DEFAULT, { + EnGeldB* enGeldB = va_arg(args, EnGeldB*); + + if (enGeldB->keyFlag == 0) { + *should = false; + Sfx_PlaySfxCentered(NA_SE_OC_ABYSS); + Play_TriggerRespawn(gPlayState); + } + }); + + // If Random Gerudo Fighters are defeated, drop some items + COND_ID_HOOK(OnEnemyDefeat, ACTOR_EN_GELDB, CVAR_ENEMY_RANDOMIZER_VALUE != CVAR_ENEMY_RANDOMIZER_DEFAULT, + OnGerudoFighterDefeat); } -static RegisterShipInitFunc initFunc(RegisterEnemyRandomizer, { CVAR_ENEMY_RANDOMIZER_NAME }); \ No newline at end of file +static RegisterShipInitFunc initFunc(RegisterEnemyRandomizer, { CVAR_ENEMY_RANDOMIZER_NAME }); diff --git a/soh/soh/Enhancements/enemyrandomizer.h b/soh/soh/Enhancements/enemyrandomizer.h index 79171246c0..729d255fcf 100644 --- a/soh/soh/Enhancements/enemyrandomizer.h +++ b/soh/soh/Enhancements/enemyrandomizer.h @@ -3,7 +3,7 @@ #include #include "item-tables/ItemTableTypes.h" -#define RANDOMIZED_ENEMY_SPAWN_TABLE_SIZE 58 +#define RANDOMIZED_ENEMY_SPAWN_TABLE_SIZE 59 extern const char* enemyCVarList[]; extern const char* enemyNameList[]; diff --git a/soh/soh/Enhancements/game-interactor/vanilla-behavior/GIVanillaBehavior.h b/soh/soh/Enhancements/game-interactor/vanilla-behavior/GIVanillaBehavior.h index f604c9c795..5728540844 100644 --- a/soh/soh/Enhancements/game-interactor/vanilla-behavior/GIVanillaBehavior.h +++ b/soh/soh/Enhancements/game-interactor/vanilla-behavior/GIVanillaBehavior.h @@ -583,6 +583,30 @@ typedef enum { // - None VB_GANON_HEAL_BEFORE_FIGHT, + // #### `result` + // ```c + // (this->invisible && !Flags_GetSwitch(play, this->actor.home.rot.z)) || this->actor.xzDistToPlayer > 300.0f + // ``` + // #### `args` + // - `EnGeldB*` + VB_GERUDO_FIGHTER_CONTINUE_WAITING, + + // #### `result` + // ```c + // true + // ``` + // #### `args` + // - `EnGeldB*` + VB_GERUDO_FIGHTER_PLAY_MINIBOSS_MUSIC, + + // #### `result` + // ```c + // true + // ``` + // #### `args` + // - `EnGeldB*` + VB_GERUDO_FIGHTER_THROW_LINK_TO_JAIL, + // #### `result` // ```c // true diff --git a/soh/src/overlays/actors/ovl_En_GeldB/z_en_geldb.c b/soh/src/overlays/actors/ovl_En_GeldB/z_en_geldb.c index db84660752..f1b68528bf 100644 --- a/soh/src/overlays/actors/ovl_En_GeldB/z_en_geldb.c +++ b/soh/src/overlays/actors/ovl_En_GeldB/z_en_geldb.c @@ -359,13 +359,18 @@ void EnGeldB_SetupWait(EnGeldB* this) { } void EnGeldB_Wait(EnGeldB* this, PlayState* play) { - if ((this->invisible && !Flags_GetSwitch(play, this->actor.home.rot.z)) || this->actor.xzDistToPlayer > 300.0f) { + if (GameInteractor_Should(VB_GERUDO_FIGHTER_CONTINUE_WAITING, + (this->invisible && !Flags_GetSwitch(play, this->actor.home.rot.z)) || + this->actor.xzDistToPlayer > 300.0f, + this)) { this->actor.shape.rot.y = this->actor.world.rot.y = this->actor.yawTowardsPlayer; this->actor.world.pos.y = this->actor.floorHeight + 120.0f; } else { this->invisible = false; this->actor.shape.shadowScale = 90.0f; - func_800F5ACC(NA_BGM_MINI_BOSS); + if (GameInteractor_Should(VB_GERUDO_FIGHTER_PLAY_MINIBOSS_MUSIC, true, this)) { + func_800F5ACC(NA_BGM_MINI_BOSS); + } } if (this->actor.bgCheckFlags & 2) { Audio_PlayActorSound2(&this->actor, NA_SE_EN_RIZA_DOWN); @@ -1567,20 +1572,22 @@ void EnGeldB_Draw(Actor* thisx, PlayState* play) { } else { this->timer--; if (this->timer == 0) { - if ((INV_CONTENT(ITEM_HOOKSHOT) == ITEM_NONE) || (INV_CONTENT(ITEM_LONGSHOT) == ITEM_NONE)) { - play->nextEntranceIndex = ENTR_GERUDO_VALLEY_1; - } else if (Flags_GetEventChkInf(EVENTCHKINF_WATCHED_GANONS_CASTLE_COLLAPSE_CAUGHT_BY_GERUDO)) { - play->nextEntranceIndex = ENTR_GERUDOS_FORTRESS_18; - } else { - play->nextEntranceIndex = ENTR_GERUDOS_FORTRESS_17; - } + if (GameInteractor_Should(VB_GERUDO_FIGHTER_THROW_LINK_TO_JAIL, true, this)) { + if ((INV_CONTENT(ITEM_HOOKSHOT) == ITEM_NONE) || (INV_CONTENT(ITEM_LONGSHOT) == ITEM_NONE)) { + play->nextEntranceIndex = ENTR_GERUDO_VALLEY_1; + } else if (Flags_GetEventChkInf(EVENTCHKINF_WATCHED_GANONS_CASTLE_COLLAPSE_CAUGHT_BY_GERUDO)) { + play->nextEntranceIndex = ENTR_GERUDOS_FORTRESS_18; + } else { + play->nextEntranceIndex = ENTR_GERUDOS_FORTRESS_17; + } - if (IS_RANDO) { - Entrance_OverrideGerudoGuardCapture(); - } + if (IS_RANDO) { + Entrance_OverrideGerudoGuardCapture(); + } - play->transitionType = TRANS_TYPE_CIRCLE(TCA_STARBURST, TCC_BLACK, TCS_FAST); - play->transitionTrigger = TRANS_TRIGGER_START; + play->transitionType = TRANS_TYPE_CIRCLE(TCA_STARBURST, TCC_BLACK, TCS_FAST); + play->transitionTrigger = TRANS_TRIGGER_START; + } } } }