// CREATURE CREATION TEMPLATE // Use this as a guide when creating new creatures (goblins, animals, etc.) // // This template ensures all creatures have consistent audio behavior and required features. // Copy the sections you need and fill in creature-specific details. /* ============================================================================ STEP 1: Add constants to src/constants.nvgt ============================================================================ */ // Example for "Goblin" creature: /* // Goblin Configuration const int GOBLIN_HEALTH = 15; const int GOBLIN_DAMAGE_MIN = 2; const int GOBLIN_DAMAGE_MAX = 4; const int GOBLIN_MOVE_INTERVAL = 1500; const int GOBLIN_ATTACK_INTERVAL = 3000; const int GOBLIN_ALERT_MIN_DELAY = 4000; const int GOBLIN_ALERT_MAX_DELAY = 8000; const int GOBLIN_FOOTSTEP_MAX_DISTANCE = 6; const float GOBLIN_SOUND_VOLUME_STEP = 3.0; // Default creature volume */ /* ============================================================================ STEP 2: Define sound arrays in src/world_state.nvgt (near top with zombies/bandits) ============================================================================ */ // Example: /* string[] goblin_sounds = { "sounds/enemies/goblin_cackle1.ogg", "sounds/enemies/goblin_cackle2.ogg", "sounds/enemies/goblin_cackle3.ogg" }; */ /* ============================================================================ STEP 3: Create creature class in src/world_state.nvgt ============================================================================ REQUIRED MEMBERS: - int position (where the creature is on the map) - int health (creature's HP) - int sound_handle (CRITICAL: for tracking active voice/alert sounds) - timer move_timer (for movement intervals) - timer attack_timer (for attack cooldown) - timer voice_timer (for periodic sounds - groans/alerts/cackles) - int next_voice_delay (randomized delay between voice sounds) - string voice_sound (which sound file to play for voice) OPTIONAL MEMBERS (creature-specific): - string weapon_type - string behavior_state - int wander_direction - etc. */ // Example: /* class Goblin { int position; int health; int sound_handle; // REQUIRED: Must track for death cleanup string voice_sound; timer move_timer; timer voice_timer; // Renamed from alert_timer/groan_timer for clarity timer attack_timer; int next_voice_delay; // Creature-specific behavior string weapon_type; // Example: "dagger" or "club" Goblin(int pos) { position = pos; health = GOBLIN_HEALTH; sound_handle = -1; // CRITICAL: Always initialize to -1 // Choose random voice sound int sound_index = random(0, goblin_sounds.length() - 1); voice_sound = goblin_sounds[sound_index]; // Choose random weapon weapon_type = (random(0, 1) == 0) ? "dagger" : "club"; // Initialize timers move_timer.restart(); voice_timer.restart(); attack_timer.restart(); next_voice_delay = random(GOBLIN_ALERT_MIN_DELAY, GOBLIN_ALERT_MAX_DELAY); } } Goblin@[] goblins; // Global array to store all active goblins */ /* ============================================================================ STEP 4: Spawn function ============================================================================ REQUIRED AUDIO: Use play_creature_voice() and store the handle */ // Example: /* void spawn_goblin() { // Find spawn location (avoid duplicates) int spawn_x = -1; for (int attempts = 0; attempts < 20; attempts++) { int candidate = random(BASE_END + 1, MAP_SIZE - 1); if (get_goblin_at(candidate) == null) { spawn_x = candidate; break; } } if (spawn_x == -1) { spawn_x = random(BASE_END + 1, MAP_SIZE - 1); } Goblin@ g = Goblin(spawn_x); goblins.insert_last(g); // REQUIRED: Use creature_audio system and store handle g.sound_handle = play_creature_voice(g.voice_sound, x, spawn_x, GOBLIN_SOUND_VOLUME_STEP); } */ /* ============================================================================ STEP 5: Update function ============================================================================ REQUIRED AUDIO: - Periodic voice: play_creature_voice() and store handle - Movement: play_creature_footstep() - Attacks: play_creature_attack_sound() */ // Example: /* void update_goblin(Goblin@ goblin) { // Play periodic voice sound if (goblin.voice_timer.elapsed > goblin.next_voice_delay) { goblin.voice_timer.restart(); goblin.next_voice_delay = random(GOBLIN_ALERT_MIN_DELAY, GOBLIN_ALERT_MAX_DELAY); // REQUIRED: Store handle for cleanup on death goblin.sound_handle = play_creature_voice(goblin.voice_sound, x, goblin.position, GOBLIN_SOUND_VOLUME_STEP); } // Try to attack player if (try_attack_player_goblin(goblin)) { return; } // Movement logic if (goblin.move_timer.elapsed < GOBLIN_MOVE_INTERVAL) return; goblin.move_timer.restart(); // [Your movement AI here - pathfinding toward player, wandering, etc.] // When goblin moves: goblin.position = target_x; // REQUIRED: Use creature_audio for footsteps play_creature_footstep(x, goblin.position, BASE_END, GRASS_END, GOBLIN_FOOTSTEP_MAX_DISTANCE, GOBLIN_SOUND_VOLUME_STEP); } */ /* ============================================================================ STEP 6: Attack functions ============================================================================ REQUIRED AUDIO: Use play_creature_attack_sound() for weapon sounds */ // Example: /* bool try_attack_player_goblin(Goblin@ goblin) { if (player_health <= 0) return false; if (abs(goblin.position - x) > 1) return false; if (goblin.attack_timer.elapsed < GOBLIN_ATTACK_INTERVAL) return false; goblin.attack_timer.restart(); // REQUIRED: Positional weapon sounds using creature_audio if (goblin.weapon_type == "dagger") { play_creature_attack_sound("sounds/weapons/dagger_swing.ogg", x, goblin.position, GOBLIN_SOUND_VOLUME_STEP); } else { play_creature_attack_sound("sounds/weapons/club_swing.ogg", x, goblin.position, GOBLIN_SOUND_VOLUME_STEP); } int damage = random(GOBLIN_DAMAGE_MIN, GOBLIN_DAMAGE_MAX); player_health -= damage; if (player_health < 0) player_health = 0; // REQUIRED: Hit sound using creature_audio play_creature_attack_sound("sounds/enemies/player_hit.ogg", x, goblin.position, GOBLIN_SOUND_VOLUME_STEP); return true; } */ /* ============================================================================ STEP 7: Damage/Death function ============================================================================ CRITICAL: MUST stop creature's sound before playing death sound */ // Example: /* bool damage_goblin_at(int pos, int damage) { for (uint i = 0; i < goblins.length(); i++) { if (goblins[i].position == pos) { goblins[i].health -= damage; if (goblins[i].health <= 0) { // CRITICAL: Stop active sounds before death sound if (goblins[i].sound_handle != -1) { p.destroy_sound(goblins[i].sound_handle); goblins[i].sound_handle = -1; } // REQUIRED: Use creature_audio for death sound play_creature_death_sound("sounds/enemies/enemy_falls.ogg", x, pos, GOBLIN_SOUND_VOLUME_STEP); goblins.remove_at(i); } return true; } } return false; } */ /* ============================================================================ STEP 8: Helper functions ============================================================================ */ // Example: /* Goblin@ get_goblin_at(int pos) { for (uint i = 0; i < goblins.length(); i++) { if (goblins[i].position == pos) { return @goblins[i]; } } return null; } void update_goblins() { for (uint i = 0; i < goblins.length(); i++) { update_goblin(goblins[i]); } } void clear_goblins() { for (uint i = 0; i < goblins.length(); i++) { if (goblins[i].sound_handle != -1) { p.destroy_sound(goblins[i].sound_handle); goblins[i].sound_handle = -1; } } goblins.resize(0); } */ /* ============================================================================ STEP 9: Update src/combat.nvgt damage functions ============================================================================ */ // Add to relevant damage functions in src/combat.nvgt: /* // In attack_enemy_ranged(): if (damage_goblin_at(check_x, damage)) { return check_x; } // In attack_enemy(): if (damage_goblin_at(target_x, damage)) { return true; } // When player damages goblin, play hit sound: play_creature_hit_sound("sounds/enemies/zombie_hit.ogg", player_x, target_x, GOBLIN_SOUND_VOLUME_STEP); */ /* ============================================================================ CHECKLIST: Required for Every Creature ============================================================================ Constants in constants.nvgt: [ ] CREATURE_HEALTH [ ] CREATURE_DAMAGE_MIN / CREATURE_DAMAGE_MAX [ ] CREATURE_MOVE_INTERVAL [ ] CREATURE_ATTACK_INTERVAL [ ] CREATURE_ALERT_MIN_DELAY / CREATURE_ALERT_MAX_DELAY [ ] CREATURE_FOOTSTEP_MAX_DISTANCE [ ] CREATURE_SOUND_VOLUME_STEP Class members: [ ] int position [ ] int health [ ] int sound_handle (initialized to -1) [ ] timer move_timer [ ] timer attack_timer [ ] timer voice_timer [ ] int next_voice_delay [ ] string voice_sound Audio usage: [ ] Spawn: play_creature_voice() with stored handle [ ] Update: play_creature_voice() with stored handle [ ] Movement: play_creature_footstep() [ ] Attacks: play_creature_attack_sound() for weapons [ ] Death: Stop sound_handle BEFORE play_creature_death_sound() [ ] Player damages creature: play_creature_hit_sound() Functions: [ ] spawn_creature() [ ] update_creature() [ ] try_attack_player_creature() [ ] damage_creature_at() [ ] get_creature_at() [ ] update_creatures() [ ] clear_creatures() Integration: [ ] Add creature to combat.nvgt damage checks [ ] Call update_creatures() in main game loop [ ] Handle cleanup (clear_creatures on game over, etc.) ============================================================================ */