Learn sounds menu added. May need more work.

This commit is contained in:
Storm Dragon
2026-02-08 15:26:21 -05:00
parent 942cbfd011
commit 9ad65a8ac5
4 changed files with 273 additions and 17 deletions

View File

@@ -38,6 +38,7 @@ sound_pool p(300);
#include "src/notify.nvgt"
#include "src/speech_history.nvgt"
#include "src/text_reader.nvgt"
#include "src/learn_sounds.nvgt"
#include "src/bosses/adventure_system.nvgt"
int run_main_menu() {
@@ -45,7 +46,8 @@ int run_main_menu() {
int selection = 0;
string load_label = has_save_game() ? "Load Game" : "Load Game (no saves found)";
string[] options = {"New Game", load_label, "Exit"};
string[] options = {"New Game", load_label, "Learn Sounds", "Exit"};
int exit_index = options.length() - 1;
speak_with_history(options[selection], true);
@@ -72,11 +74,11 @@ int run_main_menu() {
}
if (key_pressed(KEY_ESCAPE)) {
return 2;
return exit_index;
}
}
return 2;
return exit_index;
}
void run_game()
@@ -119,6 +121,9 @@ void run_game()
if (message == "") message = "Unable to load save.";
ui_info_box("Draugnorak", "Load Game", message);
}
} else if (selection == 2) {
run_learn_sounds_menu();
continue;
} else {
exit();
}

Binary file not shown.

View File

@@ -1111,8 +1111,7 @@ void start_rope_climb(bool climbing_up, int target_x, int target_elevation) {
string direction = rope_climb_up ? "up" : "down";
speak_with_history("Climbing " + direction + ". " + distance + " feet.", true);
// Start looping rope climbing sound
rope_climb_sound_handle = p.play_stationary("sounds/actions/climb_rope.ogg", true);
rope_climb_sound_handle = -1;
}
void update_rope_climbing() {
@@ -1132,6 +1131,7 @@ void update_rope_climbing() {
// Climbing up
if (y < rope_climb_target_y) {
y++;
p.play_stationary("sounds/actions/climb_rope.ogg", false);
// Check if we've reached the target
if (y >= rope_climb_target_y) {
@@ -1142,6 +1142,7 @@ void update_rope_climbing() {
// Climbing down
if (y > rope_climb_target_y) {
y--;
p.play_stationary("sounds/actions/climb_rope.ogg", false);
// Check if we've reached the target
if (y <= rope_climb_target_y) {
@@ -1157,11 +1158,7 @@ void complete_rope_climb() {
x = rope_climb_target_x;
y = rope_climb_target_y;
// Stop looping rope climbing sound
if (rope_climb_sound_handle != -1) {
p.destroy_sound(rope_climb_sound_handle);
rope_climb_sound_handle = -1;
}
rope_climb_sound_handle = -1;
// Play footstep for new terrain
play_footstep(x, BASE_END, GRASS_END);
@@ -1173,11 +1170,7 @@ void check_rope_climb_fall() {
if (!rope_climbing) return;
if (key_down(KEY_LEFT) || key_down(KEY_RIGHT)) {
// Stop rope climbing sound
if (rope_climb_sound_handle != -1) {
p.destroy_sound(rope_climb_sound_handle);
rope_climb_sound_handle = -1;
}
rope_climb_sound_handle = -1;
int currentElevation = get_mountain_elevation_at(x);
int targetElevation = get_mountain_elevation_at(rope_climb_target_x);

258
src/learn_sounds.nvgt Normal file
View File

@@ -0,0 +1,258 @@
// Learn sounds menu
// 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/nature/"
};
// 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);
}
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);
}
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;
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;
}
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();
}
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;
}
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++) {
docFiles.insert_last(mdFiles[i]);
}
}
string[]@ mdCapsFiles = find_files("files/*.MD");
if (@mdCapsFiles !is null) {
for (uint i = 0; i < mdCapsFiles.length(); i++) {
docFiles.insert_last(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);
}
}
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);
}
for (uint i = 0; i < soundFiles.length(); i++) {
string soundPath = normalize_path(soundFiles[i]);
if (should_skip_sound_path(soundPath)) continue;
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);
}
}
void run_learn_sounds_menu() {
speak_with_history("Learn sounds.", true);
string[] labels;
string[] entryPaths;
int[] entryTypes; // 0 = sound, 1 = document
add_doc_entries(labels, entryPaths, entryTypes);
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);
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);
}
}
}
}