From 3cf769c7f8936b085621ee3472e72b4e6208714f Mon Sep 17 00:00:00 2001 From: Storm Dragon Date: Fri, 6 Feb 2026 01:11:35 -0500 Subject: [PATCH] finally a use for the stable and pasture. --- src/base_system.nvgt | 75 +++++++++++++++++++++++++++- src/bosses/bandit_hideout.nvgt | 15 ++++++ src/bosses/unicorn/unicorn_boss.nvgt | 15 ++++++ src/constants.nvgt | 12 ++++- src/crafting/craft_buildings.nvgt | 55 +++++++++++++++++--- src/crafting/crafting_core.nvgt | 6 ++- src/menus/base_info.nvgt | 14 ++++-- src/save_system.nvgt | 10 ++++ src/time_system.nvgt | 1 + src/world/world_buildings.nvgt | 3 ++ 10 files changed, 189 insertions(+), 17 deletions(-) diff --git a/src/base_system.nvgt b/src/base_system.nvgt index 26db11b..91bbe3a 100644 --- a/src/base_system.nvgt +++ b/src/base_system.nvgt @@ -35,8 +35,27 @@ int get_resident_effect_multiplier() { return blessing_resident_active ? 2 : 1; } +int get_horse_success_bonus() { + if (world_stables.length() == 0) return 0; + if (world_storages.length() == 0) return 0; + if (horses_count <= 0) return 0; + int count = horses_count; + if (count > MAX_HORSES) count = MAX_HORSES; + return count * HORSE_SUCCESS_BONUS_PER; +} + +int get_horse_damage_bonus() { + if (world_stables.length() == 0) return 0; + if (world_storages.length() == 0) return 0; + if (horses_count <= 0) return 0; + int count = horses_count; + if (count > MAX_HORSES) count = MAX_HORSES; + return count / HORSE_DAMAGE_BONUS_STEP; +} + int get_resident_success_chance(int base_chance) { int chance = base_chance * get_resident_effect_multiplier(); + chance += get_horse_success_bonus(); if (chance > 100) chance = 100; return chance; } @@ -62,8 +81,12 @@ int get_resident_cooldown(int base_cooldown) { } int apply_resident_damage_bonus(int damage) { - if (!blessing_resident_active) return damage; - return damage * get_resident_effect_multiplier(); + int adjusted = damage; + if (blessing_resident_active) { + adjusted *= get_resident_effect_multiplier(); + } + adjusted += get_horse_damage_bonus(); + return adjusted; } void consume_food_for_residents() { @@ -176,6 +199,54 @@ void attempt_resident_fish_smoking() { } } +void attempt_livestock_production() { + if (world_pastures.length() == 0) return; + if (world_storages.length() == 0) return; + if (livestock_count <= 0) return; + + int count = livestock_count; + if (count > MAX_LIVESTOCK) count = MAX_LIVESTOCK; + + int meat_produced = 0; + int skins_produced = 0; + + for (int i = 0; i < count; i++) { + if (get_storage_count(ITEM_MEAT) < BASE_STORAGE_MAX) { + if (random(1, 100) <= LIVESTOCK_MEAT_CHANCE) { + add_storage_count(ITEM_MEAT, 1); + meat_produced++; + } + } + + if (get_storage_count(ITEM_SKINS) < BASE_STORAGE_MAX) { + if (random(1, 100) <= LIVESTOCK_SKIN_CHANCE) { + add_storage_count(ITEM_SKINS, 1); + skins_produced++; + } + } + } + + if ((meat_produced > 0 || skins_produced > 0) && x <= BASE_END) { + string msg = "Livestock produced "; + string[] outputs; + if (meat_produced > 0) outputs.insert_last(meat_produced + " meat"); + if (skins_produced > 0) outputs.insert_last(skins_produced + " skins"); + + for (uint i = 0; i < outputs.length(); i++) { + if (i > 0) { + if (i == outputs.length() - 1) { + msg += " and "; + } else { + msg += ", "; + } + } + msg += outputs[i]; + } + msg += " and added to storage."; + speak_with_history(msg, true); + } +} + void keep_base_fires_fed() { if (residents_count <= 0) return; if (!has_any_storage_food()) return; diff --git a/src/bosses/bandit_hideout.nvgt b/src/bosses/bandit_hideout.nvgt index 745bb93..89fba0d 100644 --- a/src/bosses/bandit_hideout.nvgt +++ b/src/bosses/bandit_hideout.nvgt @@ -684,6 +684,21 @@ void give_bandit_hideout_rewards() { } } + rewards.insert_last(""); + if (world_pastures.length() == 0 || world_storages.length() == 0) { + rewards.insert_last("Pasture or storage not built. No livestock were recovered."); + } else if (livestock_count >= MAX_LIVESTOCK) { + rewards.insert_last("Pasture is full. No livestock were recovered."); + } else { + int livestockRoll = random(1, 100); + if (livestockRoll <= LIVESTOCK_ADVENTURE_CHANCE) { + livestock_count++; + rewards.insert_last("Livestock were recovered and added to your pasture."); + } else { + rewards.insert_last("No livestock were recovered."); + } + } + bool newRune = !rune_destruction_unlocked; rune_destruction_unlocked = true; rewards.insert_last(""); diff --git a/src/bosses/unicorn/unicorn_boss.nvgt b/src/bosses/unicorn/unicorn_boss.nvgt index a11013d..fa15489 100644 --- a/src/bosses/unicorn/unicorn_boss.nvgt +++ b/src/bosses/unicorn/unicorn_boss.nvgt @@ -620,6 +620,21 @@ void give_unicorn_rewards() { rewards.insert_last("You have already mastered the Rune of Swiftness."); } + rewards.insert_last(""); + if (world_stables.length() == 0 || world_storages.length() == 0) { + rewards.insert_last("Stable or storage not built. No horses were captured."); + } else if (horses_count >= MAX_HORSES) { + rewards.insert_last("Stable is full. No horses were captured."); + } else { + int horseRoll = random(1, 100); + if (horseRoll <= HORSE_ADVENTURE_CHANCE) { + horses_count++; + rewards.insert_last("A horse joins your stable."); + } else { + rewards.insert_last("No horses were captured."); + } + } + append_adventure_completion_rewards(ADVENTURE_UNICORN, rewards); // Display rewards in text reader diff --git a/src/constants.nvgt b/src/constants.nvgt index c4de350..165152c 100644 --- a/src/constants.nvgt +++ b/src/constants.nvgt @@ -115,7 +115,7 @@ const int STORAGE_LOG_COST = 6; const int STORAGE_STONE_COST = 9; const int STORAGE_VINE_COST = 8; const int PASTURE_LOG_COST = 8; -const int PASTURE_VINE_COST = 20; +const int PASTURE_ROPE_COST = 18; const int STABLE_LOG_COST = 10; const int STABLE_STONE_COST = 15; const int STABLE_VINE_COST = 10; @@ -127,6 +127,16 @@ const int INCENSE_REED_COST = 1; const int INCENSE_HOURS_PER_STICK = 4; const double INCENSE_FAVOR_PER_HOUR = 0.3; +// Livestock and stable settings +const int MAX_HORSES = 9; +const int MAX_LIVESTOCK = 9; +const int HORSE_ADVENTURE_CHANCE = 30; +const int LIVESTOCK_ADVENTURE_CHANCE = 30; +const int HORSE_SUCCESS_BONUS_PER = 2; +const int HORSE_DAMAGE_BONUS_STEP = 3; +const int LIVESTOCK_MEAT_CHANCE = 6; +const int LIVESTOCK_SKIN_CHANCE = 3; + // Bandit settings const int BANDIT_HEALTH = 4; const int BANDIT_MAX_COUNT = 3; diff --git a/src/crafting/craft_buildings.nvgt b/src/crafting/craft_buildings.nvgt index f596f45..404013c 100644 --- a/src/crafting/craft_buildings.nvgt +++ b/src/crafting/craft_buildings.nvgt @@ -1,4 +1,30 @@ // Crafting buildings +bool has_building_options() { + bool base_has_firepit = false; + bool base_has_fire = false; + for (uint i = 0; i < world_firepits.length(); i++) { + if (world_firepits[i].position <= BASE_END) { + base_has_firepit = true; + break; + } + } + for (uint i = 0; i < world_fires.length(); i++) { + if (world_fires[i].position <= BASE_END) { + base_has_fire = true; + break; + } + } + + if (x > BASE_END || !base_has_firepit) return true; + if (x > BASE_END || !base_has_fire) return true; + if (get_herb_garden_at_base() == null) return true; + if (world_storages.length() == 0) return true; + if (world_pastures.length() == 0 && world_storages.length() > 0) return true; + if (world_stables.length() == 0 && world_storages.length() > 0) return true; + if (world_altars.length() == 0) return true; + return false; +} + void run_buildings_menu() { speak_with_history("Buildings.", true); @@ -44,15 +70,15 @@ void run_buildings_menu() { building_types.insert_last(3); } - // Only show pasture if not built - if (world_pastures.length() == 0) { - options.insert_last("Pasture (8 Logs, 20 Vines) [Base Only]"); + // Only show pasture if not built and storage exists + if (world_pastures.length() == 0 && world_storages.length() > 0) { + options.insert_last("Pasture (8 Logs, 18 Ropes) [Base Only, Requires Storage]"); building_types.insert_last(4); } - // Only show stable if not built - if (world_stables.length() == 0) { - options.insert_last("Stable (10 Logs, 15 Stones, 10 Vines) [Base Only]"); + // Only show stable if not built and storage exists + if (world_stables.length() == 0 && world_storages.length() > 0) { + options.insert_last("Stable (10 Logs, 15 Stones, 10 Vines) [Base Only, Requires Storage]"); building_types.insert_last(5); } @@ -62,6 +88,11 @@ void run_buildings_menu() { building_types.insert_last(6); } + if (options.length() == 0) { + speak_with_history("No buildings available.", true); + return; + } + while(true) { wait(5); menu_background_tick(); @@ -221,18 +252,22 @@ void craft_pasture() { speak_with_history("Pasture must be built in the base.", true); return; } + if (world_storages.length() == 0) { + speak_with_history("Storage must be built before a pasture.", true); + return; + } if (world_pastures.length() > 0) { speak_with_history("Pasture already built.", true); return; } string missing = ""; if (get_personal_count(ITEM_LOGS) < PASTURE_LOG_COST) missing += PASTURE_LOG_COST + " logs "; - if (get_personal_count(ITEM_VINES) < PASTURE_VINE_COST) missing += PASTURE_VINE_COST + " vines "; + if (get_personal_count(ITEM_ROPES) < PASTURE_ROPE_COST) missing += PASTURE_ROPE_COST + " ropes "; if (missing == "") { simulate_crafting(28); add_personal_count(ITEM_LOGS, -PASTURE_LOG_COST); - add_personal_count(ITEM_VINES, -PASTURE_VINE_COST); + add_personal_count(ITEM_ROPES, -PASTURE_ROPE_COST); add_world_pasture(x); speak_with_history("Pasture built.", true); } else { @@ -245,6 +280,10 @@ void craft_stable() { speak_with_history("Stable must be built in the base.", true); return; } + if (world_storages.length() == 0) { + speak_with_history("Storage must be built before a stable.", true); + return; + } if (world_stables.length() > 0) { speak_with_history("Stable already built.", true); return; diff --git a/src/crafting/crafting_core.nvgt b/src/crafting/crafting_core.nvgt index 1348cd4..e316e64 100644 --- a/src/crafting/crafting_core.nvgt +++ b/src/crafting/crafting_core.nvgt @@ -23,8 +23,10 @@ void run_crafting_menu() { category_types.insert_last(2); categories.insert_last("Clothing"); category_types.insert_last(3); - categories.insert_last("Buildings"); - category_types.insert_last(4); + if (has_building_options()) { + categories.insert_last("Buildings"); + category_types.insert_last(4); + } categories.insert_last("Barricade"); category_types.insert_last(5); diff --git a/src/menus/base_info.nvgt b/src/menus/base_info.nvgt index 2e87b4d..07cc18e 100644 --- a/src/menus/base_info.nvgt +++ b/src/menus/base_info.nvgt @@ -38,11 +38,17 @@ void run_base_info_menu() { if (get_herb_garden_at_base() != null) options.insert_last("Herb garden built"); else options.insert_last("Herb garden not built"); - if (world_pastures.length() > 0) options.insert_last("Pasture built"); - else options.insert_last("Pasture not built"); + if (world_pastures.length() > 0) { + options.insert_last("Pasture built. Livestock " + livestock_count + " of " + MAX_LIVESTOCK); + } else { + options.insert_last("Pasture not built"); + } - if (world_stables.length() > 0) options.insert_last("Stable built"); - else options.insert_last("Stable not built"); + if (world_stables.length() > 0) { + options.insert_last("Stable built. Horses " + horses_count + " of " + MAX_HORSES); + } else { + options.insert_last("Stable not built"); + } string filter_text = ""; int[] filtered_indices; string[] filtered_options; diff --git a/src/save_system.nvgt b/src/save_system.nvgt index 3b50ab5..17051b0 100644 --- a/src/save_system.nvgt +++ b/src/save_system.nvgt @@ -608,6 +608,8 @@ void reset_game_state() { residents_count = 0; undead_residents_count = 0; undead_residents_pending = 0; + horses_count = 0; + livestock_count = 0; current_hour = 8; current_day = 1; @@ -902,6 +904,8 @@ bool save_game_state() { saveData.set("world_residents_count", residents_count); saveData.set("world_undead_residents_count", undead_residents_count); saveData.set("world_undead_residents_pending", undead_residents_pending); + saveData.set("world_horses_count", horses_count); + saveData.set("world_livestock_count", livestock_count); saveData.set("world_expanded_terrain_types", join_string_array(expanded_terrain_types)); string[] treeData; @@ -1043,6 +1047,12 @@ bool load_game_state_from_file(const string&in filename) { if (undead_residents_count < 0) undead_residents_count = 0; undead_residents_pending = int(get_number(saveData, "world_undead_residents_pending", 0)); if (undead_residents_pending < 0) undead_residents_pending = 0; + horses_count = int(get_number(saveData, "world_horses_count", 0)); + livestock_count = int(get_number(saveData, "world_livestock_count", 0)); + if (horses_count < 0) horses_count = 0; + if (livestock_count < 0) livestock_count = 0; + if (horses_count > MAX_HORSES) horses_count = MAX_HORSES; + if (livestock_count > MAX_LIVESTOCK) livestock_count = MAX_LIVESTOCK; if (!barricade_initialized) { init_barricade(); } else { diff --git a/src/time_system.nvgt b/src/time_system.nvgt index 32045f4..1fa5863 100644 --- a/src/time_system.nvgt +++ b/src/time_system.nvgt @@ -642,6 +642,7 @@ void update_time() { attempt_resident_snare_retrieval(); attempt_resident_fishing(); attempt_resident_fish_smoking(); + attempt_livestock_production(); if (current_hour == 6) { save_game_state(); } diff --git a/src/world/world_buildings.nvgt b/src/world/world_buildings.nvgt index 444f5f5..aad0af1 100644 --- a/src/world/world_buildings.nvgt +++ b/src/world/world_buildings.nvgt @@ -55,6 +55,9 @@ class WorldAltar { } WorldAltar@[] world_altars; +int horses_count = 0; +int livestock_count = 0; + // Add functions void add_world_firepit(int pos) { WorldFirepit@ fp = WorldFirepit(pos);