Residents now handle snares, retrieval and resetting. Still a chance game can escape. Residents are not as efficient at handling this than the player, but it is helpful. Meat and skins, anything harvested from butchering is stored.

This commit is contained in:
Storm Dragon
2026-01-24 10:37:06 -05:00
parent dfd6a0f3a1
commit 63cf759002
3 changed files with 161 additions and 0 deletions

View File

@@ -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() {

View File

@@ -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;

View File

@@ -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();
}