Files
draugnorak/src/creature_template.nvgt

328 lines
10 KiB
Plaintext

// 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.)
============================================================================ */