First adventure added. A bit of sound management improvement.
This commit is contained in:
@@ -6,4 +6,7 @@ lib_windows/
|
|||||||
stub/
|
stub/
|
||||||
include/
|
include/
|
||||||
save.dat
|
save.dat
|
||||||
|
*.bak
|
||||||
|
*.wav
|
||||||
|
*.opus
|
||||||
.aider*
|
.aider*
|
||||||
|
|||||||
+6
-2
@@ -2,7 +2,8 @@
|
|||||||
#include "virtual_dialogs.nvgt"
|
#include "virtual_dialogs.nvgt"
|
||||||
|
|
||||||
// Audio
|
// Audio
|
||||||
sound_pool p(100);
|
// Increased pool size to 300 to prevent sound cutoffs and missing sounds
|
||||||
|
sound_pool p(300);
|
||||||
|
|
||||||
#include "src/constants.nvgt"
|
#include "src/constants.nvgt"
|
||||||
#include "src/player.nvgt"
|
#include "src/player.nvgt"
|
||||||
@@ -34,6 +35,7 @@ sound_pool p(100);
|
|||||||
#include "src/creature_audio.nvgt"
|
#include "src/creature_audio.nvgt"
|
||||||
#include "src/notify.nvgt"
|
#include "src/notify.nvgt"
|
||||||
#include "src/speech_history.nvgt"
|
#include "src/speech_history.nvgt"
|
||||||
|
#include "src/text_reader.nvgt"
|
||||||
#include "src/bosses/adventure_system.nvgt"
|
#include "src/bosses/adventure_system.nvgt"
|
||||||
|
|
||||||
int run_main_menu() {
|
int run_main_menu() {
|
||||||
@@ -380,7 +382,9 @@ void main()
|
|||||||
}
|
}
|
||||||
search_timer.restart();
|
search_timer.restart();
|
||||||
}
|
}
|
||||||
if (shift_down && search_timer.elapsed > 2000 && !searching)
|
// Apply rune gathering bonus to search time
|
||||||
|
int search_time = apply_rune_gather_bonus(2000);
|
||||||
|
if (shift_down && search_timer.elapsed > search_time && !searching)
|
||||||
{
|
{
|
||||||
searching = true;
|
searching = true;
|
||||||
search_delay_timer.restart();
|
search_delay_timer.restart();
|
||||||
|
|||||||
@@ -396,5 +396,29 @@ void play_unicorn_death_sequence() {
|
|||||||
|
|
||||||
void give_unicorn_rewards() {
|
void give_unicorn_rewards() {
|
||||||
speak_with_history("Victory!", true);
|
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_clothing.nvgt"
|
||||||
#include "src/crafting/craft_buildings.nvgt"
|
#include "src/crafting/craft_buildings.nvgt"
|
||||||
#include "src/crafting/craft_barricade.nvgt"
|
#include "src/crafting/craft_barricade.nvgt"
|
||||||
|
#include "src/crafting/craft_runes.nvgt"
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ void run_materials_menu() {
|
|||||||
|
|
||||||
int selection = 0;
|
int selection = 0;
|
||||||
string[] options = {
|
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]"
|
"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);
|
speak_with_history("Crafting menu.", true);
|
||||||
|
|
||||||
int selection = 0;
|
int selection = 0;
|
||||||
string[] categories = {"Weapons", "Tools", "Materials", "Clothing", "Buildings", "Barricade"};
|
string[] categories;
|
||||||
int[] category_types = {0, 1, 2, 3, 4, 5};
|
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) {
|
while(true) {
|
||||||
wait(5);
|
wait(5);
|
||||||
@@ -24,13 +44,13 @@ void run_crafting_menu() {
|
|||||||
|
|
||||||
if (key_pressed(KEY_DOWN)) {
|
if (key_pressed(KEY_DOWN)) {
|
||||||
selection++;
|
selection++;
|
||||||
if (selection >= categories.length()) selection = 0;
|
if (selection >= int(categories.length())) selection = 0;
|
||||||
speak_with_history(categories[selection], true);
|
speak_with_history(categories[selection], true);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (key_pressed(KEY_UP)) {
|
if (key_pressed(KEY_UP)) {
|
||||||
selection--;
|
selection--;
|
||||||
if (selection < 0) selection = categories.length() - 1;
|
if (selection < 0) selection = int(categories.length()) - 1;
|
||||||
speak_with_history(categories[selection], true);
|
speak_with_history(categories[selection], true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -42,6 +62,7 @@ void run_crafting_menu() {
|
|||||||
else if (category == 3) run_clothing_menu();
|
else if (category == 3) run_clothing_menu();
|
||||||
else if (category == 4) run_buildings_menu();
|
else if (category == 4) run_buildings_menu();
|
||||||
else if (category == 5) run_barricade_menu();
|
else if (category == 5) run_barricade_menu();
|
||||||
|
else if (category == 6) run_runes_menu();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -181,6 +181,13 @@ void update_bandit(Bandit@ bandit) {
|
|||||||
if (bandit.alert_timer.elapsed > bandit.next_alert_delay) {
|
if (bandit.alert_timer.elapsed > bandit.next_alert_delay) {
|
||||||
bandit.alert_timer.restart();
|
bandit.alert_timer.restart();
|
||||||
bandit.next_alert_delay = random(BANDIT_ALERT_MIN_DELAY, BANDIT_ALERT_MAX_DELAY);
|
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);
|
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);
|
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);
|
add_world_drop(creature.position, cfg.drop_type);
|
||||||
creature.health = 0;
|
creature.health = 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -134,6 +134,13 @@ void update_undead(Undead@ undead) {
|
|||||||
if (undead.groan_timer.elapsed > undead.next_groan_delay) {
|
if (undead.groan_timer.elapsed > undead.next_groan_delay) {
|
||||||
undead.groan_timer.restart();
|
undead.groan_timer.restart();
|
||||||
undead.next_groan_delay = random(ZOMBIE_GROAN_MIN_DELAY, ZOMBIE_GROAN_MAX_DELAY);
|
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);
|
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 (tree_distance <= TREE_SOUND_RANGE) {
|
||||||
if (sound_handle == -1 || !p.sound_is_active(sound_handle)) {
|
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);
|
sound_handle = p.play_1d("sounds/environment/tree.ogg", x, position, true);
|
||||||
if (sound_handle != -1) {
|
if (sound_handle != -1) {
|
||||||
p.update_sound_positioning_values(sound_handle, -1.0, TREE_SOUND_VOLUME_STEP, true);
|
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";
|
string direction = rope_climb_up ? "up" : "down";
|
||||||
speak_with_history("Climbing " + direction + ". " + distance + " feet.", true);
|
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() {
|
void update_rope_climbing() {
|
||||||
@@ -943,11 +951,9 @@ void update_rope_climbing() {
|
|||||||
if (y < rope_climb_target_y) {
|
if (y < rope_climb_target_y) {
|
||||||
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) {
|
if (y >= rope_climb_target_y) {
|
||||||
complete_rope_climb();
|
complete_rope_climb();
|
||||||
} else {
|
|
||||||
p.play_stationary("sounds/actions/climb_rope.ogg", false);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -955,11 +961,9 @@ void update_rope_climbing() {
|
|||||||
if (y > rope_climb_target_y) {
|
if (y > rope_climb_target_y) {
|
||||||
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) {
|
if (y <= rope_climb_target_y) {
|
||||||
complete_rope_climb();
|
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;
|
x = rope_climb_target_x;
|
||||||
y = rope_climb_target_y;
|
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 for new terrain
|
||||||
play_footstep(x, BASE_END, GRASS_END);
|
play_footstep(x, BASE_END, GRASS_END);
|
||||||
|
|
||||||
@@ -981,6 +991,19 @@ void check_rope_climb_fall() {
|
|||||||
if (!rope_climbing) return;
|
if (!rope_climbing) return;
|
||||||
|
|
||||||
if (key_down(KEY_LEFT) || key_down(KEY_RIGHT)) {
|
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!
|
// Fall from rope!
|
||||||
rope_climbing = false;
|
rope_climbing = false;
|
||||||
start_falling();
|
start_falling();
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
// Inventory module includes
|
// Inventory module includes
|
||||||
#include "src/inventory_items.nvgt"
|
#include "src/inventory_items.nvgt"
|
||||||
|
#include "src/runes/rune_data.nvgt"
|
||||||
|
#include "src/runes/rune_effects.nvgt"
|
||||||
#include "src/inventory_menus.nvgt"
|
#include "src/inventory_menus.nvgt"
|
||||||
#include "src/crafting.nvgt"
|
#include "src/crafting.nvgt"
|
||||||
|
|||||||
@@ -261,11 +261,21 @@ void update_max_health_from_equipment() {
|
|||||||
if (player_health > max_health) {
|
if (player_health > max_health) {
|
||||||
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;
|
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) {
|
if (blessing_speed_active && BLESSING_WALK_SPEED < walk_speed) {
|
||||||
walk_speed = BLESSING_WALK_SPEED;
|
walk_speed = BLESSING_WALK_SPEED;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Ensure minimum walk speed
|
||||||
|
if (walk_speed < 200) walk_speed = 200;
|
||||||
}
|
}
|
||||||
|
|
||||||
int get_quick_slot_key() {
|
int get_quick_slot_key() {
|
||||||
@@ -522,9 +532,21 @@ string get_equipped_weapon_name() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
string get_speed_status() {
|
string get_speed_status() {
|
||||||
if (blessing_speed_active) return "blessed";
|
string status = "";
|
||||||
if (equipped_feet == EQUIP_MOCCASINS) return "boosted by moccasins";
|
int rune_bonus = get_total_rune_walk_speed_bonus();
|
||||||
return "normal";
|
|
||||||
|
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() {
|
void cleanup_equipment_after_inventory_change() {
|
||||||
|
|||||||
@@ -9,6 +9,12 @@ void check_action_menu(int x) {
|
|||||||
|
|
||||||
void try_place_snare(int x) {
|
void try_place_snare(int x) {
|
||||||
if (inv_snares > 0) {
|
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
|
// Prevent placing if one already exists here
|
||||||
if (get_snare_at(x) != null) {
|
if (get_snare_at(x) != null) {
|
||||||
speak_with_history("There is already a snare here.", true);
|
speak_with_history("There is already a snare here.", true);
|
||||||
|
|||||||
@@ -1,13 +1,30 @@
|
|||||||
// Equipment menu system
|
// Equipment menu system
|
||||||
// Functions for managing equipment (weapons and clothing)
|
// 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() {
|
void check_equipment_menu() {
|
||||||
if (key_pressed(KEY_E)) {
|
if (key_pressed(KEY_E)) {
|
||||||
// Check if player has any equipment
|
if (!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) {
|
|
||||||
speak_with_history("Nothing to equip.", true);
|
speak_with_history("Nothing to equip.", true);
|
||||||
} else {
|
} else {
|
||||||
run_equipment_menu();
|
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() {
|
void run_equipment_menu() {
|
||||||
speak_with_history("Equipment menu.", true);
|
speak_with_history("Equipment menu.", true);
|
||||||
|
|
||||||
int selection = 0;
|
int selection = 0;
|
||||||
string[] options;
|
string[] options;
|
||||||
int[] equipment_types;
|
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) {
|
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);
|
options.insert_last("Spear" + status);
|
||||||
equipment_types.insert_last(EQUIP_SPEAR);
|
equipment_types.insert_last(EQUIP_SPEAR);
|
||||||
|
rune_types.insert_last(RUNE_NONE);
|
||||||
}
|
}
|
||||||
if (inv_slings > 0) {
|
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);
|
options.insert_last("Sling" + status);
|
||||||
equipment_types.insert_last(EQUIP_SLING);
|
equipment_types.insert_last(EQUIP_SLING);
|
||||||
|
rune_types.insert_last(RUNE_NONE);
|
||||||
}
|
}
|
||||||
if (inv_axes > 0) {
|
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);
|
options.insert_last("Stone Axe" + status);
|
||||||
equipment_types.insert_last(EQUIP_AXE);
|
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) {
|
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);
|
options.insert_last("Skin Hat" + status);
|
||||||
equipment_types.insert_last(EQUIP_HAT);
|
equipment_types.insert_last(EQUIP_HAT);
|
||||||
|
rune_types.insert_last(RUNE_NONE);
|
||||||
}
|
}
|
||||||
if (inv_skin_gloves > 0) {
|
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);
|
options.insert_last("Skin Gloves" + status);
|
||||||
equipment_types.insert_last(EQUIP_GLOVES);
|
equipment_types.insert_last(EQUIP_GLOVES);
|
||||||
|
rune_types.insert_last(RUNE_NONE);
|
||||||
}
|
}
|
||||||
if (inv_skin_pants > 0) {
|
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);
|
options.insert_last("Skin Pants" + status);
|
||||||
equipment_types.insert_last(EQUIP_PANTS);
|
equipment_types.insert_last(EQUIP_PANTS);
|
||||||
|
rune_types.insert_last(RUNE_NONE);
|
||||||
}
|
}
|
||||||
if (inv_skin_tunics > 0) {
|
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);
|
options.insert_last("Skin Tunic" + status);
|
||||||
equipment_types.insert_last(EQUIP_TUNIC);
|
equipment_types.insert_last(EQUIP_TUNIC);
|
||||||
|
rune_types.insert_last(RUNE_NONE);
|
||||||
}
|
}
|
||||||
if (inv_moccasins > 0) {
|
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);
|
options.insert_last("Moccasins" + status);
|
||||||
equipment_types.insert_last(EQUIP_MOCCASINS);
|
equipment_types.insert_last(EQUIP_MOCCASINS);
|
||||||
|
rune_types.insert_last(RUNE_NONE);
|
||||||
}
|
}
|
||||||
if (inv_skin_pouches > 0) {
|
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);
|
options.insert_last("Skin Pouch" + status);
|
||||||
equipment_types.insert_last(EQUIP_POUCH);
|
equipment_types.insert_last(EQUIP_POUCH);
|
||||||
|
rune_types.insert_last(RUNE_NONE);
|
||||||
}
|
}
|
||||||
if (inv_backpacks > 0) {
|
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);
|
options.insert_last("Backpack" + status);
|
||||||
equipment_types.insert_last(EQUIP_BACKPACK);
|
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) {
|
while(true) {
|
||||||
@@ -84,31 +147,40 @@ void run_equipment_menu() {
|
|||||||
|
|
||||||
if (key_pressed(KEY_DOWN)) {
|
if (key_pressed(KEY_DOWN)) {
|
||||||
selection++;
|
selection++;
|
||||||
if (selection >= options.length()) selection = 0;
|
if (selection >= int(options.length())) selection = 0;
|
||||||
speak_with_history(options[selection], true);
|
speak_with_history(options[selection], true);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (key_pressed(KEY_UP)) {
|
if (key_pressed(KEY_UP)) {
|
||||||
selection--;
|
selection--;
|
||||||
if (selection < 0) selection = options.length() - 1;
|
if (selection < 0) selection = int(options.length()) - 1;
|
||||||
speak_with_history(options[selection], true);
|
speak_with_history(options[selection], true);
|
||||||
}
|
}
|
||||||
|
|
||||||
int slot_index = get_quick_slot_key();
|
int slot_index = get_quick_slot_key();
|
||||||
if (slot_index != -1) {
|
if (slot_index != -1) {
|
||||||
int equip_type = equipment_types[selection];
|
int equip_type = equipment_types[selection];
|
||||||
|
int rune_type = rune_types[selection];
|
||||||
quick_slots[slot_index] = equip_type;
|
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)) {
|
if (key_pressed(KEY_RETURN)) {
|
||||||
int equip_type = equipment_types[selection];
|
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);
|
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 {
|
} else {
|
||||||
|
// Equip
|
||||||
equip_equipment_type(equip_type);
|
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();
|
update_max_health_from_equipment();
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ bool rope_climb_up = true;
|
|||||||
int rope_climb_target_x = 0;
|
int rope_climb_target_x = 0;
|
||||||
int rope_climb_target_y = 0;
|
int rope_climb_target_y = 0;
|
||||||
int rope_climb_start_y = 0;
|
int rope_climb_start_y = 0;
|
||||||
|
int rope_climb_sound_handle = -1;
|
||||||
timer rope_climb_timer;
|
timer rope_climb_timer;
|
||||||
int pending_rope_climb_x = -1;
|
int pending_rope_climb_x = -1;
|
||||||
int pending_rope_climb_elevation = 0;
|
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_x = 0;
|
||||||
rope_climb_target_y = 0;
|
rope_climb_target_y = 0;
|
||||||
rope_climb_start_y = 0;
|
rope_climb_start_y = 0;
|
||||||
|
rope_climb_sound_handle = -1;
|
||||||
pending_rope_climb_x = -1;
|
pending_rope_climb_x = -1;
|
||||||
pending_rope_climb_elevation = 0;
|
pending_rope_climb_elevation = 0;
|
||||||
|
|
||||||
@@ -618,6 +619,29 @@ bool save_game_state() {
|
|||||||
}
|
}
|
||||||
saveData.set("equipment_quick_slots", join_string_array(quickSlotData));
|
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_hour", current_hour);
|
||||||
saveData.set("time_current_day", current_day);
|
saveData.set("time_current_day", current_day);
|
||||||
saveData.set("time_is_daytime", is_daytime);
|
saveData.set("time_is_daytime", is_daytime);
|
||||||
@@ -919,6 +943,32 @@ bool load_game_state() {
|
|||||||
}
|
}
|
||||||
update_max_health_from_equipment();
|
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_hour = int(get_number(saveData, "time_current_hour", 8));
|
||||||
current_day = int(get_number(saveData, "time_current_day", 1));
|
current_day = int(get_number(saveData, "time_current_day", 1));
|
||||||
sun_setting_warned = get_bool(saveData, "time_sun_setting_warned", false);
|
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 stream_start = random(0, EXPANSION_SIZE - stream_width);
|
||||||
int actual_start = new_start + stream_start;
|
int actual_start = new_start + stream_start;
|
||||||
add_world_stream(actual_start, stream_width);
|
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 {
|
} else {
|
||||||
// Try to place a tree with proper spacing and per-area limits
|
// Try to place a tree with proper spacing and per-area limits
|
||||||
spawn_tree_in_area(new_start, new_end);
|
spawn_tree_in_area(new_start, new_end);
|
||||||
@@ -480,12 +472,22 @@ void start_crossfade(bool to_night) {
|
|||||||
if (to_night) {
|
if (to_night) {
|
||||||
// Starting night sound
|
// Starting night sound
|
||||||
if (night_sound_handle == -1 || !p.sound_is_active(night_sound_handle)) {
|
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);
|
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);
|
p.update_sound_start_values(night_sound_handle, 0.0, CROSSFADE_MIN_VOLUME, 1.0);
|
||||||
} else {
|
} else {
|
||||||
// Starting day sound
|
// Starting day sound
|
||||||
if (day_sound_handle == -1 || !p.sound_is_active(day_sound_handle)) {
|
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);
|
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);
|
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;
|
night_sound_handle = -1;
|
||||||
}
|
}
|
||||||
if (day_sound_handle == -1 || !p.sound_is_active(day_sound_handle)) {
|
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);
|
day_sound_handle = p.play_stationary("sounds/nature/day.ogg", true);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -559,6 +566,11 @@ void update_ambience(bool force_restart) {
|
|||||||
day_sound_handle = -1;
|
day_sound_handle = -1;
|
||||||
}
|
}
|
||||||
if (night_sound_handle == -1 || !p.sound_is_active(night_sound_handle)) {
|
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);
|
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() {
|
void play_wind_gust() {
|
||||||
if (wind_intensity == INTENSITY_NONE || wind_intensity > INTENSITY_HIGH) return;
|
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)
|
// Play the appropriate wind sound once (non-looping)
|
||||||
wind_sound_handle = p.play_stationary(wind_sounds[wind_intensity], false);
|
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);
|
rain_fade_duration = random(10000, 20000);
|
||||||
|
|
||||||
// Start rain sound if not playing
|
// Start rain sound if not playing
|
||||||
if (rain_sound_handle == -1 && new_intensity != INTENSITY_NONE) {
|
if (new_intensity != INTENSITY_NONE) {
|
||||||
rain_sound_handle = p.play_stationary(RAIN_SOUND, true);
|
if (rain_sound_handle == -1 || !p.sound_is_active(rain_sound_handle)) {
|
||||||
p.update_sound_start_values(rain_sound_handle, 0.0, WEATHER_MIN_VOLUME, 1.0);
|
// Clean up invalid handle if it exists
|
||||||
rain_fade_from_volume = WEATHER_MIN_VOLUME;
|
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;
|
rain_fading = true;
|
||||||
|
|||||||
@@ -42,6 +42,9 @@ class MountainRange {
|
|||||||
// Ensure at least one steep climb (≥7 feet) reaches into 10-20 elevation range
|
// Ensure at least one steep climb (≥7 feet) reaches into 10-20 elevation range
|
||||||
ensure_steep_climb();
|
ensure_steep_climb();
|
||||||
|
|
||||||
|
// Enforce plateau spacing: at least 3 walkable tiles between steep sections
|
||||||
|
enforce_plateau_spacing();
|
||||||
|
|
||||||
// Assign terrain types based on elevation
|
// Assign terrain types based on elevation
|
||||||
for (int i = 0; i < size; i++) {
|
for (int i = 0; i < size; i++) {
|
||||||
if (elevations[i] > 20) {
|
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() {
|
void place_streams() {
|
||||||
// Find valley bottoms (local minima)
|
// Find valley bottoms (local minima)
|
||||||
int[] valleys;
|
int[] valleys;
|
||||||
@@ -247,6 +321,11 @@ class MountainRange {
|
|||||||
// Keep nearest stream sound active so distance-based fade can work.
|
// Keep nearest stream sound active so distance-based fade can work.
|
||||||
if (nearest_stream != -1) {
|
if (nearest_stream != -1) {
|
||||||
if (stream_sound_handle == -1 || !p.sound_is_active(stream_sound_handle)) {
|
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_handle = p.play_1d("sounds/terrain/stream.ogg", x, nearest_stream, true);
|
||||||
stream_sound_position = nearest_stream;
|
stream_sound_position = nearest_stream;
|
||||||
if (stream_sound_handle != -1) {
|
if (stream_sound_handle != -1) {
|
||||||
|
|||||||
@@ -19,6 +19,11 @@ class WorldDrop {
|
|||||||
|
|
||||||
void update() {
|
void update() {
|
||||||
if (sound_handle == -1 || !p.sound_is_active(sound_handle)) {
|
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);
|
sound_handle = p.play_1d("sounds/items/item.ogg", x, position, true);
|
||||||
if (sound_handle != -1) {
|
if (sound_handle != -1) {
|
||||||
p.update_sound_positioning_values(sound_handle, -1.0, 3.0, true);
|
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 < 0) fire_distance = -fire_distance;
|
||||||
if (fire_distance <= FIRE_SOUND_RANGE) {
|
if (fire_distance <= FIRE_SOUND_RANGE) {
|
||||||
if (sound_handle == -1 || !p.sound_is_active(sound_handle)) {
|
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);
|
sound_handle = p.play_1d("sounds/items/fire.ogg", x, position, true);
|
||||||
if (sound_handle != -1) {
|
if (sound_handle != -1) {
|
||||||
p.update_sound_positioning_values(sound_handle, -1.0, FIRE_SOUND_VOLUME_STEP, true);
|
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 (snare_distance <= SNARE_SOUND_RANGE) {
|
||||||
if (sound_handle == -1 || !p.sound_is_active(sound_handle)) {
|
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);
|
sound_handle = p.play_1d("sounds/actions/set_snare.ogg", x, position, true);
|
||||||
if (sound_handle != -1) {
|
if (sound_handle != -1) {
|
||||||
p.update_sound_positioning_values(sound_handle, SNARE_SOUND_PAN_STEP, SNARE_SOUND_VOLUME_STEP, true);
|
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.
|
// 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_handle = p.play_1d("sounds/terrain/stream.ogg", x, sound_pos, true);
|
||||||
sound_position = sound_pos;
|
sound_position = sound_pos;
|
||||||
if (sound_handle != -1) {
|
if (sound_handle != -1) {
|
||||||
|
|||||||
Reference in New Issue
Block a user