mirror of
https://github.com/HarbourMasters/Shipwright.git
synced 2024-12-02 06:12:19 -05:00
2698d453bb
* Initial StaticData and RandoItem class definitions * Initial implementation of RandoItem class. * Rerranges RandoItem Constructor parameters The parameters were rearranged for easy copy-paste from the GET_ITEM macro calls later on. * Switches static data from map to static array. Array is all that is needed, since the item list will be contiguous and indexed by the RandomizerGet Enum values. * Defines part of the randomizer item list. * Adds more item class instances to item_list.cpp Up through bottles, many more items still to go. * Adds song items * Adds Maps and Compasses to the item_list * Added Key Items to item_list * Added Key Rings to item_list * Added Dungeon Rewards to item_list * Adds generic items and refills to item_list * Adds shop items, triforce, and hints Also added constructors that put the GET_ITEM_NONE data in for these items, since there is no corresponding GetItemEntry for them. * Adds in the stages of progressive items These are present for GetItemEntry purposes, and aren't really meant to be used in seed generation (unless we find a need for it later on.) * Remove GetItemEntry data from progressive items * Moves/adds function definitions to item/item_list * Refactors GetItemEntry data It's now a pointer to memory instantiated on the heap in the constructor. These are shared pointers so the memory is freed if any of the item instances get deconstructed (which they shouldn't but just in case.) * Adds item class member for if item is progressive * Removes unneeded stuff from initializer list macro * Replaces relevant `uint32_t`s w/ `RandomizerGet`s Also replaces calls to the ItemTable method with StaticData::RetrieveItem * Switches our runtime code to use the new itemList. * Changes just enough hint gen code to compile * Initial Definition of Location Class * Initial Implementation of Location * Fixes some names and definitions. * Extracts ActorID and SceneID enums to separate files. This allows importing them without causing weird conflicts in cpp files when importing the z64scene.h and z64actor.h files directly. Now you can just import z64scene_enum.h and z64actor_enum.h instead. The two old files also import these new files so that existing setups still work as expected. * Replaces the forward definitions with the new imports. * Adds missing data for RandomizerCheckObjects. * Definition and first entry of locationTable * Added Mido's House Locations * Added locations up through lost woods. * Adds in Hyrule Field locations. * Adds Lake Hylia locations * Adds location name comments * Adds Gerudo Valley Locations * Adds Gerudo Fortress locations. * Adds the Wasteland and Collosus locations * Adds Market locations * Adds Hyrule Castle locations. * Adds Kakariko and Graveyard locations. * Adds Death Mountain checks. * Adds Goron City locations * Adds Death Mountain Crater locations * Adds Zora's River locations * Added Zora's Domain Locations. * Added Zora's Fountain locations * Adds Lon Lon Ranch locations. * Adds Deku Tree locations * Adds Dodongo's Cavern Locations. * Adds Jabu Jabu's Belly Locations * Adds Forest Temple Locations * Adds Fire Temple Locations * Adds Water Temple Locations * Added Spirit Temple Locations * Some of shadow temple locations. * Adds remaining Shadow Temple locations * Fixes a leftover merge conflict * Adds Bottom of the Well locations. * Adds Ice Cavern locations * Adds GTG locations. * Adds Ganon's Castle and Tower locations * Adds dungeon Gold Skulltula locations. * Adds Overworld Gold Skulltula locations * Adds dungeon reward locations * Adds Heart Container Locations * Adds Cutscene and Song locations * Adds Cow locations * Adds Shop locations. * Adds hint locations * Adds function for retrieving the Location data. * Initial definition of ItemLocation structure for tracking runtime data * First push on converting code to use new location definitions * Changes hints to use the new tables * Further conversion of hints to new definitions. * Adds new Hint and Location IDs to area tables * Moves areaTable to use new RandomizerRegion keys * Removal of 3drando/item_location files. * Uses new RandomizerRegion keys in entrance.cpp * Final push for removal of massive keys.hpp enum * Uses new SceneID Enum Values * Remove RandomizerCheckObject structs in favor of new location list. * Fix a few stragglers to successfully build * Rename of RandoItem to just Item, but in the Rando namespace * Adds static hints (Light Arrows, Altar text, etc) to the new Hint table. * More hint related fixes/edits * small fix for #include path * Fix various miscellaneous issues related to seed gen and spoiler parsing. * Handle progressive items correctly. * Fixes some hint generation logic * Fix a few GetItemEntry niche bugs. * Adds missing shop GI Entries * Formatting fixes * small formatting fix * Namespace StaticData under Rando * Added a note about a potential use-after-free. I confirmed the actual pointer in question isn't currently being used, but I added the note as a reminder to fix it later and/or as a warning to anyone who changes how the return value is used. * Fixes missing location table entries * Fixes LUS submodule and removes now-unused code * Resolves weird duplicate definition issue * Fix missing include It was missed because not being included wasn't an issue on Windows. * Fixes error present on Linux builds * Fixes some issues with excluding locations * Updates the Resolve Exclusion conflicts function not sure if actually used, will look into that more later * Removes some duplicate RGs * More fixes of duplicate RG values. * Fix a few duplication issues in the check tracker. * Fix progressive bombchus. * Minor typo fix, shouldn't really be affecting anything though * Should fix some of the remaining check tracker issues. * oops wrong boolean operator * Fix skulltulas in the check tracker. * oops, missing comma * re-formatting of HintStone locations * Fixes issue when picking up second Progressive Bullet Bag * Hide bombchu bowling bombchus * Fixes missed skullScene in location_list * Reformats shop items * Re-formats cow checks * reformat song locations * reformat "cutscene" checks * reformat heart container locaitons * reformat Boss/Dungeon reward checks * Hide Triforce Completed if not playing Triforce Hunt * Fixes incorrect chest param * reformat GS Tokens locations and cuts down on duplicate data * reformat Ganons Castle checks * reformat GTG check locations * Prevents Gift from Raoru from appearing in the check tracker * more reformatting (botw, ice cavern, shadow temple) * Should fix a couple more check tracker checks * reformat spirit temple checks * reformat water and fire temple checks * fix RC_ZR_GS_ABOVE_BRIDGE flag typo * reformat Forest Temple checks * Fix RC_LW_TRADE_ODD_POTION in check tracker * reformat child dungeon locations * reformat overworld locations * reformat item entries * New Item Override system * use new ItemOverrides and use ItemLocations table for getting items * Saves/Loads directly from/to new ItemLocation table with overrides for traps * Removes gSaveContext.itemLocations * Don't load spoiler file on boot automatically. Currently this means the old spoiler will have to be manually dropped or a new one generated in order to make a new rando file. Next I want to make it so that: 1. The Randomizer Quest button on the file select menu is always unlocked, even if a spoiler is not loaded. 2. If it's selected and a spoiler is not loaded, a menu will appear that asks if you want to generate a new seed or re-generate the previous one (if a spoiler file is present). 3. On choosing to generate a new one, you may also get an in-game menu to quickly apply a preset before generating (not sure if I'm going that far just yet). 4. After that, a seed is generated and you are taken back to the file select screen with the new file present. 5. If a seed is generated via the menu ahead of time, the CVar for loading the spoiler file will be set, but the spoiler file will not be parsed. All the data needed to actually play the randomizer at that point is already in memory and in the save file (or at least I'll make it so that it is if it isn't already). 6. If a spoiler file is dropped over the window, the spoiler file is loaded, but it will only be parsed to get the seed and the settings, then the playthrough will be generated from scratch with that data. Thus allowing for hints to be in the user's language of choice no matter what language the spoiler file was generated in. 7. Additionally, there will be a plandomizer mode that, if enabled, causes dragging and dropping a spoiler file to read the entire spoiler file instead of just the seed, and making a new file will use the data from there instead of generating a new seed. This in particular may be expanded to have a "plando file" that contains more info than a spoiler file would normally have, such as cosmetic data and a more fleshed out custom message syntax for various types of custom hints and whatnot. But that will be probably much later. * Auto-gen rando seed when making a new rando file. Also adds new logic for displaying the seed hash icons. Now, it is displayed in the following situations: 1. On the confirmation page when loading a rando save, the hash icons for *that save* are displayed. 2. On the name select screen after generating a seed, the hash icons for the seed that was just generated will be shown. 3. If you have dragged a spoiler log onto the window, the hash icons for that seed will be displayed while randomizer is selected on the quest select screen. Currently the spoiler is just ignored, as the logic for pulling the settings from the spoiler file and regenerating the same seed has not been coded yet. * Fix a few typos/bugs * Partial conversion to new Settings/Option class * Further conversion to new settings/options classes * New settings struct (not fully working, need to wire it up to SaveManager) * Move save files to new settings struct. Also fixes MQ options to match 3drando * Fixes some spoilerfile related issues * Cleans up now unused arrays * Fixes some unhandled entries in parse settings switch case * Reimplements parsing of settings on file drop to re-generate seeds * Move merchantPrices into ItemLocation tables. * Move hints to new struct * Fixes a few seed gen bugs surrounding hints * Fix treasure chest game. * Relocate Entrance Shuffle code into ctx * Move entrances to new context at runtime * Remove now unused code from SaveContext and randomizer.cpp/.h * Fix non-windows builds? * Moves Dungeon Quests to new context * Move trials into new context * Whoops, forgot to construct the Trials in the context. * Fixes accidental nullptr reference * Fixes bug with saving MQ dungeons * Implements plando mode and removes now unused code. Largely untested, expect some bugfixes. * prevent a multiple definition bug * another attempt to fix the gSeedTextures multiple def error * Fixes some minor hint issues from conflict resolution * Some additional glue needed for merge * Fixes another couple of miscellaneous issues/inconsistencies. * A few french corrections * Makes CVar gRandomizeWarpSongText match the checkbox default value.
412 lines
22 KiB
C++
412 lines
22 KiB
C++
#include "enemyrandomizer.h"
|
|
#include "functions.h"
|
|
#include "macros.h"
|
|
#include "soh/Enhancements/randomizer/3drando/random.hpp"
|
|
#include "soh/Enhancements/randomizer/context.h"
|
|
#include "soh/Enhancements/enhancementTypes.h"
|
|
#include "variables.h"
|
|
|
|
extern "C" {
|
|
#include <z64.h>
|
|
}
|
|
|
|
extern "C" uint32_t ResourceMgr_IsSceneMasterQuest(s16 sceneNum);
|
|
|
|
static EnemyEntry randomizedEnemySpawnTable[RANDOMIZED_ENEMY_SPAWN_TABLE_SIZE] = {
|
|
{ ACTOR_EN_FIREFLY, 2 }, // Regular Keese
|
|
{ ACTOR_EN_FIREFLY, 1 }, // Fire Keese
|
|
{ ACTOR_EN_FIREFLY, 4 }, // Ice Keese
|
|
{ ACTOR_EN_TEST, 2 }, // Stalfos
|
|
{ ACTOR_EN_TITE, -1 }, // Tektite (red)
|
|
{ ACTOR_EN_TITE, -2 }, // Tektite (blue)
|
|
{ ACTOR_EN_WALLMAS, 1 }, // Wallmaster
|
|
{ ACTOR_EN_DODONGO, -1 }, // Dodongo
|
|
{ ACTOR_EN_PEEHAT, -1 }, // Flying Peahat (big grounded, doesn't spawn larva)
|
|
{ ACTOR_EN_PEEHAT, 1 }, // Flying Peahat Larva
|
|
{ ACTOR_EN_ZF, -1 }, // Lizalfos
|
|
{ ACTOR_EN_ZF, -2 }, // Dinolfos
|
|
{ ACTOR_EN_GOMA, 7 }, // Gohma larva (non-gohma rooms)
|
|
{ ACTOR_EN_BUBBLE, 0 }, // Shabom (bubble)
|
|
{ ACTOR_EN_DODOJR, 0 }, // Baby Dodongo
|
|
{ ACTOR_EN_TORCH2, 0 }, // Dark Link
|
|
{ ACTOR_EN_BILI, 0 }, // Biri (jellyfish)
|
|
{ ACTOR_EN_TP, -1 }, // Electric Tailparasan
|
|
{ ACTOR_EN_ST, 0 }, // Skulltula (normal)
|
|
{ ACTOR_EN_ST, 1 }, // Skulltula (big)
|
|
{ ACTOR_EN_ST, 2 }, // Skulltula (invisible)
|
|
{ ACTOR_EN_BW, 0 }, // Torch Slug
|
|
{ ACTOR_EN_EIYER, 10 }, // Stinger (land) (One in formation, sink under floor and do not activate)
|
|
{ ACTOR_EN_MB, 0 }, // Moblins (Club)
|
|
{ ACTOR_EN_DEKUBABA, 0 }, // Deku Baba (small)
|
|
{ ACTOR_EN_DEKUBABA, 1 }, // Deku Baba (large)
|
|
{ ACTOR_EN_AM, -1 }, // Armos (enemy variant)
|
|
{ ACTOR_EN_DEKUNUTS, 768 }, // Mad Scrub (triple attack) (projectiles don't work)
|
|
{ ACTOR_EN_VALI, -1 }, // Bari (big jellyfish)
|
|
{ ACTOR_EN_BB, -1 }, // Bubble (flying skull enemy) (blue)
|
|
{ ACTOR_EN_YUKABYUN, 0 }, // Flying Floor Tile
|
|
{ ACTOR_EN_VM, 1280 }, // Beamos
|
|
{ ACTOR_EN_FLOORMAS, 0 }, // Floormaster
|
|
{ ACTOR_EN_RD, 1 }, // Redead (standing)
|
|
{ ACTOR_EN_RD, 32766 }, // Gibdo (standing)
|
|
{ ACTOR_EN_SB, 0 }, // Shell Blade
|
|
{ ACTOR_EN_KAREBABA, 0 }, // Withered Deku Baba
|
|
{ ACTOR_EN_RR, 0 }, // Like-Like
|
|
{ ACTOR_EN_NY, 0 }, // Spike (rolling enemy)
|
|
{ ACTOR_EN_IK, 2 }, // Iron Knuckle (black, standing)
|
|
{ ACTOR_EN_IK, 3 }, // Iron Knuckle (white, standing)
|
|
{ ACTOR_EN_TUBO_TRAP, 0 }, // Flying pot
|
|
{ ACTOR_EN_FZ, 0 }, // Freezard
|
|
{ ACTOR_EN_CLEAR_TAG, 1 }, // Arwing
|
|
{ ACTOR_EN_WF, 0 }, // Wolfos (normal)
|
|
{ ACTOR_EN_WF, 1 }, // Wolfos (white)
|
|
{ ACTOR_EN_SKB, 1 }, // Stalchild (small)
|
|
{ ACTOR_EN_SKB, 20 }, // Stalchild (big)
|
|
{ ACTOR_EN_CROW, 0 } // Guay
|
|
|
|
// Doesn't work {ACTOR_EN_POH, 0}, // Poe (Seems to rely on other objects?)
|
|
// Doesn't work {ACTOR_EN_POH, 2}, // Poe (composer Sharp) (Seems to rely on other objects?)
|
|
// Doesn't work {ACTOR_EN_POH, 3}, // Poe (composer Flat) (Seems to rely on other objects?)
|
|
// Doesn't work {ACTOR_EN_OKUTA, 0}, // Octorok (actor directly uses water box collision to handle hiding/popping up)
|
|
// Doesn't work {ACTOR_EN_REEBA, 0}, // Leever (reliant on surface and also normally used in tandem with a leever spawner, kills itself too quickly otherwise)
|
|
// Kinda doesn't work { ACTOR_EN_FD, 0 }, // Flare Dancer (jumps out of bounds a lot, and possible cause of crashes because of spawning a ton of flame actors)
|
|
};
|
|
|
|
static int enemiesToRandomize[] = {
|
|
ACTOR_EN_FIREFLY, // Keese (including fire/ice)
|
|
ACTOR_EN_TEST, // Stalfos
|
|
ACTOR_EN_TITE, // Tektite
|
|
ACTOR_EN_POH, // Poe (normal, blue rupee, composers
|
|
ACTOR_EN_OKUTA, // Octorok
|
|
ACTOR_EN_WALLMAS, // Wallmaster
|
|
ACTOR_EN_DODONGO, // Dodongo
|
|
// ACTOR_EN_REEBA, // Leever (reliant on spawner (z_e_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 Tailparasan
|
|
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 (can be randomized, but not randomized to, so keeping it in vanilla locations means it atleast shows up in the game
|
|
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
|
|
};
|
|
|
|
extern "C" uint8_t GetRandomizedEnemy(PlayState* play, int16_t *actorId, f32 *posX, f32 *posY, f32 *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 = 92280;
|
|
}
|
|
|
|
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);
|
|
|
|
int8_t timesRandomized = 1;
|
|
|
|
// While randomized enemy isn't allowed in certain situations, randomize again.
|
|
while (!IsEnemyAllowedToSpawn(play->sceneNum, play->roomCtx.curRoom.num, randomEnemy)) {
|
|
randomEnemy = GetRandomizedEnemyEntry(seed + timesRandomized);
|
|
timesRandomized++;
|
|
}
|
|
|
|
*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;
|
|
}
|
|
|
|
EnemyEntry GetRandomizedEnemyEntry(uint32_t seed) {
|
|
if (CVarGetInteger("gRandomizedEnemies", ENEMY_RANDOMIZER_OFF) == ENEMY_RANDOMIZER_RANDOM_SEEDED) {
|
|
uint32_t finalSeed = seed + (IS_RANDO ? Rando::Context::GetInstance()->GetSettings()->GetSeed() : gSaveContext.sohStats.fileCreatedAt);
|
|
Random_Init(finalSeed);
|
|
}
|
|
|
|
uint32_t randomNumber = Random(0, RANDOMIZED_ENEMY_SPAWN_TABLE_SIZE);
|
|
return randomizedEnemySpawnTable[randomNumber];
|
|
}
|
|
|
|
bool IsEnemyFoundToRandomize(int16_t sceneNum, int8_t roomNum, int16_t actorId, int16_t params, float posX) {
|
|
|
|
uint32_t isMQ = ResourceMgr_IsSceneMasterQuest(sceneNum);
|
|
|
|
for (int i = 0; i < ARRAY_COUNT(enemiesToRandomize); i++) {
|
|
|
|
if (actorId == enemiesToRandomize[i]) {
|
|
|
|
switch (actorId) {
|
|
// Only randomize the main component of Electric Tailparasans, not the tail segments they spawn.
|
|
case ACTOR_EN_TP:
|
|
return (params == -1);
|
|
// Only randomize the initial deku scrub actor (single and triple attack), not the flower they spawn.
|
|
case ACTOR_EN_DEKUNUTS:
|
|
return (params == -256 || params == 768);
|
|
// Don't randomize the OoB wallmaster in the silver rupee room because it's only there to
|
|
// not trigger unlocking the door after killing the other wallmaster in authentic gameplay.
|
|
case ACTOR_EN_WALLMAS:
|
|
return (!(!isMQ && sceneNum == SCENE_GERUDO_TRAINING_GROUND && roomNum == 2 && posX == -2345));
|
|
// Only randomize initial floormaster actor (it can split and does some spawning on init).
|
|
case ACTOR_EN_FLOORMAS:
|
|
return (params == 0 || params == -32768);
|
|
// Only randomize the initial eggs, not the enemies that spawn from them.
|
|
case ACTOR_EN_GOMA:
|
|
return (params >= 0 && params <= 9);
|
|
// Only randomize Skullwalltulas, not Golden Skulltulas.
|
|
case ACTOR_EN_SW:
|
|
return (params == 0);
|
|
// Don't randomize Nabooru because it'll break the cutscene and the door.
|
|
// Don't randomize Iron Knuckle in MQ Spirit Trial because it's needed to
|
|
// break the thrones in the room to access a button.
|
|
case ACTOR_EN_IK:
|
|
return (params != 1280 && !(isMQ && sceneNum == SCENE_INSIDE_GANONS_CASTLE && roomNum == 17));
|
|
// Only randomize the intitial spawn of the huge jellyfish. It spawns another copy when hit with a sword.
|
|
case ACTOR_EN_VALI:
|
|
return (params == -1);
|
|
// Don't randomize lizalfos in Doodong's Cavern because the gates won't work correctly otherwise.
|
|
case ACTOR_EN_ZF:
|
|
return (params != 1280 && params != 1281 && params != 1536 && params != 1537);
|
|
// Don't randomize the Wolfos in SFM because it's needed to open the gate.
|
|
case ACTOR_EN_WF:
|
|
return (params != 7936);
|
|
// Don't randomize the Stalfos in Forest Temple because other enemies fall through the hole and don't trigger the platform.
|
|
// Don't randomize the Stalfos spawning on the boat in Shadow Temple, as randomizing them places the new enemies
|
|
// down in the river.
|
|
case ACTOR_EN_TEST:
|
|
return (params != 1 && !(sceneNum == SCENE_SHADOW_TEMPLE && roomNum == 21));
|
|
// Only randomize the enemy variant of Armos Statue.
|
|
// Leave one Armos unrandomized in the Spirit Temple room where an armos is needed to push down a button
|
|
case ACTOR_EN_AM:
|
|
return ((params == -1 || params == 255) && !(sceneNum == SCENE_SPIRIT_TEMPLE && posX == 2141));
|
|
// Don't randomize Shell Blades and Spikes in the underwater portion in Water Temple as it's impossible to kill
|
|
// most other enemies underwater with just hookshot and they're required to be killed for a grate to open.
|
|
case ACTOR_EN_SB:
|
|
case ACTOR_EN_NY:
|
|
return (!(!isMQ && sceneNum == SCENE_WATER_TEMPLE && roomNum == 2));
|
|
default:
|
|
return 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
// If no enemy is found, don't randomize the actor.
|
|
return 0;
|
|
}
|
|
|
|
bool IsEnemyAllowedToSpawn(int16_t sceneNum, int8_t roomNum, EnemyEntry enemy) {
|
|
|
|
uint32_t isMQ = ResourceMgr_IsSceneMasterQuest(sceneNum);
|
|
|
|
// Freezard - Child Link can only kill this with jump slash deku sticks or other equipment like bombs.
|
|
// Beamos - Needs bombs.
|
|
// Shell Blade & Spike - Child link can't kill these with sword or deku stick.
|
|
// 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;
|
|
|
|
// Bari - Spawns 3 more enemies, potentially extremely difficult in timed rooms.
|
|
bool enemiesToExcludeTimedRooms = enemiesToExcludeClearRooms || enemy.id == ACTOR_EN_VALI;
|
|
|
|
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 ceilling,
|
|
// 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 ceilling 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;
|
|
}
|
|
}
|