diff options
| author | Pepe20129 <72659707+Pepe20129@users.noreply.github.com> | 2026-03-21 19:46:15 +0100 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2026-03-21 18:46:15 +0000 |
| commit | 4e82ea192a0e0bb7bfb7a11ff2fa6ce499f72aab (patch) | |
| tree | aaf372f19c6b9b059f92d0a7402d00c07b066056 /soh | |
| parent | 2af5d2112543f88c4476d5cf082aeca9e601f5f2 (diff) | |
Enemy rando cleanup 2 (#6365)
Move stuff to main file, delete header & refactor lists
Diffstat (limited to 'soh')
| -rw-r--r-- | soh/soh/Enhancements/ExtraModes/EnemyRandomizer.cpp (renamed from soh/soh/Enhancements/enemyrandomizer.cpp) | 851 | ||||
| -rw-r--r-- | soh/soh/Enhancements/enemyrandomizer.h | 10 | ||||
| -rw-r--r-- | soh/soh/SohGui/SohMenuEnhancements.cpp | 53 | ||||
| -rw-r--r-- | soh/src/code/z_actor.c | 1 |
4 files changed, 390 insertions, 525 deletions
diff --git a/soh/soh/Enhancements/enemyrandomizer.cpp b/soh/soh/Enhancements/ExtraModes/EnemyRandomizer.cpp index c9f343f6b..f03080e8f 100644 --- a/soh/soh/Enhancements/enemyrandomizer.cpp +++ b/soh/soh/Enhancements/ExtraModes/EnemyRandomizer.cpp @@ -1,4 +1,3 @@ -#include "enemyrandomizer.h" #include "functions.h" #include "macros.h" #include "soh/Enhancements/randomizer/3drando/random.hpp" @@ -9,6 +8,9 @@ #include "soh/OTRGlobals.h" #include "soh/cvar_prefixes.h" #include "soh/ResourceManagerHelpers.h" +#include "soh/SohGui/MenuTypes.h" +#include "soh/SohGui/SohMenu.h" +#include "soh/SohGui/SohGui.hpp" extern "C" { #include <z64.h> @@ -25,226 +27,91 @@ extern "C" { extern PlayState* gPlayState; } +namespace SohGui { +extern std::shared_ptr<SohMenu> mSohMenu; +} + #define CVAR_ENEMY_RANDOMIZER_NAME CVAR_ENHANCEMENT("RandomizedEnemies") #define CVAR_ENEMY_RANDOMIZER_DEFAULT ENEMY_RANDOMIZER_OFF #define CVAR_ENEMY_RANDOMIZER_VALUE CVarGetInteger(CVAR_ENEMY_RANDOMIZER_NAME, CVAR_ENEMY_RANDOMIZER_DEFAULT) #define ENEMY_RANDOMIZER_ENABLED CVAR_ENEMY_RANDOMIZER_VALUE != CVAR_ENEMY_RANDOMIZER_DEFAULT typedef struct EnemyEntry { + const char* cvar; + const char* name; int16_t id; int16_t params; } EnemyEntry; -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, PlayState* play); - -const char* enemyCVarList[] = { - 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.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"), - CVAR_ENHANCEMENT("RandomizedEnemyList.IceKeese"), - CVAR_ENHANCEMENT("RandomizedEnemyList.InvisFireKeese"), - CVAR_ENHANCEMENT("RandomizedEnemyList.InvisFloormaster"), - CVAR_ENHANCEMENT("RandomizedEnemyList.InvisIceKeese"), - CVAR_ENHANCEMENT("RandomizedEnemyList.InvisKeese"), - CVAR_ENHANCEMENT("RandomizedEnemyList.InvisRedead"), - CVAR_ENHANCEMENT("RandomizedEnemyList.InvisSkulltula"), - CVAR_ENHANCEMENT("RandomizedEnemyList.InvisStalfos"), - CVAR_ENHANCEMENT("RandomizedEnemyList.Keese"), - CVAR_ENHANCEMENT("RandomizedEnemyList.LargeBaba"), - CVAR_ENHANCEMENT("RandomizedEnemyList.LikeLike"), - CVAR_ENHANCEMENT("RandomizedEnemyList.Lizalfos"), - CVAR_ENHANCEMENT("RandomizedEnemyList.MadScrub"), - CVAR_ENHANCEMENT("RandomizedEnemyList.NormalWolfos"), - CVAR_ENHANCEMENT("RandomizedEnemyList.PeahatLarva"), - // CVAR_ENHANCEMENT("RandomizedEnemyList.Poe"), - CVAR_ENHANCEMENT("RandomizedEnemyList.Redead"), - CVAR_ENHANCEMENT("RandomizedEnemyList.RedTektite"), - 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.SpearMoblin"), - CVAR_ENHANCEMENT("RandomizedEnemyList.Spike"), - CVAR_ENHANCEMENT("RandomizedEnemyList.Stalfos"), - CVAR_ENHANCEMENT("RandomizedEnemyList.Stinger"), - CVAR_ENHANCEMENT("RandomizedEnemyList.Tailparasan"), - CVAR_ENHANCEMENT("RandomizedEnemyList.TorchSlug"), - CVAR_ENHANCEMENT("RandomizedEnemyList.Wallmaster"), - CVAR_ENHANCEMENT("RandomizedEnemyList.WhiteKnuckle"), - CVAR_ENHANCEMENT("RandomizedEnemyList.WhiteWolfos"), - CVAR_ENHANCEMENT("RandomizedEnemyList.WitheredBaba"), -}; - -const char* enemyNameList[] = { - "Anubis", - "Armos", - "Arwing", - "Baby Dodongo", - "Bari", - "Beamos", - "Big Skulltula", - "Stalchild (Big)", - "Biri", - "Iron Knuckle (Black)", - "Blue Tektite", - "Bubble", - "Club Moblin", - "Dark Link", - "Dinolfos", - "Dodongo", - "Fire Keese", - //"Flare Dancer", - "Floor Tile", - "Floormaster", - "Flying Peahat", - "Flying Pot", - "Freezard", - "Gerudo Fighter", - "Gibdo", - "Gohma Larva", - "Guay", - "Ice Keese", - "Invisible Fire Keese", - "Invisible Floormaster", - "Invisible Ice Keese", - "Invisible Keese", - "Invisible Redead", - "Invisible Skulltula", - "Invisible Stalfos", - "Keese", - "Large Deku Baba", - "Like-Like", - "Lizalfos", - "Mad Scrub", - "Wolfos (Normal)", - "Peahat Larva", - //"Poe", - "Redead", - "Red Tektite", - "Shabom", - "Shell Blade", - "Skulltula", - "Skull Kid", - "Small Deku Baba", - "Stalchild (Small)", - "Spear Moblin", - "Spike", - "Stalfos", - "Stinger", - "Tailpasaran", - "Torch Slug", - "Wallmaster", - "Iron Knuckle (White)", - "Wolfos (White)", - "Withered Deku Baba", -}; - +// clang-format off static EnemyEntry randomizedEnemySpawnTable[] = { - { ACTOR_EN_ANUBICE_TAG, 1 }, // Anubis - { ACTOR_EN_AM, -1 }, // Armos - { ACTOR_EN_CLEAR_TAG, 1 }, // Arwing - { ACTOR_EN_DODOJR, 0 }, // Baby Dodongo - { ACTOR_EN_VALI, -1 }, // Bari (big jellyfish) - { ACTOR_EN_VM, 1280 }, // Beamos - { ACTOR_EN_ST, 1 }, // Skulltula (big) - { ACTOR_EN_SKB, 20 }, // Stalchild (big) - { ACTOR_EN_BILI, 0 }, // Biri (jellyfish) - { ACTOR_EN_IK, 2 }, // Iron Knuckle (black, standing) - { ACTOR_EN_TITE, -2 }, // Tektite (blue) - { ACTOR_EN_BB, -1 }, // Bubble (flying skull enemy) (blue) - { ACTOR_EN_MB, 0 }, // Club Moblin - { ACTOR_EN_TORCH2, 0 }, // Dark Link - { ACTOR_EN_ZF, -2 }, // Dinolfos - { ACTOR_EN_DODONGO, -1 }, // Dodongo - { 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_FLOORMAS, 0 }, // Floormaster - { 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 - { ACTOR_EN_FIREFLY, 4 }, // Ice Keese - { ACTOR_EN_FIREFLY, 0x8001 }, // Fire Keese (invisible) - { ACTOR_EN_FLOORMAS, 0x8000 }, // Floormaster (invisible) - { ACTOR_EN_FIREFLY, 0x8004 }, // Ice Keese (invisible) - { ACTOR_EN_FIREFLY, 0x8002 }, // Keese (invisible) - { ACTOR_EN_RD, 3 }, // Redead (invisible) - { ACTOR_EN_ST, 2 }, // Skulltula (invisible) - { ACTOR_EN_TEST, 0 }, // Stalfos (invisible) - { ACTOR_EN_FIREFLY, 2 }, // Regular Keese - { ACTOR_EN_DEKUBABA, 1 }, // Deku Baba (large) - // Doesn't work (reliant on surface, without a spawner it kills itself too quickly) - // { ACTOR_EN_REEBA, 0 }, // Leever - { ACTOR_EN_RR, 0 }, // Like-Like - { ACTOR_EN_ZF, -1 }, // Lizalfos - { ACTOR_EN_DEKUNUTS, 768 }, // Mad Scrub (triple attack) (projectiles don't work) - { 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 - // 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_TITE, -1 }, // Tektite (red) - { ACTOR_EN_BUBBLE, 0 }, // Shabom (bubble) - { ACTOR_EN_SB, 0 }, // Shell Blade - { ACTOR_EN_ST, 0 }, // Skulltula (normal) - { ACTOR_EN_SKJ, 4159 }, // Skull Kid - { ACTOR_EN_DEKUBABA, 0 }, // Deku Baba (small) - { ACTOR_EN_SKB, 1 }, // Stalchild (small) - { ACTOR_EN_MB, -1 }, // Spear Moblin - { ACTOR_EN_NY, 0 }, // Spike (rolling enemy) - { ACTOR_EN_TEST, 2 }, // Stalfos - { ACTOR_EN_EIYER, 10 }, // Stinger (land) (One in formation, sink under floor and do not activate) - { ACTOR_EN_TP, -1 }, // Electric Tailpasaran - { ACTOR_EN_BW, 0 }, // Torch Slug - { ACTOR_EN_WALLMAS, 1 }, // Wallmaster - { ACTOR_EN_IK, 3 }, // Iron Knuckle (white, standing) - { ACTOR_EN_WF, 1 }, // Wolfos (white) - { ACTOR_EN_KAREBABA, 0 }, // Withered Deku Baba + { CVAR_ENHANCEMENT("RandomizedEnemyList.Anubis"), "Anubis", ACTOR_EN_ANUBICE_TAG, 1 }, // Anubis + { CVAR_ENHANCEMENT("RandomizedEnemyList.Armos"), "Armos", ACTOR_EN_AM, -1 }, // Armos + { CVAR_ENHANCEMENT("RandomizedEnemyList.Arwing"), "Arwing", ACTOR_EN_CLEAR_TAG, 1 }, // Arwing + { CVAR_ENHANCEMENT("RandomizedEnemyList.BabyDodongo"), "Baby Dodongo", ACTOR_EN_DODOJR, 0 }, // Baby Dodongo + { CVAR_ENHANCEMENT("RandomizedEnemyList.Bari"), "Bari", ACTOR_EN_VALI, -1 }, // Bari (big jellyfish) + { CVAR_ENHANCEMENT("RandomizedEnemyList.Beamos"), "Beamos", ACTOR_EN_VM, 1280 }, // Beamos + { CVAR_ENHANCEMENT("RandomizedEnemyList.BigSkulltula"), "Big Skulltula", ACTOR_EN_ST, 1 }, // Skulltula (big) + { CVAR_ENHANCEMENT("RandomizedEnemyList.BigStalchild"), "Stalchild (Big)", ACTOR_EN_SKB, 20 }, // Stalchild (big) + { CVAR_ENHANCEMENT("RandomizedEnemyList.Biri"), "Biri", ACTOR_EN_BILI, 0 }, // Biri (jellyfish) + { CVAR_ENHANCEMENT("RandomizedEnemyList.BlackKnuckle"), "Iron Knuckle (Black)", ACTOR_EN_IK, 2 }, // Iron Knuckle (black, standing) + { CVAR_ENHANCEMENT("RandomizedEnemyList.BlueTektite"), "Blue Tektite", ACTOR_EN_TITE, -2 }, // Tektite (blue) + { CVAR_ENHANCEMENT("RandomizedEnemyList.Bubble"), "Bubble", ACTOR_EN_BB, -1 }, // Bubble (flying skull enemy) (blue) + { CVAR_ENHANCEMENT("RandomizedEnemyList.ClubMoblin"), "Club Moblin", ACTOR_EN_MB, 0 }, // Club Moblin + { CVAR_ENHANCEMENT("RandomizedEnemyList.DarkLink"), "Dark Link", ACTOR_EN_TORCH2, 0 }, // Dark Link + { CVAR_ENHANCEMENT("RandomizedEnemyList.Dinolfos"), "Dinolfos", ACTOR_EN_ZF, -2 }, // Dinolfos + { CVAR_ENHANCEMENT("RandomizedEnemyList.Dodongo"), "Dodongo", ACTOR_EN_DODONGO, -1 }, // Dodongo + { CVAR_ENHANCEMENT("RandomizedEnemyList.FireKeese"), "Fire Keese", ACTOR_EN_FIREFLY, 1 }, // Fire Keese + // { CVAR_ENHANCEMENT("RandomizedEnemyList.FlareDancer"), "Flare Dancer", ACTOR_EN_FD, 0 }, // Flare Dancer (possible cause of crashes because of spawning flame actors on sloped ground) + { CVAR_ENHANCEMENT("RandomizedEnemyList.FloorTile"), "Floor Tile", ACTOR_EN_YUKABYUN, 0 }, // Flying Floor Tile + { CVAR_ENHANCEMENT("RandomizedEnemyList.Floormaster"), "Floormaster", ACTOR_EN_FLOORMAS, 0 }, // Floormaster + { CVAR_ENHANCEMENT("RandomizedEnemyList.FlyingPeahat"), "Flying Peahat", ACTOR_EN_PEEHAT, -1 }, // Flying Peahat (big grounded, doesn't spawn larva) + { CVAR_ENHANCEMENT("RandomizedEnemyList.FlyingPot"), "Flying Pot", ACTOR_EN_TUBO_TRAP, 0 }, // Flying pot + { CVAR_ENHANCEMENT("RandomizedEnemyList.Freezard"), "Freezard", ACTOR_EN_FZ, 0 }, // Freezard + { CVAR_ENHANCEMENT("RandomizedEnemyList.GerudoFighter"), "Gerudo Fighter", ACTOR_EN_GELDB, 0 }, // Gerudo Fighter + { CVAR_ENHANCEMENT("RandomizedEnemyList.Gibdo"), "Gibdo", ACTOR_EN_RD, 32766 }, // Gibdo (standing) + { CVAR_ENHANCEMENT("RandomizedEnemyList.GohmaLarva"), "Gohma Larva", ACTOR_EN_GOMA, 7 }, // Gohma Larva (Non-Gohma rooms) + { CVAR_ENHANCEMENT("RandomizedEnemyList.Guay"), "Guay", ACTOR_EN_CROW, 0 }, // Guay + { CVAR_ENHANCEMENT("RandomizedEnemyList.IceKeese"), "Ice Keese", ACTOR_EN_FIREFLY, 4 }, // Ice Keese + { CVAR_ENHANCEMENT("RandomizedEnemyList.InvisFireKeese"), "Invisible Fire Keese", ACTOR_EN_FIREFLY, 0x8001 }, // Fire Keese (invisible) + { CVAR_ENHANCEMENT("RandomizedEnemyList.InvisFloormaster"), "Invisible Floormaster", ACTOR_EN_FLOORMAS, 0x8000 }, // Floormaster (invisible) + { CVAR_ENHANCEMENT("RandomizedEnemyList.InvisIceKeese"), "Invisible Ice Keese", ACTOR_EN_FIREFLY, 0x8004 }, // Ice Keese (invisible) + { CVAR_ENHANCEMENT("RandomizedEnemyList.InvisKeese"), "Invisible Keese", ACTOR_EN_FIREFLY, 0x8002 }, // Keese (invisible) + { CVAR_ENHANCEMENT("RandomizedEnemyList.InvisRedead"), "Invisible Redead", ACTOR_EN_RD, 3 }, // Redead (invisible) + { CVAR_ENHANCEMENT("RandomizedEnemyList.InvisSkulltula"), "Invisible Skulltula", ACTOR_EN_ST, 2 }, // Skulltula (invisible) + { CVAR_ENHANCEMENT("RandomizedEnemyList.InvisStalfos"), "Invisible Stalfos", ACTOR_EN_TEST, 0 }, // Stalfos (invisible) + { CVAR_ENHANCEMENT("RandomizedEnemyList.Keese"), "Keese", ACTOR_EN_FIREFLY, 2 }, // Regular Keese + { CVAR_ENHANCEMENT("RandomizedEnemyList.LargeBaba"), "Large Deku Baba", ACTOR_EN_DEKUBABA, 1 }, // Deku Baba (large) + // { CVAR_ENHANCEMENT("RandomizedEnemyList.Leever"), "Leever", ACTOR_EN_REEBA, 0 }, // Leever Doesn't work (reliant on surface, without a spawner it kills itself too quickly) + { CVAR_ENHANCEMENT("RandomizedEnemyList.LikeLike"), "Like-Like", ACTOR_EN_RR, 0 }, // Like-Like + { CVAR_ENHANCEMENT("RandomizedEnemyList.Lizalfos"), "Lizalfos", ACTOR_EN_ZF, -1 }, // Lizalfos + { CVAR_ENHANCEMENT("RandomizedEnemyList.MadScrub"), "Mad Scrub", ACTOR_EN_DEKUNUTS, 768 }, // Mad Scrub (triple attack) (projectiles don't work) + { CVAR_ENHANCEMENT("RandomizedEnemyList.NormalWolfos"), "Wolfos (Normal)", ACTOR_EN_WF, 0 }, // Wolfos (normal) + // { CVAR_ENHANCEMENT("RandomizedEnemyList.Octorok"), "Octorok", ACTOR_EN_OKUTA, 0 }, // Octorok Doesn't work (actor directly uses water box collision to handle hiding/popping up) + { CVAR_ENHANCEMENT("RandomizedEnemyList.PeahatLarva"), "Peahat Larva", ACTOR_EN_PEEHAT, 1 }, // Flying Peahat Larva + // { CVAR_ENHANCEMENT("RandomizedEnemyList.Poe"), "Poe", ACTOR_EN_POH, 0 }, // Poe Doesn't work (Seems to rely on other objects?) + // { CVAR_ENHANCEMENT("RandomizedEnemyList.Poe"), "Poe", ACTOR_EN_POH, 2 }, // Poe (composer Sharp) Doesn't work (Seems to rely on other objects?) + // { CVAR_ENHANCEMENT("RandomizedEnemyList.Poe"), "Poe", ACTOR_EN_POH, 3 }, // Poe (composer Flat) Doesn't work (Seems to rely on other objects?) + { CVAR_ENHANCEMENT("RandomizedEnemyList.Redead"), "Redead", ACTOR_EN_RD, 1 }, // Redead (standing) + { CVAR_ENHANCEMENT("RandomizedEnemyList.RedTektite"), "Red Tektite", ACTOR_EN_TITE, -1 }, // Tektite (red) + { CVAR_ENHANCEMENT("RandomizedEnemyList.Shabom"), "Shabom", ACTOR_EN_BUBBLE, 0 }, // Shabom (bubble) + { CVAR_ENHANCEMENT("RandomizedEnemyList.ShellBlade"), "Shell Blade", ACTOR_EN_SB, 0 }, // Shell Blade + { CVAR_ENHANCEMENT("RandomizedEnemyList.Skulltula"), "Skulltula", ACTOR_EN_ST, 0 }, // Skulltula (normal) + { CVAR_ENHANCEMENT("RandomizedEnemyList.SkullKid"), "Skull Kid", ACTOR_EN_SKJ, 4159 }, // Skull Kid + { CVAR_ENHANCEMENT("RandomizedEnemyList.SmallBaba"), "Small Deku Baba", ACTOR_EN_DEKUBABA, 0 }, // Deku Baba (small) + { CVAR_ENHANCEMENT("RandomizedEnemyList.SmallStalchild"), "Stalchild (Small)", ACTOR_EN_SKB, 1 }, // Stalchild (small) + { CVAR_ENHANCEMENT("RandomizedEnemyList.SpearMoblin"), "Spear Moblin", ACTOR_EN_MB, -1 }, // Spear Moblin + { CVAR_ENHANCEMENT("RandomizedEnemyList.Spike"), "Spike", ACTOR_EN_NY, 0 }, // Spike (rolling enemy) + { CVAR_ENHANCEMENT("RandomizedEnemyList.Stalfos"), "Stalfos", ACTOR_EN_TEST, 2 }, // Stalfos + { CVAR_ENHANCEMENT("RandomizedEnemyList.Stinger"), "Stinger", ACTOR_EN_EIYER, 10 }, // Stinger (land) (One in formation, sink under floor and do not activate) + { CVAR_ENHANCEMENT("RandomizedEnemyList.Tailparasan"), "Tailpasaran", ACTOR_EN_TP, -1 }, // Electric Tailpasaran + { CVAR_ENHANCEMENT("RandomizedEnemyList.TorchSlug"), "Torch Slug", ACTOR_EN_BW, 0 }, // Torch Slug + { CVAR_ENHANCEMENT("RandomizedEnemyList.Wallmaster"), "Wallmaster", ACTOR_EN_WALLMAS, 1 }, // Wallmaster + { CVAR_ENHANCEMENT("RandomizedEnemyList.WhiteKnuckle"), "Iron Knuckle (White)", ACTOR_EN_IK, 3 }, // Iron Knuckle (white, standing) + { CVAR_ENHANCEMENT("RandomizedEnemyList.WhiteWolfos"), "Wolfos (White)", ACTOR_EN_WF, 1 }, // Wolfos (white) + { CVAR_ENHANCEMENT("RandomizedEnemyList.WitheredBaba"), "Withered Deku Baba", ACTOR_EN_KAREBABA, 0 }, // Withered Deku Baba }; - -// assert sizes without accidental 0 initialization -static_assert(ARRAY_COUNT(enemyCVarList) == ARRAY_COUNT(enemyNameList), ""); -static_assert(ARRAY_COUNT(enemyCVarList) == ARRAY_COUNT(randomizedEnemySpawnTable), ""); -static_assert(ARRAY_COUNT(enemyCVarList) == RANDOMIZED_ENEMY_SPAWN_TABLE_SIZE, ""); +// clang-format on static int enemiesToRandomize[] = { ACTOR_EN_ANUBICE_TAG, // Anubis @@ -255,163 +122,168 @@ static int enemiesToRandomize[] = { ACTOR_EN_OKUTA, // Octorok ACTOR_EN_WALLMAS, // Wallmaster ACTOR_EN_DODONGO, // Dodongo - // ACTOR_EN_REEBA, // Leever (reliant on spawner (z_en_encount1.c)) - ACTOR_EN_PEEHAT, // Flying Peahat, big one spawning larva, larva - ACTOR_EN_ZF, // Lizalfos, Dinolfos - ACTOR_EN_GOMA, // Gohma Larva (normal, eggs, gohma eggs) - ACTOR_EN_BUBBLE, // Shabom (bubble) - ACTOR_EN_DODOJR, // Baby Dodongo - ACTOR_EN_TORCH2, // Dark Link - ACTOR_EN_BILI, // Biri (small jellyfish) - ACTOR_EN_TP, // Electric Tailpasaran - ACTOR_EN_ST, // Skulltula (normal, big, invisible) - ACTOR_EN_BW, // Torch Slug - ACTOR_EN_EIYER, // Stinger (land) - ACTOR_EN_MB, // Moblins (Club, spear) - ACTOR_EN_DEKUBABA, // Deku Baba (small, large) - ACTOR_EN_AM, // Armos (enemy variant) - ACTOR_EN_DEKUNUTS, // Mad Scrub (single attack, triple attack) - ACTOR_EN_VALI, // Bari (big jellyfish) (spawns very high up) - ACTOR_EN_BB, // Bubble (flying skull enemy) (all colors) - ACTOR_EN_YUKABYUN, // Flying Floor Tile - ACTOR_EN_VM, // Beamos - ACTOR_EN_FLOORMAS, // Floormaster - ACTOR_EN_RD, // Redead, Gibdo - ACTOR_EN_SW, // Skullwalltula - ACTOR_EN_FD, // Flare Dancer - ACTOR_EN_SB, // Shell Blade - ACTOR_EN_KAREBABA, // Withered Deku Baba - ACTOR_EN_RR, // Like-Like - ACTOR_EN_NY, // Spike (rolling enemy) - ACTOR_EN_IK, // Iron Knuckle - ACTOR_EN_TUBO_TRAP, // Flying pot - ACTOR_EN_FZ, // Freezard - ACTOR_EN_WEIYER, // Stinger (Water) - ACTOR_EN_HINTNUTS, // Hint Deku Scrubs - ACTOR_EN_WF, // Wolfos - ACTOR_EN_SKB, // Stalchild - ACTOR_EN_CROW, // Guay - ACTOR_EN_SKJ, // Skull Kid + ACTOR_EN_PEEHAT, // Flying Peahat, big one spawning larva, larva + ACTOR_EN_ZF, // Lizalfos, Dinolfos + ACTOR_EN_GOMA, // Gohma Larva (normal, eggs, gohma eggs) + ACTOR_EN_BUBBLE, // Shabom (bubble) + ACTOR_EN_DODOJR, // Baby Dodongo + ACTOR_EN_TORCH2, // Dark Link + ACTOR_EN_BILI, // Biri (small jellyfish) + ACTOR_EN_TP, // Electric Tailpasaran + ACTOR_EN_ST, // Skulltula (normal, big, invisible) + ACTOR_EN_BW, // Torch Slug + ACTOR_EN_EIYER, // Stinger (land) + ACTOR_EN_MB, // Moblins (Club, spear) + ACTOR_EN_DEKUBABA, // Deku Baba (small, large) + ACTOR_EN_AM, // Armos (enemy variant) + ACTOR_EN_DEKUNUTS, // Mad Scrub (single attack, triple attack) + ACTOR_EN_VALI, // Bari (big jellyfish) (spawns very high up) + ACTOR_EN_BB, // Bubble (flying skull enemy) (all colors) + ACTOR_EN_YUKABYUN, // Flying Floor Tile + ACTOR_EN_VM, // Beamos + ACTOR_EN_FLOORMAS, // Floormaster + ACTOR_EN_RD, // Redead, Gibdo + ACTOR_EN_SW, // Skullwalltula + ACTOR_EN_FD, // Flare Dancer + ACTOR_EN_SB, // Shell Blade + ACTOR_EN_KAREBABA, // Withered Deku Baba + ACTOR_EN_RR, // Like-Like + ACTOR_EN_NY, // Spike (rolling enemy) + ACTOR_EN_IK, // Iron Knuckle + ACTOR_EN_TUBO_TRAP, // Flying pot + ACTOR_EN_FZ, // Freezard + ACTOR_EN_WEIYER, // Stinger (Water) + ACTOR_EN_HINTNUTS, // Hint Deku Scrubs + ACTOR_EN_WF, // Wolfos + ACTOR_EN_SKB, // Stalchild + ACTOR_EN_CROW, // Guay + ACTOR_EN_SKJ, // Skull Kid + // ACTOR_EN_REEBA, // Leever (reliant on spawner (z_en_encount1.c)) }; -uint8_t GetRandomizedEnemy(PlayState* play, int16_t* actorId, s16* posX, s16* posY, s16* posZ, int16_t* rotX, - int16_t* rotY, int16_t* rotZ, int16_t* params) { - - uint32_t isMQ = ResourceMgr_IsSceneMasterQuest(play->sceneNum); - - // Hack to remove enemies that wrongfully spawn because of bypassing object dependency with enemy randomizer on. - // This should probably be handled on OTR generation in the future when object dependency is fully removed. - // Remove bats and Skulltulas from graveyard. - // Remove Octorok in Lost Woods. - if (((*actorId == ACTOR_EN_FIREFLY || (*actorId == ACTOR_EN_SW && *params == 0)) && - play->sceneNum == SCENE_GRAVEYARD) || - (*actorId == ACTOR_EN_OKUTA && play->sceneNum == SCENE_LOST_WOODS)) { - return 0; - } - - // Hack to change a pot in Spirit Temple that holds a Deku Shield to not hold anything. - // This should probably be handled on OTR generation in the future when object dependency is fully removed. - // This Deku Shield doesn't normally spawn in authentic gameplay because of object dependency. - if (*actorId == ACTOR_OBJ_TSUBO && *params == 24597) { - *params = 24067; - } - - // Lengthen timer in non-MQ Jabu Jabu bubble room. - if (!isMQ && *actorId == ACTOR_OBJ_ROOMTIMER && *params == 30760 && play->sceneNum == SCENE_JABU_JABU && - play->roomCtx.curRoom.num == 12) { - *params = (*params & ~0x3FF) | 120; - } - - if (IsEnemyFoundToRandomize(play->sceneNum, play->roomCtx.curRoom.num, *actorId, *params, *posX)) { - - // When replacing Iron Knuckles in Spirit Temple, move them away from the throne because - // some enemies can get stuck on the throne. - if (*actorId == ACTOR_EN_IK && play->sceneNum == SCENE_SPIRIT_TEMPLE) { - if (*params == 6657) { - *posX = *posX + 150; - } else if (*params == 6401) { - *posX = *posX - 150; - } - } - - // Move like-likes in MQ Jabu Jabu down into the room as they otherwise get stuck on Song of Time blocks. - if (*actorId == ACTOR_EN_RR && play->sceneNum == SCENE_JABU_JABU && play->roomCtx.curRoom.num == 11) { - if (*posX == 1003) { - *posX = *posX - 75; - } else { - *posX = *posX + 75; - } - *posY = *posY - 200; - } - - // Do a raycast from the original position of the actor to find the ground below it, then try to place - // the new actor on the ground. This way enemies don't spawn very high in the sky, and gives us control - // over height offsets per enemy from a proven grounded position. - CollisionPoly poly; - Vec3f pos; - f32 raycastResult; - - pos.x = *posX; - pos.y = *posY + 50; - pos.z = *posZ; - raycastResult = BgCheck_AnyRaycastFloor1(&play->colCtx, &poly, &pos); - - // If ground is found below actor, move actor to that height. - if (raycastResult > BGCHECK_Y_MIN) { - *posY = raycastResult; - } - - // Get randomized enemy ID and parameter. - uint32_t seed = - play->sceneNum + *actorId + (int)*posX + (int)*posY + (int)*posZ + *rotX + *rotY + *rotZ + *params; - EnemyEntry randomEnemy = GetRandomizedEnemyEntry(seed, play); +bool IsEnemyAllowedToSpawn(int16_t sceneNum, int8_t roomNum, EnemyEntry enemy) { + uint32_t isMQ = ResourceMgr_IsSceneMasterQuest(sceneNum); - *actorId = randomEnemy.id; - *params = randomEnemy.params; + // Freezard - Child Link can only kill this with jump slash Deku Sticks or other equipment like bombs. + // Beamos - Needs bombs. + // Anubis - Needs fire. + // Shell Blade & Spike - Child Link can't kill these with sword or Deku Stick. + // 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. + // 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 || enemy.id == ACTOR_EN_NY || + enemy.id == ACTOR_EN_CLEAR_TAG || enemy.id == ACTOR_EN_WALLMAS || enemy.id == ACTOR_EN_TORCH2 || + (enemy.id == ACTOR_EN_MB && enemy.params == 0) || enemy.id == ACTOR_EN_FD || enemy.id == ACTOR_EN_ANUBICE_TAG; - // Straighten out enemies so they aren't flipped on their sides when the original spawn is. - *rotX = 0; + // Bari - Spawns 3 more enemies, potentially extremely difficult in timed rooms. + bool enemiesToExcludeTimedRooms = enemiesToExcludeClearRooms || enemy.id == ACTOR_EN_VALI; - switch (*actorId) { - // When spawning big jellyfish, spawn it up high. - case ACTOR_EN_VALI: - *posY = *posY + 300; - break; - // Spawn Peahat off the ground, otherwise it kills itself by colliding with the ground. - case ACTOR_EN_PEEHAT: - if (*params == 1) { - *posY = *posY + 100; - } - break; - // Spawn Skulltulas off the ground. - case ACTOR_EN_ST: - *posY = *posY + 200; - break; - // Spawn flying enemies off the ground. - case ACTOR_EN_FIREFLY: - case ACTOR_EN_BILI: - case ACTOR_EN_BB: - case ACTOR_EN_CLEAR_TAG: - case ACTOR_EN_CROW: - *posY = *posY + 75; - break; - default: - break; - } + switch (sceneNum) { + // Deku Tree + case SCENE_DEKU_TREE: + return (!(!isMQ && enemiesToExcludeClearRooms && (roomNum == 1 || roomNum == 9)) && + !(isMQ && enemiesToExcludeClearRooms && + (roomNum == 4 || roomNum == 6 || roomNum == 9 || roomNum == 10))); + // Dodongo's Cavern + case SCENE_DODONGOS_CAVERN: + return (!(!isMQ && enemiesToExcludeClearRooms && roomNum == 15) && + !(isMQ && enemiesToExcludeClearRooms && (roomNum == 5 || roomNum == 13 || roomNum == 14))); + // Jabu Jabu + case SCENE_JABU_JABU: + return (!(!isMQ && enemiesToExcludeClearRooms && (roomNum == 8 || roomNum == 9)) && + !(!isMQ && enemiesToExcludeTimedRooms && roomNum == 12) && + !(isMQ && enemiesToExcludeClearRooms && (roomNum == 11 || roomNum == 14))); + // Forest Temple + case SCENE_FOREST_TEMPLE: + return (!(!isMQ && enemiesToExcludeClearRooms && + (roomNum == 6 || roomNum == 10 || roomNum == 18 || roomNum == 21)) && + !(isMQ && enemiesToExcludeClearRooms && + (roomNum == 5 || roomNum == 6 || roomNum == 18 || roomNum == 21))); + // Fire Temple + case SCENE_FIRE_TEMPLE: + return (!(!isMQ && enemiesToExcludeClearRooms && roomNum == 15) && + !(isMQ && enemiesToExcludeClearRooms && (roomNum == 15 || roomNum == 17 || roomNum == 18))); + // Water Temple + case SCENE_WATER_TEMPLE: + return (!(!isMQ && enemiesToExcludeClearRooms && (roomNum == 13 || roomNum == 18 || roomNum == 19)) && + !(isMQ && enemiesToExcludeClearRooms && (roomNum == 13 || roomNum == 18))); + // Spirit Temple + case SCENE_SPIRIT_TEMPLE: + return (!(!isMQ && enemiesToExcludeClearRooms && + (roomNum == 1 || roomNum == 10 || roomNum == 17 || roomNum == 20)) && + !(isMQ && enemiesToExcludeClearRooms && + (roomNum == 1 || roomNum == 2 || roomNum == 4 || roomNum == 10 || roomNum == 15 || + roomNum == 19 || roomNum == 20))); + // Shadow Temple + case SCENE_SHADOW_TEMPLE: + return ( + !(!isMQ && enemiesToExcludeClearRooms && + (roomNum == 1 || roomNum == 7 || roomNum == 11 || roomNum == 14 || roomNum == 16 || roomNum == 17 || + roomNum == 19 || roomNum == 20)) && + !(isMQ && enemiesToExcludeClearRooms && + (roomNum == 1 || roomNum == 6 || roomNum == 7 || roomNum == 11 || roomNum == 14 || roomNum == 20))); + // Ganon's Castle Trials + case SCENE_INSIDE_GANONS_CASTLE: + return (!(!isMQ && enemiesToExcludeClearRooms && (roomNum == 2 || roomNum == 5 || roomNum == 9)) && + !(isMQ && enemiesToExcludeClearRooms && + (roomNum == 0 || roomNum == 2 || roomNum == 5 || roomNum == 9))); + // Ice Caverns + case SCENE_ICE_CAVERN: + return (!(!isMQ && enemiesToExcludeClearRooms && (roomNum == 1 || roomNum == 7)) && + !(isMQ && enemiesToExcludeClearRooms && (roomNum == 3 || roomNum == 7))); + // Bottom of the Well + // Exclude Dark Link from room with holes in the floor because it can pull you in a like-like making the player + // fall down. + case SCENE_BOTTOM_OF_THE_WELL: + return (!(!isMQ && enemy.id == ACTOR_EN_TORCH2 && roomNum == 3)); + // Don't allow Dark Link in areas with lava void out zones as it voids out the player as well. + // Gerudo Training Ground. + case SCENE_GERUDO_TRAINING_GROUND: + return (!(enemy.id == ACTOR_EN_TORCH2 && roomNum == 6) && + !(!isMQ && enemiesToExcludeTimedRooms && (roomNum == 1 || roomNum == 7)) && + !(!isMQ && enemiesToExcludeClearRooms && (roomNum == 3 || roomNum == 5 || roomNum == 10)) && + !(isMQ && enemiesToExcludeTimedRooms && + (roomNum == 1 || roomNum == 3 || roomNum == 5 || roomNum == 7)) && + !(isMQ && enemiesToExcludeClearRooms && roomNum == 10)); + // Don't allow certain enemies in Ganon's Tower because they would spawn up on the ceiling, + // becoming impossible to kill. + // Ganon's Tower. + case SCENE_GANONS_TOWER: + return (!(enemiesToExcludeClearRooms || enemy.id == ACTOR_EN_VALI || + (enemy.id == ACTOR_EN_ZF && enemy.params == -1))); + // Ganon's Tower Escape. + case SCENE_GANONS_TOWER_COLLAPSE_INTERIOR: + return (!((enemiesToExcludeTimedRooms || (enemy.id == ACTOR_EN_ZF && enemy.params == -1)) && roomNum == 1)); + // Don't allow big Stalchildren, big Peahats and the large Bari (jellyfish) during the Gohma fight because they + // can clip into Gohma and it crashes the game. Likely because Gohma on the ceiling can't handle collision with + // other enemies. + case SCENE_DEKU_TREE_BOSS: + return (!enemiesToExcludeTimedRooms && !(enemy.id == ACTOR_EN_SKB && enemy.params == 20) && + !(enemy.id == ACTOR_EN_PEEHAT && enemy.params == -1)); + // Grottos. + case SCENE_GROTTOS: + return (!(enemiesToExcludeClearRooms && (roomNum == 2 || roomNum == 7))); + // Royal Grave. + case SCENE_ROYAL_FAMILYS_TOMB: + return (!(enemiesToExcludeClearRooms && roomNum == 0)); + // Don't allow Dark Link in areas with lava void out zones as it voids out the player as well. + // Death Mountain Crater. + case SCENE_DEATH_MOUNTAIN_CRATER: + return (enemy.id != ACTOR_EN_TORCH2); + default: + return 1; } - - // Enemy finished randomization process. - return 1; } static std::vector<EnemyEntry> selectedEnemyList; void GetSelectedEnemies() { selectedEnemyList.clear(); - for (int i = 0; i < RANDOMIZED_ENEMY_SPAWN_TABLE_SIZE; i++) { + for (int i = 0; i < ARRAY_COUNT(randomizedEnemySpawnTable); i++) { if (CVarGetInteger(CVAR_ENHANCEMENT("RandomizedEnemyList.All"), 0)) { selectedEnemyList.push_back(randomizedEnemySpawnTable[i]); - } else if (CVarGetInteger(enemyCVarList[i], 1)) { + } else if (CVarGetInteger(randomizedEnemySpawnTable[i].cvar, 1)) { selectedEnemyList.push_back(randomizedEnemySpawnTable[i]); } } @@ -513,119 +385,114 @@ bool IsEnemyFoundToRandomize(int16_t sceneNum, int8_t roomNum, int16_t actorId, return 0; } -bool IsEnemyAllowedToSpawn(int16_t sceneNum, int8_t roomNum, EnemyEntry enemy) { - uint32_t isMQ = ResourceMgr_IsSceneMasterQuest(sceneNum); +uint8_t GetRandomizedEnemy(PlayState* play, int16_t* actorId, s16* posX, s16* posY, s16* posZ, int16_t* rotX, + int16_t* rotY, int16_t* rotZ, int16_t* params) { - // Freezard - Child Link can only kill this with jump slash Deku Sticks or other equipment like bombs. - // Beamos - Needs bombs. - // Anubis - Needs fire. - // Shell Blade & Spike - Child Link can't kill these with sword or Deku Stick. - // 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. - // 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 || enemy.id == ACTOR_EN_NY || - enemy.id == ACTOR_EN_CLEAR_TAG || enemy.id == ACTOR_EN_WALLMAS || enemy.id == ACTOR_EN_TORCH2 || - (enemy.id == ACTOR_EN_MB && enemy.params == 0) || enemy.id == ACTOR_EN_FD || enemy.id == ACTOR_EN_ANUBICE_TAG; + uint32_t isMQ = ResourceMgr_IsSceneMasterQuest(play->sceneNum); - // Bari - Spawns 3 more enemies, potentially extremely difficult in timed rooms. - bool enemiesToExcludeTimedRooms = enemiesToExcludeClearRooms || enemy.id == ACTOR_EN_VALI; + // Hack to remove enemies that wrongfully spawn because of bypassing object dependency with enemy randomizer on. + // This should probably be handled on OTR generation in the future when object dependency is fully removed. + // Remove bats and Skulltulas from graveyard. + // Remove Octorok in Lost Woods. + if (((*actorId == ACTOR_EN_FIREFLY || (*actorId == ACTOR_EN_SW && *params == 0)) && + play->sceneNum == SCENE_GRAVEYARD) || + (*actorId == ACTOR_EN_OKUTA && play->sceneNum == SCENE_LOST_WOODS)) { + return 0; + } - switch (sceneNum) { - // Deku Tree - case SCENE_DEKU_TREE: - return (!(!isMQ && enemiesToExcludeClearRooms && (roomNum == 1 || roomNum == 9)) && - !(isMQ && enemiesToExcludeClearRooms && - (roomNum == 4 || roomNum == 6 || roomNum == 9 || roomNum == 10))); - // Dodongo's Cavern - case SCENE_DODONGOS_CAVERN: - return (!(!isMQ && enemiesToExcludeClearRooms && roomNum == 15) && - !(isMQ && enemiesToExcludeClearRooms && (roomNum == 5 || roomNum == 13 || roomNum == 14))); - // Jabu Jabu - case SCENE_JABU_JABU: - return (!(!isMQ && enemiesToExcludeClearRooms && (roomNum == 8 || roomNum == 9)) && - !(!isMQ && enemiesToExcludeTimedRooms && roomNum == 12) && - !(isMQ && enemiesToExcludeClearRooms && (roomNum == 11 || roomNum == 14))); - // Forest Temple - case SCENE_FOREST_TEMPLE: - return (!(!isMQ && enemiesToExcludeClearRooms && - (roomNum == 6 || roomNum == 10 || roomNum == 18 || roomNum == 21)) && - !(isMQ && enemiesToExcludeClearRooms && - (roomNum == 5 || roomNum == 6 || roomNum == 18 || roomNum == 21))); - // Fire Temple - case SCENE_FIRE_TEMPLE: - return (!(!isMQ && enemiesToExcludeClearRooms && roomNum == 15) && - !(isMQ && enemiesToExcludeClearRooms && (roomNum == 15 || roomNum == 17 || roomNum == 18))); - // Water Temple - case SCENE_WATER_TEMPLE: - return (!(!isMQ && enemiesToExcludeClearRooms && (roomNum == 13 || roomNum == 18 || roomNum == 19)) && - !(isMQ && enemiesToExcludeClearRooms && (roomNum == 13 || roomNum == 18))); - // Spirit Temple - case SCENE_SPIRIT_TEMPLE: - return (!(!isMQ && enemiesToExcludeClearRooms && - (roomNum == 1 || roomNum == 10 || roomNum == 17 || roomNum == 20)) && - !(isMQ && enemiesToExcludeClearRooms && - (roomNum == 1 || roomNum == 2 || roomNum == 4 || roomNum == 10 || roomNum == 15 || - roomNum == 19 || roomNum == 20))); - // Shadow Temple - case SCENE_SHADOW_TEMPLE: - return ( - !(!isMQ && enemiesToExcludeClearRooms && - (roomNum == 1 || roomNum == 7 || roomNum == 11 || roomNum == 14 || roomNum == 16 || roomNum == 17 || - roomNum == 19 || roomNum == 20)) && - !(isMQ && enemiesToExcludeClearRooms && - (roomNum == 1 || roomNum == 6 || roomNum == 7 || roomNum == 11 || roomNum == 14 || roomNum == 20))); - // Ganon's Castle Trials - case SCENE_INSIDE_GANONS_CASTLE: - return (!(!isMQ && enemiesToExcludeClearRooms && (roomNum == 2 || roomNum == 5 || roomNum == 9)) && - !(isMQ && enemiesToExcludeClearRooms && - (roomNum == 0 || roomNum == 2 || roomNum == 5 || roomNum == 9))); - // Ice Caverns - case SCENE_ICE_CAVERN: - return (!(!isMQ && enemiesToExcludeClearRooms && (roomNum == 1 || roomNum == 7)) && - !(isMQ && enemiesToExcludeClearRooms && (roomNum == 3 || roomNum == 7))); - // Bottom of the Well - // Exclude Dark Link from room with holes in the floor because it can pull you in a like-like making the player - // fall down. - case SCENE_BOTTOM_OF_THE_WELL: - return (!(!isMQ && enemy.id == ACTOR_EN_TORCH2 && roomNum == 3)); - // Don't allow Dark Link in areas with lava void out zones as it voids out the player as well. - // Gerudo Training Ground. - case SCENE_GERUDO_TRAINING_GROUND: - return (!(enemy.id == ACTOR_EN_TORCH2 && roomNum == 6) && - !(!isMQ && enemiesToExcludeTimedRooms && (roomNum == 1 || roomNum == 7)) && - !(!isMQ && enemiesToExcludeClearRooms && (roomNum == 3 || roomNum == 5 || roomNum == 10)) && - !(isMQ && enemiesToExcludeTimedRooms && - (roomNum == 1 || roomNum == 3 || roomNum == 5 || roomNum == 7)) && - !(isMQ && enemiesToExcludeClearRooms && roomNum == 10)); - // Don't allow certain enemies in Ganon's Tower because they would spawn up on the ceiling, - // becoming impossible to kill. - // Ganon's Tower. - case SCENE_GANONS_TOWER: - return (!(enemiesToExcludeClearRooms || enemy.id == ACTOR_EN_VALI || - (enemy.id == ACTOR_EN_ZF && enemy.params == -1))); - // Ganon's Tower Escape. - case SCENE_GANONS_TOWER_COLLAPSE_INTERIOR: - return (!((enemiesToExcludeTimedRooms || (enemy.id == ACTOR_EN_ZF && enemy.params == -1)) && roomNum == 1)); - // Don't allow big Stalchildren, big Peahats and the large Bari (jellyfish) during the Gohma fight because they - // can clip into Gohma and it crashes the game. Likely because Gohma on the ceiling can't handle collision with - // other enemies. - case SCENE_DEKU_TREE_BOSS: - return (!enemiesToExcludeTimedRooms && !(enemy.id == ACTOR_EN_SKB && enemy.params == 20) && - !(enemy.id == ACTOR_EN_PEEHAT && enemy.params == -1)); - // Grottos. - case SCENE_GROTTOS: - return (!(enemiesToExcludeClearRooms && (roomNum == 2 || roomNum == 7))); - // Royal Grave. - case SCENE_ROYAL_FAMILYS_TOMB: - return (!(enemiesToExcludeClearRooms && roomNum == 0)); - // Don't allow Dark Link in areas with lava void out zones as it voids out the player as well. - // Death Mountain Crater. - case SCENE_DEATH_MOUNTAIN_CRATER: - return (enemy.id != ACTOR_EN_TORCH2); - default: - return 1; + // Hack to change a pot in Spirit Temple that holds a Deku Shield to not hold anything. + // This should probably be handled on OTR generation in the future when object dependency is fully removed. + // This Deku Shield doesn't normally spawn in authentic gameplay because of object dependency. + if (*actorId == ACTOR_OBJ_TSUBO && *params == 24597) { + *params = 24067; + } + + // Lengthen timer in non-MQ Jabu Jabu bubble room. + if (!isMQ && *actorId == ACTOR_OBJ_ROOMTIMER && *params == 30760 && play->sceneNum == SCENE_JABU_JABU && + play->roomCtx.curRoom.num == 12) { + *params = (*params & ~0x3FF) | 120; + } + + if (IsEnemyFoundToRandomize(play->sceneNum, play->roomCtx.curRoom.num, *actorId, *params, *posX)) { + + // When replacing Iron Knuckles in Spirit Temple, move them away from the throne because + // some enemies can get stuck on the throne. + if (*actorId == ACTOR_EN_IK && play->sceneNum == SCENE_SPIRIT_TEMPLE) { + if (*params == 6657) { + *posX = *posX + 150; + } else if (*params == 6401) { + *posX = *posX - 150; + } + } + + // Move like-likes in MQ Jabu Jabu down into the room as they otherwise get stuck on Song of Time blocks. + if (*actorId == ACTOR_EN_RR && play->sceneNum == SCENE_JABU_JABU && play->roomCtx.curRoom.num == 11) { + if (*posX == 1003) { + *posX = *posX - 75; + } else { + *posX = *posX + 75; + } + *posY = *posY - 200; + } + + // Do a raycast from the original position of the actor to find the ground below it, then try to place + // the new actor on the ground. This way enemies don't spawn very high in the sky, and gives us control + // over height offsets per enemy from a proven grounded position. + CollisionPoly poly; + Vec3f pos; + f32 raycastResult; + + pos.x = *posX; + pos.y = *posY + 50; + pos.z = *posZ; + raycastResult = BgCheck_AnyRaycastFloor1(&play->colCtx, &poly, &pos); + + // If ground is found below actor, move actor to that height. + if (raycastResult > BGCHECK_Y_MIN) { + *posY = raycastResult; + } + + // Get randomized enemy ID and parameter. + uint32_t seed = + play->sceneNum + *actorId + (int)*posX + (int)*posY + (int)*posZ + *rotX + *rotY + *rotZ + *params; + EnemyEntry randomEnemy = GetRandomizedEnemyEntry(seed, play); + + *actorId = randomEnemy.id; + *params = randomEnemy.params; + + // Straighten out enemies so they aren't flipped on their sides when the original spawn is. + *rotX = 0; + + switch (*actorId) { + // When spawning big jellyfish, spawn it up high. + case ACTOR_EN_VALI: + *posY = *posY + 300; + break; + // Spawn Peahat off the ground, otherwise it kills itself by colliding with the ground. + case ACTOR_EN_PEEHAT: + if (*params == 1) { + *posY = *posY + 100; + } + break; + // Spawn Skulltulas off the ground. + case ACTOR_EN_ST: + *posY = *posY + 200; + break; + // Spawn flying enemies off the ground. + case ACTOR_EN_FIREFLY: + case ACTOR_EN_BILI: + case ACTOR_EN_BB: + case ACTOR_EN_CLEAR_TAG: + case ACTOR_EN_CROW: + *posY = *posY + 75; + break; + default: + break; + } } + + // Enemy finished randomization process. + return 1; } void FixClubMoblinScale(void* ptr) { @@ -1047,4 +914,66 @@ void RegisterEnemyRandomizer() { }); } +static const std::map<int32_t, const char*> enemyRandomizerModes = { + { ENEMY_RANDOMIZER_OFF, "Disabled" }, + { ENEMY_RANDOMIZER_RANDOM, "Random" }, + { ENEMY_RANDOMIZER_RANDOM_SEEDED, "Random (Seeded)" }, +}; + +void RegisterEnemyRandomizerWidgets() { + WidgetPath path = { "Enhancements", "Extra Modes", SECTION_COLUMN_2 }; + + SohGui::mSohMenu->AddWidget(path, "Enemy Randomizer", WIDGET_CVAR_COMBOBOX) + .CVar(CVAR_ENHANCEMENT("RandomizedEnemies")) + .Callback([](WidgetInfo& info) { GetSelectedEnemies(); }) + .Options( + UIWidgets::ComboboxOptions() + .DefaultIndex(ENEMY_RANDOMIZER_OFF) + .ComboMap(enemyRandomizerModes) + .Tooltip("Replaces fixed enemies throughout the game with a random enemy. Bosses, Mini-Bosses and a " + "few specific regular enemies are excluded.\n" + "Enemies that need more than Deku Nuts & either Deku Sticks or a sword to kill are excluded " + "from spawning in \"clear enemy\" rooms.\n\n" + "- Random: Enemies are randomized every time you load a room.\n" + "- Random (Seeded): Enemies are randomized based on the current randomizer seed/file.\n")); + + SohGui::mSohMenu->AddWidget(path, "Randomized Enemy Sizes", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_ENHANCEMENT("RandomizedEnemySizes")) + .Options(UIWidgets::CheckboxOptions().Tooltip("Enemies and Bosses spawn with random sizes.")); + + SohGui::mSohMenu->AddWidget(path, "Scale Health with Size", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_ENHANCEMENT("EnemySizeScalesHealth")) + .PreFunc([](WidgetInfo& info) { + info.options->disabled = !CVarGetInteger(CVAR_ENHANCEMENT("RandomizedEnemySizes"), 0); + }) + .Options(UIWidgets::CheckboxOptions().Tooltip("Scales normal enemies Health with their randomized size.\n" + "*This will NOT affect Bosses!*")); + + SohGui::mSohMenu->AddWidget(path, "Enemy List", WIDGET_SEPARATOR_TEXT).PreFunc([](WidgetInfo& info) { + info.isHidden = !CVarGetInteger(CVAR_ENHANCEMENT("RandomizedEnemies"), 0); + }); + + SohGui::mSohMenu->AddWidget(path, "Select all Enemies", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_ENHANCEMENT("RandomizedEnemyList.All")) + .PreFunc([](WidgetInfo& info) { info.isHidden = !CVarGetInteger(CVAR_ENHANCEMENT("RandomizedEnemies"), 0); }) + .Callback([](WidgetInfo& info) { GetSelectedEnemies(); }); + + SohGui::mSohMenu->AddWidget(path, "Enemy List", WIDGET_SEPARATOR).PreFunc([](WidgetInfo& info) { + info.isHidden = !CVarGetInteger(CVAR_ENHANCEMENT("RandomizedEnemies"), 0); + }); + + for (int i = 0; i < ARRAY_COUNT(randomizedEnemySpawnTable); i++) { + SohGui::mSohMenu->AddWidget(path, randomizedEnemySpawnTable[i].name, WIDGET_CVAR_CHECKBOX) + .CVar(randomizedEnemySpawnTable[i].cvar) + .Options(UIWidgets::CheckboxOptions().DefaultValue(true)) + .PreFunc([](WidgetInfo& info) { + info.isHidden = !CVarGetInteger(CVAR_ENHANCEMENT("RandomizedEnemies"), 0); + info.options->disabled = CVarGetInteger(CVAR_ENHANCEMENT("RandomizedEnemyList.All"), 0); + info.options->disabledTooltip = "These options are disabled because \"Select All Enemies\" is enabled."; + }) + .Callback([](WidgetInfo& info) { GetSelectedEnemies(); }); + } +} + static RegisterShipInitFunc initFunc(RegisterEnemyRandomizer, { CVAR_ENEMY_RANDOMIZER_NAME }); +static RegisterMenuInitFunc menuInitFunc(RegisterEnemyRandomizerWidgets); diff --git a/soh/soh/Enhancements/enemyrandomizer.h b/soh/soh/Enhancements/enemyrandomizer.h deleted file mode 100644 index 6e1708fbc..000000000 --- a/soh/soh/Enhancements/enemyrandomizer.h +++ /dev/null @@ -1,10 +0,0 @@ -#pragma once - -#include <libultraship/libultra/types.h> -#include "item-tables/ItemTableTypes.h" - -#define RANDOMIZED_ENEMY_SPAWN_TABLE_SIZE 59 - -extern const char* enemyCVarList[]; -extern const char* enemyNameList[]; -extern void GetSelectedEnemies(); diff --git a/soh/soh/SohGui/SohMenuEnhancements.cpp b/soh/soh/SohGui/SohMenuEnhancements.cpp index aab8c4d04..8e9a9059b 100644 --- a/soh/soh/SohGui/SohMenuEnhancements.cpp +++ b/soh/soh/SohGui/SohMenuEnhancements.cpp @@ -4,7 +4,6 @@ #include <soh/Enhancements/game-interactor/GameInteractor.h> #include <soh/OTRGlobals.h> #include <soh/Enhancements/cosmetics/authenticGfxPatches.h> -#include <soh/Enhancements/enemyrandomizer.h> #include <soh/Enhancements/TimeDisplay/TimeDisplay.h> extern "C" { @@ -136,12 +135,6 @@ static const std::map<int32_t, const char*> mirroredWorldModes = { { MIRRORED_WORLD_DUNGEONS_RANDOM_SEEDED, "Dungeons Random (Seeded)" }, }; -static const std::map<int32_t, const char*> enemyRandomizerModes = { - { ENEMY_RANDOMIZER_OFF, "Disabled" }, - { ENEMY_RANDOMIZER_RANDOM, "Random" }, - { ENEMY_RANDOMIZER_RANDOM_SEEDED, "Random (Seeded)" }, -}; - void SohMenu::AddMenuEnhancements() { // Add Enhancements Menu AddMenuEntry("Enhancements", CVAR_SETTING("Menu.EnhancementsSidebarSection")); @@ -1684,52 +1677,6 @@ void SohMenu::AddMenuEnhancements() { .PreFunc( [](WidgetInfo& info) { info.isHidden = CVarGetInteger(CVAR_ENHANCEMENT("ExtraTraps.Enabled"), 0) == 0; }); - path.column = SECTION_COLUMN_2; - AddWidget(path, "Enemy Randomizer", WIDGET_CVAR_COMBOBOX) - .CVar(CVAR_ENHANCEMENT("RandomizedEnemies")) - .Callback([](WidgetInfo& info) { GetSelectedEnemies(); }) - .Options( - ComboboxOptions() - .DefaultIndex(ENEMY_RANDOMIZER_OFF) - .ComboMap(enemyRandomizerModes) - .Tooltip("Replaces fixed enemies throughout the game with a random enemy. Bosses, Mini-Bosses and a " - "few specific regular enemies are excluded.\n" - "Enemies that need more than Deku Nuts & either Deku Sticks or a sword to kill are excluded " - "from spawning in \"clear enemy\" rooms.\n\n" - "- Random: Enemies are randomized every time you load a room.\n" - "- Random (Seeded): Enemies are randomized based on the current randomizer seed/file.")); - AddWidget(path, "Randomized Enemy Sizes", WIDGET_CVAR_CHECKBOX) - .CVar(CVAR_ENHANCEMENT("RandomizedEnemySizes")) - .Options(CheckboxOptions().Tooltip("Enemies and Bosses spawn with random sizes.")); - AddWidget(path, "Scale Health with Size", WIDGET_CVAR_CHECKBOX) - .CVar(CVAR_ENHANCEMENT("EnemySizeScalesHealth")) - .PreFunc([](WidgetInfo& info) { - info.options->disabled = !CVarGetInteger(CVAR_ENHANCEMENT("RandomizedEnemySizes"), 0); - }) - .Options(CheckboxOptions().Tooltip( - "Scales normal enemies Health with their randomized size. *This will NOT affect Bosses!*")); - AddWidget(path, "Enemy List", WIDGET_SEPARATOR_TEXT).PreFunc([](WidgetInfo& info) { - info.isHidden = !CVarGetInteger(CVAR_ENHANCEMENT("RandomizedEnemies"), 0); - }); - AddWidget(path, "Select all Enemies", WIDGET_CVAR_CHECKBOX) - .CVar(CVAR_ENHANCEMENT("RandomizedEnemyList.All")) - .PreFunc([](WidgetInfo& info) { info.isHidden = !CVarGetInteger(CVAR_ENHANCEMENT("RandomizedEnemies"), 0); }) - .Callback([](WidgetInfo& info) { GetSelectedEnemies(); }); - AddWidget(path, "Enemy List", WIDGET_SEPARATOR).PreFunc([](WidgetInfo& info) { - info.isHidden = !CVarGetInteger(CVAR_ENHANCEMENT("RandomizedEnemies"), 0); - }); - for (int i = 0; i < RANDOMIZED_ENEMY_SPAWN_TABLE_SIZE; i++) { - AddWidget(path, enemyNameList[i], WIDGET_CVAR_CHECKBOX) - .CVar(enemyCVarList[i]) - .Options(CheckboxOptions().DefaultValue(true)) - .PreFunc([](WidgetInfo& info) { - info.isHidden = !CVarGetInteger(CVAR_ENHANCEMENT("RandomizedEnemies"), 0); - info.options->disabled = CVarGetInteger(CVAR_ENHANCEMENT("RandomizedEnemyList.All"), 0); - info.options->disabledTooltip = "These options are disabled because \"Select All Enemies\" is enabled."; - }) - .Callback([](WidgetInfo& info) { GetSelectedEnemies(); }); - } - // Cheats path.sidebarName = "Cheats"; AddSidebarEntry("Enhancements", path.sidebarName, 3); diff --git a/soh/src/code/z_actor.c b/soh/src/code/z_actor.c index 1fe489589..e999788a4 100644 --- a/soh/src/code/z_actor.c +++ b/soh/src/code/z_actor.c @@ -11,7 +11,6 @@ #include "soh/ObjectExtension/ActorListIndex.h" #include "soh/frame_interpolation.h" #include "soh/Enhancements/cosmetics/cosmeticsTypes.h" -#include "soh/Enhancements/enemyrandomizer.h" #include "soh/Enhancements/game-interactor/GameInteractor.h" #include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h" #include "soh/Enhancements/nametag.h" |
