Skip to content
Open
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
169 changes: 110 additions & 59 deletions soh/soh/Enhancements/enemyrandomizer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#include "soh/Enhancements/randomizer/SeedContext.h"
#include "soh/Enhancements/enhancementTypes.h"
#include "variables.h"
#include "overlays/actors/ovl_En_Attack_Niw/z_en_attack_niw.h"
#include "soh/OTRGlobals.h"
#include "soh/cvar_prefixes.h"
#include "soh/ResourceManagerHelpers.h"
Expand Down Expand Up @@ -34,6 +35,7 @@ const char* enemyCVarList[RANDOMIZED_ENEMY_SPAWN_TABLE_SIZE] = {
CVAR_ENHANCEMENT("RandomizedEnemyList.Anubis"),
CVAR_ENHANCEMENT("RandomizedEnemyList.Armos"),
CVAR_ENHANCEMENT("RandomizedEnemyList.Arwing"),
CVAR_ENHANCEMENT("RandomizedEnemyList.AttackingCucco"),
CVAR_ENHANCEMENT("RandomizedEnemyList.BabyDodongo"),
CVAR_ENHANCEMENT("RandomizedEnemyList.Bari"),
CVAR_ENHANCEMENT("RandomizedEnemyList.Beamos"),
Expand Down Expand Up @@ -98,6 +100,7 @@ const char* enemyNameList[RANDOMIZED_ENEMY_SPAWN_TABLE_SIZE] = {
"Anubis",
"Armos",
"Arwing",
"Cucco (Attacking)",
"Baby Dodongo",
"Bari",
"Beamos",
Expand Down Expand Up @@ -159,23 +162,24 @@ const char* enemyNameList[RANDOMIZED_ENEMY_SPAWN_TABLE_SIZE] = {
};

static EnemyEntry randomizedEnemySpawnTable[RANDOMIZED_ENEMY_SPAWN_TABLE_SIZE] = {
{ 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_ANUBICE_TAG, 1 }, // Anubis
{ ACTOR_EN_AM, -1 }, // Armos
{ ACTOR_EN_CLEAR_TAG, 1 }, // Arwing
{ ACTOR_EN_ATTACK_NIW, 777 }, // Cucco (Attacking)
{ 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
Expand Down Expand Up @@ -242,42 +246,43 @@ static int enemiesToRandomize[] = {
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_ATTACK_NIW, // Cucco (Attacking)
};

extern "C" uint8_t GetRandomizedEnemy(PlayState* play, int16_t* actorId, f32* posX, f32* posY, f32* posZ, int16_t* rotX,
Expand Down Expand Up @@ -352,6 +357,7 @@ extern "C" uint8_t GetRandomizedEnemy(PlayState* play, int16_t* actorId, f32* po
play->sceneNum + *actorId + (int)*posX + (int)*posY + (int)*posZ + *rotX + *rotY + *rotZ + *params;
EnemyEntry randomEnemy = GetRandomizedEnemyEntry(seed, play);

int16_t prevActorId = *actorId;
*actorId = randomEnemy.id;
*params = randomEnemy.params;

Expand Down Expand Up @@ -381,6 +387,19 @@ extern "C" uint8_t GetRandomizedEnemy(PlayState* play, int16_t* actorId, f32* po
case ACTOR_EN_CROW:
*posY = *posY + 75;
break;
// Restore Vanilla Behavior on Attacking Cucco if it is replacing a vanilla one
// Otherwise spawn it in the air and rotate randomly
case ACTOR_EN_ATTACK_NIW:
if (prevActorId == ACTOR_EN_ATTACK_NIW) {
*params = 0;
// Vanilla spawn height
f32 viewY = play->view.lookAt.y - play->view.eye.y;
*posY = Rand_CenteredFloat(0.3f) + ((play->view.eye.y + 50.0f) + (viewY * 0.5f));
} else {
*posY = *posY + 75;
*rotY = Random(0, 65537) - 32768;
}
break;
default:
break;
}
Expand Down Expand Up @@ -417,7 +436,12 @@ EnemyEntry GetRandomizedEnemyEntry(uint32_t seed, PlayState* play) {
}
}
if (filteredEnemyList.size() == 0) {
filteredEnemyList = selectedEnemyList;
// Fail-safe for soft-locks if only selected enemy is attacking cuccos -- replace with Withered Deku Baba
if (selectedEnemyList.size() == 1 && selectedEnemyList[0].id == ACTOR_EN_ATTACK_NIW) {
filteredEnemyList.push_back({ ACTOR_EN_KAREBABA, 0 });
} else {
filteredEnemyList = selectedEnemyList;
}
}
if (CVAR_ENEMY_RANDOMIZER_VALUE == ENEMY_RANDOMIZER_RANDOM_SEEDED) {
uint32_t finalSeed =
Expand Down Expand Up @@ -511,13 +535,15 @@ bool IsEnemyAllowedToSpawn(int16_t sceneNum, int8_t roomNum, EnemyEntry enemy) {
// 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;
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 || enemy.id == ACTOR_EN_ATTACK_NIW;

// Bari - Spawns 3 more enemies, potentially extremely difficult in timed rooms.
bool enemiesToExcludeTimedRooms = enemiesToExcludeClearRooms || enemy.id == ACTOR_EN_VALI;
bool enemiesToExcludeTimedRooms =
enemiesToExcludeClearRooms || enemy.id == ACTOR_EN_VALI || enemy.id == ACTOR_EN_ATTACK_NIW;

switch (sceneNum) {
// Deku Tree
Expand Down Expand Up @@ -631,6 +657,28 @@ static void OnGerudoFighterDefeat(void* refActor) {
}
}

static void HandleAttackCuccoUpdate(void* refActor) {
EnAttackNiw* enAttackNiw = reinterpret_cast<EnAttackNiw*>(refActor);

// params == 777 means this Attacking Cucco was randomized, and we want these to behave differently
if (enAttackNiw->actor.params == 777) {
// if the cucco is within striking distance, handle the damage here
// because vanilla code expects having a normal cucco parent
Player* player = GET_PLAYER(gPlayState);
if (enAttackNiw->actor.xyzDistToPlayerSq < SQ(20.0f) && player->invincibilityTimer == 0) {
func_8002F6D4(gPlayState, &enAttackNiw->actor, 2.0f, enAttackNiw->actor.world.rot.y, 0.0f, 0x10);
}

// keep cucco from flying away
enAttackNiw->unk_262 = 0xFF;

// we want the cucco to face toward the player on random intervals
if (Random(0, 20) == 0) {
enAttackNiw->unk_2D4 = Rand_CenteredFloat(200.0f) + enAttackNiw->actor.yawTowardsPlayer;
}
}
}

void RegisterEnemyRandomizer() {
COND_ID_HOOK(OnActorInit, ACTOR_EN_MB, CVAR_ENEMY_RANDOMIZER_VALUE, FixClubMoblinScale);

Expand Down Expand Up @@ -725,6 +773,9 @@ void RegisterEnemyRandomizer() {
// 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);

// Handle Cucco updates for randomized Attacking Cuccos
COND_ID_HOOK(OnActorUpdate, ACTOR_EN_ATTACK_NIW, CVAR_ENEMY_RANDOMIZER_VALUE, HandleAttackCuccoUpdate);
}

static RegisterShipInitFunc initFunc(RegisterEnemyRandomizer, { CVAR_ENEMY_RANDOMIZER_NAME });
2 changes: 1 addition & 1 deletion soh/soh/Enhancements/enemyrandomizer.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
#include <libultraship/libultra/types.h>
#include "item-tables/ItemTableTypes.h"

#define RANDOMIZED_ENEMY_SPAWN_TABLE_SIZE 59
#define RANDOMIZED_ENEMY_SPAWN_TABLE_SIZE 60

extern const char* enemyCVarList[];
extern const char* enemyNameList[];
Expand Down
6 changes: 5 additions & 1 deletion soh/src/code/z_actor.c
Original file line number Diff line number Diff line change
Expand Up @@ -3436,8 +3436,12 @@ Actor* Actor_SpawnAsChild(ActorContext* actorCtx, Actor* parent, PlayState* play
// Gohma (z_boss_goma.c), the Stalchildren spawner (z_en_encount1.c) and the falling platform spawning Stalfos in
// Forest Temple (z_bg_mori_bigst.c) that normally rely on this behaviour are changed when
// Enemy Rando is on so they still work properly even without assigning a parent.
//
// Also, if the parent actor is a Cucco, and it is not spawning an Attacking Cucco, then we don't want the
// actor's parent to be this Cucco
if (CVarGetInteger(CVAR_ENHANCEMENT("RandomizedEnemies"), 0) &&
(spawnedActor->id == ACTOR_EN_FLOORMAS || spawnedActor->id == ACTOR_EN_PEEHAT)) {
((spawnedActor->id == ACTOR_EN_FLOORMAS || spawnedActor->id == ACTOR_EN_PEEHAT) ||
(parent->id == ACTOR_EN_NIW && spawnedActor->id != ACTOR_EN_ATTACK_NIW))) {
return spawnedActor;
}

Expand Down
11 changes: 8 additions & 3 deletions soh/src/overlays/actors/ovl_En_Attack_Niw/z_en_attack_niw.c
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,11 @@ s32 func_809B55EC(EnAttackNiw* this, PlayState* play) {
s16 sp1E;
s16 sp1C;

// Params == 777 means that this is a randomized Attacking Cucco, and we don't despawn them when they go off-screen
if (this->actor.params == 777) {
return 1;
}

Actor_SetFocus(&this->actor, this->unk_2E4);
Actor_GetScreenPos(play, &this->actor, &sp1E, &sp1C);
if ((this->actor.projectedPos.z < -20.0f) || (sp1E < 0) || (sp1E > SCREEN_WIDTH) || (sp1C < 0) ||
Expand Down Expand Up @@ -362,10 +367,10 @@ void EnAttackNiw_Update(Actor* thisx, PlayState* play) {
}

tmpf1 = 20.0f;
if (this->actor.xyzDistToPlayerSq < SQ(tmpf1)) {
if (this->actor.xyzDistToPlayerSq < SQ(tmpf1) && (this->actor.parent != NULL)) {
cucco = (EnNiw*)this->actor.parent;
if ((this->actor.parent->update != NULL) && (this->actor.parent != NULL) && (cucco != NULL) &&
(cucco->timer9 == 0) && (player->invincibilityTimer == 0)) {
if ((this->actor.parent->update != NULL) && (cucco != NULL) && (cucco->timer9 == 0) &&
(player->invincibilityTimer == 0)) {
func_8002F6D4(play, &this->actor, 2.0f, this->actor.world.rot.y, 0.0f, 0x10);
cucco->timer9 = 0x46;
}
Expand Down
Loading