diff --git a/draugnorak.nvgt b/draugnorak.nvgt index 3e816f3..1571b06 100755 --- a/draugnorak.nvgt +++ b/draugnorak.nvgt @@ -23,7 +23,9 @@ sound_pool p(300); #include "src/world/mountains.nvgt" #include "src/world/barricade.nvgt" #include "src/world_state.nvgt" -#include "src/ui.nvgt" +#include "libstorm-nvgt/ui.nvgt" +#include "libstorm-nvgt/menu_helpers.nvgt" +#include "src/terrain_lookup.nvgt" #include "src/inventory.nvgt" #include "src/pet_system.nvgt" #include "src/quest_system.nvgt" @@ -37,9 +39,9 @@ sound_pool p(300); #include "src/weather.nvgt" #include "src/audio_utils.nvgt" #include "src/creature_audio.nvgt" -#include "src/notify.nvgt" -#include "src/speech_history.nvgt" -#include "src/text_reader.nvgt" +#include "libstorm-nvgt/speech_history.nvgt" +#include "src/text_reader_aliases.nvgt" +#include "src/notify_compat.nvgt" #include "src/learn_sounds.nvgt" #include "src/bosses/adventure_system.nvgt" @@ -162,6 +164,7 @@ void run_game() init_master_volume(); show_window("Draugnorak"); + ui_set_default_window_title("Draugnorak"); init_flying_creature_configs(); init_item_registry(); init_pet_sounds(); diff --git a/libstorm-nvgt b/libstorm-nvgt index 65370b4..104c385 160000 --- a/libstorm-nvgt +++ b/libstorm-nvgt @@ -1 +1 @@ -Subproject commit 65370b4679b7816f0dcdc941030e02a6cb46b2a0 +Subproject commit 104c385ae4bba423382f0d64044b99c63e7a6074 diff --git a/src/learn_sounds.nvgt b/src/learn_sounds.nvgt index 2fd03a3..e3fbb9a 100644 --- a/src/learn_sounds.nvgt +++ b/src/learn_sounds.nvgt @@ -1,268 +1,52 @@ -// Learn sounds menu +#include "libstorm-nvgt/learn_sounds.nvgt" +#include "libstorm-nvgt/docs_browser.nvgt" +#include "excluded_sounds.nvgt" -// Skip entries can be full file paths or directories (ending with "/"). -string[] learnSoundSkipList = { - "sounds/quests/bone1.ogg", - "sounds/quests/bone2.ogg", - "sounds/quests/bone3.ogg", - "sounds/quests/bone4.ogg", - "sounds/quests/bone5.ogg", - "sounds/quests/bone6.ogg", - "sounds/quests/bone7.ogg", - "sounds/quests/bone8.ogg", - "sounds/actions/fishpole.ogg", - "sounds/actions/hit_ground.ogg", - "sounds/menu/", - "sounds/nature/", - "sounds/pets/" -}; +bool learnSoundsConfigured = false; +bool docsBrowserConfigured = false; -// Description entries: keep paths/texts aligned by index. -string[] learnSoundDescriptionPaths = { - "sounds/actions/bad_cast.ogg", - "sounds/actions/cast_strength.ogg", - "sounds/enemies/enter_range.ogg", - "sounds/enemies/exit_range.ogg", - "sounds/actions/falling.ogg", - "sounds/enemies/invasion.ogg", - "sounds/items/miscellaneous.ogg" -}; - -string[] learnSoundDescriptionTexts = { - "Your cast missed the water and landed in nearby foliage where it is unlikely to attract fish.", - "When casting release control when over water. When catching release when sound is over player.", - "An enemy is in range of your currently wielded weapon.", - "An enemy is no longer in range of your currently wielded weapon.", - "Lowers in pitch as the fall progresses.", - "The war drums pound! Prepare for combat!", - "You picked up an item for which there is no specific sound." -}; - -int learnSoundHandle = -1; - -string normalize_path(const string&in path) { - return path.replace("\\", "/", true); +void learn_sounds_bridge_speak(const string &in message, bool interrupt) { + speak_with_history(message, interrupt); } -bool is_directory_skip_entry(const string&in entry) { - if (entry.length() == 0) return false; - if (entry.substr(entry.length() - 1) == "/") return true; - return directory_exists(entry); +void learn_sounds_bridge_tick() { + handle_global_volume_keys(); } -bool should_skip_sound_path(const string&in path) { - string normalizedPath = normalize_path(path); - for (uint i = 0; i < learnSoundSkipList.length(); i++) { - string entry = normalize_path(learnSoundSkipList[i]); - if (entry.length() == 0) continue; +void configure_docs_browser_if_needed() { + if (docsBrowserConfigured) return; - bool isDir = is_directory_skip_entry(entry); - if (isDir) { - if (entry.substr(entry.length() - 1) != "/") entry += "/"; - if (normalizedPath.length() >= entry.length() && - normalizedPath.substr(0, entry.length()) == entry) { - return true; - } - continue; - } + docs_browser_set_speak_callback(learn_sounds_bridge_speak); + docs_browser_set_tick_callback(learn_sounds_bridge_tick); + docs_browser_set_docs_dir("files"); + docs_browser_set_menu_sound_dir("sounds/menu"); + docs_browser_set_wrap(true); + docs_browser_reset_default_extensions(); - if (normalizedPath == entry) return true; - - if (entry.find_first("/") < 0 && entry.find_first("\\") < 0) { - if (normalizedPath.length() >= entry.length() + 1 && - normalizedPath.substr(normalizedPath.length() - entry.length()) == entry) { - return true; - } - } - } - return false; -} - -string get_sound_description(const string&in path) { - string normalizedPath = normalize_path(path); - uint count = learnSoundDescriptionPaths.length(); - if (learnSoundDescriptionTexts.length() < count) count = learnSoundDescriptionTexts.length(); - for (uint i = 0; i < count; i++) { - if (normalize_path(learnSoundDescriptionPaths[i]) == normalizedPath) { - return learnSoundDescriptionTexts[i]; - } - } - return ""; -} - -void gather_sound_files(const string&in basePath, string[]@ outFiles) { - string[]@ files = find_files(basePath + "/*.ogg"); - if (@files !is null) { - for (uint i = 0; i < files.length(); i++) { - outFiles.insert_last(basePath + "/" + files[i]); - } - } - - string[]@ folders = find_directories(basePath + "/*"); - if (@folders !is null) { - for (uint i = 0; i < folders.length(); i++) { - gather_sound_files(basePath + "/" + folders[i], outFiles); - } - } -} - -bool sort_string_case_insensitive_sounds(const string &in a, const string &in b) { - return a.lower() < b.lower(); -} - -void append_unique_case_insensitive(string[]@ items, const string&in value) { - string lowerValue = value.lower(); - for (uint i = 0; i < items.length(); i++) { - if (items[i].lower() == lowerValue) return; - } - items.insert_last(value); -} - -string collapse_spaces(const string&in text) { - string result = text; - while (result.find_first(" ") > -1) { - result = result.replace(" ", " ", true); - } - return result; -} - -string format_doc_label(const string&in filename) { - string name = filename; - string lowerName = name.lower(); - if (lowerName.length() > 3 && lowerName.substr(lowerName.length() - 3) == ".md") { - name = name.substr(0, name.length() - 3); - } - name = name.replace("_", " ", true); - name = name.replace("-", " ", true); - name = collapse_spaces(name); - name = name.lower(); - name.trim_whitespace_this(); - if (name.length() == 0) return "Document"; - string first = name.substr(0, 1).upper(); - if (name.length() == 1) return first; - return first + name.substr(1); -} - -string get_sound_label_from_path(const string&in soundPath) { - string normalizedPath = normalize_path(soundPath); - int slashPos = normalizedPath.find_last_of("/"); - string name = (slashPos >= 0) ? normalizedPath.substr(slashPos + 1) : normalizedPath; - string lowerName = name.lower(); - if (lowerName.length() > 4 && lowerName.substr(lowerName.length() - 4) == ".ogg") { - name = name.substr(0, name.length() - 4); - } - name = name.replace("_", " ", true); - name = name.replace("-", " ", true); - name = collapse_spaces(name); - name = name.lower(); - name.trim_whitespace_this(); - if (name.length() == 0) return "Sound"; - return name; + docsBrowserConfigured = true; } void add_doc_entries(string[]@ labels, string[]@ paths, int[]@ types) { - if (!directory_exists("files")) return; - - string[] docFiles; - string[]@ mdFiles = find_files("files/*.md"); - if (@mdFiles !is null) { - for (uint i = 0; i < mdFiles.length(); i++) { - append_unique_case_insensitive(docFiles, mdFiles[i]); - } - } - string[]@ mdCapsFiles = find_files("files/*.MD"); - if (@mdCapsFiles !is null) { - for (uint i = 0; i < mdCapsFiles.length(); i++) { - append_unique_case_insensitive(docFiles, mdCapsFiles[i]); - } - } - - if (docFiles.length() > 1) { - docFiles.sort(sort_string_case_insensitive_sounds); - } - - for (uint i = 0; i < docFiles.length(); i++) { - string label = format_doc_label(docFiles[i]); - labels.insert_last(label); - paths.insert_last("files/" + docFiles[i]); - types.insert_last(1); - } + configure_docs_browser_if_needed(); + docs_browser_add_entries(labels, paths, types, 1); } -void add_sound_entries(string[]@ labels, string[]@ paths, int[]@ types) { - string[] soundFiles; - gather_sound_files("sounds", soundFiles); - if (soundFiles.length() > 1) { - soundFiles.sort(sort_string_case_insensitive_sounds); - } +void configure_learn_sounds_if_needed() { + if (learnSoundsConfigured) return; - for (uint i = 0; i < soundFiles.length(); i++) { - string soundPath = normalize_path(soundFiles[i]); - if (should_skip_sound_path(soundPath)) continue; + learn_sounds_set_speak_callback(learn_sounds_bridge_speak); + learn_sounds_set_tick_callback(learn_sounds_bridge_tick); + learn_sounds_set_root_dir("sounds"); + learn_sounds_set_menu_sound_dir("sounds/menu"); + learn_sounds_set_wrap(true); + learn_sounds_set_play_select_sound(false); - string label = get_sound_label_from_path(soundPath); - string description = get_sound_description(soundPath); - if (description.length() > 0) { - label += " - " + description; - } - labels.insert_last(label); - paths.insert_last(soundPath); - types.insert_last(0); - } + configure_project_learn_sounds(); + + learnSoundsConfigured = true; } void run_learn_sounds_menu() { - speak_with_history("Learn sounds.", true); - - string[] labels; - string[] entryPaths; - int[] entryTypes; // 0 = sound - - add_sound_entries(labels, entryPaths, entryTypes); - - if (labels.length() == 0) { - speak_with_history("No sounds available.", true); - return; - } - - int selection = 0; - speak_with_history(labels[selection], true); - - while (true) { - wait(5); - handle_global_volume_keys(); - - if (key_pressed(KEY_ESCAPE)) { - speak_with_history("Closed.", true); - return; - } - - if (key_pressed(KEY_DOWN)) { - play_menu_move_sound(); - selection++; - if (selection >= int(labels.length())) selection = 0; - speak_with_history(labels[selection], true); - } - - if (key_pressed(KEY_UP)) { - play_menu_move_sound(); - selection--; - if (selection < 0) selection = int(labels.length()) - 1; - speak_with_history(labels[selection], true); - } - - if (key_pressed(KEY_RETURN)) { - if (entryTypes[selection] == 1) { - text_reader_file(entryPaths[selection], labels[selection], true); - return; - } - string soundPath = entryPaths[selection]; - if (file_exists(soundPath)) { - safe_destroy_sound(learnSoundHandle); - learnSoundHandle = p.play_stationary(soundPath, false); - } else { - speak_with_history("Sound not found.", true); - } - } - } + configure_learn_sounds_if_needed(); + learn_sounds_run_menu(); } diff --git a/src/menus/menu_utils.nvgt b/src/menus/menu_utils.nvgt index 9c5189f..3410335 100644 --- a/src/menus/menu_utils.nvgt +++ b/src/menus/menu_utils.nvgt @@ -105,73 +105,13 @@ string get_base_fire_status() { } string get_menu_filter_letter() { - if (key_pressed(KEY_A)) return "a"; - if (key_pressed(KEY_B)) return "b"; - if (key_pressed(KEY_C)) return "c"; - if (key_pressed(KEY_D)) return "d"; - if (key_pressed(KEY_E)) return "e"; - if (key_pressed(KEY_F)) return "f"; - if (key_pressed(KEY_G)) return "g"; - if (key_pressed(KEY_H)) return "h"; - if (key_pressed(KEY_I)) return "i"; - if (key_pressed(KEY_J)) return "j"; - if (key_pressed(KEY_K)) return "k"; - if (key_pressed(KEY_L)) return "l"; - if (key_pressed(KEY_M)) return "m"; - if (key_pressed(KEY_N)) return "n"; - if (key_pressed(KEY_O)) return "o"; - if (key_pressed(KEY_P)) return "p"; - if (key_pressed(KEY_Q)) return "q"; - if (key_pressed(KEY_R)) return "r"; - if (key_pressed(KEY_S)) return "s"; - if (key_pressed(KEY_T)) return "t"; - if (key_pressed(KEY_U)) return "u"; - if (key_pressed(KEY_V)) return "v"; - if (key_pressed(KEY_W)) return "w"; - if (key_pressed(KEY_X)) return "x"; - if (key_pressed(KEY_Y)) return "y"; - if (key_pressed(KEY_Z)) return "z"; - return ""; + return menu_get_filter_letter(); } void apply_menu_filter(const string &in filter_text, const string[]@ options, int[]@ filtered_indices, string[]@ filtered_options) { - filtered_indices.resize(0); - filtered_options.resize(0); - string filter_lower = filter_text.lower(); - - for (uint i = 0; i < options.length(); i++) { - if (filter_lower.length() == 0) { - filtered_indices.insert_last(i); - filtered_options.insert_last(options[i]); - continue; - } - - string option_lower = options[i].lower(); - if (option_lower.length() >= filter_lower.length() && option_lower.substr(0, filter_lower.length()) == filter_lower) { - filtered_indices.insert_last(i); - filtered_options.insert_last(options[i]); - } - } + menu_apply_prefix_filter(filter_text, options, filtered_indices, filtered_options); } bool update_menu_filter_state(string &inout filter_text, const string[]@ options, int[]@ filtered_indices, string[]@ filtered_options, int &inout selection) { - bool filter_changed = false; - - if (key_pressed(KEY_BACK) && filter_text.length() > 0) { - filter_text = filter_text.substr(0, filter_text.length() - 1); - filter_changed = true; - } - - string filter_letter = get_menu_filter_letter(); - if (filter_letter != "") { - filter_text += filter_letter; - filter_changed = true; - } - - if (filter_changed) { - apply_menu_filter(filter_text, options, filtered_indices, filtered_options); - if (selection >= int(filtered_options.length())) selection = 0; - } - - return filter_changed; + return menu_update_prefix_filter(filter_text, options, filtered_indices, filtered_options, selection); } diff --git a/src/notify.nvgt b/src/notify.nvgt deleted file mode 100644 index 88a4a12..0000000 --- a/src/notify.nvgt +++ /dev/null @@ -1,108 +0,0 @@ -// Notification System -string[] notification_history; -const int MAX_NOTIFICATIONS = 10; -const int NOTIFICATION_DELAY = 3000; // 3 seconds between notifications -int current_notification_index = -1; -string[] notification_queue; -timer notification_timer; -bool notification_active = false; -int notification_sound_handle = -1; - -void notify(string message) { - // Add to queue (don't play yet) - notification_queue.insert_last(message); - - // Add to history immediately so it appears in history even if queued - notification_history.insert_last(message); - - // Keep only last 10 notifications - if (notification_history.length() > MAX_NOTIFICATIONS) { - notification_history.remove_at(0); - } - - // Reset index to most recent - current_notification_index = notification_history.length() - 1; -} - -void update_notifications() { - if (notification_queue.length() == 0) { - if (notification_active && notification_timer.elapsed >= NOTIFICATION_DELAY) { - notification_active = false; - } - notification_sound_handle = -1; - return; - } - - // If a notification is currently active, wait for delay - if (notification_active && notification_timer.elapsed < NOTIFICATION_DELAY) { - return; - } - - // If we're waiting for the notification sound to finish playing - if (notification_sound_handle != -1) { - // Check if sound is still playing - if (p.sound_is_active(notification_sound_handle)) { - return; // Still playing, wait - } - // Sound finished, now speak - speak_with_history(notification_queue[0], true); - notification_queue.remove_at(0); - notification_sound_handle = -1; - - // Start timer for next notification - notification_timer.restart(); - notification_active = true; - return; - } - - // Play next notification sound (don't speak yet) - notification_sound_handle = p.play_stationary("sounds/notify.ogg", false); -} - -void check_notification_keys() { - // [ for previous notification (older) with position - if (key_pressed(KEY_LEFTBRACKET)) { - if (notification_history.length() == 0) { - speak_with_history("No notifications.", true); - return; - } - - current_notification_index--; - if (current_notification_index < 0) { - current_notification_index = 0; - speak_with_history("Oldest notification. " + notification_history[current_notification_index], true); - return; - } - int position = current_notification_index + 1; - speak_with_history(notification_history[current_notification_index] + " " + position + " of " + notification_history.length(), true); - return; - } - - // ] for next notification (newer) with position - if (key_pressed(KEY_RIGHTBRACKET)) { - if (notification_history.length() == 0) { - speak_with_history("No notifications.", true); - return; - } - - current_notification_index++; - if (current_notification_index >= notification_history.length()) { - current_notification_index = notification_history.length() - 1; - speak_with_history("Newest notification. " + notification_history[current_notification_index], true); - return; - } - int position = current_notification_index + 1; - speak_with_history(notification_history[current_notification_index] + " " + position + " of " + notification_history.length(), true); - return; - } - - // \ for most recent notification (without position) - if (key_pressed(KEY_BACKSLASH)) { - if (notification_history.length() == 0) { - speak_with_history("No notifications.", true); - return; - } - current_notification_index = notification_history.length() - 1; - speak_with_history(notification_history[current_notification_index], true); - } -} diff --git a/src/notify_compat.nvgt b/src/notify_compat.nvgt new file mode 100644 index 0000000..c61ed71 --- /dev/null +++ b/src/notify_compat.nvgt @@ -0,0 +1,36 @@ +#include "libstorm-nvgt/notifications.nvgt" + +const int NOTIFICATION_HISTORY_LIMIT = 10; +const int NOTIFICATION_DELAY_MS = 3000; +const string NOTIFICATION_SOUND_PATH = "sounds/notify"; + +bool notificationsCompatConfigured = false; + +void notify_compat_speak(const string &in message, bool interrupt) { + speak_with_history(message, interrupt); +} + +void configure_notification_compat_if_needed() { + if (notificationsCompatConfigured) return; + + notifications_set_max_history(NOTIFICATION_HISTORY_LIMIT); + notifications_set_delay_ms(NOTIFICATION_DELAY_MS); + notifications_set_sound_path(NOTIFICATION_SOUND_PATH); + notifications_set_speak_callback(notify_compat_speak); + notificationsCompatConfigured = true; +} + +void notify(string message) { + configure_notification_compat_if_needed(); + notifications_enqueue(message); +} + +void update_notifications() { + configure_notification_compat_if_needed(); + notifications_update(); +} + +void check_notification_keys() { + configure_notification_compat_if_needed(); + notifications_check_keys(); +} diff --git a/src/speech_history.nvgt b/src/speech_history.nvgt deleted file mode 100644 index c66fbba..0000000 --- a/src/speech_history.nvgt +++ /dev/null @@ -1,72 +0,0 @@ -// Speech History System -// Tracks last 10 unique screen reader announcements for navigation with comma/period - -string[] speech_history; -const int MAX_SPEECH_HISTORY = 10; -int current_speech_index = -1; - -// Main speak function - wrapper around screen_reader_speak with history tracking -void speak_with_history(string message, bool interrupt) { - // Only add to history if not already present (no duplicates) - bool already_exists = false; - for (uint i = 0; i < speech_history.length(); i++) { - if (speech_history[i] == message) { - already_exists = true; - break; - } - } - - // Add to history if not a duplicate - if (!already_exists) { - speech_history.insert_last(message); - - // Keep only last 10 messages - if (speech_history.length() > MAX_SPEECH_HISTORY) { - speech_history.remove_at(0); - } - - // Reset index to most recent - current_speech_index = speech_history.length() - 1; - } - - // Call the built-in screen_reader_speak function - screen_reader_speak(message, interrupt); -} - -void check_speech_history_keys() { - // , (comma) for previous speech message (older) - if (key_pressed(KEY_COMMA)) { - if (speech_history.length() == 0) { - screen_reader_speak("No speech history.", true); - return; - } - - current_speech_index--; - if (current_speech_index < 0) { - current_speech_index = 0; - screen_reader_speak("Oldest message. " + speech_history[current_speech_index], true); - return; - } - int position = current_speech_index + 1; - screen_reader_speak(speech_history[current_speech_index] + " " + position + " of " + speech_history.length(), true); - return; - } - - // . (period) for next speech message (newer) - if (key_pressed(KEY_PERIOD)) { - if (speech_history.length() == 0) { - screen_reader_speak("No speech history.", true); - return; - } - - current_speech_index++; - if (current_speech_index >= speech_history.length()) { - current_speech_index = speech_history.length() - 1; - screen_reader_speak("Newest message. " + speech_history[current_speech_index], true); - return; - } - int position = current_speech_index + 1; - screen_reader_speak(speech_history[current_speech_index] + " " + position + " of " + speech_history.length(), true); - return; - } -} diff --git a/src/terrain_lookup.nvgt b/src/terrain_lookup.nvgt new file mode 100644 index 0000000..86d1e56 --- /dev/null +++ b/src/terrain_lookup.nvgt @@ -0,0 +1,26 @@ +// Game-specific terrain lookup used by movement, menus, and reports. +string get_terrain_at_position(int posX) { + if (is_position_in_water(posX) || is_mountain_stream_at(posX)) { + return "water"; + } + + MountainRange@ mountain = get_mountain_at(posX); + if (mountain !is null) { + return mountain.get_terrain_at(posX); + } + + if (posX <= BASE_END) return "wood"; + if (posX <= GRASS_END) return "grass"; + if (posX <= GRAVEL_END) return "gravel"; + + int index = posX - expanded_area_start; + if (index >= 0 && index < int(expanded_terrain_types.length())) { + string terrain = expanded_terrain_types[index]; + if (terrain.find("mountain:") == 0) { + terrain = terrain.substr(9); + } + return terrain; + } + + return "unknown"; +} diff --git a/src/text_reader.nvgt b/src/text_reader.nvgt deleted file mode 100644 index 8a69db1..0000000 --- a/src/text_reader.nvgt +++ /dev/null @@ -1,115 +0,0 @@ -// text_reader.nvgt - Simple text document reader/editor using NVGT's audio_form -// Provides accessible navigation through text documents with optional editing - -#include "form.nvgt" - -// Opens a text reader/editor window with string content -// Parameters: -// content: The text content to display (can be file contents or direct string) -// title: Window title (default: "Text Reader") -// readonly: If true, text cannot be edited (default: true) -// Returns: The modified text if readonly=false and user presses OK, empty string if canceled or readonly=true -string text_reader(string content, string title = "Text Reader", bool readonly = true) { - audio_form f; - f.create_window(title, false, true); - - // Create the multiline input box - // In readonly mode, it's still navigable with arrows/home/end/etc - // In edit mode, user can modify text - int text_control = f.create_input_box( - (readonly ? "Document (read only)" : "Document (editable)"), - content, - "", // no password mask - 0, // no max length - readonly, - true, // multiline = true - true // multiline_enter = true (Ctrl+Enter for newlines) - ); - - int ok_button = -1; - int close_button = -1; - - if (readonly) { - // In readonly mode, just have a Close button - close_button = f.create_button("&Close", true, true); - } else { - // In edit mode, have OK and Cancel buttons - ok_button = f.create_button("&OK", true); - close_button = f.create_button("&Cancel", false, true); - } - - f.focus(text_control); - - // Monitor loop - while (true) { - f.monitor(); - wait(5); - handle_global_volume_keys(); - - // Check if user pressed OK (edit mode only) - if (!readonly && ok_button != -1 && f.is_pressed(ok_button)) { - return f.get_text(text_control); - } - - // Check if user pressed Close/Cancel - if (close_button != -1 && f.is_pressed(close_button)) { - return ""; - } - - // Check for Escape key as alternative close method - if (key_pressed(KEY_ESCAPE)) { - return ""; - } - } - - return ""; -} - -// Opens a text reader/editor window with an array of lines -// Parameters: -// lines: Array of text lines to display -// title: Window title (default: "Text Reader") -// readonly: If true, text cannot be edited (default: true) -// Returns: The modified text if readonly=false and user presses OK, empty string if canceled or readonly=true -string text_reader_lines(string[] lines, string title = "Text Reader", bool readonly = true) { - string content = join(lines, "\n"); - return text_reader(content, title, readonly); -} - -// Convenience function to read a file and display it in the text reader -// Parameters: -// file_path: Path to the file to read -// title: Window title (default: uses file_path as title) -// readonly: If true, text cannot be edited (default: true) -// Returns: The modified text if readonly=false and user saves, empty string otherwise -string text_reader_file(string file_path, string title = "", bool readonly = true) { - file f; - if (!f.open(file_path, "rb")) { - screen_reader_speak("Failed to open file: " + file_path, true); - return ""; - } - - string content = f.read(); - f.close(); - - // Use file_path as title if no custom title provided - if (title == "") { - title = file_path; - } - - string result = text_reader(content, title, readonly); - - // If in edit mode and user pressed OK, save the file - if (!readonly && result != "") { - if (f.open(file_path, "wb")) { - f.write(result); - f.close(); - screen_reader_speak("File saved successfully", true); - return result; - } else { - screen_reader_speak("Failed to save file", true); - } - } - - return result; -} diff --git a/src/text_reader_aliases.nvgt b/src/text_reader_aliases.nvgt new file mode 100644 index 0000000..13340be --- /dev/null +++ b/src/text_reader_aliases.nvgt @@ -0,0 +1,13 @@ +// Compatibility aliases that keep legacy text_reader* callsites unchanged. +// file_viewer* is provided by libstorm-nvgt/docs_browser.nvgt. +string text_reader(string content, string title = "Text Reader", bool readOnly = true) { + return file_viewer(content, title, readOnly); +} + +string text_reader_lines(string[] lines, string title = "Text Reader", bool readOnly = true) { + return file_viewer_lines(lines, title, readOnly); +} + +string text_reader_file(string filePath, string title = "", bool readOnly = true) { + return file_viewer_file(filePath, title, readOnly); +} diff --git a/src/ui.nvgt b/src/ui.nvgt deleted file mode 100644 index 9906f67..0000000 --- a/src/ui.nvgt +++ /dev/null @@ -1,53 +0,0 @@ -// UI helpers -string get_terrain_at_position(int pos_x) { - // Check for water first (streams in expanded areas or mountain streams) - if (is_position_in_water(pos_x) || is_mountain_stream_at(pos_x)) { - return "water"; - } - - // Check mountain terrain - MountainRange@ mountain = get_mountain_at(pos_x); - if (mountain !is null) { - return mountain.get_terrain_at(pos_x); - } - - // Base area - if (pos_x <= BASE_END) return "wood"; - - // Grass area - if (pos_x <= GRASS_END) return "grass"; - - // Gravel area - if (pos_x <= GRAVEL_END) return "gravel"; - - // Expanded areas - int index = pos_x - expanded_area_start; - if (index >= 0 && index < int(expanded_terrain_types.length())) { - string terrain = expanded_terrain_types[index]; - // Handle "mountain:terrain" format from older saves - if (terrain.find("mountain:") == 0) { - terrain = terrain.substr(9); - } - return terrain; - } - - return "unknown"; -} - -string ui_input_box(const string title, const string prompt, const string default_value) { - string result = virtual_input_box(prompt, prompt, default_value); - show_window("Draugnorak"); - return result; -} - -int ui_question(const string title, const string prompt) { - // Put the prompt in both title (for screen reader) and message (for dialog to work) - int result = virtual_question(prompt, prompt); - show_window("Draugnorak"); - return result; -} - -void ui_info_box(const string title, const string heading, const string message) { - virtual_info_box(title, heading, message); - show_window("Draugnorak"); -}