More refactor, getting there ... slowly.
This commit is contained in:
@@ -12,6 +12,7 @@ sound_pool p(100);
|
|||||||
#include "src/enemies/undead.nvgt"
|
#include "src/enemies/undead.nvgt"
|
||||||
#include "src/enemies/bandit.nvgt"
|
#include "src/enemies/bandit.nvgt"
|
||||||
#include "src/enemies/ground_game.nvgt"
|
#include "src/enemies/ground_game.nvgt"
|
||||||
|
#include "src/enemies/flying_creatures.nvgt"
|
||||||
#include "src/world_state.nvgt"
|
#include "src/world_state.nvgt"
|
||||||
#include "src/ui.nvgt"
|
#include "src/ui.nvgt"
|
||||||
#include "src/inventory.nvgt"
|
#include "src/inventory.nvgt"
|
||||||
|
|||||||
@@ -0,0 +1,396 @@
|
|||||||
|
// Flying creatures (geese, ducks, etc.)
|
||||||
|
// Config-driven system for spawning and managing flying creatures near water
|
||||||
|
|
||||||
|
string[] goose_sounds = {"sounds/game/goose.ogg"};
|
||||||
|
|
||||||
|
class FlyingCreatureConfig {
|
||||||
|
string id;
|
||||||
|
string drop_type;
|
||||||
|
string[] sounds;
|
||||||
|
string fall_sound;
|
||||||
|
string impact_sound;
|
||||||
|
int health;
|
||||||
|
int move_interval_min;
|
||||||
|
int move_interval_max;
|
||||||
|
int min_height;
|
||||||
|
int max_height;
|
||||||
|
float sound_volume_step;
|
||||||
|
int sound_delay_min;
|
||||||
|
int sound_delay_max;
|
||||||
|
int fall_speed;
|
||||||
|
int fly_away_chance;
|
||||||
|
int max_dist_from_water;
|
||||||
|
int hourly_spawn_chance;
|
||||||
|
int max_count;
|
||||||
|
int sight_range;
|
||||||
|
bool flee_on_sight;
|
||||||
|
}
|
||||||
|
FlyingCreatureConfig@[] flying_creature_configs;
|
||||||
|
|
||||||
|
class FlyingCreature {
|
||||||
|
int position;
|
||||||
|
int health;
|
||||||
|
int height;
|
||||||
|
string state; // "flying", "falling", "fading"
|
||||||
|
int area_start;
|
||||||
|
int area_end;
|
||||||
|
string creature_type;
|
||||||
|
int sound_handle;
|
||||||
|
int fall_sound_handle;
|
||||||
|
timer move_timer;
|
||||||
|
timer sound_timer;
|
||||||
|
timer fall_timer;
|
||||||
|
int next_move_delay;
|
||||||
|
int next_sound_delay;
|
||||||
|
string voice_sound;
|
||||||
|
bool fading_out;
|
||||||
|
bool ready_to_remove;
|
||||||
|
timer fade_timer;
|
||||||
|
|
||||||
|
FlyingCreature(string type, int pos, int home_start, int home_end, FlyingCreatureConfig@ cfg) {
|
||||||
|
position = pos;
|
||||||
|
health = cfg.health;
|
||||||
|
height = random(cfg.min_height, cfg.max_height);
|
||||||
|
state = "flying";
|
||||||
|
area_start = home_start;
|
||||||
|
area_end = home_end;
|
||||||
|
creature_type = type;
|
||||||
|
sound_handle = -1;
|
||||||
|
fall_sound_handle = -1;
|
||||||
|
|
||||||
|
if (cfg.sounds.length() > 0) {
|
||||||
|
voice_sound = cfg.sounds[random(0, cfg.sounds.length() - 1)];
|
||||||
|
}
|
||||||
|
|
||||||
|
move_timer.restart();
|
||||||
|
sound_timer.restart();
|
||||||
|
|
||||||
|
next_move_delay = random(cfg.move_interval_min, cfg.move_interval_max);
|
||||||
|
next_sound_delay = random(cfg.sound_delay_min, cfg.sound_delay_max);
|
||||||
|
fading_out = false;
|
||||||
|
ready_to_remove = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
FlyingCreature@[] flying_creatures;
|
||||||
|
|
||||||
|
void init_flying_creature_configs() {
|
||||||
|
flying_creature_configs.resize(0);
|
||||||
|
|
||||||
|
FlyingCreatureConfig@ goose_cfg = FlyingCreatureConfig();
|
||||||
|
goose_cfg.id = "goose";
|
||||||
|
goose_cfg.drop_type = "goose";
|
||||||
|
goose_cfg.sounds = goose_sounds;
|
||||||
|
goose_cfg.fall_sound = "sounds/actions/falling.ogg";
|
||||||
|
goose_cfg.impact_sound = "sounds/game/game_falls.ogg";
|
||||||
|
goose_cfg.health = GOOSE_HEALTH;
|
||||||
|
goose_cfg.move_interval_min = GOOSE_MOVE_INTERVAL_MIN;
|
||||||
|
goose_cfg.move_interval_max = GOOSE_MOVE_INTERVAL_MAX;
|
||||||
|
goose_cfg.min_height = GOOSE_FLYING_HEIGHT_MIN;
|
||||||
|
goose_cfg.max_height = GOOSE_FLYING_HEIGHT_MAX;
|
||||||
|
goose_cfg.sound_volume_step = GOOSE_SOUND_VOLUME_STEP;
|
||||||
|
goose_cfg.sound_delay_min = GOOSE_FLIGHT_SOUND_DELAY_MIN;
|
||||||
|
goose_cfg.sound_delay_max = GOOSE_FLIGHT_SOUND_DELAY_MAX;
|
||||||
|
goose_cfg.fall_speed = GOOSE_FALL_SPEED;
|
||||||
|
goose_cfg.fly_away_chance = GOOSE_FLY_AWAY_CHANCE;
|
||||||
|
goose_cfg.max_dist_from_water = GOOSE_MAX_DIST_FROM_WATER;
|
||||||
|
goose_cfg.hourly_spawn_chance = GOOSE_HOURLY_SPAWN_CHANCE;
|
||||||
|
goose_cfg.max_count = GOOSE_MAX_COUNT;
|
||||||
|
goose_cfg.sight_range = GOOSE_SIGHT_RANGE;
|
||||||
|
goose_cfg.flee_on_sight = false;
|
||||||
|
flying_creature_configs.insert_last(goose_cfg);
|
||||||
|
}
|
||||||
|
|
||||||
|
FlyingCreatureConfig@ get_flying_creature_config(string creature_type) {
|
||||||
|
for (uint i = 0; i < flying_creature_configs.length(); i++) {
|
||||||
|
if (flying_creature_configs[i].id == creature_type) {
|
||||||
|
return @flying_creature_configs[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
FlyingCreatureConfig@ get_flying_creature_config_by_drop_type(string drop_type) {
|
||||||
|
for (uint i = 0; i < flying_creature_configs.length(); i++) {
|
||||||
|
if (flying_creature_configs[i].drop_type == drop_type) {
|
||||||
|
return @flying_creature_configs[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
void clear_flying_creatures() {
|
||||||
|
for (uint i = 0; i < flying_creatures.length(); i++) {
|
||||||
|
if (flying_creatures[i].sound_handle != -1) {
|
||||||
|
p.destroy_sound(flying_creatures[i].sound_handle);
|
||||||
|
flying_creatures[i].sound_handle = -1;
|
||||||
|
}
|
||||||
|
if (flying_creatures[i].fall_sound_handle != -1) {
|
||||||
|
p.destroy_sound(flying_creatures[i].fall_sound_handle);
|
||||||
|
flying_creatures[i].fall_sound_handle = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
flying_creatures.resize(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
FlyingCreature@ get_flying_creature_at(int pos) {
|
||||||
|
for (uint i = 0; i < flying_creatures.length(); i++) {
|
||||||
|
if (flying_creatures[i].position == pos) {
|
||||||
|
return @flying_creatures[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
int get_flying_creature_count(string creature_type) {
|
||||||
|
int count = 0;
|
||||||
|
for (uint i = 0; i < flying_creatures.length(); i++) {
|
||||||
|
if (flying_creatures[i].creature_type == creature_type) {
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool get_random_flying_creature_area(FlyingCreatureConfig@ cfg, int &out area_start, int &out area_end) {
|
||||||
|
int stream_count = int(world_streams.length());
|
||||||
|
int mountain_stream_count = 0;
|
||||||
|
for (uint i = 0; i < world_mountains.length(); i++) {
|
||||||
|
mountain_stream_count += int(world_mountains[i].stream_positions.length());
|
||||||
|
}
|
||||||
|
|
||||||
|
int total_areas = stream_count + mountain_stream_count;
|
||||||
|
if (total_areas <= 0) return false;
|
||||||
|
|
||||||
|
int pick = random(0, total_areas - 1);
|
||||||
|
if (pick < stream_count) {
|
||||||
|
area_start = world_streams[pick].start_position;
|
||||||
|
area_end = world_streams[pick].end_position;
|
||||||
|
} else {
|
||||||
|
pick -= stream_count;
|
||||||
|
for (uint i = 0; i < world_mountains.length(); i++) {
|
||||||
|
int local_count = int(world_mountains[i].stream_positions.length());
|
||||||
|
if (pick < local_count) {
|
||||||
|
int stream_pos = world_mountains[i].start_position + world_mountains[i].stream_positions[pick];
|
||||||
|
area_start = stream_pos;
|
||||||
|
area_end = stream_pos;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
pick -= local_count;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
area_start -= cfg.max_dist_from_water;
|
||||||
|
area_end += cfg.max_dist_from_water;
|
||||||
|
if (area_start < 0) area_start = 0;
|
||||||
|
if (area_end >= MAP_SIZE) area_end = MAP_SIZE - 1;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool find_flying_creature_spawn(FlyingCreatureConfig@ cfg, int &out spawn_x, int &out area_start, int &out area_end) {
|
||||||
|
if (!get_random_flying_creature_area(cfg, area_start, area_end)) return false;
|
||||||
|
|
||||||
|
for (int attempts = 0; attempts < 20; attempts++) {
|
||||||
|
int candidate = random(area_start, area_end);
|
||||||
|
if (get_flying_creature_at(candidate) == null) {
|
||||||
|
spawn_x = candidate;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void fly_away_flying_creature(FlyingCreature@ creature, FlyingCreatureConfig@ cfg) {
|
||||||
|
creature.state = "fading";
|
||||||
|
creature.fading_out = true;
|
||||||
|
creature.ready_to_remove = false;
|
||||||
|
creature.fade_timer.restart();
|
||||||
|
creature.health = 0;
|
||||||
|
|
||||||
|
if (creature.sound_handle == -1 || !p.sound_is_active(creature.sound_handle)) {
|
||||||
|
creature.ready_to_remove = true;
|
||||||
|
}
|
||||||
|
if (creature.fall_sound_handle != -1) {
|
||||||
|
p.destroy_sound(creature.fall_sound_handle);
|
||||||
|
creature.fall_sound_handle = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool spawn_flying_creature(string creature_type) {
|
||||||
|
FlyingCreatureConfig@ cfg = get_flying_creature_config(creature_type);
|
||||||
|
if (cfg is null) return false;
|
||||||
|
|
||||||
|
int spawn_x = -1;
|
||||||
|
int area_start = 0;
|
||||||
|
int area_end = 0;
|
||||||
|
|
||||||
|
if (!find_flying_creature_spawn(cfg, spawn_x, area_start, area_end)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
FlyingCreature@ c = FlyingCreature(creature_type, spawn_x, area_start, area_end, cfg);
|
||||||
|
flying_creatures.insert_last(c);
|
||||||
|
c.sound_handle = play_creature_voice(c.voice_sound, x, spawn_x, cfg.sound_volume_step);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void update_flying_creature(FlyingCreature@ creature) {
|
||||||
|
FlyingCreatureConfig@ cfg = get_flying_creature_config(creature.creature_type);
|
||||||
|
if (cfg is null) return;
|
||||||
|
|
||||||
|
if (creature.state == "fading") {
|
||||||
|
if (!creature.fading_out) {
|
||||||
|
creature.fading_out = true;
|
||||||
|
creature.fade_timer.restart();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (creature.sound_handle != -1 && p.sound_is_active(creature.sound_handle)) {
|
||||||
|
float progress = float(creature.fade_timer.elapsed) / float(FLYING_CREATURE_FADE_OUT_DURATION);
|
||||||
|
if (progress < 0.0) progress = 0.0;
|
||||||
|
if (progress > 1.0) progress = 1.0;
|
||||||
|
float volume = 0.0 + (FLYING_CREATURE_FADE_OUT_MIN_VOLUME * progress);
|
||||||
|
p.update_sound_start_values(creature.sound_handle, 0.0, volume, 1.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (creature.fade_timer.elapsed >= FLYING_CREATURE_FADE_OUT_DURATION) {
|
||||||
|
if (creature.sound_handle != -1) {
|
||||||
|
p.destroy_sound(creature.sound_handle);
|
||||||
|
creature.sound_handle = -1;
|
||||||
|
}
|
||||||
|
creature.ready_to_remove = true;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (creature.state == "flying") {
|
||||||
|
if (creature.position < creature.area_start || creature.position > creature.area_end) {
|
||||||
|
fly_away_flying_creature(creature, cfg);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (creature.sound_timer.elapsed > creature.next_sound_delay) {
|
||||||
|
creature.sound_timer.restart();
|
||||||
|
creature.next_sound_delay = random(cfg.sound_delay_min, cfg.sound_delay_max);
|
||||||
|
creature.sound_handle = play_creature_voice(creature.voice_sound, x, creature.position, cfg.sound_volume_step);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cfg.fly_away_chance > 0 && random(1, 1000) <= cfg.fly_away_chance) {
|
||||||
|
fly_away_flying_creature(creature, cfg);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (creature.move_timer.elapsed > creature.next_move_delay) {
|
||||||
|
creature.move_timer.restart();
|
||||||
|
creature.next_move_delay = random(cfg.move_interval_min, cfg.move_interval_max);
|
||||||
|
|
||||||
|
int dir = 0;
|
||||||
|
if (cfg.flee_on_sight && cfg.sight_range > 0) {
|
||||||
|
int distance_to_player = abs(x - creature.position);
|
||||||
|
if (distance_to_player <= cfg.sight_range) {
|
||||||
|
if (x > creature.position) dir = -1;
|
||||||
|
else if (x < creature.position) dir = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (dir == 0) dir = random(-1, 1);
|
||||||
|
if (dir != 0) {
|
||||||
|
int target_x = creature.position + dir;
|
||||||
|
if (target_x < creature.area_start || target_x > creature.area_end) {
|
||||||
|
fly_away_flying_creature(creature, cfg);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (target_x >= 0 && target_x < MAP_SIZE) {
|
||||||
|
creature.position = target_x;
|
||||||
|
if (creature.sound_handle != -1 && p.sound_is_active(creature.sound_handle)) {
|
||||||
|
p.update_sound_1d(creature.sound_handle, creature.position);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (creature.state == "falling") {
|
||||||
|
if (creature.fall_timer.elapsed > cfg.fall_speed) {
|
||||||
|
creature.fall_timer.restart();
|
||||||
|
creature.height--;
|
||||||
|
|
||||||
|
if (creature.fall_sound_handle != -1) {
|
||||||
|
p.destroy_sound(creature.fall_sound_handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
float pitch_percent = 50.0 + (50.0 * (float(creature.height) / float(cfg.max_height)));
|
||||||
|
if (pitch_percent < 50.0) pitch_percent = 50.0;
|
||||||
|
if (pitch_percent > 100.0) pitch_percent = 100.0;
|
||||||
|
|
||||||
|
creature.fall_sound_handle = p.play_extended_1d(cfg.fall_sound, x, creature.position, 0, 0, true, 0, 0.0, 0.0, pitch_percent);
|
||||||
|
if (creature.fall_sound_handle != -1) {
|
||||||
|
p.update_sound_positioning_values(creature.fall_sound_handle, -1.0, cfg.sound_volume_step, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (creature.height <= 0) {
|
||||||
|
if (creature.fall_sound_handle != -1) {
|
||||||
|
p.destroy_sound(creature.fall_sound_handle);
|
||||||
|
creature.fall_sound_handle = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
play_creature_death_sound(cfg.impact_sound, x, creature.position, cfg.sound_volume_step);
|
||||||
|
notify("A " + creature.creature_type + " fell from the sky at " + creature.position + "!");
|
||||||
|
add_world_drop(creature.position, cfg.drop_type);
|
||||||
|
creature.health = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void update_flying_creatures() {
|
||||||
|
for (uint i = 0; i < flying_creatures.length(); i++) {
|
||||||
|
update_flying_creature(flying_creatures[i]);
|
||||||
|
|
||||||
|
if (flying_creatures[i].health <= 0) {
|
||||||
|
if (flying_creatures[i].state == "falling" && flying_creatures[i].height <= 0) {
|
||||||
|
flying_creatures.remove_at(i);
|
||||||
|
i--;
|
||||||
|
} else if (flying_creatures[i].state == "flying") {
|
||||||
|
flying_creatures.remove_at(i);
|
||||||
|
i--;
|
||||||
|
} else if (flying_creatures[i].state == "fading" && flying_creatures[i].ready_to_remove) {
|
||||||
|
flying_creatures.remove_at(i);
|
||||||
|
i--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void attempt_hourly_flying_creature_spawn() {
|
||||||
|
for (uint i = 0; i < flying_creature_configs.length(); i++) {
|
||||||
|
FlyingCreatureConfig@ cfg = flying_creature_configs[i];
|
||||||
|
if (get_flying_creature_count(cfg.id) >= cfg.max_count) continue;
|
||||||
|
if (random(1, 100) <= cfg.hourly_spawn_chance) {
|
||||||
|
spawn_flying_creature(cfg.id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool damage_flying_creature_at(int pos, int damage) {
|
||||||
|
for (uint i = 0; i < flying_creatures.length(); i++) {
|
||||||
|
if (flying_creatures[i].position == pos && flying_creatures[i].state == "flying") {
|
||||||
|
FlyingCreatureConfig@ cfg = get_flying_creature_config(flying_creatures[i].creature_type);
|
||||||
|
if (cfg is null) return false;
|
||||||
|
|
||||||
|
flying_creatures[i].health -= damage;
|
||||||
|
if (flying_creatures[i].health <= 0) {
|
||||||
|
flying_creatures[i].state = "falling";
|
||||||
|
flying_creatures[i].fall_timer.restart();
|
||||||
|
|
||||||
|
if (flying_creatures[i].sound_handle != -1) {
|
||||||
|
p.destroy_sound(flying_creatures[i].sound_handle);
|
||||||
|
flying_creatures[i].sound_handle = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
float pitch_percent = 50.0 + (50.0 * (float(flying_creatures[i].height) / float(cfg.max_height)));
|
||||||
|
flying_creatures[i].fall_sound_handle = p.play_extended_1d(cfg.fall_sound, x, pos, 0, 0, true, 0, 0.0, 0.0, pitch_percent);
|
||||||
|
if (flying_creatures[i].fall_sound_handle != -1) {
|
||||||
|
p.update_sound_positioning_values(flying_creatures[i].fall_sound_handle, -1.0, cfg.sound_volume_step, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
+1
-395
@@ -7,78 +7,6 @@ int barricade_health = 0;
|
|||||||
bool barricade_initialized = false;
|
bool barricade_initialized = false;
|
||||||
int residents_count = 0;
|
int residents_count = 0;
|
||||||
|
|
||||||
string[] goose_sounds = {"sounds/game/goose.ogg"};
|
|
||||||
|
|
||||||
class FlyingCreatureConfig {
|
|
||||||
string id;
|
|
||||||
string drop_type;
|
|
||||||
string[] sounds;
|
|
||||||
string fall_sound;
|
|
||||||
string impact_sound;
|
|
||||||
int health;
|
|
||||||
int move_interval_min;
|
|
||||||
int move_interval_max;
|
|
||||||
int min_height;
|
|
||||||
int max_height;
|
|
||||||
float sound_volume_step;
|
|
||||||
int sound_delay_min;
|
|
||||||
int sound_delay_max;
|
|
||||||
int fall_speed;
|
|
||||||
int fly_away_chance;
|
|
||||||
int max_dist_from_water;
|
|
||||||
int hourly_spawn_chance;
|
|
||||||
int max_count;
|
|
||||||
int sight_range;
|
|
||||||
bool flee_on_sight;
|
|
||||||
}
|
|
||||||
FlyingCreatureConfig@[] flying_creature_configs;
|
|
||||||
|
|
||||||
class FlyingCreature {
|
|
||||||
int position;
|
|
||||||
int health;
|
|
||||||
int height;
|
|
||||||
string state; // "flying", "falling"
|
|
||||||
int area_start;
|
|
||||||
int area_end;
|
|
||||||
string creature_type;
|
|
||||||
int sound_handle;
|
|
||||||
int fall_sound_handle;
|
|
||||||
timer move_timer;
|
|
||||||
timer sound_timer;
|
|
||||||
timer fall_timer;
|
|
||||||
int next_move_delay;
|
|
||||||
int next_sound_delay;
|
|
||||||
string voice_sound;
|
|
||||||
bool fading_out;
|
|
||||||
bool ready_to_remove;
|
|
||||||
timer fade_timer;
|
|
||||||
|
|
||||||
FlyingCreature(string type, int pos, int home_start, int home_end, FlyingCreatureConfig@ cfg) {
|
|
||||||
position = pos;
|
|
||||||
health = cfg.health;
|
|
||||||
height = random(cfg.min_height, cfg.max_height);
|
|
||||||
state = "flying";
|
|
||||||
area_start = home_start;
|
|
||||||
area_end = home_end;
|
|
||||||
creature_type = type;
|
|
||||||
sound_handle = -1;
|
|
||||||
fall_sound_handle = -1;
|
|
||||||
|
|
||||||
if (cfg.sounds.length() > 0) {
|
|
||||||
voice_sound = cfg.sounds[random(0, cfg.sounds.length() - 1)];
|
|
||||||
}
|
|
||||||
|
|
||||||
move_timer.restart();
|
|
||||||
sound_timer.restart();
|
|
||||||
|
|
||||||
next_move_delay = random(cfg.move_interval_min, cfg.move_interval_max);
|
|
||||||
next_sound_delay = random(cfg.sound_delay_min, cfg.sound_delay_max);
|
|
||||||
fading_out = false;
|
|
||||||
ready_to_remove = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
FlyingCreature@[] flying_creatures;
|
|
||||||
|
|
||||||
string get_random_small_game() {
|
string get_random_small_game() {
|
||||||
int index = random(0, small_game_types.length() - 1);
|
int index = random(0, small_game_types.length() - 1);
|
||||||
return small_game_types[index];
|
return small_game_types[index];
|
||||||
@@ -1025,326 +953,4 @@ void clear_mountains() {
|
|||||||
world_mountains.resize(0);
|
world_mountains.resize(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Flying Creature Functions
|
// Flying Creature functions moved to src/enemies/flying_creatures.nvgt
|
||||||
|
|
||||||
void init_flying_creature_configs() {
|
|
||||||
flying_creature_configs.resize(0);
|
|
||||||
|
|
||||||
FlyingCreatureConfig@ goose_cfg = FlyingCreatureConfig();
|
|
||||||
goose_cfg.id = "goose";
|
|
||||||
goose_cfg.drop_type = "goose";
|
|
||||||
goose_cfg.sounds = goose_sounds;
|
|
||||||
goose_cfg.fall_sound = "sounds/actions/falling.ogg";
|
|
||||||
goose_cfg.impact_sound = "sounds/game/game_falls.ogg";
|
|
||||||
goose_cfg.health = GOOSE_HEALTH;
|
|
||||||
goose_cfg.move_interval_min = GOOSE_MOVE_INTERVAL_MIN;
|
|
||||||
goose_cfg.move_interval_max = GOOSE_MOVE_INTERVAL_MAX;
|
|
||||||
goose_cfg.min_height = GOOSE_FLYING_HEIGHT_MIN;
|
|
||||||
goose_cfg.max_height = GOOSE_FLYING_HEIGHT_MAX;
|
|
||||||
goose_cfg.sound_volume_step = GOOSE_SOUND_VOLUME_STEP;
|
|
||||||
goose_cfg.sound_delay_min = GOOSE_FLIGHT_SOUND_DELAY_MIN;
|
|
||||||
goose_cfg.sound_delay_max = GOOSE_FLIGHT_SOUND_DELAY_MAX;
|
|
||||||
goose_cfg.fall_speed = GOOSE_FALL_SPEED;
|
|
||||||
goose_cfg.fly_away_chance = GOOSE_FLY_AWAY_CHANCE;
|
|
||||||
goose_cfg.max_dist_from_water = GOOSE_MAX_DIST_FROM_WATER;
|
|
||||||
goose_cfg.hourly_spawn_chance = GOOSE_HOURLY_SPAWN_CHANCE;
|
|
||||||
goose_cfg.max_count = GOOSE_MAX_COUNT;
|
|
||||||
goose_cfg.sight_range = GOOSE_SIGHT_RANGE;
|
|
||||||
goose_cfg.flee_on_sight = false;
|
|
||||||
flying_creature_configs.insert_last(goose_cfg);
|
|
||||||
}
|
|
||||||
|
|
||||||
FlyingCreatureConfig@ get_flying_creature_config(string creature_type) {
|
|
||||||
for (uint i = 0; i < flying_creature_configs.length(); i++) {
|
|
||||||
if (flying_creature_configs[i].id == creature_type) {
|
|
||||||
return @flying_creature_configs[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
FlyingCreatureConfig@ get_flying_creature_config_by_drop_type(string drop_type) {
|
|
||||||
for (uint i = 0; i < flying_creature_configs.length(); i++) {
|
|
||||||
if (flying_creature_configs[i].drop_type == drop_type) {
|
|
||||||
return @flying_creature_configs[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
void clear_flying_creatures() {
|
|
||||||
for (uint i = 0; i < flying_creatures.length(); i++) {
|
|
||||||
if (flying_creatures[i].sound_handle != -1) {
|
|
||||||
p.destroy_sound(flying_creatures[i].sound_handle);
|
|
||||||
flying_creatures[i].sound_handle = -1;
|
|
||||||
}
|
|
||||||
if (flying_creatures[i].fall_sound_handle != -1) {
|
|
||||||
p.destroy_sound(flying_creatures[i].fall_sound_handle);
|
|
||||||
flying_creatures[i].fall_sound_handle = -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
flying_creatures.resize(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
FlyingCreature@ get_flying_creature_at(int pos) {
|
|
||||||
for (uint i = 0; i < flying_creatures.length(); i++) {
|
|
||||||
if (flying_creatures[i].position == pos) {
|
|
||||||
return @flying_creatures[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
int get_flying_creature_count(string creature_type) {
|
|
||||||
int count = 0;
|
|
||||||
for (uint i = 0; i < flying_creatures.length(); i++) {
|
|
||||||
if (flying_creatures[i].creature_type == creature_type) {
|
|
||||||
count++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return count;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool get_random_flying_creature_area(FlyingCreatureConfig@ cfg, int &out area_start, int &out area_end) {
|
|
||||||
int stream_count = int(world_streams.length());
|
|
||||||
int mountain_stream_count = 0;
|
|
||||||
for (uint i = 0; i < world_mountains.length(); i++) {
|
|
||||||
mountain_stream_count += int(world_mountains[i].stream_positions.length());
|
|
||||||
}
|
|
||||||
|
|
||||||
int total_areas = stream_count + mountain_stream_count;
|
|
||||||
if (total_areas <= 0) return false;
|
|
||||||
|
|
||||||
int pick = random(0, total_areas - 1);
|
|
||||||
if (pick < stream_count) {
|
|
||||||
area_start = world_streams[pick].start_position;
|
|
||||||
area_end = world_streams[pick].end_position;
|
|
||||||
} else {
|
|
||||||
pick -= stream_count;
|
|
||||||
for (uint i = 0; i < world_mountains.length(); i++) {
|
|
||||||
int local_count = int(world_mountains[i].stream_positions.length());
|
|
||||||
if (pick < local_count) {
|
|
||||||
int stream_pos = world_mountains[i].start_position + world_mountains[i].stream_positions[pick];
|
|
||||||
area_start = stream_pos;
|
|
||||||
area_end = stream_pos;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
pick -= local_count;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
area_start -= cfg.max_dist_from_water;
|
|
||||||
area_end += cfg.max_dist_from_water;
|
|
||||||
if (area_start < 0) area_start = 0;
|
|
||||||
if (area_end >= MAP_SIZE) area_end = MAP_SIZE - 1;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool find_flying_creature_spawn(FlyingCreatureConfig@ cfg, int &out spawn_x, int &out area_start, int &out area_end) {
|
|
||||||
if (!get_random_flying_creature_area(cfg, area_start, area_end)) return false;
|
|
||||||
|
|
||||||
for (int attempts = 0; attempts < 20; attempts++) {
|
|
||||||
int candidate = random(area_start, area_end);
|
|
||||||
if (get_flying_creature_at(candidate) == null) {
|
|
||||||
spawn_x = candidate;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void fly_away_flying_creature(FlyingCreature@ creature, FlyingCreatureConfig@ cfg) {
|
|
||||||
creature.state = "fading";
|
|
||||||
creature.fading_out = true;
|
|
||||||
creature.ready_to_remove = false;
|
|
||||||
creature.fade_timer.restart();
|
|
||||||
creature.health = 0;
|
|
||||||
|
|
||||||
if (creature.sound_handle == -1 || !p.sound_is_active(creature.sound_handle)) {
|
|
||||||
creature.ready_to_remove = true;
|
|
||||||
}
|
|
||||||
if (creature.fall_sound_handle != -1) {
|
|
||||||
p.destroy_sound(creature.fall_sound_handle);
|
|
||||||
creature.fall_sound_handle = -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool spawn_flying_creature(string creature_type) {
|
|
||||||
FlyingCreatureConfig@ cfg = get_flying_creature_config(creature_type);
|
|
||||||
if (cfg is null) return false;
|
|
||||||
|
|
||||||
int spawn_x = -1;
|
|
||||||
int area_start = 0;
|
|
||||||
int area_end = 0;
|
|
||||||
|
|
||||||
if (!find_flying_creature_spawn(cfg, spawn_x, area_start, area_end)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
FlyingCreature@ c = FlyingCreature(creature_type, spawn_x, area_start, area_end, cfg);
|
|
||||||
flying_creatures.insert_last(c);
|
|
||||||
c.sound_handle = play_creature_voice(c.voice_sound, x, spawn_x, cfg.sound_volume_step);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void update_flying_creature(FlyingCreature@ creature) {
|
|
||||||
FlyingCreatureConfig@ cfg = get_flying_creature_config(creature.creature_type);
|
|
||||||
if (cfg is null) return;
|
|
||||||
|
|
||||||
if (creature.state == "fading") {
|
|
||||||
if (!creature.fading_out) {
|
|
||||||
creature.fading_out = true;
|
|
||||||
creature.fade_timer.restart();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (creature.sound_handle != -1 && p.sound_is_active(creature.sound_handle)) {
|
|
||||||
float progress = float(creature.fade_timer.elapsed) / float(FLYING_CREATURE_FADE_OUT_DURATION);
|
|
||||||
if (progress < 0.0) progress = 0.0;
|
|
||||||
if (progress > 1.0) progress = 1.0;
|
|
||||||
float volume = 0.0 + (FLYING_CREATURE_FADE_OUT_MIN_VOLUME * progress);
|
|
||||||
p.update_sound_start_values(creature.sound_handle, 0.0, volume, 1.0);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (creature.fade_timer.elapsed >= FLYING_CREATURE_FADE_OUT_DURATION) {
|
|
||||||
if (creature.sound_handle != -1) {
|
|
||||||
p.destroy_sound(creature.sound_handle);
|
|
||||||
creature.sound_handle = -1;
|
|
||||||
}
|
|
||||||
creature.ready_to_remove = true;
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (creature.state == "flying") {
|
|
||||||
if (creature.position < creature.area_start || creature.position > creature.area_end) {
|
|
||||||
fly_away_flying_creature(creature, cfg);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (creature.sound_timer.elapsed > creature.next_sound_delay) {
|
|
||||||
creature.sound_timer.restart();
|
|
||||||
creature.next_sound_delay = random(cfg.sound_delay_min, cfg.sound_delay_max);
|
|
||||||
creature.sound_handle = play_creature_voice(creature.voice_sound, x, creature.position, cfg.sound_volume_step);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (cfg.fly_away_chance > 0 && random(1, 1000) <= cfg.fly_away_chance) {
|
|
||||||
fly_away_flying_creature(creature, cfg);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (creature.move_timer.elapsed > creature.next_move_delay) {
|
|
||||||
creature.move_timer.restart();
|
|
||||||
creature.next_move_delay = random(cfg.move_interval_min, cfg.move_interval_max);
|
|
||||||
|
|
||||||
int dir = 0;
|
|
||||||
if (cfg.flee_on_sight && cfg.sight_range > 0) {
|
|
||||||
int distance_to_player = abs(x - creature.position);
|
|
||||||
if (distance_to_player <= cfg.sight_range) {
|
|
||||||
if (x > creature.position) dir = -1;
|
|
||||||
else if (x < creature.position) dir = 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (dir == 0) dir = random(-1, 1);
|
|
||||||
if (dir != 0) {
|
|
||||||
int target_x = creature.position + dir;
|
|
||||||
if (target_x < creature.area_start || target_x > creature.area_end) {
|
|
||||||
fly_away_flying_creature(creature, cfg);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (target_x >= 0 && target_x < MAP_SIZE) {
|
|
||||||
creature.position = target_x;
|
|
||||||
if (creature.sound_handle != -1 && p.sound_is_active(creature.sound_handle)) {
|
|
||||||
p.update_sound_1d(creature.sound_handle, creature.position);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (creature.state == "falling") {
|
|
||||||
if (creature.fall_timer.elapsed > cfg.fall_speed) {
|
|
||||||
creature.fall_timer.restart();
|
|
||||||
creature.height--;
|
|
||||||
|
|
||||||
if (creature.fall_sound_handle != -1) {
|
|
||||||
p.destroy_sound(creature.fall_sound_handle);
|
|
||||||
}
|
|
||||||
|
|
||||||
float pitch_percent = 50.0 + (50.0 * (float(creature.height) / float(cfg.max_height)));
|
|
||||||
if (pitch_percent < 50.0) pitch_percent = 50.0;
|
|
||||||
if (pitch_percent > 100.0) pitch_percent = 100.0;
|
|
||||||
|
|
||||||
creature.fall_sound_handle = p.play_extended_1d(cfg.fall_sound, x, creature.position, 0, 0, true, 0, 0.0, 0.0, pitch_percent);
|
|
||||||
if (creature.fall_sound_handle != -1) {
|
|
||||||
p.update_sound_positioning_values(creature.fall_sound_handle, -1.0, cfg.sound_volume_step, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (creature.height <= 0) {
|
|
||||||
if (creature.fall_sound_handle != -1) {
|
|
||||||
p.destroy_sound(creature.fall_sound_handle);
|
|
||||||
creature.fall_sound_handle = -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
play_creature_death_sound(cfg.impact_sound, x, creature.position, cfg.sound_volume_step);
|
|
||||||
notify("A " + creature.creature_type + " fell from the sky at " + creature.position + "!");
|
|
||||||
add_world_drop(creature.position, cfg.drop_type);
|
|
||||||
creature.health = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void update_flying_creatures() {
|
|
||||||
for (uint i = 0; i < flying_creatures.length(); i++) {
|
|
||||||
update_flying_creature(flying_creatures[i]);
|
|
||||||
|
|
||||||
if (flying_creatures[i].health <= 0) {
|
|
||||||
if (flying_creatures[i].state == "falling" && flying_creatures[i].height <= 0) {
|
|
||||||
flying_creatures.remove_at(i);
|
|
||||||
i--;
|
|
||||||
} else if (flying_creatures[i].state == "flying") {
|
|
||||||
flying_creatures.remove_at(i);
|
|
||||||
i--;
|
|
||||||
} else if (flying_creatures[i].state == "fading" && flying_creatures[i].ready_to_remove) {
|
|
||||||
flying_creatures.remove_at(i);
|
|
||||||
i--;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void attempt_hourly_flying_creature_spawn() {
|
|
||||||
for (uint i = 0; i < flying_creature_configs.length(); i++) {
|
|
||||||
FlyingCreatureConfig@ cfg = flying_creature_configs[i];
|
|
||||||
if (get_flying_creature_count(cfg.id) >= cfg.max_count) continue;
|
|
||||||
if (random(1, 100) <= cfg.hourly_spawn_chance) {
|
|
||||||
spawn_flying_creature(cfg.id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool damage_flying_creature_at(int pos, int damage) {
|
|
||||||
for (uint i = 0; i < flying_creatures.length(); i++) {
|
|
||||||
if (flying_creatures[i].position == pos && flying_creatures[i].state == "flying") {
|
|
||||||
FlyingCreatureConfig@ cfg = get_flying_creature_config(flying_creatures[i].creature_type);
|
|
||||||
if (cfg is null) return false;
|
|
||||||
|
|
||||||
flying_creatures[i].health -= damage;
|
|
||||||
if (flying_creatures[i].health <= 0) {
|
|
||||||
flying_creatures[i].state = "falling";
|
|
||||||
flying_creatures[i].fall_timer.restart();
|
|
||||||
|
|
||||||
if (flying_creatures[i].sound_handle != -1) {
|
|
||||||
p.destroy_sound(flying_creatures[i].sound_handle);
|
|
||||||
flying_creatures[i].sound_handle = -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
float pitch_percent = 50.0 + (50.0 * (float(flying_creatures[i].height) / float(cfg.max_height)));
|
|
||||||
flying_creatures[i].fall_sound_handle = p.play_extended_1d(cfg.fall_sound, x, pos, 0, 0, true, 0, 0.0, 0.0, pitch_percent);
|
|
||||||
if (flying_creatures[i].fall_sound_handle != -1) {
|
|
||||||
p.update_sound_positioning_values(flying_creatures[i].fall_sound_handle, -1.0, cfg.sound_volume_step, true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|||||||
Reference in New Issue
Block a user