Even more refactor. Getting close to done.
This commit is contained in:
@@ -18,6 +18,8 @@ sound_pool p(100);
|
|||||||
#include "src/world/world_fires.nvgt"
|
#include "src/world/world_fires.nvgt"
|
||||||
#include "src/world/world_buildings.nvgt"
|
#include "src/world/world_buildings.nvgt"
|
||||||
#include "src/world/world_streams.nvgt"
|
#include "src/world/world_streams.nvgt"
|
||||||
|
#include "src/world/mountains.nvgt"
|
||||||
|
#include "src/world/barricade.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,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;
|
||||||
|
}
|
||||||
@@ -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
@@ -1,15 +1,25 @@
|
|||||||
// World Objects
|
// World State Orchestrator
|
||||||
|
//
|
||||||
int barricade_health = 0;
|
// This file coordinates all world object updates that were previously handled here.
|
||||||
bool barricade_initialized = false;
|
// Individual world objects have been split into focused modules:
|
||||||
int residents_count = 0;
|
//
|
||||||
|
// 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() {
|
void update_world_objects() {
|
||||||
for (uint i = 0; i < world_snares.length(); i++) {
|
for (uint i = 0; i < world_snares.length(); i++) {
|
||||||
world_snares[i].update();
|
world_snares[i].update();
|
||||||
@@ -18,340 +28,3 @@ void update_world_objects() {
|
|||||||
world_fires[i].update();
|
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
|
|
||||||
|
|||||||
Reference in New Issue
Block a user