First adventure added. A bit of sound management improvement.
This commit is contained in:
@@ -396,5 +396,29 @@ void play_unicorn_death_sequence() {
|
||||
|
||||
void give_unicorn_rewards() {
|
||||
speak_with_history("Victory!", true);
|
||||
favor += 50;
|
||||
|
||||
// Calculate rewards
|
||||
int favor_reward = 4 + random(1, 4); // 4 + 1d4 = 5-8 favor
|
||||
favor += favor_reward;
|
||||
|
||||
// Track boss defeat and unlock rune
|
||||
unicorn_boss_defeated = true;
|
||||
bool new_rune = !rune_swiftness_unlocked;
|
||||
rune_swiftness_unlocked = true;
|
||||
|
||||
// Build rewards display
|
||||
string[] 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("");
|
||||
if (new_rune) {
|
||||
rewards.insert_last("Learned Rune of Swiftness!");
|
||||
rewards.insert_last("You can now engrave equipment with this rune at the crafting menu.");
|
||||
} else {
|
||||
rewards.insert_last("You have already mastered the Rune of Swiftness.");
|
||||
}
|
||||
|
||||
// Display rewards in text reader
|
||||
text_reader_lines(rewards, "Unicorn Victory", true);
|
||||
}
|
||||
|
||||
@@ -19,3 +19,4 @@
|
||||
#include "src/crafting/craft_clothing.nvgt"
|
||||
#include "src/crafting/craft_buildings.nvgt"
|
||||
#include "src/crafting/craft_barricade.nvgt"
|
||||
#include "src/crafting/craft_runes.nvgt"
|
||||
|
||||
@@ -4,7 +4,7 @@ void run_materials_menu() {
|
||||
|
||||
int selection = 0;
|
||||
string[] options = {
|
||||
"Butcher Small Game (1 Small Game) [Requires Knife and Fire nearby]",
|
||||
"Butcher Game [Requires Game, Knife, Fire nearby]",
|
||||
"Incense (6 Sticks, 2 Vines, 1 Reed) [Requires Altar]"
|
||||
};
|
||||
|
||||
|
||||
@@ -0,0 +1,190 @@
|
||||
// Rune engraving crafting menu
|
||||
// Requires: Knife (tool), 1 Clay, 1 Favor
|
||||
// Must be in base area
|
||||
|
||||
// Get the base equipment name without any rune prefix
|
||||
string get_base_equipment_name(int equip_type) {
|
||||
if (equip_type == EQUIP_SPEAR) return "Spear";
|
||||
if (equip_type == EQUIP_AXE) return "Stone Axe";
|
||||
if (equip_type == EQUIP_SLING) return "Sling";
|
||||
if (equip_type == EQUIP_BOW) return "Bow";
|
||||
if (equip_type == EQUIP_HAT) return "Skin Hat";
|
||||
if (equip_type == EQUIP_GLOVES) return "Skin Gloves";
|
||||
if (equip_type == EQUIP_PANTS) return "Skin Pants";
|
||||
if (equip_type == EQUIP_TUNIC) return "Skin Tunic";
|
||||
if (equip_type == EQUIP_MOCCASINS) return "Moccasins";
|
||||
if (equip_type == EQUIP_POUCH) return "Skin Pouch";
|
||||
if (equip_type == EQUIP_BACKPACK) return "Backpack";
|
||||
return "Unknown";
|
||||
}
|
||||
|
||||
// Get inventory count for an equipment type
|
||||
int get_unruned_equipment_count(int equip_type) {
|
||||
if (equip_type == EQUIP_SPEAR) return inv_spears;
|
||||
if (equip_type == EQUIP_AXE) return inv_axes;
|
||||
if (equip_type == EQUIP_SLING) return inv_slings;
|
||||
if (equip_type == EQUIP_BOW) return inv_bows;
|
||||
if (equip_type == EQUIP_HAT) return inv_skin_hats;
|
||||
if (equip_type == EQUIP_GLOVES) return inv_skin_gloves;
|
||||
if (equip_type == EQUIP_PANTS) return inv_skin_pants;
|
||||
if (equip_type == EQUIP_TUNIC) return inv_skin_tunics;
|
||||
if (equip_type == EQUIP_MOCCASINS) return inv_moccasins;
|
||||
if (equip_type == EQUIP_POUCH) return inv_skin_pouches;
|
||||
if (equip_type == EQUIP_BACKPACK) return inv_backpacks;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Decrement inventory for an equipment type
|
||||
void decrement_unruned_equipment(int equip_type) {
|
||||
if (equip_type == EQUIP_SPEAR) { inv_spears--; return; }
|
||||
if (equip_type == EQUIP_AXE) { inv_axes--; return; }
|
||||
if (equip_type == EQUIP_SLING) { inv_slings--; return; }
|
||||
if (equip_type == EQUIP_BOW) { inv_bows--; return; }
|
||||
if (equip_type == EQUIP_HAT) { inv_skin_hats--; return; }
|
||||
if (equip_type == EQUIP_GLOVES) { inv_skin_gloves--; return; }
|
||||
if (equip_type == EQUIP_PANTS) { inv_skin_pants--; return; }
|
||||
if (equip_type == EQUIP_TUNIC) { inv_skin_tunics--; return; }
|
||||
if (equip_type == EQUIP_MOCCASINS) { inv_moccasins--; return; }
|
||||
if (equip_type == EQUIP_POUCH) { inv_skin_pouches--; return; }
|
||||
if (equip_type == EQUIP_BACKPACK) { inv_backpacks--; return; }
|
||||
}
|
||||
|
||||
void run_runes_menu() {
|
||||
// Check if in base area
|
||||
if (x > BASE_END) {
|
||||
speak_with_history("Rune engraving can only be done in the base area.", true);
|
||||
return;
|
||||
}
|
||||
|
||||
speak_with_history("Runes.", true);
|
||||
|
||||
// Build list of unlocked runes
|
||||
string[] rune_options;
|
||||
int[] rune_types;
|
||||
|
||||
if (rune_swiftness_unlocked) {
|
||||
rune_options.insert_last("Rune of Swiftness (1 Clay, 1 Favor) [Requires Knife]");
|
||||
rune_types.insert_last(RUNE_SWIFTNESS);
|
||||
}
|
||||
|
||||
if (rune_options.length() == 0) {
|
||||
speak_with_history("No runes unlocked yet.", true);
|
||||
return;
|
||||
}
|
||||
|
||||
int selection = 0;
|
||||
|
||||
while(true) {
|
||||
wait(5);
|
||||
menu_background_tick();
|
||||
if (key_pressed(KEY_ESCAPE)) {
|
||||
speak_with_history("Closed.", true);
|
||||
break;
|
||||
}
|
||||
|
||||
if (key_pressed(KEY_DOWN)) {
|
||||
selection++;
|
||||
if (selection >= int(rune_options.length())) selection = 0;
|
||||
speak_with_history(rune_options[selection], true);
|
||||
}
|
||||
|
||||
if (key_pressed(KEY_UP)) {
|
||||
selection--;
|
||||
if (selection < 0) selection = int(rune_options.length()) - 1;
|
||||
speak_with_history(rune_options[selection], true);
|
||||
}
|
||||
|
||||
if (key_pressed(KEY_RETURN)) {
|
||||
int rune_type = rune_types[selection];
|
||||
run_rune_equipment_menu(rune_type);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void run_rune_equipment_menu(int rune_type) {
|
||||
speak_with_history("Select equipment to engrave with " + get_rune_name(rune_type) + ".", true);
|
||||
|
||||
// Build list of equipment that can be runed
|
||||
string[] equipment_options;
|
||||
int[] equipment_types;
|
||||
|
||||
int[] runeable = get_runeable_equipment_types();
|
||||
for (uint i = 0; i < runeable.length(); i++) {
|
||||
int equip_type = runeable[i];
|
||||
int unruned_count = get_unruned_equipment_count(equip_type);
|
||||
if (unruned_count > 0) {
|
||||
string name = get_base_equipment_name(equip_type);
|
||||
equipment_options.insert_last(name + " (" + unruned_count + " available)");
|
||||
equipment_types.insert_last(equip_type);
|
||||
}
|
||||
}
|
||||
|
||||
if (equipment_options.length() == 0) {
|
||||
speak_with_history("No equipment available to engrave.", true);
|
||||
return;
|
||||
}
|
||||
|
||||
int selection = 0;
|
||||
|
||||
while(true) {
|
||||
wait(5);
|
||||
menu_background_tick();
|
||||
if (key_pressed(KEY_ESCAPE)) {
|
||||
speak_with_history("Closed.", true);
|
||||
break;
|
||||
}
|
||||
|
||||
if (key_pressed(KEY_DOWN)) {
|
||||
selection++;
|
||||
if (selection >= int(equipment_options.length())) selection = 0;
|
||||
speak_with_history(equipment_options[selection], true);
|
||||
}
|
||||
|
||||
if (key_pressed(KEY_UP)) {
|
||||
selection--;
|
||||
if (selection < 0) selection = int(equipment_options.length()) - 1;
|
||||
speak_with_history(equipment_options[selection], true);
|
||||
}
|
||||
|
||||
if (key_pressed(KEY_RETURN)) {
|
||||
int equip_type = equipment_types[selection];
|
||||
engrave_rune(equip_type, rune_type);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void engrave_rune(int equip_type, int rune_type) {
|
||||
// Validate requirements
|
||||
string missing = "";
|
||||
if (inv_knives < 1) missing += "Stone Knife ";
|
||||
if (inv_clay < 1) missing += "1 clay ";
|
||||
if (favor < 1.0) missing += "1 favor ";
|
||||
|
||||
// Check equipment is still available
|
||||
if (get_unruned_equipment_count(equip_type) < 1) {
|
||||
speak_with_history("No " + get_base_equipment_name(equip_type) + " available.", true);
|
||||
return;
|
||||
}
|
||||
|
||||
if (missing == "") {
|
||||
// Consume materials (knife is not consumed, it's a tool)
|
||||
inv_clay--;
|
||||
favor -= 1.0;
|
||||
|
||||
// Remove one unruned item from inventory
|
||||
decrement_unruned_equipment(equip_type);
|
||||
|
||||
// Add one runed item to dictionary
|
||||
add_runed_item(equip_type, rune_type);
|
||||
|
||||
// Play crafting animation
|
||||
simulate_crafting(6);
|
||||
|
||||
string runed_name = "Runed " + get_base_equipment_name(equip_type) + " of " + get_rune_effect_name(rune_type);
|
||||
speak_with_history("Engraved " + runed_name + ".", true);
|
||||
} else {
|
||||
speak_with_history("Missing: " + missing, true);
|
||||
}
|
||||
}
|
||||
@@ -11,8 +11,28 @@ void run_crafting_menu() {
|
||||
speak_with_history("Crafting menu.", true);
|
||||
|
||||
int selection = 0;
|
||||
string[] categories = {"Weapons", "Tools", "Materials", "Clothing", "Buildings", "Barricade"};
|
||||
int[] category_types = {0, 1, 2, 3, 4, 5};
|
||||
string[] categories;
|
||||
int[] category_types;
|
||||
|
||||
// Build categories dynamically
|
||||
categories.insert_last("Weapons");
|
||||
category_types.insert_last(0);
|
||||
categories.insert_last("Tools");
|
||||
category_types.insert_last(1);
|
||||
categories.insert_last("Materials");
|
||||
category_types.insert_last(2);
|
||||
categories.insert_last("Clothing");
|
||||
category_types.insert_last(3);
|
||||
categories.insert_last("Buildings");
|
||||
category_types.insert_last(4);
|
||||
categories.insert_last("Barricade");
|
||||
category_types.insert_last(5);
|
||||
|
||||
// Add Runes category if any rune is unlocked
|
||||
if (any_rune_unlocked()) {
|
||||
categories.insert_last("Runes");
|
||||
category_types.insert_last(6);
|
||||
}
|
||||
|
||||
while(true) {
|
||||
wait(5);
|
||||
@@ -24,13 +44,13 @@ void run_crafting_menu() {
|
||||
|
||||
if (key_pressed(KEY_DOWN)) {
|
||||
selection++;
|
||||
if (selection >= categories.length()) selection = 0;
|
||||
if (selection >= int(categories.length())) selection = 0;
|
||||
speak_with_history(categories[selection], true);
|
||||
}
|
||||
|
||||
if (key_pressed(KEY_UP)) {
|
||||
selection--;
|
||||
if (selection < 0) selection = categories.length() - 1;
|
||||
if (selection < 0) selection = int(categories.length()) - 1;
|
||||
speak_with_history(categories[selection], true);
|
||||
}
|
||||
|
||||
@@ -42,6 +62,7 @@ void run_crafting_menu() {
|
||||
else if (category == 3) run_clothing_menu();
|
||||
else if (category == 4) run_buildings_menu();
|
||||
else if (category == 5) run_barricade_menu();
|
||||
else if (category == 6) run_runes_menu();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -181,6 +181,13 @@ void update_bandit(Bandit@ bandit) {
|
||||
if (bandit.alert_timer.elapsed > bandit.next_alert_delay) {
|
||||
bandit.alert_timer.restart();
|
||||
bandit.next_alert_delay = random(BANDIT_ALERT_MIN_DELAY, BANDIT_ALERT_MAX_DELAY);
|
||||
|
||||
// Destroy old sound handle before playing new one to prevent overlapping
|
||||
if (bandit.sound_handle != -1) {
|
||||
p.destroy_sound(bandit.sound_handle);
|
||||
bandit.sound_handle = -1;
|
||||
}
|
||||
|
||||
bandit.sound_handle = play_creature_voice(bandit.alert_sound, x, bandit.position, BANDIT_SOUND_VOLUME_STEP);
|
||||
}
|
||||
|
||||
|
||||
@@ -330,7 +330,6 @@ void update_flying_creature(FlyingCreature@ creature) {
|
||||
}
|
||||
|
||||
play_creature_death_sound(cfg.impact_sound, x, creature.position, cfg.sound_volume_step);
|
||||
notify("A " + creature.creature_type + " fell from the sky at " + creature.position + "!");
|
||||
add_world_drop(creature.position, cfg.drop_type);
|
||||
creature.health = 0;
|
||||
}
|
||||
|
||||
@@ -134,6 +134,13 @@ void update_undead(Undead@ undead) {
|
||||
if (undead.groan_timer.elapsed > undead.next_groan_delay) {
|
||||
undead.groan_timer.restart();
|
||||
undead.next_groan_delay = random(ZOMBIE_GROAN_MIN_DELAY, ZOMBIE_GROAN_MAX_DELAY);
|
||||
|
||||
// Destroy old sound handle before playing new one to prevent overlapping
|
||||
if (undead.sound_handle != -1) {
|
||||
p.destroy_sound(undead.sound_handle);
|
||||
undead.sound_handle = -1;
|
||||
}
|
||||
|
||||
undead.sound_handle = play_creature_voice(undead.voice_sound, x, undead.position, ZOMBIE_SOUND_VOLUME_STEP);
|
||||
}
|
||||
|
||||
|
||||
+29
-6
@@ -86,6 +86,11 @@ class Tree {
|
||||
|
||||
if (tree_distance <= TREE_SOUND_RANGE) {
|
||||
if (sound_handle == -1 || !p.sound_is_active(sound_handle)) {
|
||||
// Clean up invalid handle if it exists
|
||||
if (sound_handle != -1) {
|
||||
p.destroy_sound(sound_handle);
|
||||
sound_handle = -1;
|
||||
}
|
||||
sound_handle = p.play_1d("sounds/environment/tree.ogg", x, position, true);
|
||||
if (sound_handle != -1) {
|
||||
p.update_sound_positioning_values(sound_handle, -1.0, TREE_SOUND_VOLUME_STEP, true);
|
||||
@@ -923,6 +928,9 @@ void start_rope_climb(bool climbing_up, int target_x, int target_elevation) {
|
||||
|
||||
string direction = rope_climb_up ? "up" : "down";
|
||||
speak_with_history("Climbing " + direction + ". " + distance + " feet.", true);
|
||||
|
||||
// Start looping rope climbing sound
|
||||
rope_climb_sound_handle = p.play_stationary("sounds/actions/climb_rope.ogg", true);
|
||||
}
|
||||
|
||||
void update_rope_climbing() {
|
||||
@@ -943,11 +951,9 @@ void update_rope_climbing() {
|
||||
if (y < rope_climb_target_y) {
|
||||
y++;
|
||||
|
||||
// Check if we've reached the target BEFORE playing the sound
|
||||
// Check if we've reached the target
|
||||
if (y >= rope_climb_target_y) {
|
||||
complete_rope_climb();
|
||||
} else {
|
||||
p.play_stationary("sounds/actions/climb_rope.ogg", false);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@@ -955,11 +961,9 @@ void update_rope_climbing() {
|
||||
if (y > rope_climb_target_y) {
|
||||
y--;
|
||||
|
||||
// Check if we've reached the target BEFORE playing the sound
|
||||
// Check if we've reached the target
|
||||
if (y <= rope_climb_target_y) {
|
||||
complete_rope_climb();
|
||||
} else {
|
||||
p.play_stationary("sounds/actions/climb_rope.ogg", false);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -971,6 +975,12 @@ void complete_rope_climb() {
|
||||
x = rope_climb_target_x;
|
||||
y = rope_climb_target_y;
|
||||
|
||||
// Stop looping rope climbing sound
|
||||
if (rope_climb_sound_handle != -1) {
|
||||
p.destroy_sound(rope_climb_sound_handle);
|
||||
rope_climb_sound_handle = -1;
|
||||
}
|
||||
|
||||
// Play footstep for new terrain
|
||||
play_footstep(x, BASE_END, GRASS_END);
|
||||
|
||||
@@ -981,6 +991,19 @@ void check_rope_climb_fall() {
|
||||
if (!rope_climbing) return;
|
||||
|
||||
if (key_down(KEY_LEFT) || key_down(KEY_RIGHT)) {
|
||||
// Stop rope climbing sound
|
||||
if (rope_climb_sound_handle != -1) {
|
||||
p.destroy_sound(rope_climb_sound_handle);
|
||||
rope_climb_sound_handle = -1;
|
||||
}
|
||||
|
||||
// Move one tile in the direction being pressed before falling
|
||||
if (key_down(KEY_LEFT) && x > 0) {
|
||||
x--;
|
||||
} else if (key_down(KEY_RIGHT) && x < MAP_SIZE - 1) {
|
||||
x++;
|
||||
}
|
||||
|
||||
// Fall from rope!
|
||||
rope_climbing = false;
|
||||
start_falling();
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
// Inventory module includes
|
||||
#include "src/inventory_items.nvgt"
|
||||
#include "src/runes/rune_data.nvgt"
|
||||
#include "src/runes/rune_effects.nvgt"
|
||||
#include "src/inventory_menus.nvgt"
|
||||
#include "src/crafting.nvgt"
|
||||
|
||||
@@ -261,11 +261,21 @@ void update_max_health_from_equipment() {
|
||||
if (player_health > max_health) {
|
||||
player_health = max_health;
|
||||
}
|
||||
|
||||
// Calculate walk speed with all bonuses
|
||||
int base_speed = (equipped_feet == EQUIP_MOCCASINS) ? MOCCASINS_WALK_SPEED : BASE_WALK_SPEED;
|
||||
walk_speed = base_speed;
|
||||
|
||||
// Apply rune speed bonuses (stacks with moccasins)
|
||||
int rune_bonus = get_total_rune_walk_speed_bonus();
|
||||
walk_speed = base_speed - rune_bonus;
|
||||
|
||||
// Apply blessing (if active and faster than current)
|
||||
if (blessing_speed_active && BLESSING_WALK_SPEED < walk_speed) {
|
||||
walk_speed = BLESSING_WALK_SPEED;
|
||||
}
|
||||
|
||||
// Ensure minimum walk speed
|
||||
if (walk_speed < 200) walk_speed = 200;
|
||||
}
|
||||
|
||||
int get_quick_slot_key() {
|
||||
@@ -522,9 +532,21 @@ string get_equipped_weapon_name() {
|
||||
}
|
||||
|
||||
string get_speed_status() {
|
||||
if (blessing_speed_active) return "blessed";
|
||||
if (equipped_feet == EQUIP_MOCCASINS) return "boosted by moccasins";
|
||||
return "normal";
|
||||
string status = "";
|
||||
int rune_bonus = get_total_rune_walk_speed_bonus();
|
||||
|
||||
if (blessing_speed_active) {
|
||||
status = "blessed";
|
||||
} else if (equipped_feet == EQUIP_MOCCASINS && rune_bonus > 0) {
|
||||
status = "boosted by moccasins and runes";
|
||||
} else if (equipped_feet == EQUIP_MOCCASINS) {
|
||||
status = "boosted by moccasins";
|
||||
} else if (rune_bonus > 0) {
|
||||
status = "boosted by runes";
|
||||
} else {
|
||||
status = "normal";
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
void cleanup_equipment_after_inventory_change() {
|
||||
|
||||
@@ -9,6 +9,12 @@ void check_action_menu(int x) {
|
||||
|
||||
void try_place_snare(int x) {
|
||||
if (inv_snares > 0) {
|
||||
// Prevent placing in base area
|
||||
if (x <= BASE_END) {
|
||||
speak_with_history("Cannot place snares in the base area.", true);
|
||||
return;
|
||||
}
|
||||
|
||||
// Prevent placing if one already exists here
|
||||
if (get_snare_at(x) != null) {
|
||||
speak_with_history("There is already a snare here.", true);
|
||||
|
||||
@@ -1,13 +1,30 @@
|
||||
// Equipment menu system
|
||||
// Functions for managing equipment (weapons and clothing)
|
||||
|
||||
// Check if player has any equipment (including runed items)
|
||||
bool has_any_equipment() {
|
||||
// Check unruned items
|
||||
if (inv_spears > 0 || inv_axes > 0 || inv_slings > 0 || inv_bows > 0 ||
|
||||
inv_skin_hats > 0 || inv_skin_gloves > 0 || inv_skin_pants > 0 ||
|
||||
inv_skin_tunics > 0 || inv_moccasins > 0 || inv_skin_pouches > 0 ||
|
||||
inv_backpacks > 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check runed items
|
||||
int[] runeable = get_runeable_equipment_types();
|
||||
for (uint i = 0; i < runeable.length(); i++) {
|
||||
if (get_runed_item_count(runeable[i], RUNE_SWIFTNESS) > 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void check_equipment_menu() {
|
||||
if (key_pressed(KEY_E)) {
|
||||
// Check if player has any equipment
|
||||
if (inv_spears == 0 && inv_axes == 0 && inv_slings == 0 &&
|
||||
inv_skin_hats == 0 && inv_skin_gloves == 0 && inv_skin_pants == 0 &&
|
||||
inv_skin_tunics == 0 && inv_moccasins == 0 && inv_skin_pouches == 0 &&
|
||||
inv_backpacks == 0) {
|
||||
if (!has_any_equipment()) {
|
||||
speak_with_history("Nothing to equip.", true);
|
||||
} else {
|
||||
run_equipment_menu();
|
||||
@@ -15,63 +32,109 @@ void check_equipment_menu() {
|
||||
}
|
||||
}
|
||||
|
||||
// Helper to get full equipment name including rune
|
||||
string get_full_equipment_name(int equip_type, int rune_type) {
|
||||
string base_name = get_base_equipment_name(equip_type);
|
||||
if (rune_type != RUNE_NONE) {
|
||||
return "Runed " + base_name + " of " + get_rune_effect_name(rune_type);
|
||||
}
|
||||
return base_name;
|
||||
}
|
||||
|
||||
// Check if an item with specific rune is equipped
|
||||
bool is_runed_item_equipped(int equip_type, int rune_type) {
|
||||
if (!equipment_is_equipped(equip_type)) return false;
|
||||
return get_equipped_rune_for_slot(equip_type) == rune_type;
|
||||
}
|
||||
|
||||
void run_equipment_menu() {
|
||||
speak_with_history("Equipment menu.", true);
|
||||
|
||||
int selection = 0;
|
||||
string[] options;
|
||||
int[] equipment_types;
|
||||
int[] rune_types; // Track which rune is on each option (RUNE_NONE for unruned)
|
||||
|
||||
// Build menu dynamically based on what player has
|
||||
// Add unruned items
|
||||
if (inv_spears > 0) {
|
||||
string status = equipment_is_equipped(EQUIP_SPEAR) ? " (equipped)" : "";
|
||||
string status = is_runed_item_equipped(EQUIP_SPEAR, RUNE_NONE) ? " (equipped)" : "";
|
||||
options.insert_last("Spear" + status);
|
||||
equipment_types.insert_last(EQUIP_SPEAR);
|
||||
rune_types.insert_last(RUNE_NONE);
|
||||
}
|
||||
if (inv_slings > 0) {
|
||||
string status = equipment_is_equipped(EQUIP_SLING) ? " (equipped)" : "";
|
||||
string status = is_runed_item_equipped(EQUIP_SLING, RUNE_NONE) ? " (equipped)" : "";
|
||||
options.insert_last("Sling" + status);
|
||||
equipment_types.insert_last(EQUIP_SLING);
|
||||
rune_types.insert_last(RUNE_NONE);
|
||||
}
|
||||
if (inv_axes > 0) {
|
||||
string status = equipment_is_equipped(EQUIP_AXE) ? " (equipped)" : "";
|
||||
string status = is_runed_item_equipped(EQUIP_AXE, RUNE_NONE) ? " (equipped)" : "";
|
||||
options.insert_last("Stone Axe" + status);
|
||||
equipment_types.insert_last(EQUIP_AXE);
|
||||
rune_types.insert_last(RUNE_NONE);
|
||||
}
|
||||
if (inv_bows > 0) {
|
||||
string status = is_runed_item_equipped(EQUIP_BOW, RUNE_NONE) ? " (equipped)" : "";
|
||||
options.insert_last("Bow" + status);
|
||||
equipment_types.insert_last(EQUIP_BOW);
|
||||
rune_types.insert_last(RUNE_NONE);
|
||||
}
|
||||
if (inv_skin_hats > 0) {
|
||||
string status = equipment_is_equipped(EQUIP_HAT) ? " (equipped)" : "";
|
||||
string status = is_runed_item_equipped(EQUIP_HAT, RUNE_NONE) ? " (equipped)" : "";
|
||||
options.insert_last("Skin Hat" + status);
|
||||
equipment_types.insert_last(EQUIP_HAT);
|
||||
rune_types.insert_last(RUNE_NONE);
|
||||
}
|
||||
if (inv_skin_gloves > 0) {
|
||||
string status = equipment_is_equipped(EQUIP_GLOVES) ? " (equipped)" : "";
|
||||
string status = is_runed_item_equipped(EQUIP_GLOVES, RUNE_NONE) ? " (equipped)" : "";
|
||||
options.insert_last("Skin Gloves" + status);
|
||||
equipment_types.insert_last(EQUIP_GLOVES);
|
||||
rune_types.insert_last(RUNE_NONE);
|
||||
}
|
||||
if (inv_skin_pants > 0) {
|
||||
string status = equipment_is_equipped(EQUIP_PANTS) ? " (equipped)" : "";
|
||||
string status = is_runed_item_equipped(EQUIP_PANTS, RUNE_NONE) ? " (equipped)" : "";
|
||||
options.insert_last("Skin Pants" + status);
|
||||
equipment_types.insert_last(EQUIP_PANTS);
|
||||
rune_types.insert_last(RUNE_NONE);
|
||||
}
|
||||
if (inv_skin_tunics > 0) {
|
||||
string status = equipment_is_equipped(EQUIP_TUNIC) ? " (equipped)" : "";
|
||||
string status = is_runed_item_equipped(EQUIP_TUNIC, RUNE_NONE) ? " (equipped)" : "";
|
||||
options.insert_last("Skin Tunic" + status);
|
||||
equipment_types.insert_last(EQUIP_TUNIC);
|
||||
rune_types.insert_last(RUNE_NONE);
|
||||
}
|
||||
if (inv_moccasins > 0) {
|
||||
string status = equipment_is_equipped(EQUIP_MOCCASINS) ? " (equipped)" : "";
|
||||
string status = is_runed_item_equipped(EQUIP_MOCCASINS, RUNE_NONE) ? " (equipped)" : "";
|
||||
options.insert_last("Moccasins" + status);
|
||||
equipment_types.insert_last(EQUIP_MOCCASINS);
|
||||
rune_types.insert_last(RUNE_NONE);
|
||||
}
|
||||
if (inv_skin_pouches > 0) {
|
||||
string status = equipment_is_equipped(EQUIP_POUCH) ? " (equipped)" : "";
|
||||
string status = is_runed_item_equipped(EQUIP_POUCH, RUNE_NONE) ? " (equipped)" : "";
|
||||
options.insert_last("Skin Pouch" + status);
|
||||
equipment_types.insert_last(EQUIP_POUCH);
|
||||
rune_types.insert_last(RUNE_NONE);
|
||||
}
|
||||
if (inv_backpacks > 0) {
|
||||
string status = equipment_is_equipped(EQUIP_BACKPACK) ? " (equipped)" : "";
|
||||
string status = is_runed_item_equipped(EQUIP_BACKPACK, RUNE_NONE) ? " (equipped)" : "";
|
||||
options.insert_last("Backpack" + status);
|
||||
equipment_types.insert_last(EQUIP_BACKPACK);
|
||||
rune_types.insert_last(RUNE_NONE);
|
||||
}
|
||||
|
||||
// Add runed items (currently only swiftness rune)
|
||||
int[] runeable = get_runeable_equipment_types();
|
||||
for (uint i = 0; i < runeable.length(); i++) {
|
||||
int equip_type = runeable[i];
|
||||
int count = get_runed_item_count(equip_type, RUNE_SWIFTNESS);
|
||||
if (count > 0) {
|
||||
string name = get_full_equipment_name(equip_type, RUNE_SWIFTNESS);
|
||||
string status = is_runed_item_equipped(equip_type, RUNE_SWIFTNESS) ? " (equipped)" : "";
|
||||
options.insert_last(name + status);
|
||||
equipment_types.insert_last(equip_type);
|
||||
rune_types.insert_last(RUNE_SWIFTNESS);
|
||||
}
|
||||
}
|
||||
|
||||
while(true) {
|
||||
@@ -84,31 +147,40 @@ void run_equipment_menu() {
|
||||
|
||||
if (key_pressed(KEY_DOWN)) {
|
||||
selection++;
|
||||
if (selection >= options.length()) selection = 0;
|
||||
if (selection >= int(options.length())) selection = 0;
|
||||
speak_with_history(options[selection], true);
|
||||
}
|
||||
|
||||
if (key_pressed(KEY_UP)) {
|
||||
selection--;
|
||||
if (selection < 0) selection = options.length() - 1;
|
||||
if (selection < 0) selection = int(options.length()) - 1;
|
||||
speak_with_history(options[selection], true);
|
||||
}
|
||||
|
||||
int slot_index = get_quick_slot_key();
|
||||
if (slot_index != -1) {
|
||||
int equip_type = equipment_types[selection];
|
||||
int rune_type = rune_types[selection];
|
||||
quick_slots[slot_index] = equip_type;
|
||||
speak_with_history(get_equipment_name(equip_type) + " set to slot " + slot_index + ".", true);
|
||||
string name = get_full_equipment_name(equip_type, rune_type);
|
||||
speak_with_history(name + " set to slot " + slot_index + ".", true);
|
||||
}
|
||||
|
||||
if (key_pressed(KEY_RETURN)) {
|
||||
int equip_type = equipment_types[selection];
|
||||
if (equipment_is_equipped(equip_type)) {
|
||||
int rune_type = rune_types[selection];
|
||||
string name = get_full_equipment_name(equip_type, rune_type);
|
||||
|
||||
if (is_runed_item_equipped(equip_type, rune_type)) {
|
||||
// Unequip
|
||||
unequip_equipment_type(equip_type);
|
||||
speak_with_history(get_equipment_name(equip_type) + " unequipped.", true);
|
||||
clear_equipped_rune_for_slot(equip_type);
|
||||
speak_with_history(name + " unequipped.", true);
|
||||
} else {
|
||||
// Equip
|
||||
equip_equipment_type(equip_type);
|
||||
speak_with_history(get_equipment_name(equip_type) + " equipped.", true);
|
||||
set_equipped_rune_for_slot(equip_type, rune_type);
|
||||
speak_with_history(name + " equipped.", true);
|
||||
}
|
||||
update_max_health_from_equipment();
|
||||
break;
|
||||
|
||||
@@ -17,6 +17,7 @@ bool rope_climb_up = true;
|
||||
int rope_climb_target_x = 0;
|
||||
int rope_climb_target_y = 0;
|
||||
int rope_climb_start_y = 0;
|
||||
int rope_climb_sound_handle = -1;
|
||||
timer rope_climb_timer;
|
||||
int pending_rope_climb_x = -1;
|
||||
int pending_rope_climb_elevation = 0;
|
||||
|
||||
@@ -0,0 +1,158 @@
|
||||
// Rune System - Core Data and Helper Functions
|
||||
// Runes can be engraved onto equipment to grant permanent bonuses
|
||||
|
||||
// Rune type constants
|
||||
const int RUNE_NONE = -1;
|
||||
const int RUNE_SWIFTNESS = 0;
|
||||
|
||||
// Rune unlock tracking (persisted in save)
|
||||
bool rune_swiftness_unlocked = false;
|
||||
|
||||
// Boss defeat tracking
|
||||
bool unicorn_boss_defeated = false;
|
||||
|
||||
// Speed bonus per rune of swiftness (milliseconds) - half of moccasins (40ms)
|
||||
const int RUNE_SWIFTNESS_SPEED_BONUS = 20;
|
||||
|
||||
// Gathering speed reduction per rune (percentage off base time)
|
||||
const int RUNE_SWIFTNESS_GATHER_BONUS = 5;
|
||||
|
||||
// Dictionary for runed item counts
|
||||
// Key format: "equip_type:rune_type" (e.g., "5:0" for pants with swiftness)
|
||||
// Value: count of that runed item type
|
||||
dictionary runed_items;
|
||||
|
||||
// Track which rune is on equipped items
|
||||
int equipped_head_rune = RUNE_NONE;
|
||||
int equipped_torso_rune = RUNE_NONE;
|
||||
int equipped_arms_rune = RUNE_NONE;
|
||||
int equipped_hands_rune = RUNE_NONE;
|
||||
int equipped_legs_rune = RUNE_NONE;
|
||||
int equipped_feet_rune = RUNE_NONE;
|
||||
int equipped_weapon_rune = RUNE_NONE;
|
||||
|
||||
// Get display name for a rune type
|
||||
string get_rune_name(int rune_type) {
|
||||
if (rune_type == RUNE_SWIFTNESS) return "Rune of Swiftness";
|
||||
return "Unknown Rune";
|
||||
}
|
||||
|
||||
// Get the effect suffix for runed item names (e.g., "of Quickness")
|
||||
string get_rune_effect_name(int rune_type) {
|
||||
if (rune_type == RUNE_SWIFTNESS) return "Quickness";
|
||||
return "Unknown";
|
||||
}
|
||||
|
||||
// Check if any rune has been unlocked
|
||||
bool any_rune_unlocked() {
|
||||
return rune_swiftness_unlocked;
|
||||
}
|
||||
|
||||
// Check if a specific rune is unlocked
|
||||
bool is_rune_unlocked(int rune_type) {
|
||||
if (rune_type == RUNE_SWIFTNESS) return rune_swiftness_unlocked;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Create dictionary key for runed item storage
|
||||
string make_runed_key(int equip_type, int rune_type) {
|
||||
return "" + equip_type + ":" + rune_type;
|
||||
}
|
||||
|
||||
// Get count of a specific runed item type
|
||||
int get_runed_item_count(int equip_type, int rune_type) {
|
||||
string key = make_runed_key(equip_type, rune_type);
|
||||
if (runed_items.exists(key)) {
|
||||
return int(runed_items[key]);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Add a runed item to inventory
|
||||
void add_runed_item(int equip_type, int rune_type) {
|
||||
string key = make_runed_key(equip_type, rune_type);
|
||||
int current = get_runed_item_count(equip_type, rune_type);
|
||||
runed_items.set(key, current + 1);
|
||||
}
|
||||
|
||||
// Remove a runed item from inventory
|
||||
void remove_runed_item(int equip_type, int rune_type) {
|
||||
string key = make_runed_key(equip_type, rune_type);
|
||||
int current = get_runed_item_count(equip_type, rune_type);
|
||||
if (current > 0) {
|
||||
runed_items.set(key, current - 1);
|
||||
}
|
||||
}
|
||||
|
||||
// Check if player has any runed version of an equipment type
|
||||
bool has_any_runed_version(int equip_type) {
|
||||
// Check all rune types
|
||||
if (get_runed_item_count(equip_type, RUNE_SWIFTNESS) > 0) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get the rune type on equipped item for a given slot
|
||||
int get_equipped_rune_for_slot(int equip_type) {
|
||||
if (equip_type == EQUIP_SPEAR || equip_type == EQUIP_AXE ||
|
||||
equip_type == EQUIP_SLING || equip_type == EQUIP_BOW) {
|
||||
return equipped_weapon_rune;
|
||||
}
|
||||
if (equip_type == EQUIP_HAT) return equipped_head_rune;
|
||||
if (equip_type == EQUIP_TUNIC) return equipped_torso_rune;
|
||||
if (equip_type == EQUIP_POUCH || equip_type == EQUIP_BACKPACK) return equipped_arms_rune;
|
||||
if (equip_type == EQUIP_GLOVES) return equipped_hands_rune;
|
||||
if (equip_type == EQUIP_PANTS) return equipped_legs_rune;
|
||||
if (equip_type == EQUIP_MOCCASINS) return equipped_feet_rune;
|
||||
return RUNE_NONE;
|
||||
}
|
||||
|
||||
// Set the rune on an equipped item slot
|
||||
void set_equipped_rune_for_slot(int equip_type, int rune_type) {
|
||||
if (equip_type == EQUIP_SPEAR || equip_type == EQUIP_AXE ||
|
||||
equip_type == EQUIP_SLING || equip_type == EQUIP_BOW) {
|
||||
equipped_weapon_rune = rune_type;
|
||||
return;
|
||||
}
|
||||
if (equip_type == EQUIP_HAT) { equipped_head_rune = rune_type; return; }
|
||||
if (equip_type == EQUIP_TUNIC) { equipped_torso_rune = rune_type; return; }
|
||||
if (equip_type == EQUIP_POUCH || equip_type == EQUIP_BACKPACK) { equipped_arms_rune = rune_type; return; }
|
||||
if (equip_type == EQUIP_GLOVES) { equipped_hands_rune = rune_type; return; }
|
||||
if (equip_type == EQUIP_PANTS) { equipped_legs_rune = rune_type; return; }
|
||||
if (equip_type == EQUIP_MOCCASINS) { equipped_feet_rune = rune_type; return; }
|
||||
}
|
||||
|
||||
// Clear rune from an equipped slot
|
||||
void clear_equipped_rune_for_slot(int equip_type) {
|
||||
set_equipped_rune_for_slot(equip_type, RUNE_NONE);
|
||||
}
|
||||
|
||||
// Get all equipment types that can be runed
|
||||
int[] get_runeable_equipment_types() {
|
||||
int[] types;
|
||||
types.insert_last(EQUIP_SPEAR);
|
||||
types.insert_last(EQUIP_AXE);
|
||||
types.insert_last(EQUIP_SLING);
|
||||
types.insert_last(EQUIP_BOW);
|
||||
types.insert_last(EQUIP_HAT);
|
||||
types.insert_last(EQUIP_GLOVES);
|
||||
types.insert_last(EQUIP_PANTS);
|
||||
types.insert_last(EQUIP_TUNIC);
|
||||
types.insert_last(EQUIP_MOCCASINS);
|
||||
types.insert_last(EQUIP_POUCH);
|
||||
types.insert_last(EQUIP_BACKPACK);
|
||||
return types;
|
||||
}
|
||||
|
||||
// Reset all rune data for new game
|
||||
void reset_rune_data() {
|
||||
rune_swiftness_unlocked = false;
|
||||
unicorn_boss_defeated = false;
|
||||
runed_items.delete_all();
|
||||
equipped_head_rune = RUNE_NONE;
|
||||
equipped_torso_rune = RUNE_NONE;
|
||||
equipped_arms_rune = RUNE_NONE;
|
||||
equipped_hands_rune = RUNE_NONE;
|
||||
equipped_legs_rune = RUNE_NONE;
|
||||
equipped_feet_rune = RUNE_NONE;
|
||||
equipped_weapon_rune = RUNE_NONE;
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
// Rune Effects - Calculate bonuses from equipped runed items
|
||||
// This file handles the actual gameplay effects of runes
|
||||
|
||||
// Calculate total walking speed bonus from all equipped runed items
|
||||
int get_total_rune_walk_speed_bonus() {
|
||||
int bonus = 0;
|
||||
|
||||
// Check each equipment slot for swiftness runes
|
||||
if (equipped_head_rune == RUNE_SWIFTNESS) bonus += RUNE_SWIFTNESS_SPEED_BONUS;
|
||||
if (equipped_torso_rune == RUNE_SWIFTNESS) bonus += RUNE_SWIFTNESS_SPEED_BONUS;
|
||||
if (equipped_arms_rune == RUNE_SWIFTNESS) bonus += RUNE_SWIFTNESS_SPEED_BONUS;
|
||||
if (equipped_hands_rune == RUNE_SWIFTNESS) bonus += RUNE_SWIFTNESS_SPEED_BONUS;
|
||||
if (equipped_legs_rune == RUNE_SWIFTNESS) bonus += RUNE_SWIFTNESS_SPEED_BONUS;
|
||||
if (equipped_feet_rune == RUNE_SWIFTNESS) bonus += RUNE_SWIFTNESS_SPEED_BONUS;
|
||||
if (equipped_weapon_rune == RUNE_SWIFTNESS) bonus += RUNE_SWIFTNESS_SPEED_BONUS;
|
||||
|
||||
return bonus;
|
||||
}
|
||||
|
||||
// Calculate gathering speed reduction percentage from equipped runed items
|
||||
// Returns total percentage reduction (e.g., 15 means 15% faster gathering)
|
||||
int get_total_rune_gather_bonus() {
|
||||
int bonus = 0;
|
||||
|
||||
// Check each equipment slot for swiftness runes
|
||||
if (equipped_head_rune == RUNE_SWIFTNESS) bonus += RUNE_SWIFTNESS_GATHER_BONUS;
|
||||
if (equipped_torso_rune == RUNE_SWIFTNESS) bonus += RUNE_SWIFTNESS_GATHER_BONUS;
|
||||
if (equipped_arms_rune == RUNE_SWIFTNESS) bonus += RUNE_SWIFTNESS_GATHER_BONUS;
|
||||
if (equipped_hands_rune == RUNE_SWIFTNESS) bonus += RUNE_SWIFTNESS_GATHER_BONUS;
|
||||
if (equipped_legs_rune == RUNE_SWIFTNESS) bonus += RUNE_SWIFTNESS_GATHER_BONUS;
|
||||
if (equipped_feet_rune == RUNE_SWIFTNESS) bonus += RUNE_SWIFTNESS_GATHER_BONUS;
|
||||
if (equipped_weapon_rune == RUNE_SWIFTNESS) bonus += RUNE_SWIFTNESS_GATHER_BONUS;
|
||||
|
||||
return bonus;
|
||||
}
|
||||
|
||||
// Apply gathering time reduction based on rune bonuses
|
||||
// Takes base time in ms, returns reduced time
|
||||
int apply_rune_gather_bonus(int base_time) {
|
||||
int bonus_percent = get_total_rune_gather_bonus();
|
||||
if (bonus_percent <= 0) return base_time;
|
||||
|
||||
// Cap at 50% reduction to prevent instant gathering
|
||||
if (bonus_percent > 50) bonus_percent = 50;
|
||||
|
||||
int reduction = (base_time * bonus_percent) / 100;
|
||||
return base_time - reduction;
|
||||
}
|
||||
|
||||
// Count total equipped swiftness runes
|
||||
int count_equipped_swiftness_runes() {
|
||||
int count = 0;
|
||||
if (equipped_head_rune == RUNE_SWIFTNESS) count++;
|
||||
if (equipped_torso_rune == RUNE_SWIFTNESS) count++;
|
||||
if (equipped_arms_rune == RUNE_SWIFTNESS) count++;
|
||||
if (equipped_hands_rune == RUNE_SWIFTNESS) count++;
|
||||
if (equipped_legs_rune == RUNE_SWIFTNESS) count++;
|
||||
if (equipped_feet_rune == RUNE_SWIFTNESS) count++;
|
||||
if (equipped_weapon_rune == RUNE_SWIFTNESS) count++;
|
||||
return count;
|
||||
}
|
||||
|
||||
// Get a human-readable description of current rune speed bonuses
|
||||
string get_rune_speed_description() {
|
||||
int walk_bonus = get_total_rune_walk_speed_bonus();
|
||||
int gather_bonus = get_total_rune_gather_bonus();
|
||||
|
||||
if (walk_bonus == 0 && gather_bonus == 0) {
|
||||
return "none";
|
||||
}
|
||||
|
||||
string desc = "";
|
||||
if (walk_bonus > 0) {
|
||||
desc += walk_bonus + "ms faster walking";
|
||||
}
|
||||
if (gather_bonus > 0) {
|
||||
if (desc.length() > 0) desc += ", ";
|
||||
desc += gather_bonus + "% faster gathering";
|
||||
}
|
||||
return desc;
|
||||
}
|
||||
@@ -169,6 +169,7 @@ void reset_game_state() {
|
||||
rope_climb_target_x = 0;
|
||||
rope_climb_target_y = 0;
|
||||
rope_climb_start_y = 0;
|
||||
rope_climb_sound_handle = -1;
|
||||
pending_rope_climb_x = -1;
|
||||
pending_rope_climb_elevation = 0;
|
||||
|
||||
@@ -618,6 +619,29 @@ bool save_game_state() {
|
||||
}
|
||||
saveData.set("equipment_quick_slots", join_string_array(quickSlotData));
|
||||
|
||||
// Rune system data
|
||||
saveData.set("rune_swiftness_unlocked", rune_swiftness_unlocked);
|
||||
saveData.set("unicorn_boss_defeated", unicorn_boss_defeated);
|
||||
saveData.set("equipped_head_rune", equipped_head_rune);
|
||||
saveData.set("equipped_torso_rune", equipped_torso_rune);
|
||||
saveData.set("equipped_arms_rune", equipped_arms_rune);
|
||||
saveData.set("equipped_hands_rune", equipped_hands_rune);
|
||||
saveData.set("equipped_legs_rune", equipped_legs_rune);
|
||||
saveData.set("equipped_feet_rune", equipped_feet_rune);
|
||||
saveData.set("equipped_weapon_rune", equipped_weapon_rune);
|
||||
|
||||
// Save runed items dictionary as key:value pairs
|
||||
string[] runed_items_data;
|
||||
string[]@ keys = runed_items.get_keys();
|
||||
for (uint i = 0; i < keys.length(); i++) {
|
||||
string key = keys[i];
|
||||
int count = int(runed_items[key]);
|
||||
if (count > 0) {
|
||||
runed_items_data.insert_last(key + "=" + count);
|
||||
}
|
||||
}
|
||||
saveData.set("runed_items", join_string_array(runed_items_data));
|
||||
|
||||
saveData.set("time_current_hour", current_hour);
|
||||
saveData.set("time_current_day", current_day);
|
||||
saveData.set("time_is_daytime", is_daytime);
|
||||
@@ -919,6 +943,32 @@ bool load_game_state() {
|
||||
}
|
||||
update_max_health_from_equipment();
|
||||
|
||||
// Load rune system data
|
||||
rune_swiftness_unlocked = get_bool(saveData, "rune_swiftness_unlocked", false);
|
||||
unicorn_boss_defeated = get_bool(saveData, "unicorn_boss_defeated", false);
|
||||
equipped_head_rune = int(get_number(saveData, "equipped_head_rune", RUNE_NONE));
|
||||
equipped_torso_rune = int(get_number(saveData, "equipped_torso_rune", RUNE_NONE));
|
||||
equipped_arms_rune = int(get_number(saveData, "equipped_arms_rune", RUNE_NONE));
|
||||
equipped_hands_rune = int(get_number(saveData, "equipped_hands_rune", RUNE_NONE));
|
||||
equipped_legs_rune = int(get_number(saveData, "equipped_legs_rune", RUNE_NONE));
|
||||
equipped_feet_rune = int(get_number(saveData, "equipped_feet_rune", RUNE_NONE));
|
||||
equipped_weapon_rune = int(get_number(saveData, "equipped_weapon_rune", RUNE_NONE));
|
||||
|
||||
// Load runed items dictionary
|
||||
runed_items.delete_all();
|
||||
string[] loaded_runed_items = get_string_list_or_split(saveData, "runed_items");
|
||||
for (uint i = 0; i < loaded_runed_items.length(); i++) {
|
||||
string entry = loaded_runed_items[i];
|
||||
int eq_pos = entry.find("=");
|
||||
if (eq_pos > 0) {
|
||||
string key = entry.substr(0, eq_pos);
|
||||
int count = parse_int(entry.substr(eq_pos + 1));
|
||||
if (count > 0) {
|
||||
runed_items.set(key, count);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
current_hour = int(get_number(saveData, "time_current_hour", 8));
|
||||
current_day = int(get_number(saveData, "time_current_day", 1));
|
||||
sun_setting_warned = get_bool(saveData, "time_sun_setting_warned", false);
|
||||
|
||||
+20
-8
@@ -99,14 +99,6 @@ void expand_regular_area() {
|
||||
int stream_start = random(0, EXPANSION_SIZE - stream_width);
|
||||
int actual_start = new_start + stream_start;
|
||||
add_world_stream(actual_start, stream_width);
|
||||
|
||||
string width_desc = "very small";
|
||||
if (stream_width == 2) width_desc = "small";
|
||||
else if (stream_width == 3) width_desc = "medium";
|
||||
else if (stream_width == 4) width_desc = "wide";
|
||||
else if (stream_width == 5) width_desc = "very wide";
|
||||
|
||||
notify("A " + width_desc + " stream flows through the new area at x " + actual_start + ".");
|
||||
} else {
|
||||
// Try to place a tree with proper spacing and per-area limits
|
||||
spawn_tree_in_area(new_start, new_end);
|
||||
@@ -480,12 +472,22 @@ void start_crossfade(bool to_night) {
|
||||
if (to_night) {
|
||||
// Starting night sound
|
||||
if (night_sound_handle == -1 || !p.sound_is_active(night_sound_handle)) {
|
||||
// Clean up invalid handle if it exists
|
||||
if (night_sound_handle != -1) {
|
||||
p.destroy_sound(night_sound_handle);
|
||||
night_sound_handle = -1;
|
||||
}
|
||||
night_sound_handle = p.play_stationary("sounds/nature/night.ogg", true);
|
||||
}
|
||||
p.update_sound_start_values(night_sound_handle, 0.0, CROSSFADE_MIN_VOLUME, 1.0);
|
||||
} else {
|
||||
// Starting day sound
|
||||
if (day_sound_handle == -1 || !p.sound_is_active(day_sound_handle)) {
|
||||
// Clean up invalid handle if it exists
|
||||
if (day_sound_handle != -1) {
|
||||
p.destroy_sound(day_sound_handle);
|
||||
day_sound_handle = -1;
|
||||
}
|
||||
day_sound_handle = p.play_stationary("sounds/nature/day.ogg", true);
|
||||
}
|
||||
p.update_sound_start_values(day_sound_handle, 0.0, CROSSFADE_MIN_VOLUME, 1.0);
|
||||
@@ -550,6 +552,11 @@ void update_ambience(bool force_restart) {
|
||||
night_sound_handle = -1;
|
||||
}
|
||||
if (day_sound_handle == -1 || !p.sound_is_active(day_sound_handle)) {
|
||||
// Clean up invalid handle if it exists
|
||||
if (day_sound_handle != -1) {
|
||||
p.destroy_sound(day_sound_handle);
|
||||
day_sound_handle = -1;
|
||||
}
|
||||
day_sound_handle = p.play_stationary("sounds/nature/day.ogg", true);
|
||||
}
|
||||
} else {
|
||||
@@ -559,6 +566,11 @@ void update_ambience(bool force_restart) {
|
||||
day_sound_handle = -1;
|
||||
}
|
||||
if (night_sound_handle == -1 || !p.sound_is_active(night_sound_handle)) {
|
||||
// Clean up invalid handle if it exists
|
||||
if (night_sound_handle != -1) {
|
||||
p.destroy_sound(night_sound_handle);
|
||||
night_sound_handle = -1;
|
||||
}
|
||||
night_sound_handle = p.play_stationary("sounds/nature/night.ogg", true);
|
||||
}
|
||||
}
|
||||
|
||||
+17
-4
@@ -280,6 +280,12 @@ void update_wind_gusts() {
|
||||
void play_wind_gust() {
|
||||
if (wind_intensity == INTENSITY_NONE || wind_intensity > INTENSITY_HIGH) return;
|
||||
|
||||
// Clean up previous wind gust if it's stale
|
||||
if (wind_sound_handle != -1 && !p.sound_is_active(wind_sound_handle)) {
|
||||
p.destroy_sound(wind_sound_handle);
|
||||
wind_sound_handle = -1;
|
||||
}
|
||||
|
||||
// Play the appropriate wind sound once (non-looping)
|
||||
wind_sound_handle = p.play_stationary(wind_sounds[wind_intensity], false);
|
||||
}
|
||||
@@ -308,10 +314,17 @@ void fade_rain_to_intensity(int new_intensity) {
|
||||
rain_fade_duration = random(10000, 20000);
|
||||
|
||||
// Start rain sound if not playing
|
||||
if (rain_sound_handle == -1 && new_intensity != INTENSITY_NONE) {
|
||||
rain_sound_handle = p.play_stationary(RAIN_SOUND, true);
|
||||
p.update_sound_start_values(rain_sound_handle, 0.0, WEATHER_MIN_VOLUME, 1.0);
|
||||
rain_fade_from_volume = WEATHER_MIN_VOLUME;
|
||||
if (new_intensity != INTENSITY_NONE) {
|
||||
if (rain_sound_handle == -1 || !p.sound_is_active(rain_sound_handle)) {
|
||||
// Clean up invalid handle if it exists
|
||||
if (rain_sound_handle != -1) {
|
||||
p.destroy_sound(rain_sound_handle);
|
||||
rain_sound_handle = -1;
|
||||
}
|
||||
rain_sound_handle = p.play_stationary(RAIN_SOUND, true);
|
||||
p.update_sound_start_values(rain_sound_handle, 0.0, WEATHER_MIN_VOLUME, 1.0);
|
||||
rain_fade_from_volume = WEATHER_MIN_VOLUME;
|
||||
}
|
||||
}
|
||||
|
||||
rain_fading = true;
|
||||
|
||||
@@ -42,6 +42,9 @@ class MountainRange {
|
||||
// Ensure at least one steep climb (≥7 feet) reaches into 10-20 elevation range
|
||||
ensure_steep_climb();
|
||||
|
||||
// Enforce plateau spacing: at least 3 walkable tiles between steep sections
|
||||
enforce_plateau_spacing();
|
||||
|
||||
// Assign terrain types based on elevation
|
||||
for (int i = 0; i < size; i++) {
|
||||
if (elevations[i] > 20) {
|
||||
@@ -94,6 +97,77 @@ class MountainRange {
|
||||
}
|
||||
}
|
||||
|
||||
void enforce_plateau_spacing() {
|
||||
int size = int(elevations.length());
|
||||
const int MIN_PLATEAU_TILES = 3;
|
||||
const int MAX_GENTLE_SLOPE = 6; // Max slope per tile that doesn't require rope
|
||||
|
||||
// Find all steep sections (positions where rope would be needed)
|
||||
int[] steep_positions;
|
||||
for (int i = 1; i < size; i++) {
|
||||
int diff = elevations[i] - elevations[i-1];
|
||||
if (diff < 0) diff = -diff;
|
||||
if (diff >= MOUNTAIN_STEEP_THRESHOLD) {
|
||||
steep_positions.insert_last(i);
|
||||
}
|
||||
}
|
||||
|
||||
// For each steep section, ensure MIN_PLATEAU_TILES of gentle slope follow it
|
||||
for (uint s = 0; s < steep_positions.length(); s++) {
|
||||
int steep_pos = steep_positions[s];
|
||||
|
||||
// Check if we have enough tiles after this steep section for a plateau
|
||||
if (steep_pos + MIN_PLATEAU_TILES >= size) continue;
|
||||
|
||||
// Get the elevation after the steep climb
|
||||
int plateau_start_elevation = elevations[steep_pos];
|
||||
|
||||
// Ensure next MIN_PLATEAU_TILES have gentle slopes
|
||||
for (int p = 1; p <= MIN_PLATEAU_TILES; p++) {
|
||||
int tile_idx = steep_pos + p;
|
||||
if (tile_idx >= size) break;
|
||||
|
||||
// Calculate max allowed elevation change for gentle slope
|
||||
int prev_elevation = elevations[tile_idx - 1];
|
||||
int max_up = prev_elevation + MAX_GENTLE_SLOPE;
|
||||
int max_down = prev_elevation - MAX_GENTLE_SLOPE;
|
||||
|
||||
// Clamp to gentle slope range
|
||||
if (elevations[tile_idx] > max_up) {
|
||||
elevations[tile_idx] = max_up;
|
||||
} else if (elevations[tile_idx] < max_down) {
|
||||
elevations[tile_idx] = max_down;
|
||||
}
|
||||
|
||||
// Never go below 0
|
||||
if (elevations[tile_idx] < 0) {
|
||||
elevations[tile_idx] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Final pass: ensure no negative elevations anywhere
|
||||
for (int i = 0; i < size; i++) {
|
||||
if (elevations[i] < 0) elevations[i] = 0;
|
||||
}
|
||||
|
||||
// Prefer climbing up before descending by adjusting early terrain
|
||||
// If first half tends downward, flip it to go up first
|
||||
if (size >= 10) {
|
||||
int mid = size / 2;
|
||||
int early_trend = elevations[mid] - elevations[0];
|
||||
|
||||
if (early_trend < 0) {
|
||||
// Terrain goes down in first half, flip to go up
|
||||
for (int i = 0; i <= mid; i++) {
|
||||
elevations[i] = elevations[0] + (elevations[mid] - elevations[0]) * i / mid;
|
||||
}
|
||||
// Re-smooth after adjustment
|
||||
smooth_slopes();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void place_streams() {
|
||||
// Find valley bottoms (local minima)
|
||||
int[] valleys;
|
||||
@@ -247,6 +321,11 @@ class MountainRange {
|
||||
// Keep nearest stream sound active so distance-based fade can work.
|
||||
if (nearest_stream != -1) {
|
||||
if (stream_sound_handle == -1 || !p.sound_is_active(stream_sound_handle)) {
|
||||
// Clean up invalid handle if it exists
|
||||
if (stream_sound_handle != -1) {
|
||||
p.destroy_sound(stream_sound_handle);
|
||||
stream_sound_handle = -1;
|
||||
}
|
||||
stream_sound_handle = p.play_1d("sounds/terrain/stream.ogg", x, nearest_stream, true);
|
||||
stream_sound_position = nearest_stream;
|
||||
if (stream_sound_handle != -1) {
|
||||
|
||||
@@ -19,6 +19,11 @@ class WorldDrop {
|
||||
|
||||
void update() {
|
||||
if (sound_handle == -1 || !p.sound_is_active(sound_handle)) {
|
||||
// Clean up invalid handle if it exists
|
||||
if (sound_handle != -1) {
|
||||
p.destroy_sound(sound_handle);
|
||||
sound_handle = -1;
|
||||
}
|
||||
sound_handle = p.play_1d("sounds/items/item.ogg", x, position, true);
|
||||
if (sound_handle != -1) {
|
||||
p.update_sound_positioning_values(sound_handle, -1.0, 3.0, true);
|
||||
|
||||
@@ -56,6 +56,11 @@ class WorldFire {
|
||||
if (fire_distance < 0) fire_distance = -fire_distance;
|
||||
if (fire_distance <= FIRE_SOUND_RANGE) {
|
||||
if (sound_handle == -1 || !p.sound_is_active(sound_handle)) {
|
||||
// Clean up invalid handle if it exists
|
||||
if (sound_handle != -1) {
|
||||
p.destroy_sound(sound_handle);
|
||||
sound_handle = -1;
|
||||
}
|
||||
sound_handle = p.play_1d("sounds/items/fire.ogg", x, position, true);
|
||||
if (sound_handle != -1) {
|
||||
p.update_sound_positioning_values(sound_handle, -1.0, FIRE_SOUND_VOLUME_STEP, true);
|
||||
|
||||
@@ -43,6 +43,11 @@ class WorldSnare {
|
||||
|
||||
if (snare_distance <= SNARE_SOUND_RANGE) {
|
||||
if (sound_handle == -1 || !p.sound_is_active(sound_handle)) {
|
||||
// Clean up invalid handle if it exists
|
||||
if (sound_handle != -1) {
|
||||
p.destroy_sound(sound_handle);
|
||||
sound_handle = -1;
|
||||
}
|
||||
sound_handle = p.play_1d("sounds/actions/set_snare.ogg", x, position, true);
|
||||
if (sound_handle != -1) {
|
||||
p.update_sound_positioning_values(sound_handle, SNARE_SOUND_PAN_STEP, SNARE_SOUND_VOLUME_STEP, true);
|
||||
|
||||
@@ -37,7 +37,14 @@ class WorldStream {
|
||||
}
|
||||
|
||||
// Keep stream sound active so distance-based fade can work.
|
||||
if (sound_handle == -1 || !p.sound_is_active(sound_handle)) {
|
||||
// Check if sound needs to be created or recreated
|
||||
bool need_sound = (sound_handle == -1 || !p.sound_is_active(sound_handle));
|
||||
if (need_sound) {
|
||||
// Clean up invalid handle if it exists
|
||||
if (sound_handle != -1) {
|
||||
p.destroy_sound(sound_handle);
|
||||
sound_handle = -1;
|
||||
}
|
||||
sound_handle = p.play_1d("sounds/terrain/stream.ogg", x, sound_pos, true);
|
||||
sound_position = sound_pos;
|
||||
if (sound_handle != -1) {
|
||||
|
||||
Reference in New Issue
Block a user