Fixed crash bug with snares and possibly in other places.
This commit is contained in:
+163
-110
@@ -31,6 +31,41 @@ bool has_burning_fire_in_base() {
|
||||
return false;
|
||||
}
|
||||
|
||||
int get_resident_effect_multiplier() {
|
||||
return blessing_resident_active ? 2 : 1;
|
||||
}
|
||||
|
||||
int get_resident_success_chance(int base_chance) {
|
||||
int chance = base_chance * get_resident_effect_multiplier();
|
||||
if (chance > 100) chance = 100;
|
||||
return chance;
|
||||
}
|
||||
|
||||
int get_resident_break_chance(int base_chance) {
|
||||
if (!blessing_resident_active) return base_chance;
|
||||
int reduced = base_chance / get_resident_effect_multiplier();
|
||||
if (reduced < 1 && base_chance > 0) reduced = 1;
|
||||
return reduced;
|
||||
}
|
||||
|
||||
int get_resident_escape_chance(int base_chance) {
|
||||
if (!blessing_resident_active) return base_chance;
|
||||
int reduced = base_chance / get_resident_effect_multiplier();
|
||||
if (reduced < 1 && base_chance > 0) reduced = 1;
|
||||
return reduced;
|
||||
}
|
||||
|
||||
int get_resident_cooldown(int base_cooldown) {
|
||||
int cooldown = base_cooldown / get_resident_effect_multiplier();
|
||||
if (cooldown < 1) cooldown = 1;
|
||||
return cooldown;
|
||||
}
|
||||
|
||||
int apply_resident_damage_bonus(int damage) {
|
||||
if (!blessing_resident_active) return damage;
|
||||
return damage * get_resident_effect_multiplier();
|
||||
}
|
||||
|
||||
void consume_food_for_residents() {
|
||||
int needed = get_food_requirement();
|
||||
if (needed <= 0) return;
|
||||
@@ -77,13 +112,15 @@ void attempt_resident_fishing() {
|
||||
|
||||
int caught = 0;
|
||||
int poles_broken = 0;
|
||||
int break_chance = get_resident_break_chance(RESIDENT_TOOL_BREAK_CHANCE);
|
||||
int fishing_chance = get_resident_success_chance(RESIDENT_FISHING_CHANCE);
|
||||
for (int i = 0; i < active_fishers; i++) {
|
||||
// Check for pole breakage during use
|
||||
if (random(1, 100) <= RESIDENT_TOOL_BREAK_CHANCE) {
|
||||
if (random(1, 100) <= break_chance) {
|
||||
poles_broken++;
|
||||
continue;
|
||||
}
|
||||
if (random(1, 100) > RESIDENT_FISHING_CHANCE) continue;
|
||||
if (random(1, 100) > fishing_chance) continue;
|
||||
if (get_storage_count(ITEM_FISH) >= BASE_STORAGE_MAX) break;
|
||||
add_storage_count(ITEM_FISH, 1);
|
||||
add_storage_fish_weight(random(FISH_WEIGHT_MIN, FISH_WEIGHT_MAX));
|
||||
@@ -116,19 +153,26 @@ void attempt_resident_fish_smoking() {
|
||||
if (get_storage_count(ITEM_FISH) <= 0) return;
|
||||
if (get_storage_count(ITEM_STICKS) <= 0) return;
|
||||
if (!has_burning_fire_in_base()) return;
|
||||
if (random(1, 100) > RESIDENT_SMOKE_FISH_CHANCE) return;
|
||||
|
||||
int weight = (storage_fish_weights.length() > 0) ? storage_fish_weights[0] : get_default_fish_weight();
|
||||
int yield = get_smoked_fish_yield(weight);
|
||||
if (get_storage_count(ITEM_SMOKED_FISH) + yield > BASE_STORAGE_MAX) return;
|
||||
int attempts = get_resident_effect_multiplier();
|
||||
int smoke_chance = get_resident_success_chance(RESIDENT_SMOKE_FISH_CHANCE);
|
||||
for (int attempt = 0; attempt < attempts; attempt++) {
|
||||
if (get_storage_count(ITEM_FISH) <= 0) return;
|
||||
if (get_storage_count(ITEM_STICKS) <= 0) return;
|
||||
if (random(1, 100) > smoke_chance) continue;
|
||||
|
||||
pop_storage_fish_weight();
|
||||
add_storage_count(ITEM_FISH, -1);
|
||||
add_storage_count(ITEM_STICKS, -1);
|
||||
add_storage_count(ITEM_SMOKED_FISH, yield);
|
||||
int weight = (storage_fish_weights.length() > 0) ? storage_fish_weights[0] : get_default_fish_weight();
|
||||
int yield = get_smoked_fish_yield(weight);
|
||||
if (get_storage_count(ITEM_SMOKED_FISH) + yield > BASE_STORAGE_MAX) return;
|
||||
|
||||
if (x <= BASE_END) {
|
||||
speak_with_history("Resident smoked a fish into " + yield + " smoked fish.", true);
|
||||
pop_storage_fish_weight();
|
||||
add_storage_count(ITEM_FISH, -1);
|
||||
add_storage_count(ITEM_STICKS, -1);
|
||||
add_storage_count(ITEM_SMOKED_FISH, yield);
|
||||
|
||||
if (x <= BASE_END) {
|
||||
speak_with_history("Resident smoked a fish into " + yield + " smoked fish.", true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -190,12 +234,12 @@ int perform_resident_defense() {
|
||||
|
||||
int damage = 0;
|
||||
if (useSpear && get_storage_count(ITEM_SPEARS) > 0) {
|
||||
damage = RESIDENT_SPEAR_DAMAGE;
|
||||
damage = apply_resident_damage_bonus(RESIDENT_SPEAR_DAMAGE);
|
||||
// Weapons don't get consumed on use - they break via daily breakage check
|
||||
// Just play the sound
|
||||
play_1d_with_volume_step("sounds/weapons/spear_swing.ogg", x, BASE_END + 1, false, RESIDENT_DEFENSE_VOLUME_STEP);
|
||||
} else if (get_storage_count(ITEM_SLINGS) > 0 && get_storage_count(ITEM_STONES) > 0) {
|
||||
damage = random(RESIDENT_SLING_DAMAGE_MIN, RESIDENT_SLING_DAMAGE_MAX);
|
||||
damage = apply_resident_damage_bonus(random(RESIDENT_SLING_DAMAGE_MIN, RESIDENT_SLING_DAMAGE_MAX));
|
||||
// Slings use stones as ammo, so consume a stone
|
||||
add_storage_count(ITEM_STONES, -1);
|
||||
play_1d_with_volume_step("sounds/weapons/sling_hit.ogg", x, BASE_END + 1, false, RESIDENT_DEFENSE_VOLUME_STEP);
|
||||
@@ -213,7 +257,7 @@ void attempt_resident_sling_defense() {
|
||||
if (get_storage_count(ITEM_SLINGS) <= 0 || get_storage_count(ITEM_STONES) <= 0) return;
|
||||
|
||||
// Cooldown between shots
|
||||
if (resident_sling_timer.elapsed < RESIDENT_SLING_COOLDOWN) return;
|
||||
if (resident_sling_timer.elapsed < get_resident_cooldown(RESIDENT_SLING_COOLDOWN)) return;
|
||||
|
||||
// Find nearest enemy within sling range
|
||||
int nearestDistance = SLING_RANGE + 1;
|
||||
@@ -249,7 +293,7 @@ void attempt_resident_sling_defense() {
|
||||
resident_sling_timer.restart();
|
||||
add_storage_count(ITEM_STONES, -1);
|
||||
|
||||
int damage = random(RESIDENT_SLING_DAMAGE_MIN, RESIDENT_SLING_DAMAGE_MAX);
|
||||
int damage = apply_resident_damage_bonus(random(RESIDENT_SLING_DAMAGE_MIN, RESIDENT_SLING_DAMAGE_MAX));
|
||||
play_1d_with_volume_step("sounds/weapons/sling_hit.ogg", x, targetPos, false, RESIDENT_DEFENSE_VOLUME_STEP);
|
||||
|
||||
if (targetIsBandit) {
|
||||
@@ -293,15 +337,16 @@ void process_daily_weapon_breakage() {
|
||||
// Perform breakage checks
|
||||
int spearsBroken = 0;
|
||||
int slingsBroken = 0;
|
||||
int break_chance = get_resident_break_chance(RESIDENT_WEAPON_BREAK_CHANCE);
|
||||
|
||||
for (int i = 0; i < spearChecks; i++) {
|
||||
if (random(1, 100) <= RESIDENT_WEAPON_BREAK_CHANCE) {
|
||||
if (random(1, 100) <= break_chance) {
|
||||
spearsBroken++;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < slingChecks; i++) {
|
||||
if (random(1, 100) <= RESIDENT_WEAPON_BREAK_CHANCE) {
|
||||
if (random(1, 100) <= break_chance) {
|
||||
slingsBroken++;
|
||||
}
|
||||
}
|
||||
@@ -337,6 +382,9 @@ void attempt_resident_snare_retrieval() {
|
||||
// Need food in storage (same limitation as other resident tasks)
|
||||
if (!has_any_storage_food()) return;
|
||||
|
||||
int check_chance = get_resident_success_chance(RESIDENT_SNARE_CHECK_CHANCE);
|
||||
int escape_chance = get_resident_escape_chance(RESIDENT_SNARE_ESCAPE_CHANCE);
|
||||
|
||||
// Check each snare that has a catch
|
||||
for (int i = int(world_snares.length()) - 1; i >= 0; i--) {
|
||||
WorldSnare@ snare = world_snares[i];
|
||||
@@ -344,10 +392,10 @@ void attempt_resident_snare_retrieval() {
|
||||
if (!snare.active) continue;
|
||||
|
||||
// Each snare has a chance to be checked by a resident this hour
|
||||
if (random(1, 100) > RESIDENT_SNARE_CHECK_CHANCE) continue;
|
||||
if (random(1, 100) > check_chance) continue;
|
||||
|
||||
// Small chance the game escapes during retrieval (like normal)
|
||||
if (random(1, 100) <= RESIDENT_SNARE_ESCAPE_CHANCE) {
|
||||
if (random(1, 100) <= escape_chance) {
|
||||
notify("A " + snare.catch_type + " escaped while a resident checked the snare at x " + snare.position + ".");
|
||||
remove_snare_at(snare.position);
|
||||
continue;
|
||||
@@ -376,7 +424,7 @@ void attempt_resident_snare_retrieval() {
|
||||
}
|
||||
}
|
||||
|
||||
// Resident butchering - processes one game per day from storage
|
||||
// Resident butchering - processes up to two games per day when blessed
|
||||
void attempt_resident_butchering() {
|
||||
// Need residents
|
||||
if (residents_count <= 0) return;
|
||||
@@ -391,100 +439,101 @@ void attempt_resident_butchering() {
|
||||
if (get_storage_count(ITEM_KNIVES) <= 0) return;
|
||||
|
||||
// Need a fire in base
|
||||
bool has_fire = false;
|
||||
for (uint i = 0; i < world_fires.length(); i++) {
|
||||
if (world_fires[i].position <= BASE_END && world_fires[i].is_burning()) {
|
||||
has_fire = true;
|
||||
break;
|
||||
if (!has_burning_fire_in_base()) return;
|
||||
|
||||
int attempts = get_resident_effect_multiplier();
|
||||
int break_chance = get_resident_break_chance(RESIDENT_TOOL_BREAK_CHANCE);
|
||||
for (int attempt = 0; attempt < attempts; attempt++) {
|
||||
// Need game in storage
|
||||
if (get_storage_count(ITEM_SMALL_GAME) <= 0 && get_storage_count(ITEM_BOAR_CARCASSES) <= 0) return;
|
||||
if (get_storage_count(ITEM_KNIVES) <= 0) return;
|
||||
|
||||
// Determine what to butcher (prioritize boar carcasses)
|
||||
string game_type = "";
|
||||
bool is_boar = false;
|
||||
|
||||
if (get_storage_count(ITEM_BOAR_CARCASSES) > 0) {
|
||||
game_type = "boar carcass";
|
||||
is_boar = true;
|
||||
} else if (storage_small_game_types.length() > 0) {
|
||||
game_type = storage_small_game_types[0];
|
||||
} else {
|
||||
game_type = "small game";
|
||||
}
|
||||
}
|
||||
if (!has_fire) return;
|
||||
|
||||
// Determine what to butcher (prioritize boar carcasses)
|
||||
string game_type = "";
|
||||
bool is_boar = false;
|
||||
// Calculate outputs and check storage capacity
|
||||
int meat_yield = 0;
|
||||
int skins_yield = 0;
|
||||
int feathers_yield = 0;
|
||||
int down_yield = 0;
|
||||
int sinew_yield = 0;
|
||||
|
||||
if (get_storage_count(ITEM_BOAR_CARCASSES) > 0) {
|
||||
game_type = "boar carcass";
|
||||
is_boar = true;
|
||||
} else if (storage_small_game_types.length() > 0) {
|
||||
game_type = storage_small_game_types[0];
|
||||
} else {
|
||||
game_type = "small game";
|
||||
}
|
||||
|
||||
// Calculate outputs and check storage capacity
|
||||
int meat_yield = 0;
|
||||
int skins_yield = 0;
|
||||
int feathers_yield = 0;
|
||||
int down_yield = 0;
|
||||
int sinew_yield = 0;
|
||||
|
||||
if (game_type == "goose") {
|
||||
meat_yield = 1;
|
||||
feathers_yield = random(3, 6);
|
||||
down_yield = random(1, 3);
|
||||
} else if (is_boar) {
|
||||
meat_yield = random(2, 3);
|
||||
skins_yield = 3;
|
||||
sinew_yield = 2;
|
||||
} else {
|
||||
meat_yield = 1;
|
||||
skins_yield = 1;
|
||||
}
|
||||
|
||||
// Check storage capacity for outputs
|
||||
if (meat_yield > 0 && get_storage_count(ITEM_MEAT) + meat_yield > BASE_STORAGE_MAX) return;
|
||||
if (skins_yield > 0 && get_storage_count(ITEM_SKINS) + skins_yield > BASE_STORAGE_MAX) return;
|
||||
if (feathers_yield > 0 && get_storage_count(ITEM_FEATHERS) + feathers_yield > BASE_STORAGE_MAX) return;
|
||||
if (down_yield > 0 && get_storage_count(ITEM_DOWN) + down_yield > BASE_STORAGE_MAX) return;
|
||||
if (sinew_yield > 0 && get_storage_count(ITEM_SINEW) + sinew_yield > BASE_STORAGE_MAX) return;
|
||||
|
||||
// Consume the game
|
||||
if (is_boar) {
|
||||
add_storage_count(ITEM_BOAR_CARCASSES, -1);
|
||||
} else {
|
||||
add_storage_count(ITEM_SMALL_GAME, -1);
|
||||
if (storage_small_game_types.length() > 0) {
|
||||
storage_small_game_types.remove_at(0);
|
||||
if (game_type == "goose") {
|
||||
meat_yield = 1;
|
||||
feathers_yield = random(3, 6);
|
||||
down_yield = random(1, 3);
|
||||
} else if (is_boar) {
|
||||
meat_yield = random(2, 3);
|
||||
skins_yield = 3;
|
||||
sinew_yield = 2;
|
||||
} else {
|
||||
meat_yield = 1;
|
||||
skins_yield = 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Check for knife breakage
|
||||
if (random(1, 100) <= RESIDENT_TOOL_BREAK_CHANCE) {
|
||||
add_storage_count(ITEM_KNIVES, -1);
|
||||
notify("A resident's knife broke while butchering.");
|
||||
}
|
||||
// Check storage capacity for outputs
|
||||
if (meat_yield > 0 && get_storage_count(ITEM_MEAT) + meat_yield > BASE_STORAGE_MAX) return;
|
||||
if (skins_yield > 0 && get_storage_count(ITEM_SKINS) + skins_yield > BASE_STORAGE_MAX) return;
|
||||
if (feathers_yield > 0 && get_storage_count(ITEM_FEATHERS) + feathers_yield > BASE_STORAGE_MAX) return;
|
||||
if (down_yield > 0 && get_storage_count(ITEM_DOWN) + down_yield > BASE_STORAGE_MAX) return;
|
||||
if (sinew_yield > 0 && get_storage_count(ITEM_SINEW) + sinew_yield > BASE_STORAGE_MAX) return;
|
||||
|
||||
// Add outputs to storage
|
||||
if (meat_yield > 0) add_storage_count(ITEM_MEAT, meat_yield);
|
||||
if (skins_yield > 0) add_storage_count(ITEM_SKINS, skins_yield);
|
||||
if (feathers_yield > 0) add_storage_count(ITEM_FEATHERS, feathers_yield);
|
||||
if (down_yield > 0) add_storage_count(ITEM_DOWN, down_yield);
|
||||
if (sinew_yield > 0) add_storage_count(ITEM_SINEW, sinew_yield);
|
||||
|
||||
// Build notification message
|
||||
string result = "Resident butchered " + game_type + ". Added ";
|
||||
string[] outputs;
|
||||
if (meat_yield > 0) outputs.insert_last(meat_yield + " meat");
|
||||
if (skins_yield > 0) outputs.insert_last(skins_yield + " skins");
|
||||
if (feathers_yield > 0) outputs.insert_last(feathers_yield + " feathers");
|
||||
if (down_yield > 0) outputs.insert_last(down_yield + " down");
|
||||
if (sinew_yield > 0) outputs.insert_last(sinew_yield + " sinew");
|
||||
|
||||
for (uint i = 0; i < outputs.length(); i++) {
|
||||
if (i > 0) {
|
||||
if (i == outputs.length() - 1) {
|
||||
result += " and ";
|
||||
} else {
|
||||
result += ", ";
|
||||
// Consume the game
|
||||
if (is_boar) {
|
||||
add_storage_count(ITEM_BOAR_CARCASSES, -1);
|
||||
} else {
|
||||
add_storage_count(ITEM_SMALL_GAME, -1);
|
||||
if (storage_small_game_types.length() > 0) {
|
||||
storage_small_game_types.remove_at(0);
|
||||
}
|
||||
}
|
||||
result += outputs[i];
|
||||
}
|
||||
result += " to storage.";
|
||||
|
||||
notify(result);
|
||||
// Check for knife breakage
|
||||
if (random(1, 100) <= break_chance) {
|
||||
add_storage_count(ITEM_KNIVES, -1);
|
||||
notify("A resident's knife broke while butchering.");
|
||||
}
|
||||
|
||||
// Add outputs to storage
|
||||
if (meat_yield > 0) add_storage_count(ITEM_MEAT, meat_yield);
|
||||
if (skins_yield > 0) add_storage_count(ITEM_SKINS, skins_yield);
|
||||
if (feathers_yield > 0) add_storage_count(ITEM_FEATHERS, feathers_yield);
|
||||
if (down_yield > 0) add_storage_count(ITEM_DOWN, down_yield);
|
||||
if (sinew_yield > 0) add_storage_count(ITEM_SINEW, sinew_yield);
|
||||
|
||||
// Build notification message
|
||||
string result = "Resident butchered " + game_type + ". Added ";
|
||||
string[] outputs;
|
||||
if (meat_yield > 0) outputs.insert_last(meat_yield + " meat");
|
||||
if (skins_yield > 0) outputs.insert_last(skins_yield + " skins");
|
||||
if (feathers_yield > 0) outputs.insert_last(feathers_yield + " feathers");
|
||||
if (down_yield > 0) outputs.insert_last(down_yield + " down");
|
||||
if (sinew_yield > 0) outputs.insert_last(sinew_yield + " sinew");
|
||||
|
||||
for (uint i = 0; i < outputs.length(); i++) {
|
||||
if (i > 0) {
|
||||
if (i == outputs.length() - 1) {
|
||||
result += " and ";
|
||||
} else {
|
||||
result += ", ";
|
||||
}
|
||||
}
|
||||
result += outputs[i];
|
||||
}
|
||||
result += " to storage.";
|
||||
|
||||
notify(result);
|
||||
}
|
||||
}
|
||||
|
||||
// Resident resource collection
|
||||
@@ -504,14 +553,16 @@ void attempt_resident_collection() {
|
||||
|
||||
// Each active collector has a 10% chance to collect something
|
||||
int baskets_broken = 0;
|
||||
int break_chance = get_resident_break_chance(RESIDENT_TOOL_BREAK_CHANCE);
|
||||
int collection_chance = get_resident_success_chance(RESIDENT_COLLECTION_CHANCE);
|
||||
for (int i = 0; i < active_collectors; i++) {
|
||||
// Check for basket breakage during use
|
||||
if (random(1, 100) <= RESIDENT_TOOL_BREAK_CHANCE) {
|
||||
if (random(1, 100) <= break_chance) {
|
||||
baskets_broken++;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (random(1, 100) > RESIDENT_COLLECTION_CHANCE) continue;
|
||||
if (random(1, 100) > collection_chance) continue;
|
||||
|
||||
// Determine what to collect (weighted random)
|
||||
// Sticks and vines more common, logs and stones less common
|
||||
@@ -573,16 +624,18 @@ void attempt_resident_foraging() {
|
||||
|
||||
int baskets_produced = 0;
|
||||
int baskets_broken = 0;
|
||||
int break_chance = get_resident_break_chance(RESIDENT_TOOL_BREAK_CHANCE);
|
||||
int forage_chance = get_resident_success_chance(RESIDENT_FORAGING_CHANCE);
|
||||
|
||||
for (int i = 0; i < active_foragers; i++) {
|
||||
// Check for basket breakage during foraging
|
||||
if (random(1, 100) <= RESIDENT_TOOL_BREAK_CHANCE) {
|
||||
if (random(1, 100) <= break_chance) {
|
||||
baskets_broken++;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check if foraging succeeds
|
||||
if (random(1, 100) > RESIDENT_FORAGING_CHANCE) continue;
|
||||
if (random(1, 100) > forage_chance) continue;
|
||||
|
||||
// Check storage capacity
|
||||
if (get_storage_count(ITEM_BASKET_FOOD) >= BASE_STORAGE_MAX) break;
|
||||
|
||||
@@ -23,6 +23,7 @@ const int BASE_STORAGE_MAX = 50;
|
||||
const int BLESSING_HEAL_AMOUNT = 3;
|
||||
const int BLESSING_BARRICADE_REPAIR = 20;
|
||||
const int BLESSING_SPEED_DURATION = 300000;
|
||||
const int BLESSING_RESIDENT_DURATION = 300000;
|
||||
const int BLESSING_TRIGGER_CHANCE = 10;
|
||||
const int BLESSING_WALK_SPEED = 320;
|
||||
const int FISH_WEIGHT_MIN = 1;
|
||||
|
||||
+26
-6
@@ -84,7 +84,12 @@ void spawn_bandit(int expansion_start, int expansion_end) {
|
||||
Bandit@ b = Bandit(spawn_x, expansion_start, expansion_end);
|
||||
bandits.insert_last(b);
|
||||
// Play looping sound that follows the bandit
|
||||
b.sound_handle = play_1d_with_volume_step(b.alert_sound, x, spawn_x, true, BANDIT_SOUND_VOLUME_STEP);
|
||||
int[] areaStarts;
|
||||
int[] areaEnds;
|
||||
get_active_audio_areas(areaStarts, areaEnds);
|
||||
if (areaStarts.length() == 0 || range_overlaps_active_areas(spawn_x, spawn_x, areaStarts, areaEnds)) {
|
||||
b.sound_handle = play_1d_with_volume_step(b.alert_sound, x, spawn_x, true, BANDIT_SOUND_VOLUME_STEP);
|
||||
}
|
||||
}
|
||||
|
||||
bool can_bandit_attack_player(Bandit@ bandit) {
|
||||
@@ -174,9 +179,14 @@ void try_attack_barricade_bandit(Bandit@ bandit) {
|
||||
}
|
||||
}
|
||||
|
||||
void update_bandit(Bandit@ bandit) {
|
||||
void update_bandit(Bandit@ bandit, bool audio_active) {
|
||||
// Update looping sound position
|
||||
if (bandit.sound_handle != -1 && p.sound_is_active(bandit.sound_handle)) {
|
||||
if (!audio_active) {
|
||||
if (bandit.sound_handle != -1) {
|
||||
p.destroy_sound(bandit.sound_handle);
|
||||
bandit.sound_handle = -1;
|
||||
}
|
||||
} else if (bandit.sound_handle != -1 && p.sound_is_active(bandit.sound_handle)) {
|
||||
p.update_sound_1d(bandit.sound_handle, bandit.position);
|
||||
} else if (bandit.sound_handle == -1 || !p.sound_is_active(bandit.sound_handle)) {
|
||||
// Restart looping sound if it stopped
|
||||
@@ -227,7 +237,9 @@ void update_bandit(Bandit@ bandit) {
|
||||
bandit.wander_direction = -bandit.wander_direction;
|
||||
} else {
|
||||
bandit.position = target_x;
|
||||
play_creature_footstep(x, bandit.position, BASE_END, GRASS_END, BANDIT_FOOTSTEP_MAX_DISTANCE, BANDIT_SOUND_VOLUME_STEP);
|
||||
if (audio_active) {
|
||||
play_creature_footstep(x, bandit.position, BASE_END, GRASS_END, BANDIT_FOOTSTEP_MAX_DISTANCE, BANDIT_SOUND_VOLUME_STEP);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Hit map boundary, reverse direction
|
||||
@@ -270,13 +282,21 @@ void update_bandit(Bandit@ bandit) {
|
||||
}
|
||||
|
||||
bandit.position = target_x;
|
||||
play_creature_footstep(x, bandit.position, BASE_END, GRASS_END, BANDIT_FOOTSTEP_MAX_DISTANCE, BANDIT_SOUND_VOLUME_STEP);
|
||||
if (audio_active) {
|
||||
play_creature_footstep(x, bandit.position, BASE_END, GRASS_END, BANDIT_FOOTSTEP_MAX_DISTANCE, BANDIT_SOUND_VOLUME_STEP);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void update_bandits() {
|
||||
int[] areaStarts;
|
||||
int[] areaEnds;
|
||||
get_active_audio_areas(areaStarts, areaEnds);
|
||||
bool limit_audio = (areaStarts.length() > 0);
|
||||
|
||||
for (uint i = 0; i < bandits.length(); i++) {
|
||||
update_bandit(bandits[i]);
|
||||
bool audio_active = !limit_audio || range_overlaps_active_areas(bandits[i].position, bandits[i].position, areaStarts, areaEnds);
|
||||
update_bandit(bandits[i], audio_active);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -226,11 +226,16 @@ bool spawn_flying_creature(string creature_type) {
|
||||
FlyingCreature@ c = FlyingCreature(creature_type, spawn_x, area_start, area_end, cfg);
|
||||
flying_creatures.insert_last(c);
|
||||
// Play looping sound that follows the flying creature
|
||||
c.sound_handle = play_1d_with_volume_step(c.voice_sound, x, spawn_x, true, cfg.sound_volume_step);
|
||||
int[] areaStarts;
|
||||
int[] areaEnds;
|
||||
get_active_audio_areas(areaStarts, areaEnds);
|
||||
if (areaStarts.length() == 0 || range_overlaps_active_areas(spawn_x, spawn_x, areaStarts, areaEnds)) {
|
||||
c.sound_handle = play_1d_with_volume_step(c.voice_sound, x, spawn_x, true, cfg.sound_volume_step);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void update_flying_creature(FlyingCreature@ creature) {
|
||||
void update_flying_creature(FlyingCreature@ creature, bool audio_active) {
|
||||
FlyingCreatureConfig@ cfg = get_flying_creature_config(creature.creature_type);
|
||||
if (cfg is null) return;
|
||||
|
||||
@@ -240,7 +245,11 @@ void update_flying_creature(FlyingCreature@ creature) {
|
||||
creature.fade_timer.restart();
|
||||
}
|
||||
|
||||
if (creature.sound_handle != -1 && p.sound_is_active(creature.sound_handle)) {
|
||||
if (!audio_active && creature.sound_handle != -1) {
|
||||
p.destroy_sound(creature.sound_handle);
|
||||
creature.sound_handle = -1;
|
||||
creature.ready_to_remove = true;
|
||||
} else if (creature.sound_handle != -1 && p.sound_is_active(creature.sound_handle)) {
|
||||
float progress = float(creature.fade_timer.elapsed) / float(FLYING_CREATURE_FADE_OUT_DURATION);
|
||||
if (progress < 0.0) progress = 0.0;
|
||||
if (progress > 1.0) progress = 1.0;
|
||||
@@ -265,7 +274,12 @@ void update_flying_creature(FlyingCreature@ creature) {
|
||||
}
|
||||
|
||||
// Update looping sound position
|
||||
if (creature.sound_handle != -1 && p.sound_is_active(creature.sound_handle)) {
|
||||
if (!audio_active) {
|
||||
if (creature.sound_handle != -1) {
|
||||
p.destroy_sound(creature.sound_handle);
|
||||
creature.sound_handle = -1;
|
||||
}
|
||||
} else if (creature.sound_handle != -1 && p.sound_is_active(creature.sound_handle)) {
|
||||
p.update_sound_1d(creature.sound_handle, creature.position);
|
||||
} else if (creature.sound_handle == -1 || !p.sound_is_active(creature.sound_handle)) {
|
||||
// Restart looping sound if it stopped
|
||||
@@ -301,7 +315,7 @@ void update_flying_creature(FlyingCreature@ creature) {
|
||||
}
|
||||
if (target_x >= 0 && target_x < MAP_SIZE) {
|
||||
creature.position = target_x;
|
||||
if (creature.sound_handle != -1 && p.sound_is_active(creature.sound_handle)) {
|
||||
if (audio_active && creature.sound_handle != -1 && p.sound_is_active(creature.sound_handle)) {
|
||||
p.update_sound_1d(creature.sound_handle, creature.position);
|
||||
}
|
||||
}
|
||||
@@ -314,15 +328,18 @@ void update_flying_creature(FlyingCreature@ creature) {
|
||||
|
||||
if (creature.fall_sound_handle != -1) {
|
||||
p.destroy_sound(creature.fall_sound_handle);
|
||||
creature.fall_sound_handle = -1;
|
||||
}
|
||||
|
||||
float pitch_percent = 50.0 + (50.0 * (float(creature.height) / float(cfg.max_height)));
|
||||
if (pitch_percent < 50.0) pitch_percent = 50.0;
|
||||
if (pitch_percent > 100.0) pitch_percent = 100.0;
|
||||
|
||||
creature.fall_sound_handle = p.play_extended_1d(cfg.fall_sound, x, creature.position, 0, 0, true, 0, 0.0, 0.0, pitch_percent);
|
||||
if (creature.fall_sound_handle != -1) {
|
||||
p.update_sound_positioning_values(creature.fall_sound_handle, -1.0, cfg.sound_volume_step, true);
|
||||
if (audio_active) {
|
||||
creature.fall_sound_handle = p.play_extended_1d(cfg.fall_sound, x, creature.position, 0, 0, true, 0, 0.0, 0.0, pitch_percent);
|
||||
if (creature.fall_sound_handle != -1) {
|
||||
p.update_sound_positioning_values(creature.fall_sound_handle, -1.0, cfg.sound_volume_step, true);
|
||||
}
|
||||
}
|
||||
|
||||
if (creature.height <= 0) {
|
||||
@@ -340,8 +357,14 @@ void update_flying_creature(FlyingCreature@ creature) {
|
||||
}
|
||||
|
||||
void update_flying_creatures() {
|
||||
int[] areaStarts;
|
||||
int[] areaEnds;
|
||||
get_active_audio_areas(areaStarts, areaEnds);
|
||||
bool limit_audio = (areaStarts.length() > 0);
|
||||
|
||||
for (uint i = 0; i < flying_creatures.length(); i++) {
|
||||
update_flying_creature(flying_creatures[i]);
|
||||
bool audio_active = !limit_audio || range_overlaps_active_areas(flying_creatures[i].position, flying_creatures[i].position, areaStarts, areaEnds);
|
||||
update_flying_creature(flying_creatures[i], audio_active);
|
||||
|
||||
if (flying_creatures[i].health <= 0) {
|
||||
if (flying_creatures[i].state == "falling" && flying_creatures[i].height <= 0) {
|
||||
|
||||
@@ -77,7 +77,12 @@ void spawn_ground_game(int expansion_start, int expansion_end) {
|
||||
GroundGame@ b = GroundGame(spawn_x, expansion_start, expansion_end, "boar");
|
||||
ground_games.insert_last(b);
|
||||
// Play looping sound that follows the boar
|
||||
b.sound_handle = play_1d_with_volume_step(b.voice_sound, x, spawn_x, true, BOAR_SOUND_VOLUME_STEP);
|
||||
int[] areaStarts;
|
||||
int[] areaEnds;
|
||||
get_active_audio_areas(areaStarts, areaEnds);
|
||||
if (areaStarts.length() == 0 || range_overlaps_active_areas(spawn_x, spawn_x, areaStarts, areaEnds)) {
|
||||
b.sound_handle = play_1d_with_volume_step(b.voice_sound, x, spawn_x, true, BOAR_SOUND_VOLUME_STEP);
|
||||
}
|
||||
}
|
||||
|
||||
bool can_ground_game_attack_player(GroundGame@ game) {
|
||||
@@ -109,9 +114,14 @@ bool try_attack_player_ground_game(GroundGame@ game) {
|
||||
return true;
|
||||
}
|
||||
|
||||
void update_ground_game(GroundGame@ game) {
|
||||
void update_ground_game(GroundGame@ game, bool audio_active) {
|
||||
// Update looping sound position
|
||||
if (game.sound_handle != -1 && p.sound_is_active(game.sound_handle)) {
|
||||
if (!audio_active) {
|
||||
if (game.sound_handle != -1) {
|
||||
p.destroy_sound(game.sound_handle);
|
||||
game.sound_handle = -1;
|
||||
}
|
||||
} else if (game.sound_handle != -1 && p.sound_is_active(game.sound_handle)) {
|
||||
p.update_sound_1d(game.sound_handle, game.position);
|
||||
} else if (game.sound_handle == -1 || !p.sound_is_active(game.sound_handle)) {
|
||||
// Restart looping sound if it stopped for some reason
|
||||
@@ -153,7 +163,9 @@ void update_ground_game(GroundGame@ game) {
|
||||
// Don't leave area or enter base
|
||||
if (target >= game.area_start && target <= game.area_end && target > BASE_END) {
|
||||
game.position = target;
|
||||
play_creature_footstep(x, game.position, BASE_END, GRASS_END, BOAR_FOOTSTEP_MAX_DISTANCE, BOAR_SOUND_VOLUME_STEP);
|
||||
if (audio_active) {
|
||||
play_creature_footstep(x, game.position, BASE_END, GRASS_END, BOAR_FOOTSTEP_MAX_DISTANCE, BOAR_SOUND_VOLUME_STEP);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Wandering
|
||||
@@ -166,7 +178,9 @@ void update_ground_game(GroundGame@ game) {
|
||||
// Don't leave area or enter base
|
||||
if (target >= game.area_start && target <= game.area_end && target > BASE_END) {
|
||||
game.position = target;
|
||||
play_creature_footstep(x, game.position, BASE_END, GRASS_END, BOAR_FOOTSTEP_MAX_DISTANCE, BOAR_SOUND_VOLUME_STEP);
|
||||
if (audio_active) {
|
||||
play_creature_footstep(x, game.position, BASE_END, GRASS_END, BOAR_FOOTSTEP_MAX_DISTANCE, BOAR_SOUND_VOLUME_STEP);
|
||||
}
|
||||
} else {
|
||||
game.wander_direction = -game.wander_direction; // Turn around
|
||||
}
|
||||
@@ -175,8 +189,14 @@ void update_ground_game(GroundGame@ game) {
|
||||
}
|
||||
|
||||
void update_ground_games() {
|
||||
int[] areaStarts;
|
||||
int[] areaEnds;
|
||||
get_active_audio_areas(areaStarts, areaEnds);
|
||||
bool limit_audio = (areaStarts.length() > 0);
|
||||
|
||||
for (uint i = 0; i < ground_games.length(); i++) {
|
||||
update_ground_game(ground_games[i]);
|
||||
bool audio_active = !limit_audio || range_overlaps_active_areas(ground_games[i].position, ground_games[i].position, areaStarts, areaEnds);
|
||||
update_ground_game(ground_games[i], audio_active);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+23
-5
@@ -62,7 +62,12 @@ void spawn_undead() {
|
||||
Undead@ z = Undead(spawn_x, "zombie");
|
||||
undeads.insert_last(z);
|
||||
// Play looping sound that follows the zombie
|
||||
z.sound_handle = play_1d_with_volume_step(z.voice_sound, x, spawn_x, true, ZOMBIE_SOUND_VOLUME_STEP);
|
||||
int[] areaStarts;
|
||||
int[] areaEnds;
|
||||
get_active_audio_areas(areaStarts, areaEnds);
|
||||
if (areaStarts.length() == 0 || range_overlaps_active_areas(spawn_x, spawn_x, areaStarts, areaEnds)) {
|
||||
z.sound_handle = play_1d_with_volume_step(z.voice_sound, x, spawn_x, true, ZOMBIE_SOUND_VOLUME_STEP);
|
||||
}
|
||||
}
|
||||
|
||||
void try_attack_barricade_undead(Undead@ undead) {
|
||||
@@ -128,9 +133,14 @@ bool try_attack_player_undead(Undead@ undead) {
|
||||
return true;
|
||||
}
|
||||
|
||||
void update_undead(Undead@ undead) {
|
||||
void update_undead(Undead@ undead, bool audio_active) {
|
||||
// Update looping sound position
|
||||
if (undead.sound_handle != -1 && p.sound_is_active(undead.sound_handle)) {
|
||||
if (!audio_active) {
|
||||
if (undead.sound_handle != -1) {
|
||||
p.destroy_sound(undead.sound_handle);
|
||||
undead.sound_handle = -1;
|
||||
}
|
||||
} else if (undead.sound_handle != -1 && p.sound_is_active(undead.sound_handle)) {
|
||||
p.update_sound_1d(undead.sound_handle, undead.position);
|
||||
} else if (undead.sound_handle == -1 || !p.sound_is_active(undead.sound_handle)) {
|
||||
// Restart looping sound if it stopped for some reason
|
||||
@@ -175,7 +185,9 @@ void update_undead(Undead@ undead) {
|
||||
}
|
||||
|
||||
undead.position = target_x;
|
||||
play_creature_footstep(x, undead.position, BASE_END, GRASS_END, ZOMBIE_FOOTSTEP_MAX_DISTANCE, ZOMBIE_SOUND_VOLUME_STEP);
|
||||
if (audio_active) {
|
||||
play_creature_footstep(x, undead.position, BASE_END, GRASS_END, ZOMBIE_FOOTSTEP_MAX_DISTANCE, ZOMBIE_SOUND_VOLUME_STEP);
|
||||
}
|
||||
}
|
||||
|
||||
void update_undeads() {
|
||||
@@ -195,8 +207,14 @@ void update_undeads() {
|
||||
spawn_undead();
|
||||
}
|
||||
|
||||
int[] areaStarts;
|
||||
int[] areaEnds;
|
||||
get_active_audio_areas(areaStarts, areaEnds);
|
||||
bool limit_audio = (areaStarts.length() > 0);
|
||||
|
||||
for (uint i = 0; i < undeads.length(); i++) {
|
||||
update_undead(undeads[i]);
|
||||
bool audio_active = !limit_audio || range_overlaps_active_areas(undeads[i].position, undeads[i].position, areaStarts, areaEnds);
|
||||
update_undead(undeads[i], audio_active);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+14
-1
@@ -481,9 +481,22 @@ void normalize_tree_positions() {
|
||||
}
|
||||
|
||||
void update_environment() {
|
||||
int[] areaStarts;
|
||||
int[] areaEnds;
|
||||
get_active_audio_areas(areaStarts, areaEnds);
|
||||
bool limit_audio = (areaStarts.length() > 0);
|
||||
|
||||
for(uint i = 0; i < trees.length(); i++) {
|
||||
trees[i].update();
|
||||
trees[i].try_regen();
|
||||
|
||||
if (limit_audio && !range_overlaps_active_areas(trees[i].position, trees[i].position, areaStarts, areaEnds)) {
|
||||
if (trees[i].sound_handle != -1) {
|
||||
p.destroy_sound(trees[i].sound_handle);
|
||||
trees[i].sound_handle = -1;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
trees[i].update();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -7,6 +7,65 @@ void check_action_menu(int x) {
|
||||
}
|
||||
}
|
||||
|
||||
WorldSnare@ get_snare_within_range(int pos, int range) {
|
||||
WorldSnare@ nearest = null;
|
||||
int nearest_distance = range + 1;
|
||||
for (uint i = 0; i < world_snares.length(); i++) {
|
||||
int dist = abs(world_snares[i].position - pos);
|
||||
if (dist <= range && dist < nearest_distance) {
|
||||
nearest_distance = dist;
|
||||
@nearest = @world_snares[i];
|
||||
}
|
||||
}
|
||||
return nearest;
|
||||
}
|
||||
|
||||
string get_fire_intensity_label(int fuel_remaining) {
|
||||
int hours_remaining = fuel_remaining / 60000;
|
||||
if (hours_remaining >= 8) return "Giant blaze";
|
||||
if (hours_remaining >= 2) return "Small fire";
|
||||
return "A few glowing coals";
|
||||
}
|
||||
|
||||
string get_fire_time_estimate(int fuel_remaining) {
|
||||
int approx_hours = (fuel_remaining + 30000) / 60000;
|
||||
if (approx_hours <= 0) return "less than 1 hour";
|
||||
if (approx_hours == 1) return "approximately 1 hour";
|
||||
return "approximately " + approx_hours + " hours";
|
||||
}
|
||||
|
||||
void check_fire_status(WorldFire@ fire) {
|
||||
if (fire == null) {
|
||||
speak_with_history("No fire nearby.", true);
|
||||
return;
|
||||
}
|
||||
string label = get_fire_intensity_label(fire.fuel_remaining);
|
||||
string estimate = get_fire_time_estimate(fire.fuel_remaining);
|
||||
speak_with_history(label + ". " + estimate + " remaining.", true);
|
||||
}
|
||||
|
||||
void check_snare_status(WorldSnare@ snare) {
|
||||
if (snare == null) {
|
||||
speak_with_history("No snare nearby.", true);
|
||||
return;
|
||||
}
|
||||
if (snare.has_catch) {
|
||||
speak_with_history("Snare holds a " + snare.catch_type + ".", true);
|
||||
} else if (!snare.active) {
|
||||
speak_with_history("Snare is set but not active yet.", true);
|
||||
} else {
|
||||
speak_with_history("Snare is set and empty.", true);
|
||||
}
|
||||
}
|
||||
|
||||
void check_fishing_status() {
|
||||
if (fish_on_line) {
|
||||
speak_with_history("Fish on the line.", true);
|
||||
return;
|
||||
}
|
||||
speak_with_history("No fish on the line.", true);
|
||||
}
|
||||
|
||||
void try_place_snare(int x) {
|
||||
if (get_personal_count(ITEM_SNARES) > 0) {
|
||||
// Prevent placing in base area
|
||||
@@ -153,10 +212,13 @@ void run_action_menu(int x) {
|
||||
int selection = 0;
|
||||
string[] options;
|
||||
int[] action_types; // Track what action each option corresponds to
|
||||
const int check_range = 2;
|
||||
|
||||
// Check if fire is nearby
|
||||
WorldFire@ nearby_fire = get_fire_near(x);
|
||||
bool can_feed_fire = nearby_fire != null;
|
||||
WorldFire@ check_fire = get_fire_within_range(x, check_range);
|
||||
WorldSnare@ check_snare = get_snare_within_range(x, check_range);
|
||||
|
||||
// Build menu options dynamically
|
||||
options.insert_last("Place Snare");
|
||||
@@ -182,6 +244,19 @@ void run_action_menu(int x) {
|
||||
action_types.insert_last(4);
|
||||
}
|
||||
|
||||
if (check_fire != null) {
|
||||
options.insert_last("Check fire");
|
||||
action_types.insert_last(5);
|
||||
}
|
||||
if (check_snare != null) {
|
||||
options.insert_last("Check snare");
|
||||
action_types.insert_last(6);
|
||||
}
|
||||
if (line_in_water || fish_on_line || is_reeling || is_casting) {
|
||||
options.insert_last("Check fishing pole");
|
||||
action_types.insert_last(7);
|
||||
}
|
||||
|
||||
while(true) {
|
||||
wait(5);
|
||||
menu_background_tick();
|
||||
@@ -214,6 +289,12 @@ void run_action_menu(int x) {
|
||||
try_feed_fire_log(nearby_fire);
|
||||
} else if (action == 4) {
|
||||
try_burn_incense();
|
||||
} else if (action == 5) {
|
||||
check_fire_status(check_fire);
|
||||
} else if (action == 6) {
|
||||
check_snare_status(check_snare);
|
||||
} else if (action == 7) {
|
||||
check_fishing_status();
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -230,6 +311,12 @@ void run_action_menu(int x) {
|
||||
try_feed_fire_log_max(nearby_fire);
|
||||
} else if (action == 4) {
|
||||
try_burn_incense_max();
|
||||
} else if (action == 5) {
|
||||
check_fire_status(check_fire);
|
||||
} else if (action == 6) {
|
||||
check_snare_status(check_snare);
|
||||
} else if (action == 7) {
|
||||
check_fishing_status();
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -12,7 +12,11 @@ void run_base_info_menu() {
|
||||
int selection = 0;
|
||||
string[] options;
|
||||
options.insert_last("Barricade health " + barricade_health + " of " + BARRICADE_MAX_HEALTH);
|
||||
options.insert_last("Residents " + residents_count);
|
||||
string resident_status = "Residents " + residents_count;
|
||||
if (blessing_resident_active) {
|
||||
resident_status += " (blessed)";
|
||||
}
|
||||
options.insert_last(resident_status);
|
||||
|
||||
if (world_storages.length() > 0) {
|
||||
options.insert_last("Storage built. Total items " + get_storage_total_items());
|
||||
|
||||
+3
-1
@@ -26,7 +26,9 @@ void notify(string message) {
|
||||
|
||||
void update_notifications() {
|
||||
if (notification_queue.length() == 0) {
|
||||
notification_active = false;
|
||||
if (notification_active && notification_timer.elapsed >= NOTIFICATION_DELAY) {
|
||||
notification_active = false;
|
||||
}
|
||||
notification_sound_handle = -1;
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -62,6 +62,8 @@ int incense_hours_remaining = 0;
|
||||
bool incense_burning = false;
|
||||
bool blessing_speed_active = false;
|
||||
timer blessing_speed_timer;
|
||||
bool blessing_resident_active = false;
|
||||
timer blessing_resident_timer;
|
||||
|
||||
// Timers
|
||||
timer walktimer;
|
||||
|
||||
@@ -202,6 +202,7 @@ void reset_game_state() {
|
||||
incense_hours_remaining = 0;
|
||||
incense_burning = false;
|
||||
blessing_speed_active = false;
|
||||
blessing_resident_active = false;
|
||||
|
||||
// Reset inventory using the registry system
|
||||
reset_inventory();
|
||||
@@ -978,7 +979,12 @@ bool load_game_state() {
|
||||
|
||||
bandits.insert_last(b);
|
||||
// Start looping sound for loaded bandit
|
||||
b.sound_handle = play_1d_with_volume_step(b.alert_sound, x, b.position, true, BANDIT_SOUND_VOLUME_STEP);
|
||||
int[] areaStarts;
|
||||
int[] areaEnds;
|
||||
get_active_audio_areas(areaStarts, areaEnds);
|
||||
if (areaStarts.length() == 0 || range_overlaps_active_areas(b.position, b.position, areaStarts, areaEnds)) {
|
||||
b.sound_handle = play_1d_with_volume_step(b.alert_sound, x, b.position, true, BANDIT_SOUND_VOLUME_STEP);
|
||||
}
|
||||
}
|
||||
|
||||
string[] mountainData = get_string_list_or_split(saveData, "mountains_data");
|
||||
|
||||
+35
-1
@@ -317,6 +317,10 @@ void update_blessings() {
|
||||
update_max_health_from_equipment();
|
||||
speak_with_history("The speed blessing fades.", true);
|
||||
}
|
||||
if (blessing_resident_active && blessing_resident_timer.elapsed >= BLESSING_RESIDENT_DURATION) {
|
||||
blessing_resident_active = false;
|
||||
speak_with_history("The residents' purpose fades.", true);
|
||||
}
|
||||
}
|
||||
|
||||
void attempt_blessing() {
|
||||
@@ -328,6 +332,7 @@ void attempt_blessing() {
|
||||
if (player_health < max_health) options.insert_last(0);
|
||||
if (!blessing_speed_active) options.insert_last(1);
|
||||
if (barricade_health < BARRICADE_MAX_HEALTH) options.insert_last(2);
|
||||
if (residents_count > 0 && !blessing_resident_active) options.insert_last(3);
|
||||
if (options.length() == 0) return;
|
||||
|
||||
int choice = options[random(0, options.length() - 1)];
|
||||
@@ -358,6 +363,10 @@ void attempt_blessing() {
|
||||
? "A divine force repairs the barricade. +" + gained + " health."
|
||||
: "A divine force surrounds the barricade.";
|
||||
notify(god_name + " favor shines upon you. " + bonus);
|
||||
} else if (choice == 3) {
|
||||
blessing_resident_active = true;
|
||||
blessing_resident_timer.restart();
|
||||
notify(god_name + " radiance fills residents with purpose.");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -401,7 +410,7 @@ void update_time() {
|
||||
|
||||
if (is_daytime && residents_count > 0 && barricade_health < BARRICADE_MAX_HEALTH && current_hour % 4 == 0) {
|
||||
if (has_any_storage_food()) {
|
||||
int gained = add_barricade_health(residents_count);
|
||||
int gained = add_barricade_health(residents_count * get_resident_effect_multiplier());
|
||||
if (gained > 0 && x <= BASE_END) {
|
||||
speak_with_history("Residents repaired the barricade. +" + gained + " health.", true);
|
||||
}
|
||||
@@ -527,6 +536,31 @@ void start_crossfade(bool to_night) {
|
||||
void update_crossfade() {
|
||||
if (!crossfade_active) return;
|
||||
|
||||
// If a handle went inactive mid-fade, cancel and restart ambience to avoid fading unrelated sounds.
|
||||
if (crossfade_to_night) {
|
||||
if (day_sound_handle == -1 || !p.sound_is_active(day_sound_handle)) {
|
||||
crossfade_active = false;
|
||||
update_ambience(true);
|
||||
return;
|
||||
}
|
||||
if (night_sound_handle == -1 || !p.sound_is_active(night_sound_handle)) {
|
||||
crossfade_active = false;
|
||||
update_ambience(true);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
if (night_sound_handle == -1 || !p.sound_is_active(night_sound_handle)) {
|
||||
crossfade_active = false;
|
||||
update_ambience(true);
|
||||
return;
|
||||
}
|
||||
if (day_sound_handle == -1 || !p.sound_is_active(day_sound_handle)) {
|
||||
crossfade_active = false;
|
||||
update_ambience(true);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
float progress = float(crossfade_timer.elapsed) / float(CROSSFADE_DURATION);
|
||||
if (progress > 1.0) progress = 1.0;
|
||||
|
||||
|
||||
@@ -360,12 +360,24 @@ bool is_mountain_stream_at(int world_x) {
|
||||
return mountain.is_stream_at(world_x);
|
||||
}
|
||||
|
||||
void update_mountains() {
|
||||
void update_mountains_with_audio_areas(int[]@ areaStarts, int[]@ areaEnds) {
|
||||
bool limit_audio = (areaStarts.length() > 0);
|
||||
for (uint i = 0; i < world_mountains.length(); i++) {
|
||||
if (limit_audio && !range_overlaps_active_areas(world_mountains[i].start_position, world_mountains[i].end_position, areaStarts, areaEnds)) {
|
||||
world_mountains[i].destroy();
|
||||
continue;
|
||||
}
|
||||
world_mountains[i].update();
|
||||
}
|
||||
}
|
||||
|
||||
void update_mountains() {
|
||||
int[] areaStarts;
|
||||
int[] areaEnds;
|
||||
get_active_audio_areas(areaStarts, areaEnds);
|
||||
update_mountains_with_audio_areas(areaStarts, areaEnds);
|
||||
}
|
||||
|
||||
void clear_mountains() {
|
||||
for (uint i = 0; i < world_mountains.length(); i++) {
|
||||
world_mountains[i].destroy();
|
||||
|
||||
@@ -49,7 +49,16 @@ void add_world_drop(int pos, string type) {
|
||||
}
|
||||
|
||||
void update_world_drops() {
|
||||
int[] areaStarts;
|
||||
int[] areaEnds;
|
||||
get_active_audio_areas(areaStarts, areaEnds);
|
||||
bool limit_audio = (areaStarts.length() > 0);
|
||||
|
||||
for (uint i = 0; i < world_drops.length(); i++) {
|
||||
if (limit_audio && !range_overlaps_active_areas(world_drops[i].position, world_drops[i].position, areaStarts, areaEnds)) {
|
||||
world_drops[i].destroy();
|
||||
continue;
|
||||
}
|
||||
world_drops[i].update();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -87,9 +87,20 @@ void add_world_fire(int pos) {
|
||||
}
|
||||
|
||||
void update_fires() {
|
||||
int[] areaStarts;
|
||||
int[] areaEnds;
|
||||
get_active_audio_areas(areaStarts, areaEnds);
|
||||
bool limit_audio = (areaStarts.length() > 0);
|
||||
|
||||
// Update all fires and remove any that have burned out
|
||||
for (uint i = 0; i < world_fires.length(); i++) {
|
||||
world_fires[i].update();
|
||||
if (limit_audio && !range_overlaps_active_areas(world_fires[i].position, world_fires[i].position, areaStarts, areaEnds)) {
|
||||
if (world_fires[i].sound_handle != -1) {
|
||||
p.destroy_sound(world_fires[i].sound_handle);
|
||||
world_fires[i].sound_handle = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Remove dead fires
|
||||
|
||||
@@ -149,7 +149,19 @@ void check_snare_collision(int player_x) {
|
||||
}
|
||||
|
||||
void update_snares() {
|
||||
int[] areaStarts;
|
||||
int[] areaEnds;
|
||||
get_active_audio_areas(areaStarts, areaEnds);
|
||||
bool limit_audio = (areaStarts.length() > 0);
|
||||
|
||||
for (int i = int(world_snares.length()) - 1; i >= 0; i--) {
|
||||
world_snares[i].update();
|
||||
WorldSnare@ snare = world_snares[i];
|
||||
snare.update();
|
||||
if (limit_audio && !range_overlaps_active_areas(snare.position, snare.position, areaStarts, areaEnds)) {
|
||||
if (snare.sound_handle != -1) {
|
||||
p.destroy_sound(snare.sound_handle);
|
||||
snare.sound_handle = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -66,15 +66,120 @@ class WorldStream {
|
||||
}
|
||||
WorldStream@[] world_streams;
|
||||
|
||||
string get_expanded_area_type(int index) {
|
||||
string terrain = expanded_terrain_types[index];
|
||||
if (terrain.find("mountain:") == 0) {
|
||||
return "mountain";
|
||||
}
|
||||
return terrain;
|
||||
}
|
||||
|
||||
bool get_audio_area_bounds_for_position(int pos, int &out areaStart, int &out areaEnd) {
|
||||
if (pos <= BASE_END) {
|
||||
areaStart = 0;
|
||||
areaEnd = BASE_END;
|
||||
return true;
|
||||
}
|
||||
if (pos <= GRASS_END) {
|
||||
areaStart = BASE_END + 1;
|
||||
areaEnd = GRASS_END;
|
||||
return true;
|
||||
}
|
||||
if (pos <= GRAVEL_END) {
|
||||
areaStart = GRAVEL_START;
|
||||
areaEnd = GRAVEL_END;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (expanded_area_start == -1 || pos < expanded_area_start || pos > expanded_area_end) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int index = pos - expanded_area_start;
|
||||
int total = int(expanded_terrain_types.length());
|
||||
if (index < 0 || index >= total) return false;
|
||||
|
||||
string areaType = get_expanded_area_type(index);
|
||||
int left = index;
|
||||
while (left > 0) {
|
||||
if (get_expanded_area_type(left - 1) != areaType) break;
|
||||
left--;
|
||||
}
|
||||
|
||||
int right = index;
|
||||
while (right + 1 < total) {
|
||||
if (get_expanded_area_type(right + 1) != areaType) break;
|
||||
right++;
|
||||
}
|
||||
|
||||
areaStart = expanded_area_start + left;
|
||||
areaEnd = expanded_area_start + right;
|
||||
return true;
|
||||
}
|
||||
|
||||
void get_active_audio_areas(int[]@ areaStarts, int[]@ areaEnds) {
|
||||
areaStarts.resize(0);
|
||||
areaEnds.resize(0);
|
||||
|
||||
int currentStart = 0;
|
||||
int currentEnd = 0;
|
||||
if (!get_audio_area_bounds_for_position(x, currentStart, currentEnd)) return;
|
||||
|
||||
areaStarts.insert_last(currentStart);
|
||||
areaEnds.insert_last(currentEnd);
|
||||
|
||||
if (currentStart > 0) {
|
||||
int prevStart = 0;
|
||||
int prevEnd = 0;
|
||||
if (get_audio_area_bounds_for_position(currentStart - 1, prevStart, prevEnd)) {
|
||||
if (prevStart != currentStart || prevEnd != currentEnd) {
|
||||
areaStarts.insert_last(prevStart);
|
||||
areaEnds.insert_last(prevEnd);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (currentEnd + 1 < MAP_SIZE) {
|
||||
int nextStart = 0;
|
||||
int nextEnd = 0;
|
||||
if (get_audio_area_bounds_for_position(currentEnd + 1, nextStart, nextEnd)) {
|
||||
if (nextStart != currentStart || nextEnd != currentEnd) {
|
||||
areaStarts.insert_last(nextStart);
|
||||
areaEnds.insert_last(nextEnd);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool range_overlaps_active_areas(int rangeStart, int rangeEnd, int[]@ areaStarts, int[]@ areaEnds) {
|
||||
for (uint i = 0; i < areaStarts.length(); i++) {
|
||||
if (rangeEnd >= areaStarts[i] && rangeStart <= areaEnds[i]) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void add_world_stream(int start_pos, int width) {
|
||||
WorldStream@ s = WorldStream(start_pos, width);
|
||||
world_streams.insert_last(s);
|
||||
}
|
||||
|
||||
void update_streams() {
|
||||
int[] areaStarts;
|
||||
int[] areaEnds;
|
||||
get_active_audio_areas(areaStarts, areaEnds);
|
||||
bool limit_audio = areaStarts.length() > 0;
|
||||
|
||||
for (uint i = 0; i < world_streams.length(); i++) {
|
||||
if (limit_audio && !range_overlaps_active_areas(world_streams[i].start_position, world_streams[i].end_position, areaStarts, areaEnds)) {
|
||||
world_streams[i].destroy();
|
||||
continue;
|
||||
}
|
||||
world_streams[i].update();
|
||||
}
|
||||
|
||||
update_mountains_with_audio_areas(areaStarts, areaEnds);
|
||||
}
|
||||
|
||||
WorldStream@ get_stream_at(int pos) {
|
||||
|
||||
@@ -17,14 +17,3 @@
|
||||
// - 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();
|
||||
}
|
||||
for (uint i = 0; i < world_fires.length(); i++) {
|
||||
world_fires[i].update();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user