From 5c107633595b8e48490fba74dc63307757694e0f Mon Sep 17 00:00:00 2001 From: Storm Dragon Date: Wed, 21 Jan 2026 19:25:28 -0500 Subject: [PATCH] Even more refactor. Getting close to done. --- draugnorak.nvgt | 2 + src/world/barricade.nvgt | 29 +++ src/world/mountains.nvgt | 302 ++++++++++++++++++++++++++++++++ src/world_state.nvgt | 369 +++------------------------------------ 4 files changed, 354 insertions(+), 348 deletions(-) create mode 100644 src/world/barricade.nvgt create mode 100644 src/world/mountains.nvgt diff --git a/draugnorak.nvgt b/draugnorak.nvgt index 0405a29..a576495 100644 --- a/draugnorak.nvgt +++ b/draugnorak.nvgt @@ -18,6 +18,8 @@ sound_pool p(100); #include "src/world/world_fires.nvgt" #include "src/world/world_buildings.nvgt" #include "src/world/world_streams.nvgt" +#include "src/world/mountains.nvgt" +#include "src/world/barricade.nvgt" #include "src/world_state.nvgt" #include "src/ui.nvgt" #include "src/inventory.nvgt" diff --git a/src/world/barricade.nvgt b/src/world/barricade.nvgt new file mode 100644 index 0000000..6ee91c4 --- /dev/null +++ b/src/world/barricade.nvgt @@ -0,0 +1,29 @@ +// Barricade - base defense system +// Protects base from zombies and bandits with reinforceable health + +int barricade_health = 0; +bool barricade_initialized = false; +int residents_count = 0; + +void init_barricade() { + if (barricade_initialized) { + return; + } + + barricade_health = BARRICADE_BASE_HEALTH; + barricade_initialized = true; +} + +int add_barricade_health(int amount) { + if (amount <= 0) { + return 0; + } + + int before = barricade_health; + barricade_health += amount; + if (barricade_health > BARRICADE_MAX_HEALTH) { + barricade_health = BARRICADE_MAX_HEALTH; + } + + return barricade_health - before; +} diff --git a/src/world/mountains.nvgt b/src/world/mountains.nvgt new file mode 100644 index 0000000..91d2a63 --- /dev/null +++ b/src/world/mountains.nvgt @@ -0,0 +1,302 @@ +// Mountain ranges - elevated terrain with variable slopes and streams +// Includes procedural generation, elevation management, and stream placement + +class MountainRange { + int start_position; + int end_position; + int[] elevations; + string[] terrain_types; + int[] stream_positions; + int stream_sound_handle; + int stream_sound_position; + + MountainRange(int start_pos, int size) { + start_position = start_pos; + end_position = start_pos + size - 1; + elevations.resize(size); + terrain_types.resize(size); + stream_sound_handle = -1; + stream_sound_position = -1; + generate_terrain(); + } + + void generate_terrain() { + int size = int(elevations.length()); + + // Initialize endpoints at low elevations (easier transition from flat ground) + elevations[0] = random(0, 5); + elevations[size - 1] = random(0, 5); + + // Use midpoint displacement for natural terrain + midpoint_displace(0, size - 1, 25); + + // Clamp values to valid range + for (int i = 0; i < size; i++) { + if (elevations[i] < MOUNTAIN_MIN_ELEVATION) elevations[i] = MOUNTAIN_MIN_ELEVATION; + if (elevations[i] > MOUNTAIN_MAX_ELEVATION) elevations[i] = MOUNTAIN_MAX_ELEVATION; + } + + // Smooth to enforce max slope constraint + smooth_slopes(); + + // Ensure at least one steep climb (≥7 feet) reaches into 10-20 elevation range + ensure_steep_climb(); + + // Assign terrain types based on elevation + for (int i = 0; i < size; i++) { + if (elevations[i] > 20) { + terrain_types[i] = "snow"; + } else if (elevations[i] > 8) { + terrain_types[i] = "stone"; + } else { + terrain_types[i] = "gravel"; + } + } + + // Place streams in valley areas + place_streams(); + } + + void midpoint_displace(int left, int right, int roughness) { + if (right - left <= 1) return; + + int mid = (left + right) / 2; + int avg = (elevations[left] + elevations[right]) / 2; + int displacement = random(-roughness, roughness); + elevations[mid] = avg + displacement; + + int new_roughness = roughness * 7 / 10; + if (new_roughness < 1) new_roughness = 1; + + midpoint_displace(left, mid, new_roughness); + midpoint_displace(mid, right, new_roughness); + } + + void smooth_slopes() { + bool changed = true; + int iterations = 0; + while (changed && iterations < 100) { + changed = false; + iterations++; + + for (int i = 1; i < int(elevations.length()); i++) { + int diff = elevations[i] - elevations[i-1]; + if (diff > MOUNTAIN_MAX_SLOPE) { + elevations[i] = elevations[i-1] + MOUNTAIN_MAX_SLOPE; + changed = true; + } else if (diff < -MOUNTAIN_MAX_SLOPE) { + elevations[i] = elevations[i-1] - MOUNTAIN_MAX_SLOPE; + changed = true; + } + } + } + } + + void place_streams() { + // Find valley bottoms (local minima) + int[] valleys; + for (int i = 2; i < int(elevations.length()) - 2; i++) { + if (elevations[i] < elevations[i-1] && elevations[i] < elevations[i+1] && + elevations[i] < elevations[i-2] && elevations[i] < elevations[i+2]) { + valleys.insert_last(i); + } + } + + // Place 1-3 streams in valleys + int num_streams = random(1, 3); + if (num_streams > int(valleys.length())) num_streams = int(valleys.length()); + + for (int i = 0; i < num_streams && valleys.length() > 0; i++) { + int idx = random(0, int(valleys.length()) - 1); + stream_positions.insert_last(valleys[idx]); + valleys.remove_at(idx); + } + } + + void ensure_steep_climb() { + int size = int(elevations.length()); + + // Check if we already have a steep climb (≥7) to/from 10-20 elevation + bool has_steep_climb = false; + for (int i = 1; i < size; i++) { + int diff = elevations[i] - elevations[i-1]; + if (diff < 0) diff = -diff; + + if (diff >= MOUNTAIN_STEEP_THRESHOLD) { + // Check if either elevation is in 10-20 range + if ((elevations[i] >= 10 && elevations[i] <= 20) || + (elevations[i-1] >= 10 && elevations[i-1] <= 20)) { + has_steep_climb = true; + break; + } + } + } + + // If no steep climb to 10-20 range, create one + if (!has_steep_climb && size >= 10) { + // Pick a random position in the middle 60% of the mountain + int pos = random(int(size * 0.2), int(size * 0.8)); + + // Set up a steep climb: create elevation 15-18 with steep approach + int target_elevation = random(15, 18); + elevations[pos] = target_elevation; + + // Make the approach from left steep (if possible) + if (pos > 0) { + elevations[pos - 1] = target_elevation - MOUNTAIN_STEEP_THRESHOLD - 1; + if (elevations[pos - 1] < MOUNTAIN_MIN_ELEVATION) { + elevations[pos - 1] = MOUNTAIN_MIN_ELEVATION; + } + } + + // Make the approach from right steep (if possible) + if (pos < size - 1) { + elevations[pos + 1] = target_elevation - MOUNTAIN_STEEP_THRESHOLD - 1; + if (elevations[pos + 1] < MOUNTAIN_MIN_ELEVATION) { + elevations[pos + 1] = MOUNTAIN_MIN_ELEVATION; + } + } + + // Re-smooth to ensure slopes aren't too extreme elsewhere + smooth_slopes(); + } + + // Ensure edge transitions aren't too steep (max 20 feet from flat ground) + // First transition: tile 0 to tile 1 + if (size >= 2) { + int first_diff = elevations[1] - elevations[0]; + if (first_diff < 0) first_diff = -first_diff; + if (first_diff > 20) { + if (elevations[1] > elevations[0]) { + elevations[1] = elevations[0] + 20; + } else { + elevations[1] = elevations[0] - 20; + } + } + } + + // Last transition: tile size-2 to tile size-1 + if (size >= 2) { + int last_diff = elevations[size-1] - elevations[size-2]; + if (last_diff < 0) last_diff = -last_diff; + if (last_diff > 20) { + if (elevations[size-2] > elevations[size-1]) { + elevations[size-2] = elevations[size-1] + 20; + } else { + elevations[size-2] = elevations[size-1] - 20; + } + } + } + } + + int get_elevation_at(int world_x) { + if (world_x < start_position || world_x > end_position) return 0; + int index = world_x - start_position; + return elevations[index]; + } + + string get_terrain_at(int world_x) { + if (world_x < start_position || world_x > end_position) return "stone"; + int index = world_x - start_position; + return terrain_types[index]; + } + + int get_elevation_change(int from_x, int to_x) { + int from_elev = get_elevation_at(from_x); + int to_elev = get_elevation_at(to_x); + return to_elev - from_elev; + } + + bool is_steep_section(int from_x, int to_x) { + int change = get_elevation_change(from_x, to_x); + if (change < 0) change = -change; + return change >= MOUNTAIN_STEEP_THRESHOLD; + } + + bool contains_position(int world_x) { + return world_x >= start_position && world_x <= end_position; + } + + bool is_stream_at(int world_x) { + if (!contains_position(world_x)) return false; + int index = world_x - start_position; + for (uint i = 0; i < stream_positions.length(); i++) { + if (stream_positions[i] == index) return true; + } + return false; + } + + void update() { + if (stream_positions.length() == 0) return; + + // Find nearest stream to player + int nearest_stream = -1; + int nearest_distance = 999; + + for (uint i = 0; i < stream_positions.length(); i++) { + int stream_world_x = start_position + stream_positions[i]; + int distance = abs(x - stream_world_x); + if (distance < nearest_distance) { + nearest_distance = distance; + nearest_stream = stream_world_x; + } + } + + // Keep nearest stream sound active so distance-based fade can work. + if (nearest_stream != -1) { + if (stream_sound_handle == -1 || !p.sound_is_active(stream_sound_handle)) { + stream_sound_handle = p.play_1d("sounds/terrain/stream.ogg", x, nearest_stream, true); + stream_sound_position = nearest_stream; + if (stream_sound_handle != -1) { + p.update_sound_positioning_values(stream_sound_handle, -1.0, MOUNTAIN_STREAM_VOLUME_STEP, true); + } + } else if (stream_sound_position != nearest_stream) { + p.update_sound_1d(stream_sound_handle, nearest_stream); + stream_sound_position = nearest_stream; + } + } + } + + void destroy() { + if (stream_sound_handle != -1) { + p.destroy_sound(stream_sound_handle); + stream_sound_handle = -1; + } + stream_sound_position = -1; + } +} +MountainRange@[] world_mountains; + +MountainRange@ get_mountain_at(int world_x) { + for (uint i = 0; i < world_mountains.length(); i++) { + if (world_mountains[i].contains_position(world_x)) { + return @world_mountains[i]; + } + } + return null; +} + +int get_mountain_elevation_at(int world_x) { + MountainRange@ mountain = get_mountain_at(world_x); + if (mountain is null) return 0; + return mountain.get_elevation_at(world_x); +} + +bool is_mountain_stream_at(int world_x) { + MountainRange@ mountain = get_mountain_at(world_x); + if (mountain is null) return false; + return mountain.is_stream_at(world_x); +} + +void update_mountains() { + for (uint i = 0; i < world_mountains.length(); i++) { + world_mountains[i].update(); + } +} + +void clear_mountains() { + for (uint i = 0; i < world_mountains.length(); i++) { + world_mountains[i].destroy(); + } + world_mountains.resize(0); +} diff --git a/src/world_state.nvgt b/src/world_state.nvgt index 96829ae..cafdb35 100644 --- a/src/world_state.nvgt +++ b/src/world_state.nvgt @@ -1,15 +1,25 @@ -// World Objects - -int barricade_health = 0; -bool barricade_initialized = false; -int residents_count = 0; - - - - - - +// World State Orchestrator +// +// This file coordinates all world object updates that were previously handled here. +// Individual world objects have been split into focused modules: +// +// Enemies (src/enemies/): +// - undead.nvgt - Undead creatures (zombies, future: vampires, ghosts) +// - bandit.nvgt - Bandit enemies with aggressive/wandering states +// - ground_game.nvgt - Ground animals (boars, future: mountain goats, rams) +// - flying_creatures.nvgt - Flying creatures (geese, config-driven) +// +// World Objects (src/world/): +// - world_drops.nvgt - Item drops with pickup logic +// - world_snares.nvgt - Traps for catching small game +// - world_fires.nvgt - Fires with fuel management +// - world_buildings.nvgt - Base buildings (Firepit, HerbGarden, Storage, etc.) +// - world_streams.nvgt - Streams for fishing and creature spawning +// - mountains.nvgt - Mountain ranges with procedural terrain generation +// - barricade.nvgt - Base defense system +// Legacy update function - retained for compatibility +// Note: This is largely obsolete as individual update functions are called from main loop void update_world_objects() { for (uint i = 0; i < world_snares.length(); i++) { world_snares[i].update(); @@ -18,340 +28,3 @@ void update_world_objects() { world_fires[i].update(); } } - - - - - -void init_barricade() { - if (barricade_initialized) { - return; - } - - barricade_health = BARRICADE_BASE_HEALTH; - barricade_initialized = true; -} - -int add_barricade_health(int amount) { - if (amount <= 0) { - return 0; - } - - int before = barricade_health; - barricade_health += amount; - if (barricade_health > BARRICADE_MAX_HEALTH) { - barricade_health = BARRICADE_MAX_HEALTH; - } - - return barricade_health - before; -} - -// Zombie functions moved to src/enemies/undead.nvgt - - -// Bandit functions moved to src/enemies/bandit.nvgt -// Boar/GroundGame functions moved to src/enemies/ground_game.nvgt - - -// Mountain Range Class -class MountainRange { - int start_position; - int end_position; - int[] elevations; - string[] terrain_types; - int[] stream_positions; - int stream_sound_handle; - int stream_sound_position; - - MountainRange(int start_pos, int size) { - start_position = start_pos; - end_position = start_pos + size - 1; - elevations.resize(size); - terrain_types.resize(size); - stream_sound_handle = -1; - stream_sound_position = -1; - generate_terrain(); - } - - void generate_terrain() { - int size = int(elevations.length()); - - // Initialize endpoints at low elevations (easier transition from flat ground) - elevations[0] = random(0, 5); - elevations[size - 1] = random(0, 5); - - // Use midpoint displacement for natural terrain - midpoint_displace(0, size - 1, 25); - - // Clamp values to valid range - for (int i = 0; i < size; i++) { - if (elevations[i] < MOUNTAIN_MIN_ELEVATION) elevations[i] = MOUNTAIN_MIN_ELEVATION; - if (elevations[i] > MOUNTAIN_MAX_ELEVATION) elevations[i] = MOUNTAIN_MAX_ELEVATION; - } - - // Smooth to enforce max slope constraint - smooth_slopes(); - - // Ensure at least one steep climb (≥7 feet) reaches into 10-20 elevation range - ensure_steep_climb(); - - // Assign terrain types based on elevation - for (int i = 0; i < size; i++) { - if (elevations[i] > 20) { - terrain_types[i] = "snow"; - } else if (elevations[i] > 8) { - terrain_types[i] = "stone"; - } else { - terrain_types[i] = "gravel"; - } - } - - // Place streams in valley areas - place_streams(); - } - - void midpoint_displace(int left, int right, int roughness) { - if (right - left <= 1) return; - - int mid = (left + right) / 2; - int avg = (elevations[left] + elevations[right]) / 2; - int displacement = random(-roughness, roughness); - elevations[mid] = avg + displacement; - - int new_roughness = roughness * 7 / 10; - if (new_roughness < 1) new_roughness = 1; - - midpoint_displace(left, mid, new_roughness); - midpoint_displace(mid, right, new_roughness); - } - - void smooth_slopes() { - bool changed = true; - int iterations = 0; - while (changed && iterations < 100) { - changed = false; - iterations++; - - for (int i = 1; i < int(elevations.length()); i++) { - int diff = elevations[i] - elevations[i-1]; - if (diff > MOUNTAIN_MAX_SLOPE) { - elevations[i] = elevations[i-1] + MOUNTAIN_MAX_SLOPE; - changed = true; - } else if (diff < -MOUNTAIN_MAX_SLOPE) { - elevations[i] = elevations[i-1] - MOUNTAIN_MAX_SLOPE; - changed = true; - } - } - } - } - - void place_streams() { - // Find valley bottoms (local minima) - int[] valleys; - for (int i = 2; i < int(elevations.length()) - 2; i++) { - if (elevations[i] < elevations[i-1] && elevations[i] < elevations[i+1] && - elevations[i] < elevations[i-2] && elevations[i] < elevations[i+2]) { - valleys.insert_last(i); - } - } - - // Place 1-3 streams in valleys - int num_streams = random(1, 3); - if (num_streams > int(valleys.length())) num_streams = int(valleys.length()); - - for (int i = 0; i < num_streams && valleys.length() > 0; i++) { - int idx = random(0, int(valleys.length()) - 1); - stream_positions.insert_last(valleys[idx]); - valleys.remove_at(idx); - } - } - - void ensure_steep_climb() { - int size = int(elevations.length()); - - // Check if we already have a steep climb (≥7) to/from 10-20 elevation - bool has_steep_climb = false; - for (int i = 1; i < size; i++) { - int diff = elevations[i] - elevations[i-1]; - if (diff < 0) diff = -diff; - - if (diff >= MOUNTAIN_STEEP_THRESHOLD) { - // Check if either elevation is in 10-20 range - if ((elevations[i] >= 10 && elevations[i] <= 20) || - (elevations[i-1] >= 10 && elevations[i-1] <= 20)) { - has_steep_climb = true; - break; - } - } - } - - // If no steep climb to 10-20 range, create one - if (!has_steep_climb && size >= 10) { - // Pick a random position in the middle 60% of the mountain - int pos = random(int(size * 0.2), int(size * 0.8)); - - // Set up a steep climb: create elevation 15-18 with steep approach - int target_elevation = random(15, 18); - elevations[pos] = target_elevation; - - // Make the approach from left steep (if possible) - if (pos > 0) { - elevations[pos - 1] = target_elevation - MOUNTAIN_STEEP_THRESHOLD - 1; - if (elevations[pos - 1] < MOUNTAIN_MIN_ELEVATION) { - elevations[pos - 1] = MOUNTAIN_MIN_ELEVATION; - } - } - - // Make the approach from right steep (if possible) - if (pos < size - 1) { - elevations[pos + 1] = target_elevation - MOUNTAIN_STEEP_THRESHOLD - 1; - if (elevations[pos + 1] < MOUNTAIN_MIN_ELEVATION) { - elevations[pos + 1] = MOUNTAIN_MIN_ELEVATION; - } - } - - // Re-smooth to ensure slopes aren't too extreme elsewhere - smooth_slopes(); - } - - // Ensure edge transitions aren't too steep (max 20 feet from flat ground) - // First transition: tile 0 to tile 1 - if (size >= 2) { - int first_diff = elevations[1] - elevations[0]; - if (first_diff < 0) first_diff = -first_diff; - if (first_diff > 20) { - if (elevations[1] > elevations[0]) { - elevations[1] = elevations[0] + 20; - } else { - elevations[1] = elevations[0] - 20; - } - } - } - - // Last transition: tile size-2 to tile size-1 - if (size >= 2) { - int last_diff = elevations[size-1] - elevations[size-2]; - if (last_diff < 0) last_diff = -last_diff; - if (last_diff > 20) { - if (elevations[size-2] > elevations[size-1]) { - elevations[size-2] = elevations[size-1] + 20; - } else { - elevations[size-2] = elevations[size-1] - 20; - } - } - } - } - - int get_elevation_at(int world_x) { - if (world_x < start_position || world_x > end_position) return 0; - int index = world_x - start_position; - return elevations[index]; - } - - string get_terrain_at(int world_x) { - if (world_x < start_position || world_x > end_position) return "stone"; - int index = world_x - start_position; - return terrain_types[index]; - } - - int get_elevation_change(int from_x, int to_x) { - int from_elev = get_elevation_at(from_x); - int to_elev = get_elevation_at(to_x); - return to_elev - from_elev; - } - - bool is_steep_section(int from_x, int to_x) { - int change = get_elevation_change(from_x, to_x); - if (change < 0) change = -change; - return change >= MOUNTAIN_STEEP_THRESHOLD; - } - - bool contains_position(int world_x) { - return world_x >= start_position && world_x <= end_position; - } - - bool is_stream_at(int world_x) { - if (!contains_position(world_x)) return false; - int index = world_x - start_position; - for (uint i = 0; i < stream_positions.length(); i++) { - if (stream_positions[i] == index) return true; - } - return false; - } - - void update() { - if (stream_positions.length() == 0) return; - - // Find nearest stream to player - int nearest_stream = -1; - int nearest_distance = 999; - - for (uint i = 0; i < stream_positions.length(); i++) { - int stream_world_x = start_position + stream_positions[i]; - int distance = abs(x - stream_world_x); - if (distance < nearest_distance) { - nearest_distance = distance; - nearest_stream = stream_world_x; - } - } - - // Keep nearest stream sound active so distance-based fade can work. - if (nearest_stream != -1) { - if (stream_sound_handle == -1 || !p.sound_is_active(stream_sound_handle)) { - stream_sound_handle = p.play_1d("sounds/terrain/stream.ogg", x, nearest_stream, true); - stream_sound_position = nearest_stream; - if (stream_sound_handle != -1) { - p.update_sound_positioning_values(stream_sound_handle, -1.0, MOUNTAIN_STREAM_VOLUME_STEP, true); - } - } else if (stream_sound_position != nearest_stream) { - p.update_sound_1d(stream_sound_handle, nearest_stream); - stream_sound_position = nearest_stream; - } - } - } - - void destroy() { - if (stream_sound_handle != -1) { - p.destroy_sound(stream_sound_handle); - stream_sound_handle = -1; - } - stream_sound_position = -1; - } -} -MountainRange@[] world_mountains; - -MountainRange@ get_mountain_at(int world_x) { - for (uint i = 0; i < world_mountains.length(); i++) { - if (world_mountains[i].contains_position(world_x)) { - return @world_mountains[i]; - } - } - return null; -} - -int get_mountain_elevation_at(int world_x) { - MountainRange@ mountain = get_mountain_at(world_x); - if (mountain is null) return 0; - return mountain.get_elevation_at(world_x); -} - -bool is_mountain_stream_at(int world_x) { - MountainRange@ mountain = get_mountain_at(world_x); - if (mountain is null) return false; - return mountain.is_stream_at(world_x); -} - -void update_mountains() { - for (uint i = 0; i < world_mountains.length(); i++) { - world_mountains[i].update(); - } -} - -void clear_mountains() { - for (uint i = 0; i < world_mountains.length(); i++) { - world_mountains[i].destroy(); - } - world_mountains.resize(0); -} - -// Flying Creature functions moved to src/enemies/flying_creatures.nvgt