From 1220cfefe28738418c2ca1b5639f06da154184e5 Mon Sep 17 00:00:00 2001 From: Storm Dragon Date: Fri, 6 Feb 2026 14:51:01 -0500 Subject: [PATCH] Residents now consume items other than food for clothing repairs etc. --- src/base_system.nvgt | 77 +++++++++++++++++++++++++++++++++++++++++ src/constants.nvgt | 2 ++ src/enemies/undead.nvgt | 63 +++++++++++++++++++++------------ src/save_system.nvgt | 18 +++++++--- src/time_system.nvgt | 1 + 5 files changed, 134 insertions(+), 27 deletions(-) diff --git a/src/base_system.nvgt b/src/base_system.nvgt index 91bbe3a..2d859bc 100644 --- a/src/base_system.nvgt +++ b/src/base_system.nvgt @@ -561,6 +561,83 @@ void process_daily_weapon_breakage() { } } +void attempt_resident_clothing_repairs() { + if (residents_count <= 0) return; + + int threshold = BASE_STORAGE_MAX / 2; + if (threshold < RESIDENT_CLOTHING_REPAIR_COST) { + threshold = RESIDENT_CLOTHING_REPAIR_COST; + } + + int vines_used = 0; + int skins_used = 0; + int down_used = 0; + int repairs_done = 0; + + for (int i = 0; i < residents_count; i++) { + int best_item = -1; + int best_count = -1; + + int vines_count = get_storage_count(ITEM_VINES); + if (vines_count >= RESIDENT_CLOTHING_REPAIR_COST && vines_count > threshold && vines_count > best_count) { + best_item = ITEM_VINES; + best_count = vines_count; + } + + int skins_count = get_storage_count(ITEM_SKINS); + if (skins_count >= RESIDENT_CLOTHING_REPAIR_COST && skins_count > threshold && skins_count > best_count) { + best_item = ITEM_SKINS; + best_count = skins_count; + } + + int down_count = get_storage_count(ITEM_DOWN); + if (down_count >= RESIDENT_CLOTHING_REPAIR_COST && down_count > threshold && down_count > best_count) { + best_item = ITEM_DOWN; + best_count = down_count; + } + + if (best_item == -1) break; + + add_storage_count(best_item, -RESIDENT_CLOTHING_REPAIR_COST); + repairs_done++; + + if (best_item == ITEM_VINES) { + vines_used += RESIDENT_CLOTHING_REPAIR_COST; + } else if (best_item == ITEM_SKINS) { + skins_used += RESIDENT_CLOTHING_REPAIR_COST; + } else if (best_item == ITEM_DOWN) { + down_used += RESIDENT_CLOTHING_REPAIR_COST; + } + } + + if (repairs_done > 0 && x <= BASE_END) { + string msg = (repairs_done == 1) + ? "A resident is mending clothing." + : "Residents are mending clothing."; + string[] materials; + if (vines_used > 0) materials.insert_last(vines_used + " vines"); + if (skins_used > 0) materials.insert_last(skins_used + " skins"); + if (down_used > 0) materials.insert_last(down_used + " down"); + + if (materials.length() > 0) { + msg += " Used "; + for (uint i = 0; i < materials.length(); i++) { + if (i > 0) { + if (i == materials.length() - 1) { + msg += " and "; + } else { + msg += ", "; + } + } + msg += materials[i]; + } + msg += "."; + } + + speak_with_history(msg, true); + } +} + // Resident snare retrieval void attempt_resident_snare_retrieval() { // Only during daytime diff --git a/src/constants.nvgt b/src/constants.nvgt index c307ff5..ede0966 100644 --- a/src/constants.nvgt +++ b/src/constants.nvgt @@ -74,6 +74,7 @@ const int WIGHT_DAMAGE_MIN = 6; const int WIGHT_DAMAGE_MAX = 8; const int WIGHT_SPAWN_CHANCE_START = 5; const int WIGHT_SPAWN_CHANCE_STEP = 5; +const int SPECIAL_UNDEAD_SPAWN_DAYS_PER_EXTRA = 9; // Vampyr settings (undead abductor) const int VAMPYR_HEALTH = 40; @@ -214,6 +215,7 @@ const int RESIDENT_SNARE_CHECK_CHANCE = 15; // 15% chance per hour to check sna const int RESIDENT_FISHING_CHANCE = 6; // 6% chance per resident per hour to catch a fish const int RESIDENT_SMOKE_FISH_CHANCE = 10; // 10% chance per hour to smoke a stored fish const int RESIDENT_TOOL_BREAK_CHANCE = 2; // 2% chance tools break during resident use (fishing poles, knives, baskets) +const int RESIDENT_CLOTHING_REPAIR_COST = 5; const float PLAYER_ITEM_BREAK_CHANCE_MIN = 1.0; const float PLAYER_ITEM_BREAK_CHANCE_MAX = 100.0; const float PLAYER_ITEM_BREAK_CHANCE_INCREMENT = 0.1; diff --git a/src/enemies/undead.nvgt b/src/enemies/undead.nvgt index 48d8695..26da7cf 100644 --- a/src/enemies/undead.nvgt +++ b/src/enemies/undead.nvgt @@ -12,9 +12,9 @@ string[] undead_vampyr_sounds = { string[] undead_resident_sounds = {"sounds/enemies/undead_resident1.ogg"}; int wight_spawn_chance = WIGHT_SPAWN_CHANCE_START; -bool wight_spawned_this_night = false; +int wight_spawned_this_night_count = 0; int vampyr_spawn_chance = VAMPYR_SPAWN_CHANCE_START; -bool vampyr_spawned_this_night = false; +int vampyr_spawned_this_night_count = 0; int get_undead_base_health(const string &in undead_type) { if (undead_type == "wight") return WIGHT_HEALTH; @@ -90,22 +90,37 @@ class Undead { } Undead@[] undeads; -bool has_wight() { +int count_wights() { + int count = 0; for (uint i = 0; i < undeads.length(); i++) { if (undeads[i].undead_type == "wight") { - return true; + count++; } } - return false; + return count; +} + +bool has_wight() { + return count_wights() > 0; +} + +int count_vampyrs() { + int count = 0; + for (uint i = 0; i < undeads.length(); i++) { + if (undeads[i].undead_type == "vampyr") { + count++; + } + } + return count; } bool has_vampyr() { - for (uint i = 0; i < undeads.length(); i++) { - if (undeads[i].undead_type == "vampyr") { - return true; - } - } - return false; + return count_vampyrs() > 0; +} + +int get_night_special_undead_spawn_limit(int day) { + if (day < 1) day = 1; + return 1 + (day / SPECIAL_UNDEAD_SPAWN_DAYS_PER_EXTRA); } void update_undead_weapon_range_audio() { @@ -414,7 +429,7 @@ void update_undeads() { void attempt_hourly_wight_spawn() { if (current_hour == 19) { - wight_spawned_this_night = false; + wight_spawned_this_night_count = 0; } if (is_daytime) { @@ -425,19 +440,20 @@ void attempt_hourly_wight_spawn() { return; } - if (wight_spawned_this_night) { - return; + int wight_count = count_wights(); + if (wight_spawned_this_night_count < wight_count) { + wight_spawned_this_night_count = wight_count; } - if (has_wight()) { - wight_spawned_this_night = true; + int spawn_limit = get_night_special_undead_spawn_limit(current_day); + if (wight_spawned_this_night_count >= spawn_limit) { return; } int roll = random(1, 100); if (roll <= wight_spawn_chance) { spawn_undead("wight"); - wight_spawned_this_night = true; + wight_spawned_this_night_count++; wight_spawn_chance = WIGHT_SPAWN_CHANCE_START; return; } @@ -448,7 +464,7 @@ void attempt_hourly_wight_spawn() { void attempt_hourly_vampyr_spawn() { if (current_hour == 19) { - vampyr_spawned_this_night = false; + vampyr_spawned_this_night_count = 0; } if (is_daytime) { @@ -463,19 +479,20 @@ void attempt_hourly_vampyr_spawn() { return; } - if (vampyr_spawned_this_night) { - return; + int vampyr_count = count_vampyrs(); + if (vampyr_spawned_this_night_count < vampyr_count) { + vampyr_spawned_this_night_count = vampyr_count; } - if (has_vampyr()) { - vampyr_spawned_this_night = true; + int spawn_limit = get_night_special_undead_spawn_limit(current_day); + if (vampyr_spawned_this_night_count >= spawn_limit) { return; } int roll = random(1, 100); if (roll <= vampyr_spawn_chance) { spawn_undead("vampyr"); - vampyr_spawned_this_night = true; + vampyr_spawned_this_night_count++; vampyr_spawn_chance = VAMPYR_SPAWN_CHANCE_START; return; } diff --git a/src/save_system.nvgt b/src/save_system.nvgt index 17051b0..77901c0 100644 --- a/src/save_system.nvgt +++ b/src/save_system.nvgt @@ -885,9 +885,9 @@ bool save_game_state() { saveData.set("player_item_break_pending_type", playerItemBreakPendingType); saveData.set("quest_roll_done_today", quest_roll_done_today); saveData.set("wight_spawn_chance", wight_spawn_chance); - saveData.set("wight_spawned_this_night", wight_spawned_this_night); + saveData.set("wight_spawned_this_night", wight_spawned_this_night_count); saveData.set("vampyr_spawn_chance", vampyr_spawn_chance); - saveData.set("vampyr_spawned_this_night", vampyr_spawned_this_night); + saveData.set("vampyr_spawned_this_night", vampyr_spawned_this_night_count); string[] questData; for (uint i = 0; i < quest_queue.length(); i++) { questData.insert_last("" + quest_queue[i]); @@ -1337,11 +1337,21 @@ bool load_game_state_from_file(const string&in filename) { reset_player_item_break_audio_state(); quest_roll_done_today = get_bool(saveData, "quest_roll_done_today", false); wight_spawn_chance = int(get_number(saveData, "wight_spawn_chance", WIGHT_SPAWN_CHANCE_START)); - wight_spawned_this_night = get_bool(saveData, "wight_spawned_this_night", false); + if (has_number_key(saveData, "wight_spawned_this_night")) { + wight_spawned_this_night_count = int(get_number(saveData, "wight_spawned_this_night", 0)); + } else { + wight_spawned_this_night_count = get_bool(saveData, "wight_spawned_this_night", false) ? 1 : 0; + } + if (wight_spawned_this_night_count < 0) wight_spawned_this_night_count = 0; if (wight_spawn_chance < WIGHT_SPAWN_CHANCE_START) wight_spawn_chance = WIGHT_SPAWN_CHANCE_START; if (wight_spawn_chance > 100) wight_spawn_chance = 100; vampyr_spawn_chance = int(get_number(saveData, "vampyr_spawn_chance", VAMPYR_SPAWN_CHANCE_START)); - vampyr_spawned_this_night = get_bool(saveData, "vampyr_spawned_this_night", false); + if (has_number_key(saveData, "vampyr_spawned_this_night")) { + vampyr_spawned_this_night_count = int(get_number(saveData, "vampyr_spawned_this_night", 0)); + } else { + vampyr_spawned_this_night_count = get_bool(saveData, "vampyr_spawned_this_night", false) ? 1 : 0; + } + if (vampyr_spawned_this_night_count < 0) vampyr_spawned_this_night_count = 0; if (vampyr_spawn_chance < VAMPYR_SPAWN_CHANCE_START) vampyr_spawn_chance = VAMPYR_SPAWN_CHANCE_START; if (vampyr_spawn_chance > 100) vampyr_spawn_chance = 100; diff --git a/src/time_system.nvgt b/src/time_system.nvgt index 1fa5863..11bf873 100644 --- a/src/time_system.nvgt +++ b/src/time_system.nvgt @@ -623,6 +623,7 @@ void update_time() { process_daily_weapon_breakage(); attempt_daily_quest(); attempt_resident_butchering(); + attempt_resident_clothing_repairs(); } if (should_attempt_resident_foraging(current_hour)) { attempt_resident_foraging();