More refactor, start splitting large unwieldy files into easier to manage components.

This commit is contained in:
Storm Dragon
2026-01-21 18:23:44 -05:00
parent af9a402e80
commit 3a56125a93
7 changed files with 274 additions and 206 deletions
+217
View File
@@ -0,0 +1,217 @@
// Undead creatures (zombies, vampires, ghosts, etc.)
// Currently only zombies are implemented
string[] undead_zombie_sounds = {"sounds/enemies/zombie1.ogg"};
class Undead {
int position;
int health;
string undead_type; // "zombie", future: "vampire", "ghost", etc.
string voice_sound;
int sound_handle;
timer move_timer;
timer groan_timer;
timer attack_timer;
int next_groan_delay;
Undead(int pos, string type = "zombie") {
position = pos;
undead_type = type;
health = ZOMBIE_HEALTH;
int sound_index = random(0, undead_zombie_sounds.length() - 1);
voice_sound = undead_zombie_sounds[sound_index];
sound_handle = -1;
move_timer.restart();
groan_timer.restart();
attack_timer.restart();
next_groan_delay = random(ZOMBIE_GROAN_MIN_DELAY, ZOMBIE_GROAN_MAX_DELAY);
}
}
Undead@[] undeads;
void clear_undeads() {
if (undeads.length() == 0) return;
for (uint i = 0; i < undeads.length(); i++) {
if (undeads[i].sound_handle != -1) {
p.destroy_sound(undeads[i].sound_handle);
undeads[i].sound_handle = -1;
}
}
undeads.resize(0);
}
Undead@ get_undead_at(int pos) {
for (uint i = 0; i < undeads.length(); i++) {
if (undeads[i].position == pos) {
return @undeads[i];
}
}
return null;
}
void spawn_undead() {
int spawn_x = -1;
for (int attempts = 0; attempts < 20; attempts++) {
int candidate = random(BASE_END + 1, MAP_SIZE - 1);
if (get_undead_at(candidate) == null) {
spawn_x = candidate;
break;
}
}
if (spawn_x == -1) {
spawn_x = random(BASE_END + 1, MAP_SIZE - 1);
}
Undead@ z = Undead(spawn_x, "zombie");
undeads.insert_last(z);
z.sound_handle = play_creature_voice(z.voice_sound, x, spawn_x, ZOMBIE_SOUND_VOLUME_STEP);
}
void try_attack_barricade_undead(Undead@ undead) {
if (barricade_health <= 0) return;
if (undead.attack_timer.elapsed < ZOMBIE_ATTACK_INTERVAL) return;
undead.attack_timer.restart();
int damage = random(ZOMBIE_DAMAGE_MIN, ZOMBIE_DAMAGE_MAX);
barricade_health -= damage;
if (barricade_health < 0) barricade_health = 0;
play_creature_attack_sound("sounds/enemies/zombie_hits_player.ogg", x, undead.position, ZOMBIE_SOUND_VOLUME_STEP);
// Resident defense counter-attack
if (can_residents_defend()) {
int counterDamage = perform_resident_defense();
if (counterDamage > 0) {
undead.health -= counterDamage;
if (undead.health <= 0 && x <= BASE_END) {
speak_with_history("Residents killed an attacking zombie.", true);
}
}
}
if (barricade_health == 0) {
notify("The barricade has fallen!");
}
}
bool can_undead_attack_player(Undead@ undead) {
if (player_health <= 0) {
return false;
}
if (barricade_health > 0 && x <= BASE_END) {
return false;
}
if (abs(undead.position - x) > 1) {
return false;
}
return y <= ZOMBIE_ATTACK_MAX_HEIGHT;
}
bool try_attack_player_undead(Undead@ undead) {
if (!can_undead_attack_player(undead)) {
return false;
}
if (undead.attack_timer.elapsed < ZOMBIE_ATTACK_INTERVAL) {
return false;
}
undead.attack_timer.restart();
int damage = random(ZOMBIE_DAMAGE_MIN, ZOMBIE_DAMAGE_MAX);
player_health -= damage;
if (player_health < 0) {
player_health = 0;
}
play_creature_attack_sound("sounds/enemies/zombie_hits_player.ogg", x, undead.position, ZOMBIE_SOUND_VOLUME_STEP);
return true;
}
void update_undead(Undead@ undead) {
if (undead.groan_timer.elapsed > undead.next_groan_delay) {
undead.groan_timer.restart();
undead.next_groan_delay = random(ZOMBIE_GROAN_MIN_DELAY, ZOMBIE_GROAN_MAX_DELAY);
undead.sound_handle = play_creature_voice(undead.voice_sound, x, undead.position, ZOMBIE_SOUND_VOLUME_STEP);
}
if (try_attack_player_undead(undead)) {
return;
}
if (undead.move_timer.elapsed < ZOMBIE_MOVE_INTERVAL) return;
undead.move_timer.restart();
if (barricade_health > 0 && undead.position == BASE_END + 1) {
try_attack_barricade_undead(undead);
return;
}
int direction = 0;
if (x > BASE_END) {
if (x > undead.position) {
direction = 1;
} else if (x < undead.position) {
direction = -1;
} else {
return;
}
} else {
direction = random(-1, 1);
if (direction == 0) return;
}
int target_x = undead.position + direction;
if (target_x < 0 || target_x >= MAP_SIZE) return;
if (target_x <= BASE_END && barricade_health > 0) {
try_attack_barricade_undead(undead);
return;
}
undead.position = target_x;
play_creature_footstep(x, undead.position, BASE_END, GRASS_END, ZOMBIE_FOOTSTEP_MAX_DISTANCE, ZOMBIE_SOUND_VOLUME_STEP);
}
void update_undeads() {
if (is_daytime) {
clear_undeads();
return;
}
while (undeads.length() < ZOMBIE_MAX_COUNT) {
spawn_undead();
}
for (uint i = 0; i < undeads.length(); i++) {
update_undead(undeads[i]);
}
}
bool damage_undead_at(int pos, int damage) {
for (uint i = 0; i < undeads.length(); i++) {
if (undeads[i].position == pos) {
undeads[i].health -= damage;
if (undeads[i].health <= 0) {
if (undeads[i].sound_handle != -1) {
p.destroy_sound(undeads[i].sound_handle);
undeads[i].sound_handle = -1;
}
play_creature_death_sound("sounds/enemies/enemy_falls.ogg", x, pos, ZOMBIE_SOUND_VOLUME_STEP);
undeads.remove_at(i);
}
return true;
}
}
return false;
}
// Backward compatibility aliases (to be removed after full refactoring)
Undead@[]@ zombies = @undeads; // Array alias for backward compatibility
Undead@ get_zombie_at(int pos) { return get_undead_at(pos); }
bool damage_zombie_at(int pos, int damage) { return damage_undead_at(pos, damage); }
void update_zombies() { update_undeads(); }
void clear_zombies() { clear_undeads(); }
void spawn_zombie() { spawn_undead(); }