From 1b76ccc129ccd96ceed40734ba27de7091efa6dd Mon Sep 17 00:00:00 2001 From: Storm Dragon Date: Sat, 24 Jan 2026 18:18:02 -0500 Subject: [PATCH] Heal scrolls added. Gained from adventures or quests. --- draugnorak.nvgt | 8 ++-- src/base_system.nvgt | 41 +++++++++++++++++++ src/bosses/unicorn/unicorn_boss.nvgt | 3 ++ src/constants.nvgt | 1 + src/inventory_items.nvgt | 60 ++++++++++++++++++++++------ src/item_registry.nvgt | 5 ++- src/menus/base_info.nvgt | 5 ++- src/menus/menu_utils.nvgt | 8 ++-- src/quest_system.nvgt | 7 ++++ src/save_system.nvgt | 39 +++++++++++++++--- src/time_system.nvgt | 4 +- 11 files changed, 153 insertions(+), 28 deletions(-) diff --git a/draugnorak.nvgt b/draugnorak.nvgt index b4b6645..bd540db 100644 --- a/draugnorak.nvgt +++ b/draugnorak.nvgt @@ -189,9 +189,11 @@ void main() // Death check if (player_health <= 0) { - speak_with_history("You have died.", true); - wait(2000); - exit(); + if (!try_consume_heal_scroll()) { + speak_with_history("You have died.", true); + wait(2000); + exit(); + } } // Inventory & Actions diff --git a/src/base_system.nvgt b/src/base_system.nvgt index 03b028d..9d92076 100644 --- a/src/base_system.nvgt +++ b/src/base_system.nvgt @@ -58,7 +58,13 @@ void attempt_resident_fishing() { if (poles < active_fishers) active_fishers = poles; int caught = 0; + int poles_broken = 0; for (int i = 0; i < active_fishers; i++) { + // Check for pole breakage during use + if (random(1, 100) <= RESIDENT_TOOL_BREAK_CHANCE) { + poles_broken++; + continue; + } if (random(1, 100) > RESIDENT_FISHING_CHANCE) continue; if (get_storage_count(ITEM_FISH) >= BASE_STORAGE_MAX) break; add_storage_count(ITEM_FISH, 1); @@ -66,6 +72,17 @@ void attempt_resident_fishing() { caught++; } + // Apply pole breakage + if (poles_broken > 0) { + add_storage_count(ITEM_FISHING_POLES, -poles_broken); + if (x <= BASE_END) { + string msg = (poles_broken == 1) + ? "A resident's fishing pole broke." + : poles_broken + " fishing poles broke."; + speak_with_history(msg, true); + } + } + if (caught > 0 && x <= BASE_END) { if (caught == 1) { speak_with_history("Resident caught a fish and added it to storage.", true); @@ -418,6 +435,12 @@ void attempt_resident_butchering() { } } + // 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."); + } + // 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); @@ -465,7 +488,14 @@ void attempt_resident_collection() { int active_collectors = (residents_count < get_storage_count(ITEM_REED_BASKETS)) ? residents_count : get_storage_count(ITEM_REED_BASKETS); // Each active collector has a 10% chance to collect something + int baskets_broken = 0; for (int i = 0; i < active_collectors; i++) { + // Check for basket breakage during use + if (random(1, 100) <= RESIDENT_TOOL_BREAK_CHANCE) { + baskets_broken++; + continue; + } + if (random(1, 100) > RESIDENT_COLLECTION_CHANCE) continue; // Determine what to collect (weighted random) @@ -496,4 +526,15 @@ void attempt_resident_collection() { speak_with_history("Resident added " + item_name + " to storage.", true); } } + + // Apply basket breakage + if (baskets_broken > 0) { + add_storage_count(ITEM_REED_BASKETS, -baskets_broken); + if (x <= BASE_END) { + string msg = (baskets_broken == 1) + ? "A resident's basket broke." + : baskets_broken + " baskets broke."; + speak_with_history(msg, true); + } + } } diff --git a/src/bosses/unicorn/unicorn_boss.nvgt b/src/bosses/unicorn/unicorn_boss.nvgt index e17d2c9..9bdcc5c 100644 --- a/src/bosses/unicorn/unicorn_boss.nvgt +++ b/src/bosses/unicorn/unicorn_boss.nvgt @@ -418,6 +418,8 @@ void give_unicorn_rewards() { // Calculate rewards int favor_reward = 4 + random(1, 4); // 4 + 1d4 = 5-8 favor favor += favor_reward; + int scrolls_added = add_to_stack(get_personal_count(ITEM_HEAL_SCROLL), 1); + add_personal_count(ITEM_HEAL_SCROLL, scrolls_added); // Track boss defeat and unlock rune unicorn_boss_defeated = true; @@ -429,6 +431,7 @@ void give_unicorn_rewards() { rewards.insert_last("=== Victory Rewards ==="); rewards.insert_last(""); rewards.insert_last("The gods are pleased with your victory! " + favor_reward + " favor awarded."); + rewards.insert_last("Heal Scrolls: +" + scrolls_added + "."); rewards.insert_last(""); if (new_rune) { rewards.insert_last("Learned Rune of Swiftness!"); diff --git a/src/constants.nvgt b/src/constants.nvgt index bdc128f..1471a55 100644 --- a/src/constants.nvgt +++ b/src/constants.nvgt @@ -176,6 +176,7 @@ const int RESIDENT_SNARE_ESCAPE_CHANCE = 5; // 5% chance game escapes when resi const int RESIDENT_SNARE_CHECK_CHANCE = 15; // 15% chance per hour to check snares 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 = 5; // 5% chance tools break during resident use (fishing poles, knives, baskets) // Goose settings const int GOOSE_HEALTH = 1; diff --git a/src/inventory_items.nvgt b/src/inventory_items.nvgt index 6766fe9..b967437 100644 --- a/src/inventory_items.nvgt +++ b/src/inventory_items.nvgt @@ -216,6 +216,17 @@ int get_quick_slot_key() { return -1; } +bool try_consume_heal_scroll() { + if (player_health > 0) return false; + if (get_personal_count(ITEM_HEAL_SCROLL) <= 0) return false; + + add_personal_count(ITEM_HEAL_SCROLL, -1); + player_health = max_health / 2; + if (player_health < 1) player_health = 1; + p.play_stationary("sounds/actions/heal_scroll.ogg", false); + return true; +} + void activate_quick_slot(int slot_index) { if (slot_index < 0 || slot_index >= int(quick_slots.length())) { return; @@ -293,18 +304,43 @@ string get_speed_status() { } void cleanup_equipment_after_inventory_change() { - if (get_personal_count(ITEM_SPEARS) <= 0) spear_equipped = false; - if (get_personal_count(ITEM_AXES) <= 0) axe_equipped = false; - if (get_personal_count(ITEM_SLINGS) <= 0) sling_equipped = false; - if (get_personal_count(ITEM_BOWS) <= 0) bow_equipped = false; - if (get_personal_count(ITEM_FISHING_POLES) <= 0) fishing_pole_equipped = false; - if (get_personal_count(ITEM_SKIN_HATS) <= 0) equipped_head = EQUIP_NONE; - if (get_personal_count(ITEM_SKIN_GLOVES) <= 0) equipped_hands = EQUIP_NONE; - if (get_personal_count(ITEM_SKIN_PANTS) <= 0) equipped_legs = EQUIP_NONE; - if (get_personal_count(ITEM_SKIN_TUNICS) <= 0) equipped_torso = EQUIP_NONE; - if (get_personal_count(ITEM_MOCCASINS) <= 0) equipped_feet = EQUIP_NONE; - if (get_personal_count(ITEM_SKIN_POUCHES) <= 0 && !has_any_runed_version(EQUIP_POUCH) && equipped_arms == EQUIP_POUCH) equipped_arms = EQUIP_NONE; - if (get_personal_count(ITEM_BACKPACKS) <= 0 && !has_any_runed_version(EQUIP_BACKPACK) && equipped_arms == EQUIP_BACKPACK) equipped_arms = EQUIP_NONE; + if (!equipment_available(EQUIP_SPEAR)) spear_equipped = false; + if (!equipment_available(EQUIP_AXE)) axe_equipped = false; + if (!equipment_available(EQUIP_SLING)) sling_equipped = false; + if (!equipment_available(EQUIP_BOW)) bow_equipped = false; + if (!equipment_available(EQUIP_FISHING_POLE)) fishing_pole_equipped = false; + + bool any_weapon_equipped = spear_equipped || axe_equipped || sling_equipped || bow_equipped || fishing_pole_equipped; + if (!any_weapon_equipped) equipped_weapon_rune = RUNE_NONE; + + if (!equipment_available(EQUIP_HAT)) { + equipped_head = EQUIP_NONE; + equipped_head_rune = RUNE_NONE; + } + if (!equipment_available(EQUIP_GLOVES)) { + equipped_hands = EQUIP_NONE; + equipped_hands_rune = RUNE_NONE; + } + if (!equipment_available(EQUIP_PANTS)) { + equipped_legs = EQUIP_NONE; + equipped_legs_rune = RUNE_NONE; + } + if (!equipment_available(EQUIP_TUNIC)) { + equipped_torso = EQUIP_NONE; + equipped_torso_rune = RUNE_NONE; + } + if (!equipment_available(EQUIP_MOCCASINS)) { + equipped_feet = EQUIP_NONE; + equipped_feet_rune = RUNE_NONE; + } + if (equipped_arms == EQUIP_POUCH && !equipment_available(EQUIP_POUCH)) { + equipped_arms = EQUIP_NONE; + equipped_arms_rune = RUNE_NONE; + } + if (equipped_arms == EQUIP_BACKPACK && !equipment_available(EQUIP_BACKPACK)) { + equipped_arms = EQUIP_NONE; + equipped_arms_rune = RUNE_NONE; + } clamp_arrows_to_quiver_limit(); update_max_health_from_equipment(); } diff --git a/src/item_registry.nvgt b/src/item_registry.nvgt index 31afe9d..3e8617b 100644 --- a/src/item_registry.nvgt +++ b/src/item_registry.nvgt @@ -39,7 +39,8 @@ const int ITEM_BACKPACKS = 33; const int ITEM_CANOES = 34; const int ITEM_FISH = 35; const int ITEM_SMOKED_FISH = 36; -const int ITEM_COUNT = 37; // Total number of item types +const int ITEM_HEAL_SCROLL = 37; +const int ITEM_COUNT = 38; // Total number of item types // Item definition class class ItemDefinition { @@ -124,6 +125,7 @@ void init_item_registry() { item_registry[ITEM_CANOES] = ItemDefinition(ITEM_CANOES, "canoes", "canoe", "Canoes", 4.00); item_registry[ITEM_FISH] = ItemDefinition(ITEM_FISH, "fish", "fish", "Fish", 0.10); item_registry[ITEM_SMOKED_FISH] = ItemDefinition(ITEM_SMOKED_FISH, "smoked fish", "smoked fish", "Smoked Fish", 0.20); + item_registry[ITEM_HEAL_SCROLL] = ItemDefinition(ITEM_HEAL_SCROLL, "heal scrolls", "heal scroll", "Heal Scrolls", 0.50); // Define display order for inventory menus // This controls the order items appear in menus @@ -148,6 +150,7 @@ void init_item_registry() { ITEM_SMOKED_FISH, // Misc items ITEM_INCENSE, + ITEM_HEAL_SCROLL, // Weapons ITEM_SPEARS, ITEM_SLINGS, diff --git a/src/menus/base_info.nvgt b/src/menus/base_info.nvgt index dc51119..ddeb0e7 100644 --- a/src/menus/base_info.nvgt +++ b/src/menus/base_info.nvgt @@ -17,7 +17,10 @@ void run_base_info_menu() { if (world_storages.length() > 0) { options.insert_last("Storage built. Total items " + get_storage_total_items()); int daily_food = residents_count * 3; // 1 per resident per 8 hours = 3 per day - options.insert_last("Food in storage " + get_storage_count(ITEM_MEAT) + " meat. Daily use " + daily_food); + int meat_in_storage = get_storage_count(ITEM_MEAT); + int smoked_fish_in_storage = get_storage_count(ITEM_SMOKED_FISH); + int total_food = meat_in_storage + smoked_fish_in_storage; + options.insert_last("Food in storage " + meat_in_storage + " meat, " + smoked_fish_in_storage + " smoked fish. Total " + total_food + ". Daily use " + daily_food); } else { options.insert_last("Storage not built"); } diff --git a/src/menus/menu_utils.nvgt b/src/menus/menu_utils.nvgt index 41a220c..277af30 100644 --- a/src/menus/menu_utils.nvgt +++ b/src/menus/menu_utils.nvgt @@ -32,9 +32,11 @@ void menu_background_tick() { // Death check if (player_health <= 0) { - speak_with_history("You have died.", true); - wait(2000); - exit(); + if (!try_consume_heal_scroll()) { + speak_with_history("You have died.", true); + wait(2000); + exit(); + } } } diff --git a/src/quest_system.nvgt b/src/quest_system.nvgt index 32f64f8..bc0cb21 100644 --- a/src/quest_system.nvgt +++ b/src/quest_system.nvgt @@ -85,6 +85,10 @@ void apply_quest_reward(int score) { int stones_gain = (score >= QUEST_STONE_SCORE) ? 1 : 0; int logs_gain = (score >= QUEST_LOG_SCORE) ? 1 : 0; int skins_gain = (score >= QUEST_SKIN_SCORE) ? 1 : 0; + int scrolls_gain = 0; + if (score >= QUEST_STONE_SCORE) scrolls_gain++; + if (score >= QUEST_LOG_SCORE) scrolls_gain++; + if (score >= QUEST_SKIN_SCORE) scrolls_gain++; int stones_added = add_to_stack(get_personal_count(ITEM_STONES), stones_gain); add_personal_count(ITEM_STONES, stones_added); @@ -92,12 +96,15 @@ void apply_quest_reward(int score) { add_personal_count(ITEM_LOGS, logs_added); int skins_added = add_to_stack(get_personal_count(ITEM_SKINS), skins_gain); add_personal_count(ITEM_SKINS, skins_added); + int scrolls_added = add_to_stack(get_personal_count(ITEM_HEAL_SCROLL), scrolls_gain); + add_personal_count(ITEM_HEAL_SCROLL, scrolls_added); string message = "Quest Complete!\n\nRewards:\n"; message += "Favor: +" + format_favor(favor_gain) + "\n"; if (stones_gain > 0) message += "Stones: +" + stones_added + "\n"; if (logs_gain > 0) message += "Logs: +" + logs_added + "\n"; if (skins_gain > 0) message += "Skins: +" + skins_added + "\n"; + if (scrolls_gain > 0) message += "Heal Scrolls: +" + scrolls_added + "\n"; message += "\nScore: " + score; text_reader(message, "Quest Rewards", true); } diff --git a/src/save_system.nvgt b/src/save_system.nvgt index 0d5bd82..1f3fe52 100644 --- a/src/save_system.nvgt +++ b/src/save_system.nvgt @@ -775,12 +775,39 @@ bool load_game_state() { } // Validate equipped items now that runed items are loaded - if (!equipment_available(equipped_head)) equipped_head = EQUIP_NONE; - if (!equipment_available(equipped_torso)) equipped_torso = EQUIP_NONE; - if (!equipment_available(equipped_hands)) equipped_hands = EQUIP_NONE; - if (!equipment_available(equipped_legs)) equipped_legs = EQUIP_NONE; - if (!equipment_available(equipped_feet)) equipped_feet = EQUIP_NONE; - if (!equipment_available(equipped_arms)) equipped_arms = EQUIP_NONE; + if (!equipment_available(EQUIP_SPEAR)) spear_equipped = false; + if (!equipment_available(EQUIP_AXE)) axe_equipped = false; + if (!equipment_available(EQUIP_SLING)) sling_equipped = false; + if (!equipment_available(EQUIP_BOW)) bow_equipped = false; + if (!equipment_available(EQUIP_FISHING_POLE)) fishing_pole_equipped = false; + + bool any_weapon_equipped = spear_equipped || axe_equipped || sling_equipped || bow_equipped || fishing_pole_equipped; + if (!any_weapon_equipped) equipped_weapon_rune = RUNE_NONE; + + if (!equipment_available(equipped_head)) { + equipped_head = EQUIP_NONE; + equipped_head_rune = RUNE_NONE; + } + if (!equipment_available(equipped_torso)) { + equipped_torso = EQUIP_NONE; + equipped_torso_rune = RUNE_NONE; + } + if (!equipment_available(equipped_hands)) { + equipped_hands = EQUIP_NONE; + equipped_hands_rune = RUNE_NONE; + } + if (!equipment_available(equipped_legs)) { + equipped_legs = EQUIP_NONE; + equipped_legs_rune = RUNE_NONE; + } + if (!equipment_available(equipped_feet)) { + equipped_feet = EQUIP_NONE; + equipped_feet_rune = RUNE_NONE; + } + if (!equipment_available(equipped_arms)) { + equipped_arms = EQUIP_NONE; + equipped_arms_rune = RUNE_NONE; + } // Now that both equipment and runes are loaded, update stats update_max_health_from_equipment(); diff --git a/src/time_system.nvgt b/src/time_system.nvgt index 7a53d05..5293d23 100644 --- a/src/time_system.nvgt +++ b/src/time_system.nvgt @@ -109,8 +109,8 @@ void expand_regular_area() { } if (place_tree) { - // Try to place a tree with proper spacing and per-area limits - spawn_tree_in_area(new_start, new_end); + // Fill the new area up to its tree cap with proper spacing + spawn_trees(new_start, new_end); } else { int stream_width = random(1, 5); int stream_start = random(0, EXPANSION_SIZE - stream_width);