Adjusted weather. Hopefully fixed trees for real this time, so far so good.
This commit is contained in:
@@ -129,6 +129,8 @@ const float STREAM_SOUND_VOLUME_STEP = 4.3; // 30 dB over 7 tiles
|
|||||||
|
|
||||||
const float TREE_SOUND_VOLUME_STEP = 4.0; // Similar to snares for good audibility
|
const float TREE_SOUND_VOLUME_STEP = 4.0; // Similar to snares for good audibility
|
||||||
const int TREE_SOUND_RANGE = 4;
|
const int TREE_SOUND_RANGE = 4;
|
||||||
|
const int TREE_MIN_DISTANCE = 10;
|
||||||
|
const int TREE_MAX_PER_AREA = 2;
|
||||||
|
|
||||||
const float RESIDENT_DEFENSE_VOLUME_STEP = 3.0; // Default volume for resident counter-attacks
|
const float RESIDENT_DEFENSE_VOLUME_STEP = 3.0; // Default volume for resident counter-attacks
|
||||||
const float PLAYER_WEAPON_SOUND_VOLUME_STEP = 3.0;
|
const float PLAYER_WEAPON_SOUND_VOLUME_STEP = 3.0;
|
||||||
@@ -174,3 +176,29 @@ const int GOOSE_MAX_DIST_FROM_WATER = 4; // How far they can wander from water
|
|||||||
const int GOOSE_MAX_COUNT = 3;
|
const int GOOSE_MAX_COUNT = 3;
|
||||||
const int GOOSE_HOURLY_SPAWN_CHANCE = 35; // Percent chance per hour to spawn a goose
|
const int GOOSE_HOURLY_SPAWN_CHANCE = 35; // Percent chance per hour to spawn a goose
|
||||||
const int GOOSE_SIGHT_RANGE = 0;
|
const int GOOSE_SIGHT_RANGE = 0;
|
||||||
|
|
||||||
|
// Weather settings
|
||||||
|
const int WEATHER_FADE_DURATION = 8000; // 8 seconds for smooth audio transitions
|
||||||
|
const float WEATHER_MIN_VOLUME = -30.0;
|
||||||
|
const float WEATHER_MAX_VOLUME = 0.0;
|
||||||
|
const float RAIN_VOLUME_LIGHT = -18.0;
|
||||||
|
const float RAIN_VOLUME_MODERATE = -10.0;
|
||||||
|
const float RAIN_VOLUME_HEAVY = -3.0;
|
||||||
|
const int WIND_GUST_MIN_DELAY = 30000; // Min 30 seconds between gusts
|
||||||
|
const int WIND_GUST_MAX_DELAY = 60000; // Max 60 seconds between gusts
|
||||||
|
const int THUNDER_MIN_INTERVAL = 8000; // Min 8 seconds between thunder
|
||||||
|
const int THUNDER_MAX_INTERVAL = 35000; // Max 35 seconds between thunder
|
||||||
|
const int THUNDER_MOVEMENT_SPEED = 2000; // ms per tile movement (slow roll across sky)
|
||||||
|
const float THUNDER_SOUND_VOLUME_STEP = 2.0; // Gentler volume falloff
|
||||||
|
const int THUNDER_SPAWN_DISTANCE_MIN = 20; // Min distance from player
|
||||||
|
const int THUNDER_SPAWN_DISTANCE_MAX = 40; // Max distance from player
|
||||||
|
const int CHANCE_CLEAR_TO_WINDY = 15;
|
||||||
|
const int CHANCE_CLEAR_TO_RAINY = 6;
|
||||||
|
const int CHANCE_CLEAR_TO_STORMY = 5;
|
||||||
|
const int CHANCE_WINDY_STAY = 55;
|
||||||
|
const int CHANCE_WINDY_TO_CLEAR = 25;
|
||||||
|
const int CHANCE_WINDY_TO_STORMY = 12;
|
||||||
|
const int CHANCE_RAINY_STAY = 40;
|
||||||
|
const int CHANCE_RAINY_TO_STORMY = 35;
|
||||||
|
const int CHANCE_STORMY_STAY = 40;
|
||||||
|
const int CHANCE_STORMY_TO_RAINY = 35;
|
||||||
|
|||||||
@@ -28,7 +28,6 @@ void apply_falling_damage(int fall_height) {
|
|||||||
// Feedback
|
// Feedback
|
||||||
screen_reader_speak("Fell " + fall_height + " feet! Took " + damage + " damage. " + player_health + " health remaining.", true);
|
screen_reader_speak("Fell " + fall_height + " feet! Took " + damage + " damage. " + player_health + " health remaining.", true);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Tree Object
|
// Tree Object
|
||||||
class Tree {
|
class Tree {
|
||||||
int position;
|
int position;
|
||||||
@@ -61,12 +60,24 @@ class Tree {
|
|||||||
minutes_since_depletion = 0;
|
minutes_since_depletion = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void respawn(int grass_start, int grass_end) {
|
void respawn() {
|
||||||
if (sound_handle != -1) {
|
if (sound_handle != -1) {
|
||||||
p.destroy_sound(sound_handle);
|
p.destroy_sound(sound_handle);
|
||||||
sound_handle = -1;
|
sound_handle = -1;
|
||||||
}
|
}
|
||||||
position = random(grass_start, grass_end);
|
|
||||||
|
int areaStart = 0;
|
||||||
|
int areaEnd = 0;
|
||||||
|
if (!get_tree_area_bounds_for_position(position, areaStart, areaEnd)) {
|
||||||
|
areaStart = BASE_END + 1;
|
||||||
|
areaEnd = GRASS_END;
|
||||||
|
}
|
||||||
|
|
||||||
|
Tree@ currentTree = @this;
|
||||||
|
if (!place_tree_in_area(currentTree, areaStart, areaEnd)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
refill();
|
refill();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -106,7 +117,7 @@ class Tree {
|
|||||||
|
|
||||||
if (is_chopped) {
|
if (is_chopped) {
|
||||||
if (minutes_since_depletion >= 5) {
|
if (minutes_since_depletion >= 5) {
|
||||||
respawn(BASE_END + 1, GRASS_END);
|
respawn();
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -170,33 +181,200 @@ class Tree {
|
|||||||
}
|
}
|
||||||
Tree@[] trees;
|
Tree@[] trees;
|
||||||
|
|
||||||
bool tree_too_close(int pos) {
|
bool get_tree_area_bounds_for_position(int pos, int &out areaStart, int &out areaEnd) {
|
||||||
// Check distance from base (must be at least 5 tiles away)
|
if (pos >= BASE_END + 1 && pos <= GRASS_END) {
|
||||||
if (pos <= BASE_END + 5) {
|
areaStart = BASE_END + 1;
|
||||||
|
areaEnd = GRASS_END;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check distance from other trees (must be at least 10 tiles apart)
|
if (expanded_area_start == -1 || pos < expanded_area_start || pos > expanded_area_end) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
int index = pos - expanded_area_start;
|
||||||
|
if (index < 0 || index >= int(expanded_terrain_types.length())) return false;
|
||||||
|
if (expanded_terrain_types[index] != "grass") return false;
|
||||||
|
|
||||||
|
int left = index;
|
||||||
|
while (left > 0 && expanded_terrain_types[left - 1] == "grass") {
|
||||||
|
left--;
|
||||||
|
}
|
||||||
|
|
||||||
|
int right = index;
|
||||||
|
int maxIndex = int(expanded_terrain_types.length()) - 1;
|
||||||
|
while (right < maxIndex && expanded_terrain_types[right + 1] == "grass") {
|
||||||
|
right++;
|
||||||
|
}
|
||||||
|
|
||||||
|
areaStart = expanded_area_start + left;
|
||||||
|
areaEnd = expanded_area_start + right;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
int count_trees_in_area(int areaStart, int areaEnd, Tree@ ignoreTree) {
|
||||||
|
int count = 0;
|
||||||
for (uint i = 0; i < trees.length(); i++) {
|
for (uint i = 0; i < trees.length(); i++) {
|
||||||
|
if (@trees[i] is ignoreTree) continue;
|
||||||
|
if (trees[i].position >= areaStart && trees[i].position <= areaEnd) {
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool tree_too_close_in_area(int pos, int areaStart, int areaEnd, Tree@ ignoreTree) {
|
||||||
|
// Keep trees away from the base edge
|
||||||
|
if (pos < BASE_END + 5) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (uint i = 0; i < trees.length(); i++) {
|
||||||
|
if (@trees[i] is ignoreTree) continue;
|
||||||
|
if (trees[i].position < areaStart || trees[i].position > areaEnd) continue;
|
||||||
|
|
||||||
int distance = trees[i].position - pos;
|
int distance = trees[i].position - pos;
|
||||||
if (distance < 0) distance = -distance;
|
if (distance < 0) distance = -distance;
|
||||||
if (distance < 10) {
|
if (distance < TREE_MIN_DISTANCE) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void spawn_trees(int grass_start, int grass_end) {
|
bool place_tree_in_area(Tree@ tree, int areaStart, int areaEnd) {
|
||||||
int attempts = 10;
|
if (count_trees_in_area(areaStart, areaEnd, tree) >= TREE_MAX_PER_AREA) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
int attempts = 20;
|
||||||
for (int i = 0; i < attempts; i++) {
|
for (int i = 0; i < attempts; i++) {
|
||||||
int pos = random(grass_start, grass_end);
|
int pos = random(areaStart, areaEnd);
|
||||||
if (tree_too_close(pos)) {
|
if (tree_too_close_in_area(pos, areaStart, areaEnd, tree)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
tree.position = pos;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool spawn_tree_in_area(int areaStart, int areaEnd) {
|
||||||
|
if (count_trees_in_area(areaStart, areaEnd, null) >= TREE_MAX_PER_AREA) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
int attempts = 20;
|
||||||
|
for (int i = 0; i < attempts; i++) {
|
||||||
|
int pos = random(areaStart, areaEnd);
|
||||||
|
if (tree_too_close_in_area(pos, areaStart, areaEnd, null)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
Tree@ t = Tree(pos);
|
Tree@ t = Tree(pos);
|
||||||
trees.insert_last(t);
|
trees.insert_last(t);
|
||||||
return;
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void spawn_trees(int grass_start, int grass_end) {
|
||||||
|
spawn_tree_in_area(grass_start, grass_end);
|
||||||
|
}
|
||||||
|
|
||||||
|
void get_grass_areas(int[]@ areaStarts, int[]@ areaEnds) {
|
||||||
|
areaStarts.resize(0);
|
||||||
|
areaEnds.resize(0);
|
||||||
|
|
||||||
|
areaStarts.insert_last(BASE_END + 1);
|
||||||
|
areaEnds.insert_last(GRASS_END);
|
||||||
|
|
||||||
|
if (expanded_area_start == -1) return;
|
||||||
|
int total = int(expanded_terrain_types.length());
|
||||||
|
int index = 0;
|
||||||
|
while (index < total) {
|
||||||
|
if (expanded_terrain_types[index] == "grass") {
|
||||||
|
int segmentStart = index;
|
||||||
|
while (index + 1 < total && expanded_terrain_types[index + 1] == "grass") {
|
||||||
|
index++;
|
||||||
|
}
|
||||||
|
int segmentEnd = index;
|
||||||
|
areaStarts.insert_last(expanded_area_start + segmentStart);
|
||||||
|
areaEnds.insert_last(expanded_area_start + segmentEnd);
|
||||||
|
}
|
||||||
|
index++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool relocate_tree_to_any_area(Tree@ tree, int[]@ areaStarts, int[]@ areaEnds) {
|
||||||
|
for (uint i = 0; i < areaStarts.length(); i++) {
|
||||||
|
if (count_trees_in_area(areaStarts[i], areaEnds[i], tree) >= TREE_MAX_PER_AREA) continue;
|
||||||
|
if (place_tree_in_area(tree, areaStarts[i], areaEnds[i])) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void normalize_tree_positions() {
|
||||||
|
int[] areaStarts;
|
||||||
|
int[] areaEnds;
|
||||||
|
get_grass_areas(areaStarts, areaEnds);
|
||||||
|
if (areaStarts.length() == 0) return;
|
||||||
|
|
||||||
|
for (uint i = 0; i < trees.length(); i++) {
|
||||||
|
int areaStart = 0;
|
||||||
|
int areaEnd = 0;
|
||||||
|
if (!get_tree_area_bounds_for_position(trees[i].position, areaStart, areaEnd)) {
|
||||||
|
if (!relocate_tree_to_any_area(trees[i], areaStarts, areaEnds)) {
|
||||||
|
if (trees[i].sound_handle != -1) {
|
||||||
|
p.destroy_sound(trees[i].sound_handle);
|
||||||
|
}
|
||||||
|
trees.remove_at(i);
|
||||||
|
i--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (uint areaIndex = 0; areaIndex < areaStarts.length(); areaIndex++) {
|
||||||
|
int areaStart = areaStarts[areaIndex];
|
||||||
|
int areaEnd = areaEnds[areaIndex];
|
||||||
|
|
||||||
|
int[] areaTreeIndices;
|
||||||
|
for (uint i = 0; i < trees.length(); i++) {
|
||||||
|
if (trees[i].position >= areaStart && trees[i].position <= areaEnd) {
|
||||||
|
areaTreeIndices.insert_last(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
while (areaTreeIndices.length() > TREE_MAX_PER_AREA) {
|
||||||
|
uint treeIndex = areaTreeIndices[areaTreeIndices.length() - 1];
|
||||||
|
Tree@ tree = trees[treeIndex];
|
||||||
|
if (!relocate_tree_to_any_area(tree, areaStarts, areaEnds)) {
|
||||||
|
if (tree.sound_handle != -1) {
|
||||||
|
p.destroy_sound(tree.sound_handle);
|
||||||
|
}
|
||||||
|
trees.remove_at(treeIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
areaTreeIndices.resize(0);
|
||||||
|
for (uint i = 0; i < trees.length(); i++) {
|
||||||
|
if (trees[i].position >= areaStart && trees[i].position <= areaEnd) {
|
||||||
|
areaTreeIndices.insert_last(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (areaTreeIndices.length() == 2) {
|
||||||
|
Tree@ firstTree = trees[areaTreeIndices[0]];
|
||||||
|
Tree@ secondTree = trees[areaTreeIndices[1]];
|
||||||
|
int distance = firstTree.position - secondTree.position;
|
||||||
|
if (distance < 0) distance = -distance;
|
||||||
|
if (distance < TREE_MIN_DISTANCE) {
|
||||||
|
if (!place_tree_in_area(secondTree, areaStart, areaEnd)) {
|
||||||
|
place_tree_in_area(firstTree, areaStart, areaEnd);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -291,6 +291,7 @@ void reset_game_state() {
|
|||||||
void start_new_game() {
|
void start_new_game() {
|
||||||
reset_game_state();
|
reset_game_state();
|
||||||
spawn_trees(5, 19);
|
spawn_trees(5, 19);
|
||||||
|
normalize_tree_positions();
|
||||||
init_barricade();
|
init_barricade();
|
||||||
init_time();
|
init_time();
|
||||||
init_weather();
|
init_weather();
|
||||||
@@ -966,6 +967,7 @@ bool load_game_state() {
|
|||||||
tree.regen_timer.restart();
|
tree.regen_timer.restart();
|
||||||
trees.insert_last(tree);
|
trees.insert_last(tree);
|
||||||
}
|
}
|
||||||
|
normalize_tree_positions();
|
||||||
|
|
||||||
string[] snareData = get_string_list_or_split(saveData, "snares_data");
|
string[] snareData = get_string_list_or_split(saveData, "snares_data");
|
||||||
for (uint i = 0; i < snareData.length(); i++) {
|
for (uint i = 0; i < snareData.length(); i++) {
|
||||||
|
|||||||
@@ -104,15 +104,8 @@ void expand_regular_area() {
|
|||||||
|
|
||||||
notify("A " + width_desc + " stream flows through the new area at x " + actual_start + ".");
|
notify("A " + width_desc + " stream flows through the new area at x " + actual_start + ".");
|
||||||
} else {
|
} else {
|
||||||
// Try to place a tree with proper spacing
|
// Try to place a tree with proper spacing and per-area limits
|
||||||
for (int attempt = 0; attempt < 20; attempt++) {
|
spawn_tree_in_area(new_start, new_end);
|
||||||
int tree_pos = random(new_start, new_end);
|
|
||||||
if (!tree_too_close(tree_pos)) {
|
|
||||||
Tree@ t = Tree(tree_pos);
|
|
||||||
trees.insert_last(t);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
area_expanded_today = true;
|
area_expanded_today = true;
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
// Weather System
|
// Weather System
|
||||||
// Provides ambient wind, rain, and thunder effects
|
// Provides ambient wind, rain, and thunder effects
|
||||||
|
// Tunable constants are in src/constants.nvgt
|
||||||
|
|
||||||
// Weather states
|
// Weather states
|
||||||
const int WEATHER_CLEAR = 0;
|
const int WEATHER_CLEAR = 0;
|
||||||
@@ -13,40 +14,6 @@ const int INTENSITY_LOW = 1;
|
|||||||
const int INTENSITY_MEDIUM = 2;
|
const int INTENSITY_MEDIUM = 2;
|
||||||
const int INTENSITY_HIGH = 3;
|
const int INTENSITY_HIGH = 3;
|
||||||
|
|
||||||
// Audio fade settings
|
|
||||||
const int WEATHER_FADE_DURATION = 8000; // 8 seconds for smooth transitions
|
|
||||||
const float WEATHER_MIN_VOLUME = -30.0;
|
|
||||||
const float WEATHER_MAX_VOLUME = 0.0;
|
|
||||||
|
|
||||||
// Rain volume levels by intensity
|
|
||||||
const float RAIN_VOLUME_LIGHT = -18.0;
|
|
||||||
const float RAIN_VOLUME_MODERATE = -10.0;
|
|
||||||
const float RAIN_VOLUME_HEAVY = -3.0;
|
|
||||||
|
|
||||||
// Wind gust settings
|
|
||||||
const int WIND_GUST_MIN_DELAY = 10000; // Min 10 seconds between gusts
|
|
||||||
const int WIND_GUST_MAX_DELAY = 25000; // Max 25 seconds between gusts
|
|
||||||
|
|
||||||
// Thunder timing
|
|
||||||
const int THUNDER_MIN_INTERVAL = 8000; // Min 8 seconds between thunder
|
|
||||||
const int THUNDER_MAX_INTERVAL = 35000; // Max 35 seconds between thunder
|
|
||||||
const int THUNDER_MOVEMENT_SPEED = 2000; // ms per tile movement (slow roll across sky)
|
|
||||||
const float THUNDER_SOUND_VOLUME_STEP = 2.0; // Gentler volume falloff
|
|
||||||
const int THUNDER_SPAWN_DISTANCE_MIN = 20; // Min distance from player
|
|
||||||
const int THUNDER_SPAWN_DISTANCE_MAX = 40; // Max distance from player
|
|
||||||
|
|
||||||
// Weather transition chances (out of 100)
|
|
||||||
const int CHANCE_CLEAR_TO_WINDY = 15;
|
|
||||||
const int CHANCE_CLEAR_TO_RAINY = 4;
|
|
||||||
const int CHANCE_CLEAR_TO_STORMY = 2;
|
|
||||||
const int CHANCE_WINDY_STAY = 55;
|
|
||||||
const int CHANCE_WINDY_TO_CLEAR = 25;
|
|
||||||
const int CHANCE_WINDY_TO_STORMY = 12;
|
|
||||||
const int CHANCE_RAINY_STAY = 45;
|
|
||||||
const int CHANCE_RAINY_TO_STORMY = 25;
|
|
||||||
const int CHANCE_STORMY_STAY = 40;
|
|
||||||
const int CHANCE_STORMY_TO_RAINY = 35;
|
|
||||||
|
|
||||||
// State variables
|
// State variables
|
||||||
int weather_state = WEATHER_CLEAR;
|
int weather_state = WEATHER_CLEAR;
|
||||||
int wind_intensity = INTENSITY_NONE;
|
int wind_intensity = INTENSITY_NONE;
|
||||||
|
|||||||
Reference in New Issue
Block a user