328 lines
10 KiB
Plaintext
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.)
|
|
|
|
============================================================================ */
|