diff --git a/draugnorak.nvgt b/draugnorak.nvgt index b12908c..0405a29 100644 --- a/draugnorak.nvgt +++ b/draugnorak.nvgt @@ -13,6 +13,11 @@ sound_pool p(100); #include "src/enemies/bandit.nvgt" #include "src/enemies/ground_game.nvgt" #include "src/enemies/flying_creatures.nvgt" +#include "src/world/world_drops.nvgt" +#include "src/world/world_snares.nvgt" +#include "src/world/world_fires.nvgt" +#include "src/world/world_buildings.nvgt" +#include "src/world/world_streams.nvgt" #include "src/world_state.nvgt" #include "src/ui.nvgt" #include "src/inventory.nvgt" diff --git a/src/world/world_buildings.nvgt b/src/world/world_buildings.nvgt new file mode 100644 index 0000000..444f5f5 --- /dev/null +++ b/src/world/world_buildings.nvgt @@ -0,0 +1,163 @@ +// World buildings - base structures that provide various benefits +// Includes Firepit, HerbGarden, Storage, Pasture, Stable, and Altar + +class WorldFirepit { + int position; + + WorldFirepit(int pos) { + position = pos; + } +} +WorldFirepit@[] world_firepits; + +class WorldHerbGarden { + int position; + + WorldHerbGarden(int pos) { + position = pos; + } +} +WorldHerbGarden@[] world_herb_gardens; + +class WorldStorage { + int position; + + WorldStorage(int pos) { + position = pos; + } +} +WorldStorage@[] world_storages; + +class WorldPasture { + int position; + + WorldPasture(int pos) { + position = pos; + } +} +WorldPasture@[] world_pastures; + +class WorldStable { + int position; + + WorldStable(int pos) { + position = pos; + } +} +WorldStable@[] world_stables; + +class WorldAltar { + int position; + + WorldAltar(int pos) { + position = pos; + } +} +WorldAltar@[] world_altars; + +// Add functions +void add_world_firepit(int pos) { + WorldFirepit@ fp = WorldFirepit(pos); + world_firepits.insert_last(fp); +} + +void add_world_herb_garden(int pos) { + WorldHerbGarden@ hg = WorldHerbGarden(pos); + world_herb_gardens.insert_last(hg); +} + +void add_world_storage(int pos) { + WorldStorage@ s = WorldStorage(pos); + world_storages.insert_last(s); +} + +void add_world_pasture(int pos) { + WorldPasture@ p = WorldPasture(pos); + world_pastures.insert_last(p); +} + +void add_world_stable(int pos) { + WorldStable@ s = WorldStable(pos); + world_stables.insert_last(s); +} + +void add_world_altar(int pos) { + WorldAltar@ a = WorldAltar(pos); + world_altars.insert_last(a); +} + +// Getter functions +WorldFirepit@ get_firepit_at(int pos) { + for (uint i = 0; i < world_firepits.length(); i++) { + if (world_firepits[i].position == pos) { + return @world_firepits[i]; + } + } + return null; +} + +WorldFirepit@ get_firepit_near(int pos, int range) { + // Check for firepit within specified range + for (int check_x = pos - range; check_x <= pos + range; check_x++) { + WorldFirepit@ firepit = get_firepit_at(check_x); + if (firepit != null) { + return @firepit; + } + } + return null; +} + +WorldHerbGarden@ get_herb_garden_at(int pos) { + for (uint i = 0; i < world_herb_gardens.length(); i++) { + if (world_herb_gardens[i].position == pos) { + return @world_herb_gardens[i]; + } + } + return null; +} + +WorldHerbGarden@ get_herb_garden_at_base() { + // Check if herb garden exists anywhere in base area (0-4) + for (uint i = 0; i < world_herb_gardens.length(); i++) { + if (world_herb_gardens[i].position <= BASE_END) { + return @world_herb_gardens[i]; + } + } + return null; +} + +WorldStorage@ get_storage_at(int pos) { + for (uint i = 0; i < world_storages.length(); i++) { + if (world_storages[i].position == pos) { + return @world_storages[i]; + } + } + return null; +} + +WorldPasture@ get_pasture_at(int pos) { + for (uint i = 0; i < world_pastures.length(); i++) { + if (world_pastures[i].position == pos) { + return @world_pastures[i]; + } + } + return null; +} + +WorldStable@ get_stable_at(int pos) { + for (uint i = 0; i < world_stables.length(); i++) { + if (world_stables[i].position == pos) { + return @world_stables[i]; + } + } + return null; +} + +WorldAltar@ get_altar_at(int pos) { + for (uint i = 0; i < world_altars.length(); i++) { + if (world_altars[i].position == pos) { + return @world_altars[i]; + } + } + return null; +} diff --git a/src/world/world_drops.nvgt b/src/world/world_drops.nvgt new file mode 100644 index 0000000..16c6437 --- /dev/null +++ b/src/world/world_drops.nvgt @@ -0,0 +1,104 @@ +// World drops - items that can be picked up from the world +// Includes item placement, sound management, and pickup logic + +class WorldDrop { + int position; + string type; + int sound_handle; + + WorldDrop(int pos, string t) { + position = pos; + type = t; + sound_handle = -1; + // Start looping item sound at position + sound_handle = p.play_1d("sounds/items/item.ogg", x, position, true); + if (sound_handle != -1) { + p.update_sound_positioning_values(sound_handle, -1.0, 3.0, true); + } + } + + void update() { + if (sound_handle == -1 || !p.sound_is_active(sound_handle)) { + sound_handle = p.play_1d("sounds/items/item.ogg", x, position, true); + if (sound_handle != -1) { + p.update_sound_positioning_values(sound_handle, -1.0, 3.0, true); + } + } else { + // Update source position for 1d sound + p.update_sound_1d(sound_handle, position); + } + } + + void destroy() { + if (sound_handle != -1) { + p.destroy_sound(sound_handle); + sound_handle = -1; + } + } +} +WorldDrop@[] world_drops; + +void add_world_drop(int pos, string type) { + WorldDrop@ d = WorldDrop(pos, type); + world_drops.insert_last(d); +} + +void update_world_drops() { + for (uint i = 0; i < world_drops.length(); i++) { + world_drops[i].update(); + } +} + +WorldDrop@ get_drop_at(int pos) { + for (uint i = 0; i < world_drops.length(); i++) { + if (world_drops[i].position == pos) { + return @world_drops[i]; + } + } + return null; +} + +void remove_drop_at(int pos) { + for (uint i = 0; i < world_drops.length(); i++) { + if (world_drops[i].position == pos) { + world_drops[i].destroy(); + world_drops.remove_at(i); + return; + } + } +} + +void clear_world_drops() { + for (uint i = 0; i < world_drops.length(); i++) { + world_drops[i].destroy(); + } + world_drops.resize(0); +} + +bool try_pickup_small_game(string game_type) { + if (inv_small_game >= get_personal_stack_limit()) { + speak_with_history("You can't carry any more small game.", true); + return false; + } + inv_small_game++; + inv_small_game_types.insert_last(game_type); + speak_with_history("Picked up " + game_type + ".", true); + return true; +} + +bool try_pickup_world_drop(WorldDrop@ drop) { + if (get_flying_creature_config_by_drop_type(drop.type) !is null) { + return try_pickup_small_game(drop.type); + } + if (drop.type == "boar carcass") { + if (inv_boar_carcasses >= get_personal_stack_limit()) { + speak_with_history("You can't carry any more boar carcasses.", true); + return false; + } + inv_boar_carcasses++; + speak_with_history("Picked up boar carcass.", true); + return true; + } + speak_with_history("Picked up " + drop.type + ".", true); + return true; +} diff --git a/src/world/world_fires.nvgt b/src/world/world_fires.nvgt new file mode 100644 index 0000000..6ad3040 --- /dev/null +++ b/src/world/world_fires.nvgt @@ -0,0 +1,130 @@ +// World fires - burning fires that require fuel +// Includes fire placement, fuel management, burnout logic, and sound + +class WorldFire { + int position; + int sound_handle; + timer fuel_timer; + int fuel_remaining; + bool low_fuel_warned; + + WorldFire(int pos) { + position = pos; + sound_handle = -1; + fuel_remaining = 720000; // Start with 12 minutes (12 hours in-game) + low_fuel_warned = false; + fuel_timer.restart(); + } + + void add_fuel(int amount) { + fuel_remaining += amount; + low_fuel_warned = false; + } + + bool is_burning() { + return fuel_remaining > 0; + } + + void update() { + // Update fuel + if (fuel_remaining > 0) { + int elapsed = fuel_timer.elapsed; + fuel_timer.restart(); + fuel_remaining -= elapsed; + + // Warn when fuel is low (30 seconds remaining) + if (!low_fuel_warned && fuel_remaining <= 30000 && fuel_remaining > 0) { + low_fuel_warned = true; + notify("Fire at x " + position + " y " + y + " is getting low!"); + } + + // Fire went out + if (fuel_remaining <= 0) { + fuel_remaining = 0; + notify("Fire at x " + position + " y " + y + " has gone out."); + if (sound_handle != -1) { + p.destroy_sound(sound_handle); + sound_handle = -1; + } + return; + } + } + + // Hard cutoff for fire sound. + if (is_burning()) { + int fire_distance = x - position; + if (fire_distance < 0) fire_distance = -fire_distance; + if (fire_distance <= FIRE_SOUND_RANGE) { + if (sound_handle == -1 || !p.sound_is_active(sound_handle)) { + sound_handle = p.play_1d("sounds/items/fire.ogg", x, position, true); + if (sound_handle != -1) { + p.update_sound_positioning_values(sound_handle, -1.0, FIRE_SOUND_VOLUME_STEP, true); + } + } + } else if (sound_handle != -1) { + p.destroy_sound(sound_handle); + sound_handle = -1; + } + } + } + + void destroy() { + if (sound_handle != -1) { + p.destroy_sound(sound_handle); + sound_handle = -1; + } + } +} +WorldFire@[] world_fires; + +void add_world_fire(int pos) { + WorldFire@ f = WorldFire(pos); + world_fires.insert_last(f); +} + +void update_fires() { + // Update all fires and remove any that have burned out + for (uint i = 0; i < world_fires.length(); i++) { + world_fires[i].update(); + } + + // Remove dead fires + for (uint i = 0; i < world_fires.length(); i++) { + if (!world_fires[i].is_burning()) { + world_fires[i].destroy(); + world_fires.remove_at(i); + i--; + } + } +} + +WorldFire@ get_fire_at(int pos) { + for (uint i = 0; i < world_fires.length(); i++) { + if (world_fires[i].position == pos) { + return @world_fires[i]; + } + } + return null; +} + +WorldFire@ get_fire_near(int pos) { + // Check for fire at current position or adjacent tiles + for (int check_x = pos - 1; check_x <= pos + 1; check_x++) { + WorldFire@ fire = get_fire_at(check_x); + if (fire != null && fire.is_burning()) { + return @fire; + } + } + return null; +} + +WorldFire@ get_fire_within_range(int pos, int range) { + // Check for fire within specified range + for (int check_x = pos - range; check_x <= pos + range; check_x++) { + WorldFire@ fire = get_fire_at(check_x); + if (fire != null && fire.is_burning()) { + return @fire; + } + } + return null; +} diff --git a/src/world/world_snares.nvgt b/src/world/world_snares.nvgt new file mode 100644 index 0000000..27218a5 --- /dev/null +++ b/src/world/world_snares.nvgt @@ -0,0 +1,141 @@ +// World snares - traps for catching small game +// Includes snare placement, catch mechanics, escape logic, and collision detection + +// Small game types that can be caught in snares +string[] small_game_types = {"rabbit", "squirrel", "raccoon", "opossum", "groundhog"}; + +string get_random_small_game() { + int index = random(0, small_game_types.length() - 1); + return small_game_types[index]; +} + +class WorldSnare { + int position; + bool has_catch; + string catch_type; // What type of small game was caught + int catch_chance; + int escape_chance; + int sound_handle; + timer minute_timer; + bool active; // To prevent immediate breakage on placement + + WorldSnare(int pos) { + position = pos; + has_catch = false; + catch_type = ""; + catch_chance = 5; + escape_chance = 5; + active = false; // Becomes active when player steps off + sound_handle = -1; + + minute_timer.restart(); + } + + void update() { + // Activate if player moves away + if (!active && x != position) { + active = true; + minute_timer.restart(); + } + + int snare_distance = x - position; + if (snare_distance < 0) snare_distance = -snare_distance; + + if (snare_distance <= SNARE_SOUND_RANGE) { + if (sound_handle == -1 || !p.sound_is_active(sound_handle)) { + sound_handle = p.play_1d("sounds/actions/set_snare.ogg", x, position, true); + if (sound_handle != -1) { + p.update_sound_positioning_values(sound_handle, SNARE_SOUND_PAN_STEP, SNARE_SOUND_VOLUME_STEP, true); + } + } + } else if (sound_handle != -1) { + p.destroy_sound(sound_handle); + sound_handle = -1; + } + + // Every minute logic (only when active) + if (active && minute_timer.elapsed >= 60000) { + minute_timer.restart(); + + if (has_catch) { + // Animal trying to escape + if (escape_chance < 95) escape_chance += 5; + + int roll = random(1, 100); + if (roll <= escape_chance) { + // Animal escaped! + notify("A " + catch_type + " escaped from your snare at x " + position + " y 0!"); + remove_snare_at(position); + return; + } + } else { + // Trying to catch small game + if (catch_chance < 75) catch_chance += 5; + + int roll = random(1, 100); + if (roll <= catch_chance) { + // Caught something! + has_catch = true; + catch_type = get_random_small_game(); + escape_chance = 5; // Reset escape chance + notify(catch_type + " caught in snare at x " + position + " y 0!"); + } + } + } + } + + void destroy() { + if (sound_handle != -1) { + p.destroy_sound(sound_handle); + sound_handle = -1; + } + } +} +WorldSnare@[] world_snares; + +void add_world_snare(int pos) { + WorldSnare@ s = WorldSnare(pos); + world_snares.insert_last(s); +} + +WorldSnare@ get_snare_at(int pos) { + for (uint i = 0; i < world_snares.length(); i++) { + if (world_snares[i].position == pos) { + return @world_snares[i]; + } + } + return null; +} + +void remove_snare_at(int pos) { + for (uint i = 0; i < world_snares.length(); i++) { + if (world_snares[i].position == pos) { + world_snares[i].destroy(); + world_snares.remove_at(i); + return; + } + } +} + +// Called when player moves onto a tile +void check_snare_collision(int player_x) { + WorldSnare@ s = get_snare_at(player_x); + if (s != null && s.active) { + // Break the snare + p.play_stationary("sounds/actions/break_snare.ogg", false); + + if (s.has_catch) { + speak_with_history("You stepped on your snare! The " + s.catch_type + " escaped.", true); + } else { + speak_with_history("You stepped on your snare and broke it!", true); + } + + remove_snare_at(player_x); + } +} + +void update_snares() { + for (int i = int(world_snares.length()) - 1; i >= 0; i--) { + world_snares[i].update(); + } +} diff --git a/src/world/world_streams.nvgt b/src/world/world_streams.nvgt new file mode 100644 index 0000000..c9639ed --- /dev/null +++ b/src/world/world_streams.nvgt @@ -0,0 +1,84 @@ +// World streams - flowing water for fishing and geese spawning +// Includes stream placement, sound management, and position queries + +class WorldStream { + int start_position; + int end_position; + int sound_handle; + int sound_position; + + WorldStream(int start_pos, int width) { + start_position = start_pos; + end_position = start_pos + width - 1; + sound_handle = -1; + sound_position = -1; + } + + bool contains_position(int pos) { + return pos >= start_position && pos <= end_position; + } + + int get_width() { + return end_position - start_position + 1; + } + + int get_center_position() { + return (start_position + end_position) / 2; + } + + void update() { + int sound_pos = 0; + if (x < start_position) { + sound_pos = start_position; + } else if (x > end_position) { + sound_pos = end_position; + } else { + sound_pos = x; + } + + // Keep stream sound active so distance-based fade can work. + if (sound_handle == -1 || !p.sound_is_active(sound_handle)) { + sound_handle = p.play_1d("sounds/terrain/stream.ogg", x, sound_pos, true); + sound_position = sound_pos; + if (sound_handle != -1) { + p.update_sound_positioning_values(sound_handle, -1.0, STREAM_SOUND_VOLUME_STEP, true); + } + } else if (sound_position != sound_pos) { + p.update_sound_1d(sound_handle, sound_pos); + sound_position = sound_pos; + } + } + + void destroy() { + if (sound_handle != -1) { + p.destroy_sound(sound_handle); + sound_handle = -1; + } + sound_position = -1; + } +} +WorldStream@[] world_streams; + +void add_world_stream(int start_pos, int width) { + WorldStream@ s = WorldStream(start_pos, width); + world_streams.insert_last(s); +} + +void update_streams() { + for (uint i = 0; i < world_streams.length(); i++) { + world_streams[i].update(); + } +} + +WorldStream@ get_stream_at(int pos) { + for (uint i = 0; i < world_streams.length(); i++) { + if (world_streams[i].contains_position(pos)) { + return @world_streams[i]; + } + } + return null; +} + +bool is_position_in_water(int pos) { + return get_stream_at(pos) != null; +} diff --git a/src/world_state.nvgt b/src/world_state.nvgt index 876fd1c..96829ae 100644 --- a/src/world_state.nvgt +++ b/src/world_state.nvgt @@ -1,461 +1,14 @@ // World Objects -// Small game types that can be caught in snares -string[] small_game_types = {"rabbit", "squirrel", "raccoon", "opossum", "groundhog"}; - int barricade_health = 0; bool barricade_initialized = false; int residents_count = 0; -string get_random_small_game() { - int index = random(0, small_game_types.length() - 1); - return small_game_types[index]; -} -class WorldDrop { - int position; - string type; - int sound_handle; - - WorldDrop(int pos, string t) { - position = pos; - type = t; - sound_handle = -1; - // Start looping item sound at position - sound_handle = p.play_1d("sounds/items/item.ogg", x, position, true); - if (sound_handle != -1) { - p.update_sound_positioning_values(sound_handle, -1.0, 3.0, true); - } - } - - void update() { - if (sound_handle == -1 || !p.sound_is_active(sound_handle)) { - sound_handle = p.play_1d("sounds/items/item.ogg", x, position, true); - if (sound_handle != -1) { - p.update_sound_positioning_values(sound_handle, -1.0, 3.0, true); - } - } else { - // Update source position for 1d sound - p.update_sound_1d(sound_handle, position); - } - } - - void destroy() { - if (sound_handle != -1) { - p.destroy_sound(sound_handle); - sound_handle = -1; - } - } -} -WorldDrop@[] world_drops; -void add_world_drop(int pos, string type) { - WorldDrop@ d = WorldDrop(pos, type); - world_drops.insert_last(d); -} -void update_world_drops() { - for (uint i = 0; i < world_drops.length(); i++) { - world_drops[i].update(); - } -} -WorldDrop@ get_drop_at(int pos) { - for (uint i = 0; i < world_drops.length(); i++) { - if (world_drops[i].position == pos) { - return @world_drops[i]; - } - } - return null; -} -void remove_drop_at(int pos) { - for (uint i = 0; i < world_drops.length(); i++) { - if (world_drops[i].position == pos) { - world_drops[i].destroy(); - world_drops.remove_at(i); - return; - } - } -} - -void clear_world_drops() { - for (uint i = 0; i < world_drops.length(); i++) { - world_drops[i].destroy(); - } - world_drops.resize(0); -} - -bool try_pickup_small_game(string game_type) { - if (inv_small_game >= get_personal_stack_limit()) { - speak_with_history("You can't carry any more small game.", true); - return false; - } - inv_small_game++; - inv_small_game_types.insert_last(game_type); - speak_with_history("Picked up " + game_type + ".", true); - return true; -} - -bool try_pickup_world_drop(WorldDrop@ drop) { - if (get_flying_creature_config_by_drop_type(drop.type) !is null) { - return try_pickup_small_game(drop.type); - } - if (drop.type == "boar carcass") { - if (inv_boar_carcasses >= get_personal_stack_limit()) { - speak_with_history("You can't carry any more boar carcasses.", true); - return false; - } - inv_boar_carcasses++; - speak_with_history("Picked up boar carcass.", true); - return true; - } - speak_with_history("Picked up " + drop.type + ".", true); - return true; -} - -class WorldSnare { - int position; - bool has_catch; - string catch_type; // What type of small game was caught - int catch_chance; - int escape_chance; - int sound_handle; - timer minute_timer; - bool active; // To prevent immediate breakage on placement - - WorldSnare(int pos) { - position = pos; - has_catch = false; - catch_type = ""; - catch_chance = 5; - escape_chance = 5; - active = false; // Becomes active when player steps off - sound_handle = -1; - - minute_timer.restart(); - } - - void update() { - // Activate if player moves away - if (!active && x != position) { - active = true; - minute_timer.restart(); - } - - int snare_distance = x - position; - if (snare_distance < 0) snare_distance = -snare_distance; - - if (snare_distance <= SNARE_SOUND_RANGE) { - if (sound_handle == -1 || !p.sound_is_active(sound_handle)) { - sound_handle = p.play_1d("sounds/actions/set_snare.ogg", x, position, true); - if (sound_handle != -1) { - p.update_sound_positioning_values(sound_handle, SNARE_SOUND_PAN_STEP, SNARE_SOUND_VOLUME_STEP, true); - } - } - } else if (sound_handle != -1) { - p.destroy_sound(sound_handle); - sound_handle = -1; - } - - // Every minute logic (only when active) - if (active && minute_timer.elapsed >= 60000) { - minute_timer.restart(); - - if (has_catch) { - // Animal trying to escape - if (escape_chance < 95) escape_chance += 5; - - int roll = random(1, 100); - if (roll <= escape_chance) { - // Animal escaped! - notify("A " + catch_type + " escaped from your snare at x " + position + " y 0!"); - remove_snare_at(position); - return; - } - } else { - // Trying to catch small game - if (catch_chance < 75) catch_chance += 5; - - int roll = random(1, 100); - if (roll <= catch_chance) { - // Caught something! - has_catch = true; - catch_type = get_random_small_game(); - escape_chance = 5; // Reset escape chance - notify(catch_type + " caught in snare at x " + position + " y 0!"); - } - } - } - } - - void destroy() { - if (sound_handle != -1) { - p.destroy_sound(sound_handle); - sound_handle = -1; - } - } -} -WorldSnare@[] world_snares; - -class WorldFire { - int position; - int sound_handle; - timer fuel_timer; - int fuel_remaining; - bool low_fuel_warned; - - WorldFire(int pos) { - position = pos; - sound_handle = -1; - fuel_remaining = 720000; // Start with 12 minutes (12 hours in-game) - low_fuel_warned = false; - fuel_timer.restart(); - } - - void add_fuel(int amount) { - fuel_remaining += amount; - low_fuel_warned = false; - } - - bool is_burning() { - return fuel_remaining > 0; - } - - void update() { - // Update fuel - if (fuel_remaining > 0) { - int elapsed = fuel_timer.elapsed; - fuel_timer.restart(); - fuel_remaining -= elapsed; - - // Warn when fuel is low (30 seconds remaining) - if (!low_fuel_warned && fuel_remaining <= 30000 && fuel_remaining > 0) { - low_fuel_warned = true; - notify("Fire at x " + position + " y " + y + " is getting low!"); - } - - // Fire went out - if (fuel_remaining <= 0) { - fuel_remaining = 0; - notify("Fire at x " + position + " y " + y + " has gone out."); - if (sound_handle != -1) { - p.destroy_sound(sound_handle); - sound_handle = -1; - } - return; - } - } - - // Hard cutoff for fire sound. - if (is_burning()) { - int fire_distance = x - position; - if (fire_distance < 0) fire_distance = -fire_distance; - if (fire_distance <= FIRE_SOUND_RANGE) { - if (sound_handle == -1 || !p.sound_is_active(sound_handle)) { - sound_handle = p.play_1d("sounds/items/fire.ogg", x, position, true); - if (sound_handle != -1) { - p.update_sound_positioning_values(sound_handle, -1.0, FIRE_SOUND_VOLUME_STEP, true); - } - } - } else if (sound_handle != -1) { - p.destroy_sound(sound_handle); - sound_handle = -1; - } - } - } - - void destroy() { - if (sound_handle != -1) { - p.destroy_sound(sound_handle); - sound_handle = -1; - } - } -} -WorldFire@[] world_fires; - -class WorldFirepit { - int position; - - WorldFirepit(int pos) { - position = pos; - } -} -WorldFirepit@[] world_firepits; - -class WorldHerbGarden { - int position; - - WorldHerbGarden(int pos) { - position = pos; - } -} -WorldHerbGarden@[] world_herb_gardens; - -class WorldStorage { - int position; - - WorldStorage(int pos) { - position = pos; - } -} -WorldStorage@[] world_storages; - -class WorldPasture { - int position; - - WorldPasture(int pos) { - position = pos; - } -} -WorldPasture@[] world_pastures; - -class WorldStable { - int position; - - WorldStable(int pos) { - position = pos; - } -} -WorldStable@[] world_stables; - -class WorldAltar { - int position; - - WorldAltar(int pos) { - position = pos; - } -} -WorldAltar@[] world_altars; - -class WorldStream { - int start_position; - int end_position; - int sound_handle; - int sound_position; - - WorldStream(int start_pos, int width) { - start_position = start_pos; - end_position = start_pos + width - 1; - sound_handle = -1; - sound_position = -1; - } - - bool contains_position(int pos) { - return pos >= start_position && pos <= end_position; - } - - int get_width() { - return end_position - start_position + 1; - } - - int get_center_position() { - return (start_position + end_position) / 2; - } - - void update() { - int sound_pos = 0; - if (x < start_position) { - sound_pos = start_position; - } else if (x > end_position) { - sound_pos = end_position; - } else { - sound_pos = x; - } - - // Keep stream sound active so distance-based fade can work. - if (sound_handle == -1 || !p.sound_is_active(sound_handle)) { - sound_handle = p.play_1d("sounds/terrain/stream.ogg", x, sound_pos, true); - sound_position = sound_pos; - if (sound_handle != -1) { - p.update_sound_positioning_values(sound_handle, -1.0, STREAM_SOUND_VOLUME_STEP, true); - } - } else if (sound_position != sound_pos) { - p.update_sound_1d(sound_handle, sound_pos); - sound_position = sound_pos; - } - } - - void destroy() { - if (sound_handle != -1) { - p.destroy_sound(sound_handle); - sound_handle = -1; - } - sound_position = -1; - } -} -WorldStream@[] world_streams; - -void add_world_snare(int pos) { - WorldSnare@ s = WorldSnare(pos); - world_snares.insert_last(s); -} - -void add_world_fire(int pos) { - WorldFire@ f = WorldFire(pos); - world_fires.insert_last(f); -} - -void add_world_firepit(int pos) { - WorldFirepit@ fp = WorldFirepit(pos); - world_firepits.insert_last(fp); -} - -void add_world_storage(int pos) { - WorldStorage@ s = WorldStorage(pos); - world_storages.insert_last(s); -} - -void add_world_pasture(int pos) { - WorldPasture@ p = WorldPasture(pos); - world_pastures.insert_last(p); -} - -void add_world_stable(int pos) { - WorldStable@ s = WorldStable(pos); - world_stables.insert_last(s); -} - -void add_world_altar(int pos) { - WorldAltar@ a = WorldAltar(pos); - world_altars.insert_last(a); -} - -WorldStorage@ get_storage_at(int pos) { - for (uint i = 0; i < world_storages.length(); i++) { - if (world_storages[i].position == pos) { - return @world_storages[i]; - } - } - return null; -} - -WorldPasture@ get_pasture_at(int pos) { - for (uint i = 0; i < world_pastures.length(); i++) { - if (world_pastures[i].position == pos) { - return @world_pastures[i]; - } - } - return null; -} - -WorldStable@ get_stable_at(int pos) { - for (uint i = 0; i < world_stables.length(); i++) { - if (world_stables[i].position == pos) { - return @world_stables[i]; - } - } - return null; -} - -WorldAltar@ get_altar_at(int pos) { - for (uint i = 0; i < world_altars.length(); i++) { - if (world_altars[i].position == pos) { - return @world_altars[i]; - } - } - return null; -} void update_world_objects() { for (uint i = 0; i < world_snares.length(); i++) { @@ -466,125 +19,9 @@ void update_world_objects() { } } -WorldSnare@ get_snare_at(int pos) { - for (uint i = 0; i < world_snares.length(); i++) { - if (world_snares[i].position == pos) { - return @world_snares[i]; - } - } - return null; -} -void remove_snare_at(int pos) { - for (uint i = 0; i < world_snares.length(); i++) { - if (world_snares[i].position == pos) { - world_snares[i].destroy(); - world_snares.remove_at(i); - return; - } - } -} -// Called when player moves onto a tile -void check_snare_collision(int player_x) { - WorldSnare@ s = get_snare_at(player_x); - if (s != null && s.active) { - // Break the snare - p.play_stationary("sounds/actions/break_snare.ogg", false); - if (s.has_catch) { - speak_with_history("You stepped on your snare! The " + s.catch_type + " escaped.", true); - } else { - speak_with_history("You stepped on your snare and broke it!", true); - } - - remove_snare_at(player_x); - } -} - -void update_snares() { - for (int i = int(world_snares.length()) - 1; i >= 0; i--) { - world_snares[i].update(); - } -} - -void update_streams() { - for (uint i = 0; i < world_streams.length(); i++) { - world_streams[i].update(); - } -} - -void update_fires() { - // Update all fires and remove any that have burned out - for (uint i = 0; i < world_fires.length(); i++) { - world_fires[i].update(); - } - - // Remove dead fires - for (uint i = 0; i < world_fires.length(); i++) { - if (!world_fires[i].is_burning()) { - world_fires[i].destroy(); - world_fires.remove_at(i); - i--; - } - } -} - -WorldFire@ get_fire_at(int pos) { - for (uint i = 0; i < world_fires.length(); i++) { - if (world_fires[i].position == pos) { - return @world_fires[i]; - } - } - return null; -} - -WorldFire@ get_fire_near(int pos) { - // Check for fire at current position or adjacent tiles - for (int check_x = pos - 1; check_x <= pos + 1; check_x++) { - WorldFire@ fire = get_fire_at(check_x); - if (fire != null && fire.is_burning()) { - return @fire; - } - } - return null; -} - -WorldFire@ get_fire_within_range(int pos, int range) { - // Check for fire within specified range - for (int check_x = pos - range; check_x <= pos + range; check_x++) { - WorldFire@ fire = get_fire_at(check_x); - if (fire != null && fire.is_burning()) { - return @fire; - } - } - return null; -} - -WorldFirepit@ get_firepit_at(int pos) { - for (uint i = 0; i < world_firepits.length(); i++) { - if (world_firepits[i].position == pos) { - return @world_firepits[i]; - } - } - return null; -} - -WorldFirepit@ get_firepit_near(int pos, int range) { - // Check for firepit within specified range - for (int check_x = pos - range; check_x <= pos + range; check_x++) { - WorldFirepit@ firepit = get_firepit_at(check_x); - if (firepit != null) { - return @firepit; - } - } - return null; -} - -void add_world_herb_garden(int pos) { - WorldHerbGarden@ hg = WorldHerbGarden(pos); - world_herb_gardens.insert_last(hg); -} void init_barricade() { if (barricade_initialized) { @@ -611,46 +48,10 @@ int add_barricade_health(int amount) { // Zombie functions moved to src/enemies/undead.nvgt -WorldHerbGarden@ get_herb_garden_at(int pos) { - for (uint i = 0; i < world_herb_gardens.length(); i++) { - if (world_herb_gardens[i].position == pos) { - return @world_herb_gardens[i]; - } - } - return null; -} - -WorldHerbGarden@ get_herb_garden_at_base() { - // Check if herb garden exists anywhere in base area (0-4) - for (uint i = 0; i < world_herb_gardens.length(); i++) { - if (world_herb_gardens[i].position <= BASE_END) { - return @world_herb_gardens[i]; - } - } - return null; -} // Bandit functions moved to src/enemies/bandit.nvgt // Boar/GroundGame functions moved to src/enemies/ground_game.nvgt -// Stream Functions -void add_world_stream(int start_pos, int width) { - WorldStream@ s = WorldStream(start_pos, width); - world_streams.insert_last(s); -} - -WorldStream@ get_stream_at(int pos) { - for (uint i = 0; i < world_streams.length(); i++) { - if (world_streams[i].contains_position(pos)) { - return @world_streams[i]; - } - } - return null; -} - -bool is_position_in_water(int pos) { - return get_stream_at(pos) != null; -} // Mountain Range Class class MountainRange {