Even more refactor. Getting close to done.

This commit is contained in:
Storm Dragon
2026-01-21 19:25:28 -05:00
parent 9a580597a5
commit 5c10763359
4 changed files with 354 additions and 348 deletions
+2
View File
@@ -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"
+29
View File
@@ -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;
}
+302
View File
@@ -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);
}
+21 -348
View File
@@ -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