Heal scrolls added. Gained from adventures or quests.

This commit is contained in:
Storm Dragon
2026-01-24 18:18:02 -05:00
parent 595de51da0
commit 1b76ccc129
11 changed files with 153 additions and 28 deletions

View File

@@ -189,9 +189,11 @@ void main()
// Death check // Death check
if (player_health <= 0) { if (player_health <= 0) {
speak_with_history("You have died.", true); if (!try_consume_heal_scroll()) {
wait(2000); speak_with_history("You have died.", true);
exit(); wait(2000);
exit();
}
} }
// Inventory & Actions // Inventory & Actions

View File

@@ -58,7 +58,13 @@ void attempt_resident_fishing() {
if (poles < active_fishers) active_fishers = poles; if (poles < active_fishers) active_fishers = poles;
int caught = 0; int caught = 0;
int poles_broken = 0;
for (int i = 0; i < active_fishers; i++) { 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 (random(1, 100) > RESIDENT_FISHING_CHANCE) continue;
if (get_storage_count(ITEM_FISH) >= BASE_STORAGE_MAX) break; if (get_storage_count(ITEM_FISH) >= BASE_STORAGE_MAX) break;
add_storage_count(ITEM_FISH, 1); add_storage_count(ITEM_FISH, 1);
@@ -66,6 +72,17 @@ void attempt_resident_fishing() {
caught++; 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 > 0 && x <= BASE_END) {
if (caught == 1) { if (caught == 1) {
speak_with_history("Resident caught a fish and added it to storage.", true); 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 // Add outputs to storage
if (meat_yield > 0) add_storage_count(ITEM_MEAT, meat_yield); if (meat_yield > 0) add_storage_count(ITEM_MEAT, meat_yield);
if (skins_yield > 0) add_storage_count(ITEM_SKINS, skins_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); 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 // Each active collector has a 10% chance to collect something
int baskets_broken = 0;
for (int i = 0; i < active_collectors; i++) { 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; if (random(1, 100) > RESIDENT_COLLECTION_CHANCE) continue;
// Determine what to collect (weighted random) // Determine what to collect (weighted random)
@@ -496,4 +526,15 @@ void attempt_resident_collection() {
speak_with_history("Resident added " + item_name + " to storage.", true); 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);
}
}
} }

View File

@@ -418,6 +418,8 @@ void give_unicorn_rewards() {
// Calculate rewards // Calculate rewards
int favor_reward = 4 + random(1, 4); // 4 + 1d4 = 5-8 favor int favor_reward = 4 + random(1, 4); // 4 + 1d4 = 5-8 favor
favor += favor_reward; 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 // Track boss defeat and unlock rune
unicorn_boss_defeated = true; unicorn_boss_defeated = true;
@@ -429,6 +431,7 @@ void give_unicorn_rewards() {
rewards.insert_last("=== Victory Rewards ==="); rewards.insert_last("=== Victory Rewards ===");
rewards.insert_last(""); rewards.insert_last("");
rewards.insert_last("The gods are pleased with your victory! " + favor_reward + " favor awarded."); rewards.insert_last("The gods are pleased with your victory! " + favor_reward + " favor awarded.");
rewards.insert_last("Heal Scrolls: +" + scrolls_added + ".");
rewards.insert_last(""); rewards.insert_last("");
if (new_rune) { if (new_rune) {
rewards.insert_last("Learned Rune of Swiftness!"); rewards.insert_last("Learned Rune of Swiftness!");

View File

@@ -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_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_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_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 // Goose settings
const int GOOSE_HEALTH = 1; const int GOOSE_HEALTH = 1;

View File

@@ -216,6 +216,17 @@ int get_quick_slot_key() {
return -1; 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) { void activate_quick_slot(int slot_index) {
if (slot_index < 0 || slot_index >= int(quick_slots.length())) { if (slot_index < 0 || slot_index >= int(quick_slots.length())) {
return; return;
@@ -293,18 +304,43 @@ string get_speed_status() {
} }
void cleanup_equipment_after_inventory_change() { void cleanup_equipment_after_inventory_change() {
if (get_personal_count(ITEM_SPEARS) <= 0) spear_equipped = false; if (!equipment_available(EQUIP_SPEAR)) spear_equipped = false;
if (get_personal_count(ITEM_AXES) <= 0) axe_equipped = false; if (!equipment_available(EQUIP_AXE)) axe_equipped = false;
if (get_personal_count(ITEM_SLINGS) <= 0) sling_equipped = false; if (!equipment_available(EQUIP_SLING)) sling_equipped = false;
if (get_personal_count(ITEM_BOWS) <= 0) bow_equipped = false; if (!equipment_available(EQUIP_BOW)) bow_equipped = false;
if (get_personal_count(ITEM_FISHING_POLES) <= 0) fishing_pole_equipped = false; if (!equipment_available(EQUIP_FISHING_POLE)) 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; bool any_weapon_equipped = spear_equipped || axe_equipped || sling_equipped || bow_equipped || fishing_pole_equipped;
if (get_personal_count(ITEM_SKIN_PANTS) <= 0) equipped_legs = EQUIP_NONE; if (!any_weapon_equipped) equipped_weapon_rune = RUNE_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 (!equipment_available(EQUIP_HAT)) {
if (get_personal_count(ITEM_SKIN_POUCHES) <= 0 && !has_any_runed_version(EQUIP_POUCH) && equipped_arms == EQUIP_POUCH) equipped_arms = EQUIP_NONE; equipped_head = EQUIP_NONE;
if (get_personal_count(ITEM_BACKPACKS) <= 0 && !has_any_runed_version(EQUIP_BACKPACK) && equipped_arms == EQUIP_BACKPACK) equipped_arms = 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(); clamp_arrows_to_quiver_limit();
update_max_health_from_equipment(); update_max_health_from_equipment();
} }

View File

@@ -39,7 +39,8 @@ const int ITEM_BACKPACKS = 33;
const int ITEM_CANOES = 34; const int ITEM_CANOES = 34;
const int ITEM_FISH = 35; const int ITEM_FISH = 35;
const int ITEM_SMOKED_FISH = 36; 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 // Item definition class
class ItemDefinition { 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_CANOES] = ItemDefinition(ITEM_CANOES, "canoes", "canoe", "Canoes", 4.00);
item_registry[ITEM_FISH] = ItemDefinition(ITEM_FISH, "fish", "fish", "Fish", 0.10); 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_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 // Define display order for inventory menus
// This controls the order items appear in menus // This controls the order items appear in menus
@@ -148,6 +150,7 @@ void init_item_registry() {
ITEM_SMOKED_FISH, ITEM_SMOKED_FISH,
// Misc items // Misc items
ITEM_INCENSE, ITEM_INCENSE,
ITEM_HEAL_SCROLL,
// Weapons // Weapons
ITEM_SPEARS, ITEM_SPEARS,
ITEM_SLINGS, ITEM_SLINGS,

View File

@@ -17,7 +17,10 @@ void run_base_info_menu() {
if (world_storages.length() > 0) { if (world_storages.length() > 0) {
options.insert_last("Storage built. Total items " + get_storage_total_items()); 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 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 { } else {
options.insert_last("Storage not built"); options.insert_last("Storage not built");
} }

View File

@@ -32,9 +32,11 @@ void menu_background_tick() {
// Death check // Death check
if (player_health <= 0) { if (player_health <= 0) {
speak_with_history("You have died.", true); if (!try_consume_heal_scroll()) {
wait(2000); speak_with_history("You have died.", true);
exit(); wait(2000);
exit();
}
} }
} }

View File

@@ -85,6 +85,10 @@ void apply_quest_reward(int score) {
int stones_gain = (score >= QUEST_STONE_SCORE) ? 1 : 0; int stones_gain = (score >= QUEST_STONE_SCORE) ? 1 : 0;
int logs_gain = (score >= QUEST_LOG_SCORE) ? 1 : 0; int logs_gain = (score >= QUEST_LOG_SCORE) ? 1 : 0;
int skins_gain = (score >= QUEST_SKIN_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); int stones_added = add_to_stack(get_personal_count(ITEM_STONES), stones_gain);
add_personal_count(ITEM_STONES, stones_added); add_personal_count(ITEM_STONES, stones_added);
@@ -92,12 +96,15 @@ void apply_quest_reward(int score) {
add_personal_count(ITEM_LOGS, logs_added); add_personal_count(ITEM_LOGS, logs_added);
int skins_added = add_to_stack(get_personal_count(ITEM_SKINS), skins_gain); int skins_added = add_to_stack(get_personal_count(ITEM_SKINS), skins_gain);
add_personal_count(ITEM_SKINS, skins_added); 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"; string message = "Quest Complete!\n\nRewards:\n";
message += "Favor: +" + format_favor(favor_gain) + "\n"; message += "Favor: +" + format_favor(favor_gain) + "\n";
if (stones_gain > 0) message += "Stones: +" + stones_added + "\n"; if (stones_gain > 0) message += "Stones: +" + stones_added + "\n";
if (logs_gain > 0) message += "Logs: +" + logs_added + "\n"; if (logs_gain > 0) message += "Logs: +" + logs_added + "\n";
if (skins_gain > 0) message += "Skins: +" + skins_added + "\n"; if (skins_gain > 0) message += "Skins: +" + skins_added + "\n";
if (scrolls_gain > 0) message += "Heal Scrolls: +" + scrolls_added + "\n";
message += "\nScore: " + score; message += "\nScore: " + score;
text_reader(message, "Quest Rewards", true); text_reader(message, "Quest Rewards", true);
} }

View File

@@ -775,12 +775,39 @@ bool load_game_state() {
} }
// Validate equipped items now that runed items are loaded // Validate equipped items now that runed items are loaded
if (!equipment_available(equipped_head)) equipped_head = EQUIP_NONE; if (!equipment_available(EQUIP_SPEAR)) spear_equipped = false;
if (!equipment_available(equipped_torso)) equipped_torso = EQUIP_NONE; if (!equipment_available(EQUIP_AXE)) axe_equipped = false;
if (!equipment_available(equipped_hands)) equipped_hands = EQUIP_NONE; if (!equipment_available(EQUIP_SLING)) sling_equipped = false;
if (!equipment_available(equipped_legs)) equipped_legs = EQUIP_NONE; if (!equipment_available(EQUIP_BOW)) bow_equipped = false;
if (!equipment_available(equipped_feet)) equipped_feet = EQUIP_NONE; if (!equipment_available(EQUIP_FISHING_POLE)) fishing_pole_equipped = false;
if (!equipment_available(equipped_arms)) equipped_arms = EQUIP_NONE;
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 // Now that both equipment and runes are loaded, update stats
update_max_health_from_equipment(); update_max_health_from_equipment();

View File

@@ -109,8 +109,8 @@ void expand_regular_area() {
} }
if (place_tree) { if (place_tree) {
// Try to place a tree with proper spacing and per-area limits // Fill the new area up to its tree cap with proper spacing
spawn_tree_in_area(new_start, new_end); spawn_trees(new_start, new_end);
} else { } else {
int stream_width = random(1, 5); int stream_width = random(1, 5);
int stream_start = random(0, EXPANSION_SIZE - stream_width); int stream_start = random(0, EXPANSION_SIZE - stream_width);