diff --git a/src/base_system.nvgt b/src/base_system.nvgt index 5b6b147..2b1c93d 100644 --- a/src/base_system.nvgt +++ b/src/base_system.nvgt @@ -211,6 +211,163 @@ void process_daily_weapon_breakage() { } } +// Resident snare retrieval +void attempt_resident_snare_retrieval() { + // Only during daytime + if (!is_daytime) return; + + // Need residents + if (residents_count <= 0) return; + + // Need food in storage (same limitation as other resident tasks) + if (get_storage_count(ITEM_MEAT) <= 0) return; + + // Check each snare that has a catch + for (int i = int(world_snares.length()) - 1; i >= 0; i--) { + WorldSnare@ snare = world_snares[i]; + if (!snare.has_catch) continue; + 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; + + // Small chance the game escapes during retrieval (like normal) + if (random(1, 100) <= RESIDENT_SNARE_ESCAPE_CHANCE) { + notify("A " + snare.catch_type + " escaped while a resident checked the snare at x " + snare.position + "."); + remove_snare_at(snare.position); + continue; + } + + // Check if storage has room for small game + if (get_storage_count(ITEM_SMALL_GAME) >= BASE_STORAGE_MAX) continue; + + // Retrieve the game + string game_type = snare.catch_type; + int pos = snare.position; + + // Add game to storage + add_storage_count(ITEM_SMALL_GAME, 1); + storage_small_game_types.insert_last(game_type); + + // Reset the snare in place (resident empties it and resets for more game) + snare.has_catch = false; + snare.catch_type = ""; + snare.catch_chance = 5; + snare.escape_chance = 5; + snare.minute_timer.restart(); + + // Notify if player is in base + if (x <= BASE_END) { + speak_with_history("Resident retrieved " + game_type + " from snare at x " + pos + " and reset it.", true); + } + } +} + +// Resident butchering - processes one game per day from storage +void attempt_resident_butchering() { + // Need residents + if (residents_count <= 0) return; + + // Need food in storage (same limitation as other resident tasks) + if (get_storage_count(ITEM_MEAT) <= 0) return; + + // Need game in storage + if (get_storage_count(ITEM_SMALL_GAME) <= 0 && get_storage_count(ITEM_BOAR_CARCASSES) <= 0) return; + + // Need a knife in storage + 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_fire) 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"; + } + + // 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); + } + } + + // 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 void attempt_resident_collection() { diff --git a/src/constants.nvgt b/src/constants.nvgt index 019cb70..b0502d5 100644 --- a/src/constants.nvgt +++ b/src/constants.nvgt @@ -170,6 +170,8 @@ const int RESIDENT_WEAPON_BREAK_CHANCE = 10; const int RESIDENT_SPEAR_DAMAGE = 2; const int RESIDENT_SLING_DAMAGE_MIN = 3; const int RESIDENT_SLING_DAMAGE_MAX = 5; +const int RESIDENT_SNARE_ESCAPE_CHANCE = 5; // 5% chance game escapes when resident retrieves +const int RESIDENT_SNARE_CHECK_CHANCE = 15; // 15% chance per hour to check snares // Goose settings const int GOOSE_HEALTH = 1; diff --git a/src/time_system.nvgt b/src/time_system.nvgt index 3d27021..beb6c83 100644 --- a/src/time_system.nvgt +++ b/src/time_system.nvgt @@ -393,6 +393,7 @@ void update_time() { if (current_hour == 6) { process_daily_weapon_breakage(); attempt_daily_quest(); + attempt_resident_butchering(); } attempt_daily_invasion(); keep_base_fires_fed(); @@ -403,6 +404,7 @@ void update_time() { attempt_blessing(); check_weather_transition(); attempt_resident_collection(); + attempt_resident_snare_retrieval(); if (current_hour == 6) { save_game_state(); }