Implement i18n audit/localization cleanup and sync libstorm submodule

This commit is contained in:
Storm Dragon
2026-02-24 23:14:40 -05:00
parent b77b895685
commit c5d26d5edd
68 changed files with 9169 additions and 853 deletions
+16 -36
View File
@@ -1,3 +1,5 @@
#include "libstorm-nvgt/volume_controls.nvgt"
bool audio_asset_exists(const string& in soundFile) {
if (file_exists(soundFile)) {
return true;
@@ -190,51 +192,29 @@ void play_player_damage_sound() {
// Safe sound handle cleanup - checks if handle is valid and sound is active before destroying
void safe_destroy_sound(int& inout handle) {
if (handle != -1) {
if (p.sound_is_active(handle)) {
p.destroy_sound(handle);
}
handle = -1;
}
safe_destroy_sound_in_pool(p, handle);
}
float master_volume_db = MASTER_VOLUME_MAX_DB;
void apply_master_volume_from_controls(float volumeDb) {
master_volume_db = volumeDb;
sound_master_volume = volumeDb;
}
void init_master_volume() {
master_volume_db = MASTER_VOLUME_MAX_DB;
sound_master_volume = master_volume_db;
volume_controls_set_apply_callback(apply_master_volume_from_controls);
volume_controls_configure(MASTER_VOLUME_MIN_DB, MASTER_VOLUME_MAX_DB, MASTER_VOLUME_STEP_DB, MASTER_VOLUME_MAX_DB);
volume_controls_set_current_db(MASTER_VOLUME_MAX_DB, false);
master_volume_db = volume_controls_get_current_db();
}
void set_game_master_volume_db(float volume_db, bool announce = true) {
float clamped = volume_db;
if (clamped > MASTER_VOLUME_MAX_DB)
clamped = MASTER_VOLUME_MAX_DB;
if (clamped < MASTER_VOLUME_MIN_DB)
clamped = MASTER_VOLUME_MIN_DB;
if (clamped == master_volume_db)
return;
master_volume_db = clamped;
sound_master_volume = master_volume_db;
if (announce) {
float range = MASTER_VOLUME_MAX_DB - MASTER_VOLUME_MIN_DB;
float normalized = (master_volume_db - MASTER_VOLUME_MIN_DB) / range;
int volumePercent = int(normalized * 100.0f + 0.5f);
if (volumePercent < 0)
volumePercent = 0;
if (volumePercent > 100)
volumePercent = 100;
screen_reader_speak("Volume " + volumePercent + ".", true);
}
volume_controls_set_current_db(volume_db, announce);
master_volume_db = volume_controls_get_current_db();
}
void handle_global_volume_keys() {
if (key_pressed(KEY_PAGEDOWN)) {
set_game_master_volume_db(master_volume_db - MASTER_VOLUME_STEP_DB);
}
if (key_pressed(KEY_PAGEUP)) {
set_game_master_volume_db(master_volume_db + MASTER_VOLUME_STEP_DB);
}
volume_controls_handle_keys(KEY_PAGEDOWN, KEY_PAGEUP, true);
master_volume_db = volume_controls_get_current_db();
}
+2
View File
@@ -40,6 +40,8 @@ void run_adventure_menu(int player_x) {
adventure_ids.insert_last(ADVENTURE_BANDIT_HIDEOUT);
}
i18n_translate_string_array_in_place(options);
if (options.length() == 0) {
speak_with_history("No adventures found in this area.", true);
return;
+8 -6
View File
@@ -302,10 +302,11 @@ void run_bandit_hideout_adventure() {
intro.insert_last("The base lies far to the east, guarded by a barricade.");
intro.insert_last("");
intro.insert_last("Objective:");
intro.insert_last(" - Reach the base and destroy the barricade");
intro.insert_last("- Reach the base and destroy the barricade");
intro.insert_last("");
intro.insert_last("Bandits will, of course, not take this lying down.");
text_reader_lines(intro, "Adventure", true);
i18n_translate_string_array_in_place(intro);
text_reader_lines(intro, i18n_text("Adventure"), true);
begin_pet_adventure(@pet_find_hideout_target, @pet_damage_hideout_target, hideoutPlayerX);
@@ -522,7 +523,7 @@ void update_hideout_search() {
void perform_hideout_search() {
if (random(1, 100) <= 10) {
speak_with_history("Found nothing.", true);
speak_with_history(tr("system.search.found_nothing"), true);
return;
}
@@ -533,7 +534,7 @@ void perform_hideout_search() {
}
}
speak_with_history("Found nothing.", true);
speak_with_history(tr("system.search.found_nothing"), true);
}
int get_hideout_melee_weapon_type() {
@@ -763,7 +764,7 @@ int add_hideout_storage_item(int itemType, int amount) {
if (itemType == ITEM_SMALL_GAME) {
for (int i = 0; i < addedAmount; i++) {
storage_small_game_types.insert_last("small game");
storage_small_game_types.insert_last(i18n_text("small game"));
}
}
if (itemType == ITEM_FISH) {
@@ -856,6 +857,7 @@ void give_bandit_hideout_rewards() {
}
}
text_reader_lines(rewards, "Bandit's Hideout", true);
i18n_translate_string_array_in_place(rewards);
text_reader_lines(rewards, i18n_text("Bandit's Hideout"), true);
attempt_pet_offer_from_adventure();
}
+9 -7
View File
@@ -110,14 +110,15 @@ void run_unicorn_adventure() {
intro.insert_last("that can be destroyed with an axe.");
intro.insert_last("");
intro.insert_last("Strategy:");
intro.insert_last(" - Use your axe to destroy a bridge support");
intro.insert_last(" - Lure the Unicorn onto the bridge");
intro.insert_last(" - Or fight the Unicorn directly (it has massive health)");
intro.insert_last(" - Jump (UP arrow) to avoid being trampled");
intro.insert_last(" - When the bridge collapses with the Unicorn on it, you win!");
intro.insert_last("- Use your axe to destroy a bridge support");
intro.insert_last("- Lure the Unicorn onto the bridge");
intro.insert_last("- Or fight the Unicorn directly (it has massive health)");
intro.insert_last("- Jump (UP arrow) to avoid being trampled");
intro.insert_last("- When the bridge collapses with the Unicorn on it, you win!");
intro.insert_last("");
intro.insert_last("Controls: LEFT/RIGHT to move, UP to jump, CTRL to attack, ESC to flee");
text_reader_lines(intro, "Adventure", true);
i18n_translate_string_array_in_place(intro);
text_reader_lines(intro, i18n_text("Adventure"), true);
begin_pet_adventure(@pet_find_unicorn_target, @pet_damage_unicorn_target, player_arena_x);
@@ -723,6 +724,7 @@ void give_unicorn_rewards() {
append_adventure_completion_rewards(ADVENTURE_UNICORN, rewards);
// Display rewards in text reader
text_reader_lines(rewards, "Unicorn Victory", true);
i18n_translate_string_array_in_place(rewards);
text_reader_lines(rewards, i18n_text("Unicorn Victory"), true);
attempt_pet_offer_from_adventure();
}
+21 -13
View File
@@ -1,4 +1,11 @@
// Crafting barricade reinforcements
string get_barricade_option_text(const string& in key, int cost, int health) {
dictionary args;
args.set("cost", cost);
args.set("health", health);
return trf(key, args);
}
void run_barricade_menu() {
if (barricade_health >= BARRICADE_MAX_HEALTH) {
speak_with_history("Barricade is already at full health.", true);
@@ -10,23 +17,24 @@ void run_barricade_menu() {
int[] action_types; // 0 = sticks, 1 = vines, 2 = log, 3 = stones
if (get_personal_count(ITEM_STICKS) >= BARRICADE_STICK_COST) {
options.insert_last("Reinforce with sticks (" + BARRICADE_STICK_COST + " sticks, +" + BARRICADE_STICK_HEALTH +
" health)");
options.insert_last(get_barricade_option_text("system.crafting.barricade.option.reinforce_sticks",
BARRICADE_STICK_COST, BARRICADE_STICK_HEALTH));
action_types.insert_last(0);
}
if (get_personal_count(ITEM_VINES) >= BARRICADE_VINE_COST) {
options.insert_last("Reinforce with vines (" + BARRICADE_VINE_COST + " vines, +" + BARRICADE_VINE_HEALTH +
" health)");
options.insert_last(get_barricade_option_text("system.crafting.barricade.option.reinforce_vines",
BARRICADE_VINE_COST, BARRICADE_VINE_HEALTH));
action_types.insert_last(1);
}
if (get_personal_count(ITEM_LOGS) >= BARRICADE_LOG_COST) {
options.insert_last("Reinforce with log (" + BARRICADE_LOG_COST + " log, +" + BARRICADE_LOG_HEALTH +
" health)");
options.insert_last(
get_barricade_option_text("system.crafting.barricade.option.reinforce_log", BARRICADE_LOG_COST,
BARRICADE_LOG_HEALTH));
action_types.insert_last(2);
}
if (get_personal_count(ITEM_STONES) >= BARRICADE_STONE_COST) {
options.insert_last("Reinforce with stones (" + BARRICADE_STONE_COST + " stones, +" + BARRICADE_STONE_HEALTH +
" health)");
options.insert_last(get_barricade_option_text("system.crafting.barricade.option.reinforce_stones",
BARRICADE_STONE_COST, BARRICADE_STONE_HEALTH));
action_types.insert_last(3);
}
@@ -34,7 +42,7 @@ void run_barricade_menu() {
speak_with_history("No materials to reinforce the barricade.", true);
return;
}
speak_with_history("Barricade. " + options[selection], true);
speak_menu_prompt("system.crafting.barricade.prompt", options[selection]);
while (true) {
wait(5);
@@ -171,7 +179,7 @@ void reinforce_barricade_max_with_sticks() {
}
if (get_personal_count(ITEM_STICKS) < BARRICADE_STICK_COST) {
speak_with_history("Missing: " + BARRICADE_STICK_COST + " sticks", true);
speak_crafting_missing(BARRICADE_STICK_COST + " sticks");
return;
}
@@ -200,7 +208,7 @@ void reinforce_barricade_max_with_vines() {
}
if (get_personal_count(ITEM_VINES) < BARRICADE_VINE_COST) {
speak_with_history("Missing: " + BARRICADE_VINE_COST + " vines", true);
speak_crafting_missing(BARRICADE_VINE_COST + " vines");
return;
}
@@ -229,7 +237,7 @@ void reinforce_barricade_max_with_log() {
}
if (get_personal_count(ITEM_LOGS) < BARRICADE_LOG_COST) {
speak_with_history("Missing: " + BARRICADE_LOG_COST + " log", true);
speak_crafting_missing(BARRICADE_LOG_COST + " log");
return;
}
@@ -257,7 +265,7 @@ void reinforce_barricade_max_with_stones() {
}
if (get_personal_count(ITEM_STONES) < BARRICADE_STONE_COST) {
speak_with_history("Missing: " + BARRICADE_STONE_COST + " stones", true);
speak_crafting_missing(BARRICADE_STONE_COST + " stones");
return;
}
+16 -16
View File
@@ -55,44 +55,44 @@ void run_buildings_menu() {
// Firepit and Fire are always available outside base,
// but only one of each can exist in base.
if (x > BASE_END || !base_has_firepit) {
options.insert_last("Firepit (9 Stones)");
options.insert_last(tr("system.crafting.buildings.option.firepit"));
building_types.insert_last(0);
}
if (x > BASE_END || !base_has_fire) {
options.insert_last("Fire (2 Sticks, 1 Log) [Requires Firepit]");
options.insert_last(tr("system.crafting.buildings.option.fire"));
building_types.insert_last(1);
}
// Only show herb garden if not already built in base
if (get_herb_garden_at_base() == null) {
options.insert_last("Herb Garden (9 Stones, 3 Vines, 2 Logs) [Base Only]");
options.insert_last(tr("system.crafting.buildings.option.herb_garden"));
building_types.insert_last(2);
}
// Storage upgrades
if (storage_level < STORAGE_LEVEL_UPGRADE_1) {
options.insert_last("Upgrade Storage (6 Logs, 9 Stones, 8 Vines) [Base Only, 50 each]");
options.insert_last(tr("system.crafting.buildings.option.storage_upgrade_1"));
building_types.insert_last(3);
} else if (storage_level == STORAGE_LEVEL_UPGRADE_1) {
options.insert_last("Upgrade Storage (12 Logs, 18 Stones, 16 Vines) [Base Only, 100 each]");
options.insert_last(tr("system.crafting.buildings.option.storage_upgrade_2"));
building_types.insert_last(3);
}
// Only show pasture if not built and storage is upgraded
if (world_pastures.length() == 0 && storage_level >= STORAGE_LEVEL_UPGRADE_1) {
options.insert_last("Pasture (8 Logs, 18 Ropes) [Base Only, Requires Storage Upgrade]");
options.insert_last(tr("system.crafting.buildings.option.pasture"));
building_types.insert_last(4);
}
// Only show stable if not built and storage is upgraded
if (world_stables.length() == 0 && storage_level >= STORAGE_LEVEL_UPGRADE_1) {
options.insert_last("Stable (10 Logs, 15 Stones, 10 Vines) [Base Only, Requires Storage Upgrade]");
options.insert_last(tr("system.crafting.buildings.option.stable"));
building_types.insert_last(5);
}
// Only show altar if not built
if (world_altars.length() == 0) {
options.insert_last("Altar (9 Stones, 3 Sticks) [Base Only]");
options.insert_last(tr("system.crafting.buildings.option.altar"));
building_types.insert_last(6);
}
@@ -100,7 +100,7 @@ void run_buildings_menu() {
speak_with_history("No buildings available.", true);
return;
}
speak_with_history("Buildings. " + options[selection], true);
speak_menu_prompt("system.crafting.buildings.prompt", options[selection]);
while (true) {
wait(5);
@@ -177,7 +177,7 @@ void craft_firepit() {
add_world_firepit(x);
speak_with_history("Firepit built here.", true);
} else {
speak_with_history("Missing: " + missing, true);
speak_crafting_missing(missing);
}
}
@@ -213,7 +213,7 @@ void craft_campfire() {
add_world_fire(firepit.position);
speak_with_history("Fire built at firepit.", true);
} else {
speak_with_history("Missing: " + missing, true);
speak_crafting_missing(missing);
}
}
@@ -246,7 +246,7 @@ void craft_herb_garden() {
add_world_herb_garden(x);
speak_with_history("Herb garden built. The base now heals faster.", true);
} else {
speak_with_history("Missing: " + missing, true);
speak_crafting_missing(missing);
}
}
@@ -292,7 +292,7 @@ void craft_storage() {
storage_level = targetLevel;
speak_with_history("Storage upgraded. Capacity is now " + newCapacity + " per item.", true);
} else {
speak_with_history("Missing: " + missing, true);
speak_crafting_missing(missing);
}
}
@@ -322,7 +322,7 @@ void craft_pasture() {
add_world_pasture(x);
speak_with_history("Pasture built.", true);
} else {
speak_with_history("Missing: " + missing, true);
speak_crafting_missing(missing);
}
}
@@ -355,7 +355,7 @@ void craft_stable() {
add_world_stable(x);
speak_with_history("Stable built.", true);
} else {
speak_with_history("Missing: " + missing, true);
speak_crafting_missing(missing);
}
}
@@ -381,6 +381,6 @@ void craft_altar() {
add_world_altar(x);
speak_with_history("Altar built.", true);
} else {
speak_with_history("Missing: " + missing, true);
speak_crafting_missing(missing);
}
}
+36 -36
View File
@@ -44,14 +44,14 @@ void consume_pouches(int amount) {
void run_clothing_menu() {
int selection = 0;
string[] options = {"Skin Hat (1 Skin, 1 Vine)",
"Skin Gloves (1 Skin, 1 Vine)",
"Skin Pants (6 Skins, 3 Vines)",
"Skin Tunic (4 Skins, 2 Vines)",
"Moccasins (2 Skins, 1 Vine)",
"Skin Pouch (2 Skins, 1 Vine)",
"Backpack (11 Skins, 5 Vines, 4 Skin Pouches)"};
speak_with_history("Clothing. " + options[selection], true);
string[] options = {tr("system.crafting.clothing.option.skin_hat"),
tr("system.crafting.clothing.option.skin_gloves"),
tr("system.crafting.clothing.option.skin_pants"),
tr("system.crafting.clothing.option.skin_tunic"),
tr("system.crafting.clothing.option.moccasins"),
tr("system.crafting.clothing.option.skin_pouch"),
tr("system.crafting.clothing.option.backpack")};
speak_menu_prompt("system.crafting.clothing.prompt", options[selection]);
while (true) {
wait(5);
@@ -128,7 +128,7 @@ void craft_skin_hat() {
if (missing == "") {
if (get_personal_count(ITEM_SKIN_HATS) >= get_personal_stack_limit()) {
speak_with_history("You can't carry any more skin hats.", true);
speak_cant_carry_any_more_item(ITEM_SKIN_HATS);
return;
}
simulate_crafting(2);
@@ -137,13 +137,13 @@ void craft_skin_hat() {
add_personal_count(ITEM_SKIN_HATS, 1);
speak_with_history("Crafted a Skin Hat.", true);
} else {
speak_with_history("Missing: " + missing, true);
speak_crafting_missing(missing);
}
}
void craft_skin_hat_max() {
if (get_personal_count(ITEM_SKIN_HATS) >= get_personal_stack_limit()) {
speak_with_history("You can't carry any more skin hats.", true);
speak_cant_carry_any_more_item(ITEM_SKIN_HATS);
return;
}
@@ -163,7 +163,7 @@ void craft_skin_hat_max() {
missing += "1 skin ";
if (get_personal_count(ITEM_VINES) < 1)
missing += "1 vine ";
speak_with_history("Missing: " + missing, true);
speak_crafting_missing(missing);
return;
}
@@ -185,7 +185,7 @@ void craft_skin_gloves() {
if (missing == "") {
if (get_personal_count(ITEM_SKIN_GLOVES) >= get_personal_stack_limit()) {
speak_with_history("You can't carry any more skin gloves.", true);
speak_cant_carry_any_more_item(ITEM_SKIN_GLOVES);
return;
}
simulate_crafting(2);
@@ -194,13 +194,13 @@ void craft_skin_gloves() {
add_personal_count(ITEM_SKIN_GLOVES, 1);
speak_with_history("Crafted Skin Gloves.", true);
} else {
speak_with_history("Missing: " + missing, true);
speak_crafting_missing(missing);
}
}
void craft_skin_gloves_max() {
if (get_personal_count(ITEM_SKIN_GLOVES) >= get_personal_stack_limit()) {
speak_with_history("You can't carry any more skin gloves.", true);
speak_cant_carry_any_more_item(ITEM_SKIN_GLOVES);
return;
}
@@ -220,7 +220,7 @@ void craft_skin_gloves_max() {
missing += "1 skin ";
if (get_personal_count(ITEM_VINES) < 1)
missing += "1 vine ";
speak_with_history("Missing: " + missing, true);
speak_crafting_missing(missing);
return;
}
@@ -242,7 +242,7 @@ void craft_skin_pants() {
if (missing == "") {
if (get_personal_count(ITEM_SKIN_PANTS) >= get_personal_stack_limit()) {
speak_with_history("You can't carry any more skin pants.", true);
speak_cant_carry_any_more_item(ITEM_SKIN_PANTS);
return;
}
simulate_crafting(9);
@@ -251,13 +251,13 @@ void craft_skin_pants() {
add_personal_count(ITEM_SKIN_PANTS, 1);
speak_with_history("Crafted Skin Pants.", true);
} else {
speak_with_history("Missing: " + missing, true);
speak_crafting_missing(missing);
}
}
void craft_skin_pants_max() {
if (get_personal_count(ITEM_SKIN_PANTS) >= get_personal_stack_limit()) {
speak_with_history("You can't carry any more skin pants.", true);
speak_cant_carry_any_more_item(ITEM_SKIN_PANTS);
return;
}
@@ -277,7 +277,7 @@ void craft_skin_pants_max() {
missing += "6 skins ";
if (get_personal_count(ITEM_VINES) < 3)
missing += "3 vines ";
speak_with_history("Missing: " + missing, true);
speak_crafting_missing(missing);
return;
}
@@ -298,7 +298,7 @@ void craft_skin_tunic() {
if (missing == "") {
if (get_personal_count(ITEM_SKIN_TUNICS) >= get_personal_stack_limit()) {
speak_with_history("You can't carry any more skin tunics.", true);
speak_cant_carry_any_more_item(ITEM_SKIN_TUNICS);
return;
}
simulate_crafting(6);
@@ -307,13 +307,13 @@ void craft_skin_tunic() {
add_personal_count(ITEM_SKIN_TUNICS, 1);
speak_with_history("Crafted a Skin Tunic.", true);
} else {
speak_with_history("Missing: " + missing, true);
speak_crafting_missing(missing);
}
}
void craft_skin_tunic_max() {
if (get_personal_count(ITEM_SKIN_TUNICS) >= get_personal_stack_limit()) {
speak_with_history("You can't carry any more skin tunics.", true);
speak_cant_carry_any_more_item(ITEM_SKIN_TUNICS);
return;
}
@@ -333,7 +333,7 @@ void craft_skin_tunic_max() {
missing += "4 skins ";
if (get_personal_count(ITEM_VINES) < 2)
missing += "2 vines ";
speak_with_history("Missing: " + missing, true);
speak_crafting_missing(missing);
return;
}
@@ -354,7 +354,7 @@ void craft_moccasins() {
if (missing == "") {
if (get_personal_count(ITEM_MOCCASINS) >= get_personal_stack_limit()) {
speak_with_history("You can't carry any more moccasins.", true);
speak_cant_carry_any_more_item(ITEM_MOCCASINS);
return;
}
simulate_crafting(3);
@@ -363,13 +363,13 @@ void craft_moccasins() {
add_personal_count(ITEM_MOCCASINS, 1);
speak_with_history("Crafted moccasins.", true);
} else {
speak_with_history("Missing: " + missing, true);
speak_crafting_missing(missing);
}
}
void craft_moccasins_max() {
if (get_personal_count(ITEM_MOCCASINS) >= get_personal_stack_limit()) {
speak_with_history("You can't carry any more moccasins.", true);
speak_cant_carry_any_more_item(ITEM_MOCCASINS);
return;
}
@@ -389,7 +389,7 @@ void craft_moccasins_max() {
missing += "2 skins ";
if (get_personal_count(ITEM_VINES) < 1)
missing += "1 vine ";
speak_with_history("Missing: " + missing, true);
speak_crafting_missing(missing);
return;
}
@@ -411,7 +411,7 @@ void craft_skin_pouch() {
if (missing == "") {
if (get_personal_count(ITEM_SKIN_POUCHES) >= get_personal_stack_limit()) {
speak_with_history("You can't carry any more skin pouches.", true);
speak_cant_carry_any_more_item(ITEM_SKIN_POUCHES);
return;
}
simulate_crafting(3);
@@ -420,13 +420,13 @@ void craft_skin_pouch() {
add_personal_count(ITEM_SKIN_POUCHES, 1);
speak_with_history("Crafted a Skin Pouch.", true);
} else {
speak_with_history("Missing: " + missing, true);
speak_crafting_missing(missing);
}
}
void craft_skin_pouch_max() {
if (get_personal_count(ITEM_SKIN_POUCHES) >= get_personal_stack_limit()) {
speak_with_history("You can't carry any more skin pouches.", true);
speak_cant_carry_any_more_item(ITEM_SKIN_POUCHES);
return;
}
@@ -446,7 +446,7 @@ void craft_skin_pouch_max() {
missing += "2 skins ";
if (get_personal_count(ITEM_VINES) < 1)
missing += "1 vine ";
speak_with_history("Missing: " + missing, true);
speak_crafting_missing(missing);
return;
}
@@ -470,7 +470,7 @@ void craft_backpack() {
if (missing == "") {
if (get_personal_count(ITEM_BACKPACKS) >= get_personal_stack_limit()) {
speak_with_history("You can't carry any more backpacks.", true);
speak_cant_carry_any_more_item(ITEM_BACKPACKS);
return;
}
simulate_crafting(20);
@@ -480,13 +480,13 @@ void craft_backpack() {
add_personal_count(ITEM_BACKPACKS, 1);
speak_with_history("Crafted a Backpack.", true);
} else {
speak_with_history("Missing: " + missing, true);
speak_crafting_missing(missing);
}
}
void craft_backpack_max() {
if (get_personal_count(ITEM_BACKPACKS) >= get_personal_stack_limit()) {
speak_with_history("You can't carry any more backpacks.", true);
speak_cant_carry_any_more_item(ITEM_BACKPACKS);
return;
}
@@ -511,7 +511,7 @@ void craft_backpack_max() {
missing += "5 vines ";
if (get_total_pouch_count() < 4)
missing += "4 skin pouches ";
speak_with_history("Missing: " + missing, true);
speak_crafting_missing(missing);
return;
}
+36 -35
View File
@@ -1,11 +1,12 @@
// Crafting materials
void run_materials_menu() {
int selection = 0;
string[] options = {
"Butcher Game [Requires Game, Knife, Fire nearby]", "Smoke Fish (1 Fish, 1 Stick) [Requires Fire nearby]",
"Arrows (2 Sticks, 4 Feathers, 2 Stones) [Requires Quiver]", "Bowstring (3 Sinew) [Requires Fire nearby]",
"Incense (6 Sticks, 2 Vines, 1 Reed) [Requires Altar]"};
speak_with_history("Materials. " + options[selection], true);
string[] options = {tr("system.crafting.materials.option.butcher_game"),
tr("system.crafting.materials.option.smoke_fish"),
tr("system.crafting.materials.option.arrows"),
tr("system.crafting.materials.option.bowstring"),
tr("system.crafting.materials.option.incense")};
speak_menu_prompt("system.crafting.materials.prompt", options[selection]);
while (true) {
wait(5);
@@ -94,7 +95,7 @@ void craft_arrows() {
add_personal_count(ITEM_ARROWS, ARROWS_PER_CRAFT);
speak_with_history("Crafted " + ARROWS_PER_CRAFT + " arrows.", true);
} else {
speak_with_history("Missing: " + missing, true);
speak_crafting_missing(missing);
}
}
@@ -131,7 +132,7 @@ void craft_arrows_max() {
missing += "4 feathers ";
if (get_personal_count(ITEM_STONES) < 2)
missing += "2 stones ";
speak_with_history("Missing: " + missing, true);
speak_crafting_missing(missing);
return;
}
@@ -148,7 +149,7 @@ void craft_arrows_max() {
void craft_bowstring() {
WorldFire @fire = get_fire_within_range(x, 3);
if (fire == null) {
speak_with_history("You need a fire within 3 tiles to make bowstring.", true);
speak_with_history(tr("system.crafting.require.fire_within_three_bowstring"), true);
return;
}
@@ -158,7 +159,7 @@ void craft_bowstring() {
if (missing == "") {
if (get_personal_count(ITEM_BOWSTRINGS) >= get_personal_stack_limit()) {
speak_with_history("You can't carry any more bowstrings.", true);
speak_cant_carry_any_more_item(ITEM_BOWSTRINGS);
return;
}
simulate_crafting(3);
@@ -166,19 +167,19 @@ void craft_bowstring() {
add_personal_count(ITEM_BOWSTRINGS, 1);
speak_with_history("Crafted a bowstring.", true);
} else {
speak_with_history("Missing: " + missing, true);
speak_crafting_missing(missing);
}
}
void craft_bowstring_max() {
WorldFire @fire = get_fire_within_range(x, 3);
if (fire == null) {
speak_with_history("You need a fire within 3 tiles to make bowstring.", true);
speak_with_history(tr("system.crafting.require.fire_within_three_bowstring"), true);
return;
}
if (get_personal_count(ITEM_BOWSTRINGS) >= get_personal_stack_limit()) {
speak_with_history("You can't carry any more bowstrings.", true);
speak_cant_carry_any_more_item(ITEM_BOWSTRINGS);
return;
}
@@ -190,7 +191,7 @@ void craft_bowstring_max() {
max_craft = space;
if (max_craft <= 0) {
speak_with_history("Missing: 3 sinew", true);
speak_crafting_missing("3 sinew");
return;
}
@@ -204,7 +205,7 @@ void craft_bowstring_max() {
void craft_incense() {
if (world_altars.length() == 0) {
speak_with_history("You need an altar to craft incense.", true);
speak_with_history(tr("system.crafting.require.altar_incense"), true);
return;
}
@@ -218,7 +219,7 @@ void craft_incense() {
if (missing == "") {
if (get_personal_count(ITEM_INCENSE) >= get_personal_stack_limit()) {
speak_with_history("You can't carry any more incense.", true);
speak_cant_carry_any_more_item(ITEM_INCENSE);
return;
}
simulate_crafting(INCENSE_STICK_COST + INCENSE_VINE_COST + INCENSE_REED_COST);
@@ -228,18 +229,18 @@ void craft_incense() {
add_personal_count(ITEM_INCENSE, 1);
speak_with_history("Crafted incense.", true);
} else {
speak_with_history("Missing: " + missing, true);
speak_crafting_missing(missing);
}
}
void craft_incense_max() {
if (world_altars.length() == 0) {
speak_with_history("You need an altar to craft incense.", true);
speak_with_history(tr("system.crafting.require.altar_incense"), true);
return;
}
if (get_personal_count(ITEM_INCENSE) >= get_personal_stack_limit()) {
speak_with_history("You can't carry any more incense.", true);
speak_cant_carry_any_more_item(ITEM_INCENSE);
return;
}
@@ -264,7 +265,7 @@ void craft_incense_max() {
missing += INCENSE_VINE_COST + " vines ";
if (get_personal_count(ITEM_REEDS) < INCENSE_REED_COST)
missing += INCENSE_REED_COST + " reed ";
speak_with_history("Missing: " + missing, true);
speak_crafting_missing(missing);
return;
}
@@ -281,7 +282,7 @@ void craft_incense_max() {
void craft_smoke_fish() {
WorldFire @fire = get_fire_within_range(x, 3);
if (fire == null) {
speak_with_history("You need a fire within 3 tiles to smoke fish.", true);
speak_with_history(tr("system.crafting.require.fire_within_three_smoke_fish"), true);
return;
}
@@ -296,7 +297,7 @@ void craft_smoke_fish() {
int yield = get_smoked_fish_yield(weight);
int space = get_personal_stack_limit() - get_personal_count(ITEM_SMOKED_FISH);
if (space < yield) {
speak_with_history("You can't carry any more smoked fish.", true);
speak_cant_carry_any_more_item(ITEM_SMOKED_FISH);
return;
}
simulate_crafting(2);
@@ -306,14 +307,14 @@ void craft_smoke_fish() {
add_personal_count(ITEM_SMOKED_FISH, yield);
speak_with_history("Smoked a fish into " + yield + " smoked fish.", true);
} else {
speak_with_history("Missing: " + missing, true);
speak_crafting_missing(missing);
}
}
void craft_smoke_fish_max() {
WorldFire @fire = get_fire_within_range(x, 3);
if (fire == null) {
speak_with_history("You need a fire within 3 tiles to smoke fish.", true);
speak_with_history(tr("system.crafting.require.fire_within_three_smoke_fish"), true);
return;
}
@@ -329,13 +330,13 @@ void craft_smoke_fish_max() {
missing += "1 fish ";
if (get_personal_count(ITEM_STICKS) < 1)
missing += "1 stick ";
speak_with_history("Missing: " + missing, true);
speak_crafting_missing(missing);
return;
}
int space = get_personal_stack_limit() - get_personal_count(ITEM_SMOKED_FISH);
if (space <= 0) {
speak_with_history("You can't carry any more smoked fish.", true);
speak_cant_carry_any_more_item(ITEM_SMOKED_FISH);
return;
}
@@ -351,7 +352,7 @@ void craft_smoke_fish_max() {
}
if (fish_to_smoke <= 0) {
speak_with_history("You can't carry any more smoked fish.", true);
speak_cant_carry_any_more_item(ITEM_SMOKED_FISH);
return;
}
@@ -381,17 +382,17 @@ void butcher_small_game() {
// Check for fire within 3 tiles (can hear it)
WorldFire @fire = get_fire_within_range(x, 3);
if (fire == null) {
speak_with_history("You need a fire within 3 tiles to butcher.", true);
speak_with_history(tr("system.crafting.require.fire_within_three_butcher"), true);
return;
}
if (missing == "") {
if (get_personal_count(ITEM_MEAT) >= get_personal_stack_limit()) {
speak_with_history("You can't carry any more meat.", true);
speak_cant_carry_any_more_item(ITEM_MEAT);
return;
}
if (get_personal_count(ITEM_SKINS) >= get_personal_stack_limit()) {
speak_with_history("You can't carry any more skins.", true);
speak_cant_carry_any_more_item(ITEM_SKINS);
return;
}
simulate_crafting(1);
@@ -429,7 +430,7 @@ void butcher_small_game() {
// Play sound
p.play_stationary("sounds/items/miscellaneous.ogg", false);
} else {
speak_with_history("Missing: " + missing, true);
speak_crafting_missing(missing);
}
}
@@ -438,20 +439,20 @@ void butcher_small_game_max() {
// Check for knife
if (get_personal_count(ITEM_KNIVES) < 1) {
speak_with_history("Missing: Stone Knife", true);
speak_crafting_missing("Stone Knife");
return;
}
// Check for small game or boar
if (get_personal_count(ITEM_SMALL_GAME) < 1 && get_personal_count(ITEM_BOAR_CARCASSES) < 1) {
speak_with_history("Missing: Game", true);
speak_crafting_missing("Game");
return;
}
// Check for fire within 3 tiles (can hear it)
WorldFire @fire = get_fire_within_range(x, 3);
if (fire == null) {
speak_with_history("You need a fire within 3 tiles to butcher.", true);
speak_with_history(tr("system.crafting.require.fire_within_three_butcher"), true);
return;
}
@@ -460,11 +461,11 @@ void butcher_small_game_max() {
int skins_space = get_personal_stack_limit() - get_personal_count(ITEM_SKINS);
if (meat_space <= 0) {
speak_with_history("You can't carry any more meat.", true);
speak_cant_carry_any_more_item(ITEM_MEAT);
return;
}
if (skins_space <= 0) {
speak_with_history("You can't carry any more skins.", true);
speak_cant_carry_any_more_item(ITEM_SKINS);
return;
}
+30 -30
View File
@@ -5,58 +5,58 @@
// Get the base equipment name without any rune prefix
string get_base_equipment_name(int equip_type) {
if (equip_type == EQUIP_SPEAR)
return "Spear";
return i18n_lookup_key_with_fallback("system.equipment.name.spear", "Spear");
if (equip_type == EQUIP_AXE)
return "Stone Axe";
return i18n_lookup_key_with_fallback("system.equipment.name.stone_axe", "Stone Axe");
if (equip_type == EQUIP_SLING)
return "Sling";
return i18n_lookup_key_with_fallback("system.equipment.name.sling", "Sling");
if (equip_type == EQUIP_BOW)
return "Bow";
return i18n_lookup_key_with_fallback("system.equipment.name.bow", "Bow");
if (equip_type == EQUIP_HAT)
return "Skin Hat";
return i18n_lookup_key_with_fallback("system.equipment.name.skin_hat", "Skin Hat");
if (equip_type == EQUIP_GLOVES)
return "Skin Gloves";
return i18n_lookup_key_with_fallback("system.equipment.name.skin_gloves", "Skin Gloves");
if (equip_type == EQUIP_PANTS)
return "Skin Pants";
return i18n_lookup_key_with_fallback("system.equipment.name.skin_pants", "Skin Pants");
if (equip_type == EQUIP_TUNIC)
return "Skin Tunic";
return i18n_lookup_key_with_fallback("system.equipment.name.skin_tunic", "Skin Tunic");
if (equip_type == EQUIP_MOCCASINS)
return "Moccasins";
return i18n_lookup_key_with_fallback("system.equipment.name.moccasins", "Moccasins");
if (equip_type == EQUIP_POUCH)
return "Skin Pouch";
return i18n_lookup_key_with_fallback("system.equipment.name.skin_pouch", "Skin Pouch");
if (equip_type == EQUIP_BACKPACK)
return "Backpack";
return i18n_lookup_key_with_fallback("system.equipment.name.backpack", "Backpack");
if (equip_type == EQUIP_FISHING_POLE)
return "Fishing Pole";
return "Unknown";
return i18n_lookup_key_with_fallback("system.equipment.name.fishing_pole", "Fishing Pole");
return i18n_lookup_key_with_fallback("system.equipment.name.unknown", "Unknown");
}
string get_base_equipment_name_plural(int equip_type) {
if (equip_type == EQUIP_SPEAR)
return "Spears";
return i18n_lookup_key_with_fallback("system.equipment.name_plural.spears", "Spears");
if (equip_type == EQUIP_AXE)
return "Stone Axes";
return i18n_lookup_key_with_fallback("system.equipment.name_plural.stone_axes", "Stone Axes");
if (equip_type == EQUIP_SLING)
return "Slings";
return i18n_lookup_key_with_fallback("system.equipment.name_plural.slings", "Slings");
if (equip_type == EQUIP_BOW)
return "Bows";
return i18n_lookup_key_with_fallback("system.equipment.name_plural.bows", "Bows");
if (equip_type == EQUIP_HAT)
return "Skin Hats";
return i18n_lookup_key_with_fallback("system.equipment.name_plural.skin_hats", "Skin Hats");
if (equip_type == EQUIP_GLOVES)
return "Skin Gloves";
return i18n_lookup_key_with_fallback("system.equipment.name_plural.skin_gloves", "Skin Gloves");
if (equip_type == EQUIP_PANTS)
return "Skin Pants";
return i18n_lookup_key_with_fallback("system.equipment.name_plural.skin_pants", "Skin Pants");
if (equip_type == EQUIP_TUNIC)
return "Skin Tunics";
return i18n_lookup_key_with_fallback("system.equipment.name_plural.skin_tunics", "Skin Tunics");
if (equip_type == EQUIP_MOCCASINS)
return "Moccasins";
return i18n_lookup_key_with_fallback("system.equipment.name_plural.moccasins", "Moccasins");
if (equip_type == EQUIP_POUCH)
return "Skin Pouches";
return i18n_lookup_key_with_fallback("system.equipment.name_plural.skin_pouches", "Skin Pouches");
if (equip_type == EQUIP_BACKPACK)
return "Backpacks";
return i18n_lookup_key_with_fallback("system.equipment.name_plural.backpacks", "Backpacks");
if (equip_type == EQUIP_FISHING_POLE)
return "Fishing Poles";
return "Items";
return i18n_lookup_key_with_fallback("system.equipment.name_plural.fishing_poles", "Fishing Poles");
return i18n_lookup_key_with_fallback("system.equipment.name_plural.items", "Items");
}
// Get inventory count for an equipment type
@@ -213,7 +213,7 @@ void run_rune_equipment_menu(int rune_type) {
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_options.insert_last(i18n_text(name + " (" + unruned_count + " available)"));
equipment_types.insert_last(equip_type);
}
}
@@ -302,7 +302,7 @@ void engrave_rune(int equip_type, int rune_type) {
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);
speak_crafting_missing(missing);
}
}
@@ -327,7 +327,7 @@ void engrave_rune_max(int equip_type, int rune_type) {
max_craft = favor_count;
if (missing != "") {
speak_with_history("Missing: " + missing, true);
speak_crafting_missing(missing);
return;
}
@@ -338,7 +338,7 @@ void engrave_rune_max(int equip_type, int rune_type) {
missing += "1 favor ";
if (missing == "")
missing = "resources";
speak_with_history("Missing: " + missing, true);
speak_crafting_missing(missing);
return;
}
+44 -44
View File
@@ -1,16 +1,16 @@
// Crafting tools
void run_tools_menu() {
int selection = 0;
string[] options = {"Stone Knife (2 Stones)",
"Snare (1 Stick, 2 Vines)",
"Stone Axe (1 Stick, 1 Vine, 2 Stones) [Requires Knife]",
"Fishing Pole (1 Stick, 2 Vines)",
"Rope (3 Vines)",
"Quiver (2 Skins, 2 Vines)",
"Canoe (4 Logs, 11 Sticks, 11 Vines, 6 Skins, 2 Rope, 6 Reeds)",
"Reed Basket (3 Reeds)",
"Clay Pot (3 Clay)"};
speak_with_history("Tools. " + options[selection], true);
string[] options = {tr("system.crafting.tools.option.stone_knife"),
tr("system.crafting.tools.option.snare"),
tr("system.crafting.tools.option.stone_axe"),
tr("system.crafting.tools.option.fishing_pole"),
tr("system.crafting.tools.option.rope"),
tr("system.crafting.tools.option.quiver"),
tr("system.crafting.tools.option.canoe"),
tr("system.crafting.tools.option.reed_basket"),
tr("system.crafting.tools.option.clay_pot")};
speak_menu_prompt("system.crafting.tools.prompt", options[selection]);
while (true) {
wait(5);
@@ -93,7 +93,7 @@ void craft_knife() {
if (missing == "") {
if (get_personal_count(ITEM_KNIVES) >= get_personal_stack_limit()) {
speak_with_history("You can't carry any more stone knives.", true);
speak_cant_carry_any_more_item(ITEM_KNIVES);
return;
}
simulate_crafting(2);
@@ -101,13 +101,13 @@ void craft_knife() {
add_personal_count(ITEM_KNIVES, 1);
speak_with_history("Crafted a Stone Knife.", true);
} else {
speak_with_history("Missing: " + missing, true);
speak_crafting_missing(missing);
}
}
void craft_knife_max() {
if (get_personal_count(ITEM_KNIVES) >= get_personal_stack_limit()) {
speak_with_history("You can't carry any more stone knives.", true);
speak_cant_carry_any_more_item(ITEM_KNIVES);
return;
}
@@ -117,7 +117,7 @@ void craft_knife_max() {
max_possible = space;
if (max_possible <= 0) {
speak_with_history("Missing: 2 stones", true);
speak_crafting_missing("2 stones");
return;
}
@@ -138,7 +138,7 @@ void craft_snare() {
if (missing == "") {
if (get_personal_count(ITEM_SNARES) >= get_personal_stack_limit()) {
speak_with_history("You can't carry any more snares.", true);
speak_cant_carry_any_more_item(ITEM_SNARES);
return;
}
simulate_crafting(3);
@@ -147,13 +147,13 @@ void craft_snare() {
add_personal_count(ITEM_SNARES, 1);
speak_with_history("Crafted a Snare.", true);
} else {
speak_with_history("Missing: " + missing, true);
speak_crafting_missing(missing);
}
}
void craft_snare_max() {
if (get_personal_count(ITEM_SNARES) >= get_personal_stack_limit()) {
speak_with_history("You can't carry any more snares.", true);
speak_cant_carry_any_more_item(ITEM_SNARES);
return;
}
@@ -173,7 +173,7 @@ void craft_snare_max() {
missing += "1 stick ";
if (get_personal_count(ITEM_VINES) < 2)
missing += "2 vines ";
speak_with_history("Missing: " + missing, true);
speak_crafting_missing(missing);
return;
}
@@ -195,7 +195,7 @@ void craft_fishing_pole() {
if (missing == "") {
if (get_personal_count(ITEM_FISHING_POLES) >= get_personal_stack_limit()) {
speak_with_history("You can't carry any more fishing poles.", true);
speak_cant_carry_any_more_item(ITEM_FISHING_POLES);
return;
}
simulate_crafting(3);
@@ -204,13 +204,13 @@ void craft_fishing_pole() {
add_personal_count(ITEM_FISHING_POLES, 1);
speak_with_history("Crafted a Fishing Pole.", true);
} else {
speak_with_history("Missing: " + missing, true);
speak_crafting_missing(missing);
}
}
void craft_fishing_pole_max() {
if (get_personal_count(ITEM_FISHING_POLES) >= get_personal_stack_limit()) {
speak_with_history("You can't carry any more fishing poles.", true);
speak_cant_carry_any_more_item(ITEM_FISHING_POLES);
return;
}
@@ -230,7 +230,7 @@ void craft_fishing_pole_max() {
missing += "1 stick ";
if (get_personal_count(ITEM_VINES) < 2)
missing += "2 vines ";
speak_with_history("Missing: " + missing, true);
speak_crafting_missing(missing);
return;
}
@@ -250,7 +250,7 @@ void craft_rope() {
if (missing == "") {
if (get_personal_count(ITEM_ROPES) >= get_personal_stack_limit()) {
speak_with_history("You can't carry any more rope.", true);
speak_cant_carry_any_more_item(ITEM_ROPES);
return;
}
simulate_crafting(3);
@@ -258,13 +258,13 @@ void craft_rope() {
add_personal_count(ITEM_ROPES, 1);
speak_with_history("Crafted rope.", true);
} else {
speak_with_history("Missing: " + missing, true);
speak_crafting_missing(missing);
}
}
void craft_rope_max() {
if (get_personal_count(ITEM_ROPES) >= get_personal_stack_limit()) {
speak_with_history("You can't carry any more rope.", true);
speak_cant_carry_any_more_item(ITEM_ROPES);
return;
}
@@ -275,7 +275,7 @@ void craft_rope_max() {
max_craft = space;
if (max_craft <= 0) {
speak_with_history("Missing: 3 vines", true);
speak_crafting_missing("3 vines");
return;
}
@@ -296,7 +296,7 @@ void craft_quiver() {
if (missing == "") {
if (get_personal_count(ITEM_QUIVERS) >= get_personal_stack_limit()) {
speak_with_history("You can't carry any more quivers.", true);
speak_cant_carry_any_more_item(ITEM_QUIVERS);
return;
}
simulate_crafting(4);
@@ -305,13 +305,13 @@ void craft_quiver() {
add_personal_count(ITEM_QUIVERS, 1);
speak_with_history("Crafted a Quiver.", true);
} else {
speak_with_history("Missing: " + missing, true);
speak_crafting_missing(missing);
}
}
void craft_quiver_max() {
if (get_personal_count(ITEM_QUIVERS) >= get_personal_stack_limit()) {
speak_with_history("You can't carry any more quivers.", true);
speak_cant_carry_any_more_item(ITEM_QUIVERS);
return;
}
@@ -331,7 +331,7 @@ void craft_quiver_max() {
missing += "2 skins ";
if (get_personal_count(ITEM_VINES) < 2)
missing += "2 vines ";
speak_with_history("Missing: " + missing, true);
speak_crafting_missing(missing);
return;
}
@@ -361,7 +361,7 @@ void craft_canoe() {
if (missing == "") {
if (get_personal_count(ITEM_CANOES) >= get_personal_stack_limit()) {
speak_with_history("You can't carry any more canoes.", true);
speak_cant_carry_any_more_item(ITEM_CANOES);
return;
}
simulate_crafting(40);
@@ -374,13 +374,13 @@ void craft_canoe() {
add_personal_count(ITEM_CANOES, 1);
speak_with_history("Crafted a Canoe.", true);
} else {
speak_with_history("Missing: " + missing, true);
speak_crafting_missing(missing);
}
}
void craft_canoe_max() {
if (get_personal_count(ITEM_CANOES) >= get_personal_stack_limit()) {
speak_with_history("You can't carry any more canoes.", true);
speak_cant_carry_any_more_item(ITEM_CANOES);
return;
}
@@ -420,7 +420,7 @@ void craft_canoe_max() {
missing += "2 rope ";
if (get_personal_count(ITEM_REEDS) < 6)
missing += "6 reeds ";
speak_with_history("Missing: " + missing, true);
speak_crafting_missing(missing);
return;
}
@@ -444,7 +444,7 @@ void craft_reed_basket() {
if (missing == "") {
if (get_personal_count(ITEM_REED_BASKETS) >= get_personal_stack_limit()) {
speak_with_history("You can't carry any more reed baskets.", true);
speak_cant_carry_any_more_item(ITEM_REED_BASKETS);
return;
}
simulate_crafting(3);
@@ -452,13 +452,13 @@ void craft_reed_basket() {
add_personal_count(ITEM_REED_BASKETS, 1);
speak_with_history("Crafted a reed basket.", true);
} else {
speak_with_history("Missing: " + missing, true);
speak_crafting_missing(missing);
}
}
void craft_reed_basket_max() {
if (get_personal_count(ITEM_REED_BASKETS) >= get_personal_stack_limit()) {
speak_with_history("You can't carry any more reed baskets.", true);
speak_cant_carry_any_more_item(ITEM_REED_BASKETS);
return;
}
@@ -469,7 +469,7 @@ void craft_reed_basket_max() {
max_craft = space;
if (max_craft <= 0) {
speak_with_history("Missing: 3 reeds", true);
speak_crafting_missing("3 reeds");
return;
}
@@ -489,13 +489,13 @@ void craft_clay_pot() {
// Check for fire within 3 tiles (can hear it)
WorldFire @fire = get_fire_within_range(x, 3);
if (fire == null) {
speak_with_history("You need a fire within 3 tiles to craft a clay pot.", true);
speak_with_history(tr("system.crafting.require.fire_within_three_clay_pot"), true);
return;
}
if (missing == "") {
if (get_personal_count(ITEM_CLAY_POTS) >= get_personal_stack_limit()) {
speak_with_history("You can't carry any more clay pots.", true);
speak_cant_carry_any_more_item(ITEM_CLAY_POTS);
return;
}
simulate_crafting(3);
@@ -503,7 +503,7 @@ void craft_clay_pot() {
add_personal_count(ITEM_CLAY_POTS, 1);
speak_with_history("Crafted a clay pot.", true);
} else {
speak_with_history("Missing: " + missing, true);
speak_crafting_missing(missing);
}
}
@@ -511,12 +511,12 @@ void craft_clay_pot_max() {
// Check for fire within 3 tiles (can hear it)
WorldFire @fire = get_fire_within_range(x, 3);
if (fire == null) {
speak_with_history("You need a fire within 3 tiles to craft clay pots.", true);
speak_with_history(tr("system.crafting.require.fire_within_three_clay_pots"), true);
return;
}
if (get_personal_count(ITEM_CLAY_POTS) >= get_personal_stack_limit()) {
speak_with_history("You can't carry any more clay pots.", true);
speak_cant_carry_any_more_item(ITEM_CLAY_POTS);
return;
}
@@ -527,7 +527,7 @@ void craft_clay_pot_max() {
max_craft = space;
if (max_craft <= 0) {
speak_with_history("Missing: 3 clay", true);
speak_crafting_missing("3 clay");
return;
}
+21 -21
View File
@@ -1,9 +1,9 @@
// Crafting weapons
void run_weapons_menu() {
int selection = 0;
string[] options = {"Spear (1 Stick, 1 Vine, 1 Stone) [Requires Knife]", "Sling (1 Skin, 2 Vines)",
"Bow (1 Stick, 1 Bowstring)"};
speak_with_history("Weapons. " + options[selection], true);
string[] options = {tr("system.crafting.weapons.option.spear"), tr("system.crafting.weapons.option.sling"),
tr("system.crafting.weapons.option.bow")};
speak_menu_prompt("system.crafting.weapons.prompt", options[selection]);
while (true) {
wait(5);
@@ -68,7 +68,7 @@ void craft_spear() {
if (missing == "") {
if (get_personal_count(ITEM_SPEARS) >= get_personal_stack_limit()) {
speak_with_history("You can't carry any more spears.", true);
speak_cant_carry_any_more_item(ITEM_SPEARS);
return;
}
simulate_crafting(3);
@@ -78,17 +78,17 @@ void craft_spear() {
add_personal_count(ITEM_SPEARS, 1);
speak_with_history("Crafted a Spear.", true);
} else {
speak_with_history("Missing: " + missing, true);
speak_crafting_missing(missing);
}
}
void craft_spear_max() {
if (get_personal_count(ITEM_KNIVES) < 1) {
speak_with_history("Missing: Stone Knife", true);
speak_crafting_missing("Stone Knife");
return;
}
if (get_personal_count(ITEM_SPEARS) >= get_personal_stack_limit()) {
speak_with_history("You can't carry any more spears.", true);
speak_cant_carry_any_more_item(ITEM_SPEARS);
return;
}
@@ -113,7 +113,7 @@ void craft_spear_max() {
missing += "1 vine ";
if (get_personal_count(ITEM_STONES) < 1)
missing += "1 stone ";
speak_with_history("Missing: " + missing, true);
speak_crafting_missing(missing);
return;
}
@@ -136,7 +136,7 @@ void craft_sling() {
if (missing == "") {
if (get_personal_count(ITEM_SLINGS) >= get_personal_stack_limit()) {
speak_with_history("You can't carry any more slings.", true);
speak_cant_carry_any_more_item(ITEM_SLINGS);
return;
}
simulate_crafting(3);
@@ -145,13 +145,13 @@ void craft_sling() {
add_personal_count(ITEM_SLINGS, 1);
speak_with_history("Crafted a Sling.", true);
} else {
speak_with_history("Missing: " + missing, true);
speak_crafting_missing(missing);
}
}
void craft_sling_max() {
if (get_personal_count(ITEM_SLINGS) >= get_personal_stack_limit()) {
speak_with_history("You can't carry any more slings.", true);
speak_cant_carry_any_more_item(ITEM_SLINGS);
return;
}
@@ -171,7 +171,7 @@ void craft_sling_max() {
missing += "1 skin ";
if (get_personal_count(ITEM_VINES) < 2)
missing += "2 vines ";
speak_with_history("Missing: " + missing, true);
speak_crafting_missing(missing);
return;
}
@@ -193,7 +193,7 @@ void craft_bow() {
if (missing == "") {
if (get_personal_count(ITEM_BOWS) >= get_personal_stack_limit()) {
speak_with_history("You can't carry any more bows.", true);
speak_cant_carry_any_more_item(ITEM_BOWS);
return;
}
simulate_crafting(3);
@@ -202,13 +202,13 @@ void craft_bow() {
add_personal_count(ITEM_BOWS, 1);
speak_with_history("Crafted a Bow.", true);
} else {
speak_with_history("Missing: " + missing, true);
speak_crafting_missing(missing);
}
}
void craft_bow_max() {
if (get_personal_count(ITEM_BOWS) >= get_personal_stack_limit()) {
speak_with_history("You can't carry any more bows.", true);
speak_cant_carry_any_more_item(ITEM_BOWS);
return;
}
@@ -228,7 +228,7 @@ void craft_bow_max() {
missing += "1 stick ";
if (get_personal_count(ITEM_BOWSTRINGS) < 1)
missing += "1 bowstring ";
speak_with_history("Missing: " + missing, true);
speak_crafting_missing(missing);
return;
}
@@ -254,7 +254,7 @@ void craft_axe() {
if (missing == "") {
if (get_personal_count(ITEM_AXES) >= get_personal_stack_limit()) {
speak_with_history("You can't carry any more stone axes.", true);
speak_cant_carry_any_more_item(ITEM_AXES);
return;
}
simulate_crafting(4);
@@ -264,17 +264,17 @@ void craft_axe() {
add_personal_count(ITEM_AXES, 1);
speak_with_history("Crafted a Stone Axe.", true);
} else {
speak_with_history("Missing: " + missing, true);
speak_crafting_missing(missing);
}
}
void craft_axe_max() {
if (get_personal_count(ITEM_KNIVES) < 1) {
speak_with_history("Missing: Stone Knife", true);
speak_crafting_missing("Stone Knife");
return;
}
if (get_personal_count(ITEM_AXES) >= get_personal_stack_limit()) {
speak_with_history("You can't carry any more stone axes.", true);
speak_cant_carry_any_more_item(ITEM_AXES);
return;
}
@@ -299,7 +299,7 @@ void craft_axe_max() {
missing += "1 vine ";
if (get_personal_count(ITEM_STONES) < 2)
missing += "2 stones ";
speak_with_history("Missing: " + missing, true);
speak_crafting_missing(missing);
return;
}
+127 -8
View File
@@ -1,4 +1,123 @@
// Crafting core menu and shared helpers
void speak_menu_prompt(const string& in promptKey, const string& in option) {
dictionary args;
args.set("option", option);
speak_with_history(trf(promptKey, args), true);
}
bool crafting_is_numeric_token(const string& in token) {
if (token == "")
return false;
for (uint i = 0; i < token.length(); i++) {
string ch = token.substr(i, 1);
if (ch < "0" || ch > "9")
return false;
}
return true;
}
string crafting_translate_requirement_label(const string& in englishLabel) {
string trimmed = i18n_trim_whitespace(englishLabel);
if (trimmed == "")
return "";
string lower = trimmed.lower();
if (lower == "stone knife")
return tr("system.crafting.requirement.stone_knife");
if (lower == "game")
return tr("system.crafting.requirement.game");
if (lower == "favor")
return tr("system.crafting.requirement.favor");
if (lower == "resources")
return tr("system.crafting.requirement.resources");
string localized = i18n_translate_fragment_value(trimmed);
if (localized != trimmed)
return localized;
string localizedLower = i18n_translate_fragment_value(lower);
if (localizedLower != lower)
return localizedLower;
return trimmed;
}
void crafting_append_requirement_fragment(string &inout joined, const string& in fragment) {
string trimmed = i18n_trim_whitespace(fragment);
if (trimmed == "")
return;
if (joined != "")
joined += ", ";
joined += trimmed;
}
string crafting_localize_missing_requirements(const string& in missingEnglish) {
string trimmed = i18n_trim_whitespace(missingEnglish);
if (trimmed == "")
return "";
string[] tokens = trimmed.split(" ");
string localized = "";
uint i = 0;
while (i < tokens.length()) {
if (tokens[i] == "") {
i++;
continue;
}
if (crafting_is_numeric_token(tokens[i])) {
string countText = tokens[i];
i++;
string label = "";
while (i < tokens.length()) {
if (tokens[i] == "") {
i++;
continue;
}
if (crafting_is_numeric_token(tokens[i]))
break;
if (label != "")
label += " ";
label += tokens[i];
i++;
}
label = crafting_translate_requirement_label(label);
crafting_append_requirement_fragment(localized, countText + " " + label);
continue;
}
string label = "";
while (i < tokens.length()) {
if (tokens[i] == "") {
i++;
continue;
}
if (crafting_is_numeric_token(tokens[i]))
break;
if (label != "")
label += " ";
label += tokens[i];
i++;
}
label = crafting_translate_requirement_label(label);
crafting_append_requirement_fragment(localized, label);
}
if (localized == "")
localized = crafting_translate_requirement_label(trimmed);
return localized;
}
void speak_crafting_missing(const string& in missingEnglish) {
dictionary args;
args.set("requirements", crafting_localize_missing_requirements(missingEnglish));
speak_with_history(trf("system.crafting.missing", args), true);
}
void check_crafting_menu(int x, int base_end_tile) {
if (x <= base_end_tile) {
if (key_pressed(KEY_C)) {
@@ -13,27 +132,27 @@ void run_crafting_menu() {
int[] category_types;
// Build categories dynamically
categories.insert_last("Weapons");
categories.insert_last(tr("system.crafting.category.weapons"));
category_types.insert_last(0);
categories.insert_last("Tools");
categories.insert_last(tr("system.crafting.category.tools"));
category_types.insert_last(1);
categories.insert_last("Materials");
categories.insert_last(tr("system.crafting.category.materials"));
category_types.insert_last(2);
categories.insert_last("Clothing");
categories.insert_last(tr("system.crafting.category.clothing"));
category_types.insert_last(3);
if (has_building_options()) {
categories.insert_last("Buildings");
categories.insert_last(tr("system.crafting.category.buildings"));
category_types.insert_last(4);
}
categories.insert_last("Barricade");
categories.insert_last(tr("system.crafting.category.barricade"));
category_types.insert_last(5);
// Add Runes category if any rune is unlocked
if (any_rune_unlocked()) {
categories.insert_last("Runes");
categories.insert_last(tr("system.crafting.category.runes"));
category_types.insert_last(6);
}
speak_with_history("Crafting menu. " + categories[selection], true);
speak_menu_prompt("system.crafting.menu.prompt", categories[selection]);
while (true) {
wait(5);
+30 -33
View File
@@ -218,19 +218,19 @@ void init_search_pools() {
@search_pools[SEARCH_POOL_STREAM_BANK] = SearchPool();
search_pools[SEARCH_POOL_STREAM_BANK].item_types = {ITEM_REEDS, ITEM_CLAY};
search_pools[SEARCH_POOL_STREAM_BANK].weights = {30, 70};
search_pools[SEARCH_POOL_STREAM_BANK].found_messages = {"Found a reed.", "Found clay."};
search_pools[SEARCH_POOL_STREAM_BANK].found_messages = {"", ""};
search_pools[SEARCH_POOL_STREAM_BANK].terrain_tags = {"stream_bank"};
@search_pools[SEARCH_POOL_FOREST] = SearchPool();
search_pools[SEARCH_POOL_FOREST].item_types = {ITEM_STICKS, ITEM_VINES};
search_pools[SEARCH_POOL_FOREST].weights = {1, 1};
search_pools[SEARCH_POOL_FOREST].found_messages = {"Found a stick.", "Found a vine."};
search_pools[SEARCH_POOL_FOREST].found_messages = {"", ""};
search_pools[SEARCH_POOL_FOREST].terrain_tags = {"forest", "deep_forest"};
@search_pools[SEARCH_POOL_STONE_TERRAIN] = SearchPool();
search_pools[SEARCH_POOL_STONE_TERRAIN].item_types = {ITEM_STONES};
search_pools[SEARCH_POOL_STONE_TERRAIN].weights = {1};
search_pools[SEARCH_POOL_STONE_TERRAIN].found_messages = {"Found a stone."};
search_pools[SEARCH_POOL_STONE_TERRAIN].found_messages = {""};
search_pools[SEARCH_POOL_STONE_TERRAIN].terrain_tags = {"gravel", "stone", "hard_stone"};
// Mass nouns for auto "Found X." fallback (no article).
@@ -596,14 +596,19 @@ void damage_tree(int target_x, int damage) {
add_personal_count(ITEM_VINES, vines_added);
add_personal_count(ITEM_LOGS, logs_added);
string drop_message = "Tree fell!";
string drop_message = tr("system.environment.tree.fell");
if (sticks_added > 0 || vines_added > 0 || logs_added > 0) {
string log_label = (logs_added == 1) ? " log" : " logs";
drop_message += " Got " + sticks_added + " sticks, " + vines_added + " vines, and " + logs_added +
log_label + ".";
dictionary loot_args;
loot_args.set("sticks", sticks_added);
loot_args.set("vines", vines_added);
loot_args.set("logs", logs_added);
loot_args.set("sticks_label", get_item_label_for_count(ITEM_STICKS, sticks_added));
loot_args.set("vines_label", get_item_label_for_count(ITEM_VINES, vines_added));
loot_args.set("logs_label", get_item_label_for_count(ITEM_LOGS, logs_added));
drop_message = trf("system.environment.tree.fell_with_loot", loot_args);
}
if (sticks_added < sticks_dropped || vines_added < vines_dropped || logs_added < 1) {
drop_message += " Inventory full.";
drop_message += " " + tr("system.environment.tree.inventory_full");
}
play_item_collect_sound("stick");
@@ -616,7 +621,7 @@ void damage_tree(int target_x, int damage) {
string build_item_list(int[] item_types) {
string list_text = "";
for (uint i = 0; i < item_types.length(); i++) {
string item_name = item_registry[item_types[i]].name;
string item_name = get_item_label(item_types[i]);
if (i == 0) {
list_text = item_name;
} else if (i == item_types.length() - 1) {
@@ -642,17 +647,9 @@ bool is_mass_noun_item(int item_type) {
}
string get_auto_found_message(int item_type) {
string singular = item_registry[item_type].singular;
if (is_mass_noun_item(item_type)) {
return "Found " + singular + ".";
}
string first_letter = singular.substr(0, 1);
if (first_letter == "a" || first_letter == "e" || first_letter == "i" || first_letter == "o" ||
first_letter == "u") {
return "Found an " + singular + ".";
}
return "Found a " + singular + ".";
dictionary args;
args.set("item", get_item_label_singular(item_type));
return trf("system.search.found_item", args);
}
int get_search_weight(int[] weights, uint index) {
@@ -685,7 +682,7 @@ bool try_find_weighted_resource(int[] item_types, int[] weights, string[] found_
}
if (available_indices.length() == 0) {
speak_with_history("You can't carry any more " + build_item_list(item_types) + ".", true);
speak_cant_carry_any_more_label(build_item_list(item_types));
return true;
}
@@ -758,11 +755,11 @@ void perform_search(int current_x) {
if (s != null) {
if (s.has_catch) {
if (get_personal_count(ITEM_SMALL_GAME) >= get_personal_stack_limit()) {
speak_with_history("You can't carry any more small game.", true);
speak_cant_carry_any_more_item(ITEM_SMALL_GAME);
return;
}
if (get_personal_count(ITEM_SNARES) >= get_personal_stack_limit()) {
speak_with_history("You can't carry any more snares.", true);
speak_cant_carry_any_more_item(ITEM_SNARES);
return;
}
add_personal_count(ITEM_SMALL_GAME, 1);
@@ -771,7 +768,7 @@ void perform_search(int current_x) {
speak_with_history("Collected " + s.catch_type + " and snare.", true);
} else {
if (get_personal_count(ITEM_SNARES) >= get_personal_stack_limit()) {
speak_with_history("You can't carry any more snares.", true);
speak_cant_carry_any_more_item(ITEM_SNARES);
return;
}
add_personal_count(ITEM_SNARES, 1); // Recover snare
@@ -785,7 +782,7 @@ void perform_search(int current_x) {
}
if (random(1, 100) <= 10) {
speak_with_history("Found nothing.", true);
speak_with_history(tr("system.search.found_nothing"), true);
return;
}
@@ -865,13 +862,13 @@ void perform_search(int current_x) {
nearest.sticks--;
add_personal_count(ITEM_STICKS, 1);
play_item_collect_sound("stick");
speak_with_history("Found a stick.", true);
speak_with_history(get_auto_found_message(ITEM_STICKS), true);
took_item = true;
} else if (nearest.vines > 0 && get_personal_count(ITEM_VINES) < get_personal_stack_limit()) {
nearest.vines--;
add_personal_count(ITEM_VINES, 1);
play_item_collect_sound("vine");
speak_with_history("Found a vine.", true);
speak_with_history(get_auto_found_message(ITEM_VINES), true);
took_item = true;
}
} else {
@@ -879,24 +876,24 @@ void perform_search(int current_x) {
nearest.vines--;
add_personal_count(ITEM_VINES, 1);
play_item_collect_sound("vine");
speak_with_history("Found a vine.", true);
speak_with_history(get_auto_found_message(ITEM_VINES), true);
took_item = true;
} else if (nearest.sticks > 0 && get_personal_count(ITEM_STICKS) < get_personal_stack_limit()) {
nearest.sticks--;
add_personal_count(ITEM_STICKS, 1);
play_item_collect_sound("stick");
speak_with_history("Found a stick.", true);
speak_with_history(get_auto_found_message(ITEM_STICKS), true);
took_item = true;
}
}
if (!took_item) {
if (nearest.sticks > 0 && nearest.vines > 0) {
speak_with_history("You can't carry any more sticks or vines.", true);
speak_cant_carry_any_more_label(build_item_list({ITEM_STICKS, ITEM_VINES}));
} else if (nearest.sticks > 0) {
speak_with_history("You can't carry any more sticks.", true);
speak_cant_carry_any_more_item(ITEM_STICKS);
} else {
speak_with_history("You can't carry any more vines.", true);
speak_cant_carry_any_more_item(ITEM_VINES);
}
return;
}
@@ -916,7 +913,7 @@ void perform_search(int current_x) {
return;
}
speak_with_history("Found nothing.", true);
speak_with_history(tr("system.search.found_nothing"), true);
}
// Climbing functions
+1 -1
View File
@@ -310,7 +310,7 @@ void update_reeling() {
void catch_fish() {
if (get_personal_count(ITEM_FISH) >= get_personal_stack_limit()) {
speak_with_history("You can't carry any more fish.", true);
speak_cant_carry_any_more_item(ITEM_FISH);
reset_fishing_session();
return;
}
+4 -3
View File
@@ -88,7 +88,7 @@ void append_adventure_completion_rewards(int adventureId, string[] @rewards) {
string stageName = fylgjaStageNames[stageIndex];
string targetName = adventureStageTargets[adventureIndex];
rewards.insert_last("You have a " + stageName + " connection with the " + targetName + ".");
rewards.insert_last(i18n_text("You have a " + stageName + " connection with the " + targetName + "."));
int fylgjaIndex = get_fylgja_index_for_adventure(adventureId);
if (fylgjaIndex == -1)
@@ -96,9 +96,9 @@ void append_adventure_completion_rewards(int adventureId, string[] @rewards) {
if (completionCount >= FYLGJA_STAGE_COUNT) {
if (completionCount == FYLGJA_STAGE_COUNT) {
rewards.insert_last("You have unlocked the " + fylgjaNames[fylgjaIndex] + " Fylgja!");
rewards.insert_last(i18n_text("You have unlocked the " + fylgjaNames[fylgjaIndex] + " Fylgja!"));
} else {
rewards.insert_last("You have already unlocked the " + fylgjaNames[fylgjaIndex] + " Fylgja.");
rewards.insert_last(i18n_text("You have already unlocked the " + fylgjaNames[fylgjaIndex] + " Fylgja."));
}
}
}
@@ -129,6 +129,7 @@ void run_fylgja_menu() {
fylgjaIndices.insert_last(int(i));
}
}
i18n_translate_string_array_in_place(options);
if (options.length() == 0)
return;
+770
View File
@@ -0,0 +1,770 @@
// Localization and translation helpers.
// Provides key-based lookups plus runtime template matching for existing English output.
const string I18N_DEFAULT_LANGUAGE_CODE = "en";
const string I18N_LANGUAGE_DIRECTORY = "lang";
const string I18N_ENGLISH_CATALOG_PATH = "lang/en.ini";
const string I18N_LANGUAGE_PREF_FILENAME = "draugnorak.dat";
const string I18N_LANGUAGE_PREF_KEY = "language";
const string I18N_PREF_COMPANY_NAME = "stormux";
const string I18N_PREF_PRODUCT_NAME = "Draugnorak";
const string I18N_PREF_SUBDIR = "config";
string activeLanguageCode = I18N_DEFAULT_LANGUAGE_CODE;
bool i18nInitialized = false;
bool i18nCatalogLoaded = false;
dictionary @i18nEnglishCatalog = dictionary();
dictionary @i18nActiveCatalog = dictionary();
dictionary @i18nExactEnglishToKey = dictionary();
dictionary @i18nMissingKeyLog = dictionary();
string[] i18nTemplateKeys;
string[] i18nTemplateEnglish;
string i18n_trim_whitespace(string text) {
if (text.length() == 0)
return text;
int start = 0;
int end = text.length() - 1;
while (start <= end) {
string ch = text.substr(start, 1);
if (ch != " " && ch != "\t" && ch != "\r" && ch != "\n")
break;
start++;
}
while (end >= start) {
string ch = text.substr(end, 1);
if (ch != " " && ch != "\t" && ch != "\r" && ch != "\n")
break;
end--;
}
if (end < start)
return "";
return text.substr(start, end - start + 1);
}
string i18n_trim_trailing_path_separator(string path) {
while (path.length() > 1) {
string last = path.substr(path.length() - 1, 1);
if (last != "/" && last != "\\")
break;
path = path.substr(0, path.length() - 1);
}
return path;
}
string i18n_get_path_basename(const string& in path) {
int lastSeparator = -1;
for (uint i = 0; i < path.length(); i++) {
string ch = path.substr(i, 1);
if (ch == "/" || ch == "\\") {
lastSeparator = int(i);
}
}
if (lastSeparator < 0)
return path;
int start = lastSeparator + 1;
if (start >= int(path.length()))
return "";
return path.substr(start);
}
string i18n_get_pref_directory_path() {
string path = join({DIRECTORY_APPDATA, I18N_PREF_COMPANY_NAME, I18N_PREF_PRODUCT_NAME, I18N_PREF_SUBDIR}, "/");
path = i18n_trim_trailing_path_separator(path);
if (path == "")
path = ".";
if (!directory_exists(path) && !directory_create(path)) {
return ".";
}
return path;
}
string i18n_get_pref_file_path() {
string prefDirectory = i18n_get_pref_directory_path();
if (prefDirectory == "." || prefDirectory == "") {
return I18N_LANGUAGE_PREF_FILENAME;
}
return prefDirectory + "/" + I18N_LANGUAGE_PREF_FILENAME;
}
bool i18n_read_file_text(const string& in filePath, string& out content) {
content = "";
file handle;
if (!handle.open(filePath, "rb"))
return false;
content = handle.read();
handle.close();
return true;
}
bool i18n_write_file_text(const string& in filePath, const string& in content) {
file handle;
if (!handle.open(filePath, "wb"))
return false;
int written = handle.write(content);
handle.close();
return written >= int(content.length());
}
string i18n_unescape_catalog_value(const string& in value) {
string result = "";
bool escaping = false;
for (uint i = 0; i < value.length(); i++) {
string ch = value.substr(i, 1);
if (!escaping) {
if (ch == "\\") {
escaping = true;
continue;
}
result += ch;
continue;
}
if (ch == "n") {
result += "\n";
} else if (ch == "r") {
result += "\r";
} else if (ch == "t") {
result += "\t";
} else {
result += ch;
}
escaping = false;
}
if (escaping)
result += "\\";
return result;
}
bool i18n_parse_catalog_line(const string& in lineText, string& out key, string& out value) {
key = "";
value = "";
string trimmed = i18n_trim_whitespace(lineText);
if (trimmed == "")
return false;
if (trimmed.substr(0, 1) == ";" || trimmed.substr(0, 1) == "#")
return false;
int separator = trimmed.find_first("=");
if (separator < 0)
return false;
string rawKey = i18n_trim_whitespace(trimmed.substr(0, separator));
string rawValue = "";
if (separator + 1 < int(trimmed.length())) {
rawValue = i18n_trim_whitespace(trimmed.substr(separator + 1));
}
if (rawKey == "")
return false;
key = rawKey;
value = i18n_unescape_catalog_value(rawValue);
return true;
}
bool i18n_load_catalog_file(const string& in filePath, dictionary @catalog) {
if (@catalog is null)
return false;
catalog.clear();
string data = "";
if (!i18n_read_file_text(filePath, data))
return false;
string[] lines = data.split("\n");
string currentSection = "";
for (uint i = 0; i < lines.length(); i++) {
string line = lines[i];
if (line.length() > 0 && line.substr(line.length() - 1) == "\r") {
line = line.substr(0, line.length() - 1);
}
string trimmed = i18n_trim_whitespace(line);
if (trimmed.length() >= 2 && trimmed.substr(0, 1) == "[" && trimmed.substr(trimmed.length() - 1) == "]") {
currentSection = i18n_trim_whitespace(trimmed.substr(1, trimmed.length() - 2));
continue;
}
string key = "";
string value = "";
if (!i18n_parse_catalog_line(trimmed, key, value))
continue;
if (currentSection != "") {
if (currentSection == "messages") {
// Keep message keys stable as msg.<id> so runtime template matching can resolve them directly.
if (key.find_first(".") < 0)
key = currentSection + "." + key;
} else {
string sectionPrefix = currentSection + ".";
if (key.find_first(sectionPrefix) != 0)
key = sectionPrefix + key;
}
}
catalog.set(key, value);
}
return catalog.get_size() > 0;
}
bool i18n_catalog_get_string(dictionary @catalog, const string& in key, string& out value) {
value = "";
if (@catalog is null)
return false;
if (catalog.get(key, value))
return true;
int valueInt = 0;
if (catalog.get(key, valueInt)) {
value = "" + valueInt;
return true;
}
double valueDouble = 0.0;
if (catalog.get(key, valueDouble)) {
value = "" + valueDouble;
return true;
}
bool valueBool = false;
if (catalog.get(key, valueBool)) {
value = valueBool ? "true" : "false";
return true;
}
return false;
}
bool i18n_has_placeholders(const string& in templateText) {
int open = templateText.find_first("{");
if (open < 0)
return false;
int close = templateText.find_first("}", open + 1);
return close > open;
}
int i18n_template_literal_length(const string& in templateText) {
int literalCount = 0;
bool inPlaceholder = false;
for (uint i = 0; i < templateText.length(); i++) {
string ch = templateText.substr(i, 1);
if (!inPlaceholder && ch == "{") {
inPlaceholder = true;
continue;
}
if (inPlaceholder && ch == "}") {
inPlaceholder = false;
continue;
}
if (!inPlaceholder) {
literalCount++;
}
}
return literalCount;
}
string i18n_lookup_key_with_fallback(const string& in key, const string& in defaultValue) {
string localized = "";
if (@i18nActiveCatalog !is null && i18n_catalog_get_string(i18nActiveCatalog, key, localized) && localized != "") {
return localized;
}
if (@i18nEnglishCatalog !is null && i18n_catalog_get_string(i18nEnglishCatalog, key, localized) && localized != "") {
return localized;
}
return defaultValue;
}
void i18n_rebuild_indexes() {
i18nExactEnglishToKey.clear();
i18nTemplateKeys.resize(0);
i18nTemplateEnglish.resize(0);
if (@i18nEnglishCatalog is null)
return;
string[] @keys = i18nEnglishCatalog.get_keys();
if (@keys is null)
return;
for (uint i = 0; i < keys.length(); i++) {
string key = keys[i];
if (key.find_first("meta.") == 0)
continue;
string english = "";
if (!i18n_catalog_get_string(i18nEnglishCatalog, key, english))
continue;
if (english == "")
continue;
if (i18n_has_placeholders(english)) {
if (i18n_template_literal_length(english) == 0)
continue;
i18nTemplateKeys.insert_last(key);
i18nTemplateEnglish.insert_last(english);
} else if (!i18nExactEnglishToKey.exists(english)) {
i18nExactEnglishToKey.set(english, key);
}
}
}
string tr(const string& in key) {
if (key == "")
return "";
string value = "";
if (@i18nActiveCatalog !is null && i18n_catalog_get_string(i18nActiveCatalog, key, value) && value != "")
return value;
if (@i18nEnglishCatalog !is null && i18n_catalog_get_string(i18nEnglishCatalog, key, value) && value != "")
return value;
if (!i18nMissingKeyLog.exists(key)) {
i18nMissingKeyLog.set(key, true);
}
return key;
}
string i18n_format_template(const string& in templateText, dictionary @args) {
string result = "";
uint cursor = 0;
while (cursor < templateText.length()) {
int open = templateText.find_first("{", cursor);
if (open < 0) {
result += templateText.substr(cursor);
break;
}
result += templateText.substr(cursor, open - cursor);
int close = templateText.find_first("}", open + 1);
if (close < 0) {
result += templateText.substr(open);
break;
}
string placeholder = templateText.substr(open + 1, close - open - 1);
string replacement = "";
if (!i18n_catalog_get_string(args, placeholder, replacement)) {
replacement = "{" + placeholder + "}";
}
result += replacement;
cursor = close + 1;
}
return result;
}
string trf(const string& in key, dictionary @args) {
string templateText = tr(key);
return i18n_format_template(templateText, args);
}
string trn(const string& in baseKey, int count, dictionary @args = null) {
string key = baseKey + ((count == 1) ? ".one" : ".other");
dictionary localArgs;
dictionary @argsHandle = @args;
if (@argsHandle is null) {
@argsHandle = @localArgs;
}
argsHandle.set("count", count);
return trf(key, argsHandle);
}
bool i18n_match_template(const string& in templateText, const string& in inputText, dictionary @captures) {
if (@captures is null)
return false;
captures.clear();
uint templateCursor = 0;
uint inputCursor = 0;
while (templateCursor < templateText.length()) {
int open = templateText.find_first("{", templateCursor);
if (open < 0) {
string tail = templateText.substr(templateCursor);
uint remaining = inputText.length() - inputCursor;
if (tail.length() != remaining)
return false;
if (tail.length() > 0 && inputText.substr(inputCursor, tail.length()) != tail)
return false;
inputCursor = inputText.length();
templateCursor = templateText.length();
break;
}
string literalBefore = templateText.substr(templateCursor, open - templateCursor);
if (literalBefore.length() > 0) {
if (inputCursor + literalBefore.length() > inputText.length())
return false;
if (inputText.substr(inputCursor, literalBefore.length()) != literalBefore)
return false;
inputCursor += literalBefore.length();
}
int close = templateText.find_first("}", open + 1);
if (close < 0)
return false;
string placeholder = templateText.substr(open + 1, close - open - 1);
if (placeholder == "")
return false;
int nextOpen = templateText.find_first("{", close + 1);
string nextLiteral = "";
if (nextOpen >= 0) {
nextLiteral = templateText.substr(close + 1, nextOpen - close - 1);
} else {
nextLiteral = templateText.substr(close + 1);
}
string captured = "";
if (nextLiteral == "") {
if (nextOpen >= 0)
return false;
if (inputCursor <= inputText.length()) {
captured = inputText.substr(inputCursor);
}
inputCursor = inputText.length();
} else {
int found = inputText.find_first(nextLiteral, inputCursor);
if (found < 0)
return false;
captured = inputText.substr(inputCursor, found - inputCursor);
inputCursor = found;
}
captures.set(placeholder, captured);
templateCursor = close + 1;
}
return inputCursor == inputText.length();
}
string i18n_translate_fragment_value(const string& in value) {
if (value == "" || activeLanguageCode == I18N_DEFAULT_LANGUAGE_CODE)
return value;
string key = "";
if (!i18n_catalog_get_string(i18nExactEnglishToKey, value, key))
return value;
return i18n_lookup_key_with_fallback(key, value);
}
string i18n_translate_speech_message(const string& in message) {
if (message == "")
return message;
if (!i18nCatalogLoaded)
return message;
if (activeLanguageCode == I18N_DEFAULT_LANGUAGE_CODE)
return message;
string key = "";
if (i18n_catalog_get_string(i18nExactEnglishToKey, message, key)) {
return i18n_lookup_key_with_fallback(key, message);
}
for (uint i = 0; i < i18nTemplateEnglish.length(); i++) {
dictionary captures;
if (!i18n_match_template(i18nTemplateEnglish[i], message, captures))
continue;
string[] @captureKeys = captures.get_keys();
if (@captureKeys !is null) {
for (uint keyIndex = 0; keyIndex < captureKeys.length(); keyIndex++) {
string captureKey = captureKeys[keyIndex];
string captureValue = "";
if (!i18n_catalog_get_string(captures, captureKey, captureValue))
continue;
captures.set(captureKey, i18n_translate_fragment_value(captureValue));
}
}
string localizedTemplate = i18n_lookup_key_with_fallback(i18nTemplateKeys[i], i18nTemplateEnglish[i]);
return i18n_format_template(localizedTemplate, captures);
}
return message;
}
string i18n_text(const string& in message) {
return i18n_translate_speech_message(message);
}
void i18n_translate_string_array_in_place(string[] &inout values) {
for (uint i = 0; i < values.length(); i++) {
values[i] = i18n_translate_speech_message(values[i]);
}
}
bool i18n_load_language(const string& in languageCode) {
if (@i18nActiveCatalog is null)
@i18nActiveCatalog = dictionary();
string normalized = i18n_trim_whitespace(languageCode);
if (normalized == "")
normalized = I18N_DEFAULT_LANGUAGE_CODE;
if (normalized == I18N_DEFAULT_LANGUAGE_CODE) {
i18nActiveCatalog.clear();
activeLanguageCode = I18N_DEFAULT_LANGUAGE_CODE;
i18nCatalogLoaded = true;
return true;
}
string catalogPath = I18N_LANGUAGE_DIRECTORY + "/" + normalized + ".ini";
dictionary loadedCatalog;
if (!i18n_load_catalog_file(catalogPath, loadedCatalog)) {
return false;
}
i18nActiveCatalog.clear();
string[] @loadedKeys = loadedCatalog.get_keys();
if (@loadedKeys !is null) {
for (uint i = 0; i < loadedKeys.length(); i++) {
string key = loadedKeys[i];
string value = "";
if (!i18n_catalog_get_string(loadedCatalog, key, value))
continue;
i18nActiveCatalog.set(key, value);
}
}
activeLanguageCode = normalized;
i18nCatalogLoaded = true;
return true;
}
void i18n_save_language_preference(const string& in languageCode) {
string code = i18n_trim_whitespace(languageCode);
if (code == "")
code = I18N_DEFAULT_LANGUAGE_CODE;
string content = I18N_LANGUAGE_PREF_KEY + "=" + code + "\n";
i18n_write_file_text(i18n_get_pref_file_path(), content);
}
string i18n_load_language_preference() {
string data = "";
if (!i18n_read_file_text(i18n_get_pref_file_path(), data))
return "";
string[] lines = data.split("\n");
for (uint i = 0; i < lines.length(); i++) {
string line = lines[i];
if (line.length() > 0 && line.substr(line.length() - 1) == "\r")
line = line.substr(0, line.length() - 1);
string key = "";
string value = "";
if (!i18n_parse_catalog_line(line, key, value))
continue;
if (key == I18N_LANGUAGE_PREF_KEY)
return i18n_trim_whitespace(value);
}
return "";
}
void i18n_get_available_languages(string[] &out languageCodes, string[] &out languageLabels) {
languageCodes.resize(0);
languageLabels.resize(0);
string[] @paths = glob(I18N_LANGUAGE_DIRECTORY + "/*.ini");
if (@paths is null)
return;
bool hasEnglish = false;
for (uint i = 0; i < paths.length(); i++) {
string fileName = i18n_get_path_basename(paths[i]);
string lowerFileName = fileName.lower();
if (lowerFileName.length() >= 13 && lowerFileName.substr(lowerFileName.length() - 13) == ".template.ini")
continue;
if (fileName.length() < 5)
continue;
if (fileName.substr(fileName.length() - 4).lower() != ".ini")
continue;
string code = fileName.substr(0, fileName.length() - 4);
if (code == "")
continue;
if (code.lower() == I18N_DEFAULT_LANGUAGE_CODE)
hasEnglish = true;
dictionary tempCatalog;
string label = code;
if (i18n_load_catalog_file(paths[i], tempCatalog)) {
string nativeName = "";
if (i18n_catalog_get_string(tempCatalog, "meta.native_name", nativeName) && nativeName != "") {
label = nativeName + " (" + code + ")";
} else if (i18n_catalog_get_string(tempCatalog, "meta.name", nativeName) && nativeName != "") {
label = nativeName + " (" + code + ")";
}
}
languageCodes.insert_last(code);
languageLabels.insert_last(label);
}
if (!hasEnglish) {
languageCodes.insert_last(I18N_DEFAULT_LANGUAGE_CODE);
languageLabels.insert_last(i18n_lookup_key_with_fallback("system.language.english_label", "English (en)"));
}
}
int i18n_find_language_index(const string[] @languageCodes, const string& in languageCode) {
if (@languageCodes is null || languageCodes.length() == 0)
return -1;
string target = i18n_trim_whitespace(languageCode).lower();
if (target == "")
return -1;
for (uint i = 0; i < languageCodes.length(); i++) {
if (languageCodes[i].lower() == target)
return i;
}
return -1;
}
int i18n_run_language_selection_menu(const string[] @languageCodes, const string[] @languageLabels,
int defaultIndex = 0) {
if (@languageCodes is null || @languageLabels is null)
return -1;
if (languageCodes.length() == 0 || languageLabels.length() == 0)
return -1;
int selection = defaultIndex;
if (selection < 0 || selection >= int(languageLabels.length()))
selection = 0;
string prompt = tr("system.language.select_prompt");
if (prompt.length() > 0 && prompt.substr(prompt.length() - 1) == ".") {
prompt = prompt.substr(0, prompt.length() - 1);
}
if (prompt != "") {
speak_with_history(prompt + " " + languageLabels[selection], true);
} else {
speak_with_history(languageLabels[selection], true);
}
while (true) {
wait(5);
handle_global_volume_keys();
if (key_pressed(KEY_DOWN)) {
play_menu_move_sound();
selection++;
if (selection >= int(languageLabels.length()))
selection = 0;
speak_with_history(languageLabels[selection], true);
}
if (key_pressed(KEY_UP)) {
play_menu_move_sound();
selection--;
if (selection < 0)
selection = languageLabels.length() - 1;
speak_with_history(languageLabels[selection], true);
}
if (key_pressed(KEY_RETURN)) {
play_menu_select_sound();
return selection;
}
if (key_pressed(KEY_ESCAPE)) {
exit();
}
}
return selection;
}
bool i18n_load_english_catalog() {
if (@i18nEnglishCatalog is null)
@i18nEnglishCatalog = dictionary();
if (!i18n_load_catalog_file(I18N_ENGLISH_CATALOG_PATH, i18nEnglishCatalog)) {
i18nEnglishCatalog.clear();
i18nEnglishCatalog.set("system.language.select_prompt", "Select your language.");
i18nEnglishCatalog.set("system.language.selected", "Language set to {language}.");
i18nEnglishCatalog.set("system.ui.window_title", "Draugnorak");
}
i18n_rebuild_indexes();
return i18nEnglishCatalog.get_size() > 0;
}
void i18n_init() {
if (i18nInitialized)
return;
i18nInitialized = true;
i18n_load_english_catalog();
speech_history_set_message_transform_callback(i18n_translate_speech_message);
ui_set_text_transform_callback(i18n_translate_speech_message);
string[] languageCodes;
string[] languageLabels;
i18n_get_available_languages(languageCodes, languageLabels);
string preferredLanguage = i18n_load_language_preference();
int preferredIndex = i18n_find_language_index(languageCodes, preferredLanguage);
bool promptedForLanguage = false;
if (preferredIndex < 0) {
promptedForLanguage = true;
int englishIndex = i18n_find_language_index(languageCodes, I18N_DEFAULT_LANGUAGE_CODE);
if (englishIndex < 0)
englishIndex = 0;
preferredIndex = i18n_run_language_selection_menu(languageCodes, languageLabels, englishIndex);
}
string chosenLanguage = I18N_DEFAULT_LANGUAGE_CODE;
if (preferredIndex >= 0 && preferredIndex < int(languageCodes.length())) {
chosenLanguage = languageCodes[preferredIndex];
}
if (!i18n_load_language(chosenLanguage)) {
i18n_load_language(I18N_DEFAULT_LANGUAGE_CODE);
chosenLanguage = I18N_DEFAULT_LANGUAGE_CODE;
promptedForLanguage = true;
}
i18n_save_language_preference(chosenLanguage);
if (promptedForLanguage) {
dictionary args;
string selectedLabel = chosenLanguage;
int selectedLanguageIndex = i18n_find_language_index(languageCodes, chosenLanguage);
if (selectedLanguageIndex >= 0 && selectedLanguageIndex < int(languageLabels.length())) {
selectedLabel = languageLabels[selectedLanguageIndex];
}
args.set("language", selectedLabel);
speak_with_history(trf("system.language.selected", args), true);
}
}
+23 -44
View File
@@ -94,38 +94,14 @@ void clamp_arrows_to_quiver_limit() {
set_personal_count(ITEM_ARROWS, maxArrows);
if (maxArrows == 0) {
speak_with_history("You need a quiver to carry arrows.", true);
speak_need_quiver_for_arrows();
} else {
speak_with_history("You can only carry " + maxArrows + " arrows with your current quivers.", true);
}
}
string get_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";
if (equip_type == EQUIP_FISHING_POLE)
return "Fishing Pole";
return "Unknown";
return get_base_equipment_name(equip_type);
}
bool equipment_available(int equip_type) {
@@ -481,7 +457,9 @@ void activate_quick_slot(int slot_index) {
int count = get_item_count_for_type(item_type);
speak_with_history(get_item_count_label(item_type, count) + ".", true);
} else {
speak_with_history("No item bound to slot " + slot_index + ".", true);
dictionary args;
args.set("slot", slot_index);
speak_with_history(trf("system.quick_slot.no_item_bound", args), true);
}
return;
}
@@ -556,34 +534,35 @@ string format_favor(double value) {
string get_equipped_weapon_name() {
if (spear_equipped)
return "Spear";
return get_base_equipment_name(EQUIP_SPEAR);
if (axe_equipped)
return "Stone Axe";
return get_base_equipment_name(EQUIP_AXE);
if (sling_equipped)
return "Sling";
return get_base_equipment_name(EQUIP_SLING);
if (bow_equipped)
return "Bow";
return get_base_equipment_name(EQUIP_BOW);
if (fishing_pole_equipped)
return "Fishing Pole";
return "None";
return get_base_equipment_name(EQUIP_FISHING_POLE);
return i18n_lookup_key_with_fallback("system.equipment.name.none", "None");
}
string get_speed_status() {
string status = "";
int rune_bonus = get_total_rune_walk_speed_bonus();
if (blessing_speed_active) {
status = "blessed";
} else if (equipped_feet == EQUIP_MOCCASINS && rune_bonus > 0) {
status = "boosted by moccasins and runes";
} else if (equipped_feet == EQUIP_MOCCASINS) {
status = "boosted by moccasins";
} else if (rune_bonus > 0) {
status = "boosted by runes";
} else {
status = "normal";
return i18n_lookup_key_with_fallback("system.character.speed.blessed", "blessed");
}
return status;
if (equipped_feet == EQUIP_MOCCASINS && rune_bonus > 0) {
return i18n_lookup_key_with_fallback(
"system.character.speed.boosted_moccasins_and_runes", "boosted by moccasins and runes");
}
if (equipped_feet == EQUIP_MOCCASINS) {
return i18n_lookup_key_with_fallback("system.character.speed.boosted_moccasins", "boosted by moccasins");
}
if (rune_bonus > 0) {
return i18n_lookup_key_with_fallback("system.character.speed.boosted_runes", "boosted by runes");
}
return i18n_lookup_key_with_fallback("system.character.speed.normal", "normal");
}
void cleanup_equipment_after_inventory_change() {
+1 -1
View File
@@ -37,7 +37,7 @@ void check_inventory_keys(int x) {
run_inventory_root_menu();
} else {
if (in_base && world_storages.length() == 0) {
speak_with_history("No storage built.", true);
speak_with_history(tr("system.storage.no_storage_built"), true);
}
run_inventory_menu(false);
}
+28 -6
View File
@@ -281,20 +281,20 @@ int get_smoked_fish_yield(int weight) {
// Item metadata lookups
string get_item_label(int item_type) {
if (item_type < 0 || item_type >= ITEM_COUNT)
return "items";
return item_registry[item_type].name;
return i18n_lookup_key_with_fallback("system.item.label.items", "items");
return i18n_translate_speech_message(item_registry[item_type].name);
}
string get_item_label_singular(int item_type) {
if (item_type < 0 || item_type >= ITEM_COUNT)
return "item";
return item_registry[item_type].singular;
return i18n_lookup_key_with_fallback("system.item.label.item", "item");
return i18n_translate_speech_message(item_registry[item_type].singular);
}
string get_item_display_name(int item_type) {
if (item_type < 0 || item_type >= ITEM_COUNT)
return "Unknown";
return item_registry[item_type].display_name;
return i18n_lookup_key_with_fallback("system.item.label.unknown", "Unknown");
return i18n_translate_speech_message(item_registry[item_type].display_name);
}
double get_item_favor_value(int item_type) {
@@ -311,6 +311,28 @@ string get_item_label_for_count(int item_type, int count) {
return get_item_label(item_type);
}
string format_cant_carry_any_more_label(const string& in item_label) {
dictionary args;
args.set("item", item_label);
return trf("system.inventory.cant_carry_any_more_item", args);
}
void speak_cant_carry_any_more_label(const string& in item_label) {
speak_with_history(format_cant_carry_any_more_label(item_label), true);
}
void speak_cant_carry_any_more_item(int item_type) {
speak_cant_carry_any_more_label(get_item_label(item_type));
}
string get_need_quiver_for_arrows_message() {
return tr("system.inventory.need_quiver_for_arrows");
}
void speak_need_quiver_for_arrows() {
speak_with_history(get_need_quiver_for_arrows_message(), true);
}
// Encode runed item info into a negative item type
// Format: -1000 - (equip_type * 10) - rune_type
// This allows us to extract both equip_type and rune_type from a single int
+1
View File
@@ -260,6 +260,7 @@ void run_action_menu(int x) {
options.insert_last("Check fishing pole");
action_types.insert_last(7);
}
i18n_translate_string_array_in_place(options);
string filter_text = "";
int[] filtered_indices;
string[] filtered_options;
+1
View File
@@ -53,6 +53,7 @@ void run_base_info_menu() {
} else {
options.insert_last("Stable not built");
}
i18n_translate_string_array_in_place(options);
string filter_text = "";
int[] filtered_indices;
string[] filtered_options;
+83 -41
View File
@@ -3,16 +3,18 @@
bool run_pet_abandon_confirm_menu() {
if (!petActive) {
speak_with_history("No pet.", true);
speak_with_history(tr("system.character.pet.no_pet"), true);
return false;
}
string[] options;
options.insert_last("No");
options.insert_last("Yes");
options.insert_last(tr("system.option.no"));
options.insert_last(tr("system.option.yes"));
int selection = 0;
speak_with_history("Really abandon your pet? " + options[selection], true);
dictionary promptArgs;
promptArgs.set("option", options[selection]);
speak_with_history(trf("system.character.pet.abandon_confirm", promptArgs), true);
while (true) {
wait(5);
@@ -20,7 +22,7 @@ bool run_pet_abandon_confirm_menu() {
return false;
}
if (key_pressed(KEY_ESCAPE)) {
speak_with_history("Canceled.", true);
speak_with_history(tr("system.menu.canceled"), true);
return false;
}
if (key_pressed(KEY_DOWN)) {
@@ -42,7 +44,7 @@ bool run_pet_abandon_confirm_menu() {
if (selection == 1) {
return abandon_pet();
}
speak_with_history("Canceled.", true);
speak_with_history(tr("system.menu.canceled"), true);
return false;
}
}
@@ -55,63 +57,98 @@ void run_character_info_menu() {
string[] missing_slots;
if (equipped_head == EQUIP_HAT)
equipped_clothing.insert_last("Skin Hat");
equipped_clothing.insert_last(get_base_equipment_name(EQUIP_HAT));
else
missing_slots.insert_last("head");
missing_slots.insert_last(tr("system.character.slot.head"));
if (equipped_torso == EQUIP_TUNIC)
equipped_clothing.insert_last("Skin Tunic");
equipped_clothing.insert_last(get_base_equipment_name(EQUIP_TUNIC));
else
missing_slots.insert_last("torso");
missing_slots.insert_last(tr("system.character.slot.torso"));
if (equipped_arms == EQUIP_POUCH)
equipped_clothing.insert_last("Skin Pouch");
equipped_clothing.insert_last(get_base_equipment_name(EQUIP_POUCH));
else if (equipped_arms == EQUIP_BACKPACK)
equipped_clothing.insert_last("Backpack");
equipped_clothing.insert_last(get_base_equipment_name(EQUIP_BACKPACK));
else
missing_slots.insert_last("arms");
missing_slots.insert_last(tr("system.character.slot.arms"));
if (equipped_hands == EQUIP_GLOVES)
equipped_clothing.insert_last("Skin Gloves");
equipped_clothing.insert_last(get_base_equipment_name(EQUIP_GLOVES));
else
missing_slots.insert_last("hands");
missing_slots.insert_last(tr("system.character.slot.hands"));
if (equipped_legs == EQUIP_PANTS)
equipped_clothing.insert_last("Skin Pants");
equipped_clothing.insert_last(get_base_equipment_name(EQUIP_PANTS));
else
missing_slots.insert_last("legs");
missing_slots.insert_last(tr("system.character.slot.legs"));
if (equipped_feet == EQUIP_MOCCASINS)
equipped_clothing.insert_last("Moccasins");
equipped_clothing.insert_last(get_base_equipment_name(EQUIP_MOCCASINS));
else
missing_slots.insert_last("feet");
missing_slots.insert_last(tr("system.character.slot.feet"));
string[] options;
int petOptionIndex = -1;
if (player_name != "") {
string sex_label = (player_sex == SEX_FEMALE) ? "Female" : "Male";
options.insert_last("Name " + player_name + ". Sex " + sex_label + ".");
string sex_label = (player_sex == SEX_FEMALE) ? tr("system.sex.female") : tr("system.sex.male");
dictionary nameArgs;
nameArgs.set("name", player_name);
nameArgs.set("sex", sex_label);
options.insert_last(trf("system.character.info.name_sex", nameArgs));
} else {
options.insert_last("Name unknown.");
options.insert_last(tr("system.character.info.name_unknown"));
}
options.insert_last("Health " + player_health + " of " + max_health + ".");
options.insert_last("Weapon " + get_equipped_weapon_name() + ".");
dictionary healthArgs;
healthArgs.set("health", player_health);
healthArgs.set("max", max_health);
options.insert_last(trf("system.character.info.health", healthArgs));
dictionary weaponArgs;
weaponArgs.set("weapon", get_equipped_weapon_name());
options.insert_last(trf("system.character.info.weapon", weaponArgs));
if (equipped_clothing.length() > 0) {
options.insert_last("Clothing equipped: " + join_string_list(equipped_clothing) + ".");
dictionary clothingArgs;
clothingArgs.set("items", join_string_list(equipped_clothing));
options.insert_last(trf("system.character.info.clothing_equipped", clothingArgs));
} else {
options.insert_last("No clothing equipped.");
options.insert_last(tr("system.character.info.no_clothing"));
}
if (missing_slots.length() > 0) {
options.insert_last("Missing " + join_string_list(missing_slots) + ".");
dictionary missingArgs;
missingArgs.set("slots", join_string_list(missing_slots));
options.insert_last(trf("system.character.info.missing", missingArgs));
}
options.insert_last("Favor " + format_favor(favor) + ".");
options.insert_last("Speed " + get_speed_status() + ".");
dictionary favorArgs;
favorArgs.set("favor", format_favor(favor));
options.insert_last(trf("system.character.info.favor", favorArgs));
dictionary speedArgs;
speedArgs.set("status", get_speed_status());
options.insert_last(trf("system.character.info.speed", speedArgs));
if (petActive) {
string petInfo = "Pet " + get_pet_display_name() + ". " + petHealth + " health of " + PET_HEALTH_MAX + ".";
petInfo += " Loyalty " + petLoyalty + " of " + PET_LOYALTY_MAX + ".";
dictionary petArgs;
petArgs.set("pet", get_pet_display_name());
petArgs.set("health", petHealth);
petArgs.set("max_health", PET_HEALTH_MAX);
petArgs.set("loyalty", petLoyalty);
petArgs.set("max_loyalty", PET_LOYALTY_MAX);
string petInfo = trf("system.character.info.pet", petArgs);
if (petKnockoutHoursRemaining > 0) {
string hourLabel = (petKnockoutHoursRemaining == 1) ? "hour" : "hours";
petInfo += " Knocked out. " + petKnockoutHoursRemaining + " " + hourLabel + " remaining.";
dictionary petKnockoutArgs;
petKnockoutArgs.set("hours", petKnockoutHoursRemaining);
if (petKnockoutHoursRemaining == 1) {
petKnockoutArgs.set("hour_label", tr("system.character.word.hour"));
} else {
petKnockoutArgs.set("hour_label", tr("system.character.word.hours"));
}
petInfo += " " + trf("system.character.info.pet_knocked_out", petKnockoutArgs);
}
options.insert_last(petInfo);
petOptionIndex = options.length() - 1;
} else {
options.insert_last("Pet none.");
options.insert_last(tr("system.character.info.pet_none"));
}
int selection = 0;
@@ -120,9 +157,11 @@ void run_character_info_menu() {
string[] filtered_options;
apply_menu_filter(filter_text, options, filtered_indices, filtered_options);
if (filtered_options.length() == 0) {
speak_with_history("Character info. No options.", true);
speak_with_history(tr("system.character.menu.no_options"), true);
} else {
speak_with_history("Character info. " + filtered_options[selection], true);
dictionary promptArgs;
promptArgs.set("option", filtered_options[selection]);
speak_with_history(trf("system.character.menu.prompt", promptArgs), true);
}
while (true) {
@@ -131,7 +170,7 @@ void run_character_info_menu() {
return;
}
if (key_pressed(KEY_ESCAPE)) {
speak_with_history("Closed.", true);
speak_with_history(tr("system.menu.closed"), true);
break;
}
@@ -140,9 +179,11 @@ void run_character_info_menu() {
if (filter_changed) {
if (filtered_options.length() == 0) {
if (filter_text.length() > 0) {
speak_with_history("No matches for " + filter_text + ".", true);
dictionary filterArgs;
filterArgs.set("arg1", filter_text);
speak_with_history(trf("system.menu.no_matches", filterArgs), true);
} else {
speak_with_history("No options.", true);
speak_with_history(tr("system.menu.no_options"), true);
}
} else {
speak_with_history(filtered_options[selection], true);
@@ -176,8 +217,9 @@ void run_character_info_menu() {
int optionIndex = filtered_indices[selection];
if (optionIndex == petOptionIndex && petActive) {
if (is_pet_knocked_out()) {
speak_with_history(
"Your " + get_pet_display_name() + " is unconscious. You can't abandon it right now.", true);
dictionary args;
args.set("pet", get_pet_display_name());
speak_with_history(trf("system.character.pet.unconscious_cannot_abandon", args), true);
continue;
}
+31 -25
View File
@@ -54,6 +54,12 @@ bool is_runed_item_equipped(int equip_type, int rune_type) {
return get_equipped_rune_for_slot(equip_type) == rune_type;
}
string get_equipment_menu_equipped_suffix(bool equipped) {
if (!equipped)
return "";
return i18n_lookup_key_with_fallback("system.equipment.menu.equipped_suffix", " (equipped)");
}
void run_equipment_menu() {
speak_with_history("Equipment menu.", true);
@@ -64,74 +70,74 @@ void run_equipment_menu() {
// Add unruned items
if (get_personal_count(ITEM_SPEARS) > 0) {
string status = is_runed_item_equipped(EQUIP_SPEAR, RUNE_NONE) ? " (equipped)" : "";
options.insert_last("Spear" + status);
string status = get_equipment_menu_equipped_suffix(is_runed_item_equipped(EQUIP_SPEAR, RUNE_NONE));
options.insert_last(get_full_equipment_name(EQUIP_SPEAR, RUNE_NONE) + status);
equipment_types.insert_last(EQUIP_SPEAR);
rune_types.insert_last(RUNE_NONE);
}
if (get_personal_count(ITEM_SLINGS) > 0) {
string status = is_runed_item_equipped(EQUIP_SLING, RUNE_NONE) ? " (equipped)" : "";
options.insert_last("Sling" + status);
string status = get_equipment_menu_equipped_suffix(is_runed_item_equipped(EQUIP_SLING, RUNE_NONE));
options.insert_last(get_full_equipment_name(EQUIP_SLING, RUNE_NONE) + status);
equipment_types.insert_last(EQUIP_SLING);
rune_types.insert_last(RUNE_NONE);
}
if (get_personal_count(ITEM_AXES) > 0) {
string status = is_runed_item_equipped(EQUIP_AXE, RUNE_NONE) ? " (equipped)" : "";
options.insert_last("Stone Axe" + status);
string status = get_equipment_menu_equipped_suffix(is_runed_item_equipped(EQUIP_AXE, RUNE_NONE));
options.insert_last(get_full_equipment_name(EQUIP_AXE, RUNE_NONE) + status);
equipment_types.insert_last(EQUIP_AXE);
rune_types.insert_last(RUNE_NONE);
}
if (get_personal_count(ITEM_BOWS) > 0) {
string status = is_runed_item_equipped(EQUIP_BOW, RUNE_NONE) ? " (equipped)" : "";
options.insert_last("Bow" + status);
string status = get_equipment_menu_equipped_suffix(is_runed_item_equipped(EQUIP_BOW, RUNE_NONE));
options.insert_last(get_full_equipment_name(EQUIP_BOW, RUNE_NONE) + status);
equipment_types.insert_last(EQUIP_BOW);
rune_types.insert_last(RUNE_NONE);
}
if (get_personal_count(ITEM_FISHING_POLES) > 0) {
string status = is_runed_item_equipped(EQUIP_FISHING_POLE, RUNE_NONE) ? " (equipped)" : "";
options.insert_last("Fishing Pole" + status);
string status = get_equipment_menu_equipped_suffix(is_runed_item_equipped(EQUIP_FISHING_POLE, RUNE_NONE));
options.insert_last(get_full_equipment_name(EQUIP_FISHING_POLE, RUNE_NONE) + status);
equipment_types.insert_last(EQUIP_FISHING_POLE);
rune_types.insert_last(RUNE_NONE);
}
if (get_personal_count(ITEM_SKIN_HATS) > 0) {
string status = is_runed_item_equipped(EQUIP_HAT, RUNE_NONE) ? " (equipped)" : "";
options.insert_last("Skin Hat" + status);
string status = get_equipment_menu_equipped_suffix(is_runed_item_equipped(EQUIP_HAT, RUNE_NONE));
options.insert_last(get_full_equipment_name(EQUIP_HAT, RUNE_NONE) + status);
equipment_types.insert_last(EQUIP_HAT);
rune_types.insert_last(RUNE_NONE);
}
if (get_personal_count(ITEM_SKIN_GLOVES) > 0) {
string status = is_runed_item_equipped(EQUIP_GLOVES, RUNE_NONE) ? " (equipped)" : "";
options.insert_last("Skin Gloves" + status);
string status = get_equipment_menu_equipped_suffix(is_runed_item_equipped(EQUIP_GLOVES, RUNE_NONE));
options.insert_last(get_full_equipment_name(EQUIP_GLOVES, RUNE_NONE) + status);
equipment_types.insert_last(EQUIP_GLOVES);
rune_types.insert_last(RUNE_NONE);
}
if (get_personal_count(ITEM_SKIN_PANTS) > 0) {
string status = is_runed_item_equipped(EQUIP_PANTS, RUNE_NONE) ? " (equipped)" : "";
options.insert_last("Skin Pants" + status);
string status = get_equipment_menu_equipped_suffix(is_runed_item_equipped(EQUIP_PANTS, RUNE_NONE));
options.insert_last(get_full_equipment_name(EQUIP_PANTS, RUNE_NONE) + status);
equipment_types.insert_last(EQUIP_PANTS);
rune_types.insert_last(RUNE_NONE);
}
if (get_personal_count(ITEM_SKIN_TUNICS) > 0) {
string status = is_runed_item_equipped(EQUIP_TUNIC, RUNE_NONE) ? " (equipped)" : "";
options.insert_last("Skin Tunic" + status);
string status = get_equipment_menu_equipped_suffix(is_runed_item_equipped(EQUIP_TUNIC, RUNE_NONE));
options.insert_last(get_full_equipment_name(EQUIP_TUNIC, RUNE_NONE) + status);
equipment_types.insert_last(EQUIP_TUNIC);
rune_types.insert_last(RUNE_NONE);
}
if (get_personal_count(ITEM_MOCCASINS) > 0) {
string status = is_runed_item_equipped(EQUIP_MOCCASINS, RUNE_NONE) ? " (equipped)" : "";
options.insert_last("Moccasins" + status);
string status = get_equipment_menu_equipped_suffix(is_runed_item_equipped(EQUIP_MOCCASINS, RUNE_NONE));
options.insert_last(get_full_equipment_name(EQUIP_MOCCASINS, RUNE_NONE) + status);
equipment_types.insert_last(EQUIP_MOCCASINS);
rune_types.insert_last(RUNE_NONE);
}
if (get_personal_count(ITEM_SKIN_POUCHES) > 0) {
string status = is_runed_item_equipped(EQUIP_POUCH, RUNE_NONE) ? " (equipped)" : "";
options.insert_last("Skin Pouch" + status);
string status = get_equipment_menu_equipped_suffix(is_runed_item_equipped(EQUIP_POUCH, RUNE_NONE));
options.insert_last(get_full_equipment_name(EQUIP_POUCH, RUNE_NONE) + status);
equipment_types.insert_last(EQUIP_POUCH);
rune_types.insert_last(RUNE_NONE);
}
if (get_personal_count(ITEM_BACKPACKS) > 0) {
string status = is_runed_item_equipped(EQUIP_BACKPACK, RUNE_NONE) ? " (equipped)" : "";
options.insert_last("Backpack" + status);
string status = get_equipment_menu_equipped_suffix(is_runed_item_equipped(EQUIP_BACKPACK, RUNE_NONE));
options.insert_last(get_full_equipment_name(EQUIP_BACKPACK, RUNE_NONE) + status);
equipment_types.insert_last(EQUIP_BACKPACK);
rune_types.insert_last(RUNE_NONE);
}
@@ -148,7 +154,7 @@ void run_equipment_menu() {
if (count <= 0)
continue;
string name = get_full_equipment_name(equip_type, rune_type);
string status = is_runed_item_equipped(equip_type, rune_type) ? " (equipped)" : "";
string status = get_equipment_menu_equipped_suffix(is_runed_item_equipped(equip_type, rune_type));
options.insert_last(name + status);
equipment_types.insert_last(equip_type);
rune_types.insert_last(rune_type);
+29 -12
View File
@@ -1,6 +1,18 @@
// Personal inventory menu system
// Functions for displaying and managing personal inventory
void speak_inventory_menu_prompt(const string& in option_label) {
dictionary args;
args.set("option", option_label);
speak_with_history(trf("system.inventory.menu.prompt", args), true);
}
void speak_menu_no_matches(const string& in filter_text) {
dictionary args;
args.set("arg1", filter_text);
speak_with_history(trf("system.menu.no_matches", args), true);
}
void build_personal_inventory_options(string[] @options, int[] @item_types) {
options.resize(0);
item_types.resize(0);
@@ -78,10 +90,9 @@ void show_inventory() {
}
void run_inventory_root_menu() {
speak_with_history("Inventory menu.", true);
int selection = 0;
string[] options = {"Personal inventory", "Base storage"};
string[] options = {tr("system.inventory.option.personal"), tr("system.inventory.option.base_storage")};
speak_inventory_menu_prompt(options[selection]);
while (true) {
wait(5);
@@ -89,7 +100,7 @@ void run_inventory_root_menu() {
return;
}
if (key_pressed(KEY_ESCAPE)) {
speak_with_history("Closed.", true);
speak_with_history(tr("system.menu.closed"), true);
break;
}
@@ -121,8 +132,6 @@ void run_inventory_root_menu() {
}
void run_inventory_menu(bool allow_deposit) {
speak_with_history("Inventory menu.", true);
int selection = 0;
string[] options;
int[] item_types;
@@ -131,6 +140,11 @@ void run_inventory_menu(bool allow_deposit) {
int[] filtered_indices;
string[] filtered_options;
apply_menu_filter(filter_text, options, filtered_indices, filtered_options);
if (filtered_options.length() == 0) {
speak_with_history(tr("system.inventory.menu.no_options"), true);
} else {
speak_inventory_menu_prompt(filtered_options[selection]);
}
while (true) {
wait(5);
@@ -138,7 +152,7 @@ void run_inventory_menu(bool allow_deposit) {
return;
}
if (key_pressed(KEY_ESCAPE)) {
speak_with_history("Closed.", true);
speak_with_history(tr("system.menu.closed"), true);
break;
}
@@ -147,9 +161,9 @@ void run_inventory_menu(bool allow_deposit) {
if (filter_changed) {
if (filtered_options.length() == 0) {
if (filter_text.length() > 0) {
speak_with_history("No matches for " + filter_text + ".", true);
speak_menu_no_matches(filter_text);
} else {
speak_with_history("No options.", true);
speak_with_history(tr("system.menu.no_options"), true);
}
} else {
speak_with_history(filtered_options[selection], true);
@@ -182,7 +196,10 @@ void run_inventory_menu(bool allow_deposit) {
if (slot_index >= 0 && slot_index < int(item_count_slots.length())) {
item_count_slots[slot_index] = item_type;
string name = get_item_count_binding_name(item_type);
speak_with_history(name + " count set to slot " + slot_index + ".", true);
dictionary args;
args.set("name", name);
args.set("slot", slot_index);
speak_with_history(trf("system.inventory.count_set_to_slot", args), true);
}
}
@@ -197,7 +214,7 @@ void run_inventory_menu(bool allow_deposit) {
if (filtered_options.length() > 0) {
speak_with_history(filtered_options[selection], true);
} else if (filter_text.length() > 0) {
speak_with_history("No matches for " + filter_text + ".", true);
speak_menu_no_matches(filter_text);
}
}
}
@@ -213,7 +230,7 @@ void run_inventory_menu(bool allow_deposit) {
if (filtered_options.length() > 0) {
speak_with_history(filtered_options[selection], true);
} else if (filter_text.length() > 0) {
speak_with_history("No matches for " + filter_text + ".", true);
speak_menu_no_matches(filter_text);
}
}
}
+46 -28
View File
@@ -1,6 +1,18 @@
// Base storage menu system
// Functions for interacting with base storage (deposit/withdraw items)
void speak_storage_menu_prompt(const string& in option_label) {
dictionary args;
args.set("option", option_label);
speak_with_history(trf("system.storage.menu.prompt", args), true);
}
void speak_storage_menu_no_matches(const string& in filter_text) {
dictionary args;
args.set("arg1", filter_text);
speak_with_history(trf("system.menu.no_matches", args), true);
}
void move_small_game_to_storage(int amount) {
for (int i = 0; i < amount; i++) {
string game_type = "small game";
@@ -31,8 +43,11 @@ void move_fish_to_personal(int amount) {
move_fish_weights_to_personal(amount);
}
int prompt_transfer_amount(const string prompt, int max_amount) {
string input = ui_input_box("Inventory", prompt + " (max " + max_amount + ")", "");
int prompt_transfer_amount(const string prompt_label, int max_amount) {
dictionary args;
args.set("prompt", prompt_label);
args.set("max", max_amount);
string input = ui_input_box(tr("system.storage.window_title"), trf("system.storage.transfer_prompt", args), "");
int amount = parse_int(input);
if (amount <= 0)
return 0;
@@ -51,27 +66,27 @@ void deposit_item(int item_type) {
cleanup_equipment_after_inventory_change();
speak_with_history("Deposited " + name + ".", true);
} else {
speak_with_history("Nothing to deposit.", true);
speak_with_history(tr("system.storage.nothing_to_deposit"), true);
}
return;
}
// Handle legacy -2 marker (shouldn't happen anymore but keep for safety)
if (item_type == -2) {
speak_with_history("Runed items cannot be deposited into storage.", true);
speak_with_history(tr("system.storage.runed_cannot_deposit"), true);
return;
}
int available = get_personal_count(item_type);
if (available <= 0) {
speak_with_history("Nothing to deposit.", true);
speak_with_history(tr("system.storage.nothing_to_deposit"), true);
return;
}
int capacity = get_storage_stack_limit() - get_storage_count(item_type);
if (capacity <= 0) {
speak_with_history("Storage for that item is full.", true);
speak_with_history(tr("system.storage.item_full"), true);
return;
}
int max_transfer = (available < capacity) ? available : capacity;
int amount = prompt_transfer_amount("Deposit how many?", max_transfer);
int amount = prompt_transfer_amount(tr("system.storage.deposit_how_many"), max_transfer);
if (amount <= 0)
return;
@@ -98,7 +113,7 @@ void deposit_item_max(int item_type) {
decode_runed_item_type(item_type, equip_type, rune_type);
int personal = get_runed_item_count(equip_type, rune_type);
if (personal <= 0) {
speak_with_history("Nothing to deposit.", true);
speak_with_history(tr("system.storage.nothing_to_deposit"), true);
return;
}
int count = 0;
@@ -113,18 +128,18 @@ void deposit_item_max(int item_type) {
}
// Handle legacy -2 marker
if (item_type == -2) {
speak_with_history("Runed items cannot be deposited into storage.", true);
speak_with_history(tr("system.storage.runed_cannot_deposit"), true);
return;
}
int available = get_personal_count(item_type);
if (available <= 0) {
speak_with_history("Nothing to deposit.", true);
speak_with_history(tr("system.storage.nothing_to_deposit"), true);
return;
}
int capacity = get_storage_stack_limit() - get_storage_count(item_type);
if (capacity <= 0) {
speak_with_history("Storage for that item is full.", true);
speak_with_history(tr("system.storage.item_full"), true);
return;
}
@@ -155,31 +170,31 @@ void withdraw_item(int item_type) {
string name = "Runed " + get_base_equipment_name(equip_type) + " of " + get_rune_effect_name(rune_type);
speak_with_history("Withdrew " + name + ".", true);
} else {
speak_with_history("Nothing to withdraw.", true);
speak_with_history(tr("system.storage.nothing_to_withdraw"), true);
}
return;
}
int available = get_storage_count(item_type);
if (available <= 0) {
speak_with_history("Nothing to withdraw.", true);
speak_with_history(tr("system.storage.nothing_to_withdraw"), true);
return;
}
int capacity = 0;
if (item_type == ITEM_ARROWS) {
capacity = get_arrow_limit() - get_personal_count(ITEM_ARROWS);
if (capacity <= 0) {
speak_with_history("You can't carry any more arrows.", true);
speak_cant_carry_any_more_item(ITEM_ARROWS);
return;
}
} else {
capacity = get_personal_stack_limit() - get_personal_count(item_type);
}
if (capacity <= 0) {
speak_with_history("You can't carry any more " + get_item_label(item_type) + ".", true);
speak_cant_carry_any_more_item(item_type);
return;
}
int max_transfer = (available < capacity) ? available : capacity;
int amount = prompt_transfer_amount("Withdraw how many?", max_transfer);
int amount = prompt_transfer_amount(tr("system.storage.withdraw_how_many"), max_transfer);
if (amount <= 0)
return;
@@ -205,7 +220,7 @@ void withdraw_item_max(int item_type) {
decode_runed_item_type(item_type, equip_type, rune_type);
int stored = get_stored_runed_item_count(equip_type, rune_type);
if (stored <= 0) {
speak_with_history("Nothing to withdraw.", true);
speak_with_history(tr("system.storage.nothing_to_withdraw"), true);
return;
}
int count = 0;
@@ -219,7 +234,7 @@ void withdraw_item_max(int item_type) {
}
int available = get_storage_count(item_type);
if (available <= 0) {
speak_with_history("Nothing to withdraw.", true);
speak_with_history(tr("system.storage.nothing_to_withdraw"), true);
return;
}
@@ -229,7 +244,7 @@ void withdraw_item_max(int item_type) {
personalLimit = get_arrow_limit();
currentPersonal = get_personal_count(ITEM_ARROWS);
if (personalLimit <= 0) {
speak_with_history("You need a quiver to carry arrows.", true);
speak_need_quiver_for_arrows();
return;
}
} else {
@@ -239,7 +254,7 @@ void withdraw_item_max(int item_type) {
int space = personalLimit - currentPersonal;
if (space <= 0) {
speak_with_history("Can't carry any more.", true);
speak_cant_carry_any_more_item(item_type);
return;
}
@@ -293,12 +308,10 @@ void build_storage_inventory_options(string[] @options, int[] @item_types) {
void run_storage_menu() {
if (world_storages.length() == 0) {
speak_with_history("No storage built.", true);
speak_with_history(tr("system.storage.no_storage_built"), true);
return;
}
speak_with_history("Base storage.", true);
int selection = 0;
string[] options;
int[] item_types;
@@ -307,6 +320,11 @@ void run_storage_menu() {
int[] filtered_indices;
string[] filtered_options;
apply_menu_filter(filter_text, options, filtered_indices, filtered_options);
if (filtered_options.length() == 0) {
speak_with_history(tr("system.storage.menu.no_options"), true);
} else {
speak_storage_menu_prompt(filtered_options[selection]);
}
while (true) {
wait(5);
@@ -314,7 +332,7 @@ void run_storage_menu() {
return;
}
if (key_pressed(KEY_ESCAPE)) {
speak_with_history("Closed.", true);
speak_with_history(tr("system.menu.closed"), true);
break;
}
@@ -323,9 +341,9 @@ void run_storage_menu() {
if (filter_changed) {
if (filtered_options.length() == 0) {
if (filter_text.length() > 0) {
speak_with_history("No matches for " + filter_text + ".", true);
speak_storage_menu_no_matches(filter_text);
} else {
speak_with_history("No options.", true);
speak_with_history(tr("system.menu.no_options"), true);
}
} else {
speak_with_history(filtered_options[selection], true);
@@ -363,7 +381,7 @@ void run_storage_menu() {
if (filtered_options.length() > 0) {
speak_with_history(filtered_options[selection], true);
} else if (filter_text.length() > 0) {
speak_with_history("No matches for " + filter_text + ".", true);
speak_storage_menu_no_matches(filter_text);
}
}
}
@@ -379,7 +397,7 @@ void run_storage_menu() {
if (filtered_options.length() > 0) {
speak_with_history(filtered_options[selection], true);
} else if (filter_text.length() > 0) {
speak_with_history("No matches for " + filter_text + ".", true);
speak_storage_menu_no_matches(filter_text);
}
}
}
+13 -3
View File
@@ -228,10 +228,19 @@ int get_pet_listener_pos() {
}
string get_health_report() {
string report = player_health + " health of " + max_health;
dictionary playerArgs;
playerArgs.set("health", player_health);
playerArgs.set("max", max_health);
string report = trf("system.character.report.player", playerArgs);
if (petActive) {
report += ", " + get_pet_display_name() + ", " + petHealth + " health of " + PET_HEALTH_MAX;
report += ", loyalty " + petLoyalty + " of " + PET_LOYALTY_MAX;
dictionary petArgs;
petArgs.set("pet", get_pet_display_name());
petArgs.set("health", petHealth);
petArgs.set("max", PET_HEALTH_MAX);
petArgs.set("loyalty", petLoyalty);
petArgs.set("max_loyalty", PET_LOYALTY_MAX);
report += ", " + trf("system.character.report.pet", petArgs);
}
return report;
}
@@ -891,6 +900,7 @@ bool run_pet_offer_menu(const string& in soundPath, const string& in reasonText)
string[] options;
options.insert_last("Yes");
options.insert_last("No");
i18n_translate_string_array_in_place(options);
int selection = 0;
speak_with_history(prompt + " " + options[selection], true);
+1 -1
View File
@@ -175,7 +175,7 @@ void apply_quest_reward(int score) {
}
message += "\nScore: " + score;
text_reader(message, "Quest Rewards", true);
text_reader(message, i18n_text("Quest Rewards"), true);
attempt_pet_offer_from_quest(score);
}
+6 -5
View File
@@ -9,13 +9,14 @@ int run_bat_invasion() {
instructions.insert_last("Bats are invading! Throw your spear to defend.");
instructions.insert_last("");
instructions.insert_last("How to play:");
instructions.insert_last(" - Listen for bats flying past from left or right");
instructions.insert_last(" - Press SPACE to throw your spear when the bat sounds centered");
instructions.insert_last(" - Accurate throws earn 2 points each");
instructions.insert_last(" - You have 10 attempts");
instructions.insert_last("- Listen for bats flying past from left or right");
instructions.insert_last("- Press SPACE to throw your spear when the bat sounds centered");
instructions.insert_last("- Accurate throws earn 2 points each");
instructions.insert_last("- You have 10 attempts");
instructions.insert_last("");
instructions.insert_last("Close this screen to begin.");
text_reader_lines(instructions, "Quest Instructions", true);
i18n_translate_string_array_in_place(instructions);
text_reader_lines(instructions, i18n_text("Quest Instructions"), true);
speak_with_history("Starting.", true);
wait(500);
+6 -5
View File
@@ -6,13 +6,14 @@ int run_catch_the_boomerang() {
instructions.insert_last("Throw a boomerang and catch it on the return.");
instructions.insert_last("");
instructions.insert_last("How to play:");
instructions.insert_last(" - Press Space to throw");
instructions.insert_last(" - Press Space again when it sounds close");
instructions.insert_last(" - There are 5 turns");
instructions.insert_last(" - Better timing earns more points (up to 4)");
instructions.insert_last("- Press Space to throw");
instructions.insert_last("- Press Space again when it sounds close");
instructions.insert_last("- There are 5 turns");
instructions.insert_last("- Better timing earns more points (up to 4)");
instructions.insert_last("");
instructions.insert_last("Close this screen to begin.");
text_reader_lines(instructions, "Quest Instructions", true);
i18n_translate_string_array_in_place(instructions);
text_reader_lines(instructions, i18n_text("Quest Instructions"), true);
wait(500);
+6 -5
View File
@@ -28,14 +28,15 @@ void run_practice_mode() {
instructions.insert_last("Repeat the magical pattern to earn favor from the gods.");
instructions.insert_last("");
instructions.insert_last("Controls:");
instructions.insert_last(" F or K - First note (lowest pitch)");
instructions.insert_last(" D or J - Second note");
instructions.insert_last(" R or I - Third note");
instructions.insert_last(" E or U - Fourth note (highest pitch)");
instructions.insert_last("F or K - First note (lowest pitch)");
instructions.insert_last("D or J - Second note");
instructions.insert_last("R or I - Third note");
instructions.insert_last("E or U - Fourth note (highest pitch)");
instructions.insert_last("");
instructions.insert_last("After closing this screen, you can practice the notes.");
instructions.insert_last("Press Enter when ready to begin, or Escape to cancel.");
text_reader_lines(instructions, "Quest Instructions", true);
i18n_translate_string_array_in_place(instructions);
text_reader_lines(instructions, i18n_text("Quest Instructions"), true);
// Practice mode announcement
speak_with_history("Practice mode. Press Enter to begin, Escape to cancel.", true);
+7 -6
View File
@@ -8,14 +8,15 @@ int run_escape_from_hel() {
instructions.insert_last("You have plundered the treasure, and the Draugr are displeased.!");
instructions.insert_last("");
instructions.insert_last("How to play:");
instructions.insert_last(" - You run automatically, speed increases over time");
instructions.insert_last(" - Listen for open graves approaching (growing louder)");
instructions.insert_last(" - Press SPACE to jump over graves");
instructions.insert_last(" - Each successful jump earns 2 points");
instructions.insert_last(" - The run ends when you fall into a grave");
instructions.insert_last("- You run automatically, speed increases over time");
instructions.insert_last("- Listen for open graves approaching (growing louder)");
instructions.insert_last("- Press SPACE to jump over graves");
instructions.insert_last("- Each successful jump earns 2 points");
instructions.insert_last("- The run ends when you fall into a grave");
instructions.insert_last("");
instructions.insert_last("Close this screen to begin.");
text_reader_lines(instructions, "Quest Instructions", true);
i18n_translate_string_array_in_place(instructions);
text_reader_lines(instructions, i18n_text("Quest Instructions"), true);
wait(500);
+6 -5
View File
@@ -45,13 +45,14 @@ int run_skeletal_bard() {
instructions.insert_last("Listen to the melody and count the notes.");
instructions.insert_last("");
instructions.insert_last("How to play:");
instructions.insert_last(" - After the tune, choose the number of notes");
instructions.insert_last(" - Use Up/Down to change the number");
instructions.insert_last(" - Press Enter to confirm");
instructions.insert_last(" - There are 5 rounds, up to 4 points each");
instructions.insert_last("- After the tune, choose the number of notes");
instructions.insert_last("- Use Up/Down to change the number");
instructions.insert_last("- Press Enter to confirm");
instructions.insert_last("- There are 5 rounds, up to 4 points each");
instructions.insert_last("");
instructions.insert_last("Close this screen to begin.");
text_reader_lines(instructions, "Quest Instructions", true);
i18n_translate_string_array_in_place(instructions);
text_reader_lines(instructions, i18n_text("Quest Instructions"), true);
wait(500);
+46 -206
View File
@@ -1,4 +1,8 @@
// Save system
#include "libstorm-nvgt/crash_logger.nvgt"
#include "libstorm-nvgt/dict_utils.nvgt"
#include "libstorm-nvgt/name_sanitize.nvgt"
#include "libstorm-nvgt/save_utils.nvgt"
const string SAVE_EXTENSION = ".dat";
const string SAVE_ENCRYPTION_KEY = "draugnorak_save_v1";
@@ -145,7 +149,7 @@ string[] get_save_files() {
}
bool sort_string_case_insensitive(const string& in a, const string& in b) {
return a.lower() < b.lower();
return save_utils_sort_string_case_insensitive(a, b);
}
bool has_save_game() {
@@ -153,244 +157,67 @@ bool has_save_game() {
}
string encrypt_save_data(const string& in rawData) {
return string_aes_encrypt(rawData, SAVE_ENCRYPTION_KEY);
return encrypt_string_aes(rawData, SAVE_ENCRYPTION_KEY);
}
string decrypt_save_data(const string& in encryptedData) {
return string_aes_decrypt(encryptedData, SAVE_ENCRYPTION_KEY);
return decrypt_string_aes(encryptedData, SAVE_ENCRYPTION_KEY);
}
bool save_data(const string& in filename, const string& in data) {
if (data.length() == 0) {
return false;
}
string resolvedPath = resolve_save_path(filename);
file tmp;
if (!tmp.open(resolvedPath, "wb")) {
return false;
}
if (tmp.write(data) < data.length()) {
tmp.close();
return false;
}
tmp.close();
return true;
return save_string_file(resolve_save_path(filename), data);
}
bool read_file_string(const string& in filename, string& out data) {
string resolvedPath = resolve_save_path(filename);
file tmp;
if (!tmp.open(resolvedPath, "rb")) {
return false;
}
data = tmp.read();
tmp.close();
return data.length() > 0;
return read_string_file(resolve_save_path(filename), data, false);
}
double get_number(dictionary @data, const string& in key, double defaultValue) {
double value;
if (@data == null)
return defaultValue;
if (data.get(key, value))
return value;
int value_int;
if (data.get(key, value_int))
return value_int;
string value_str;
if (data.get(key, value_str)) {
return parse_int(value_str);
}
return defaultValue;
return dict_get_number(data, key, defaultValue);
}
bool get_bool(dictionary @data, const string& in key, bool defaultValue) {
bool value;
if (@data == null)
return defaultValue;
if (data.get(key, value))
return value;
int value_int;
if (data.get(key, value_int))
return value_int != 0;
string value_str;
if (data.get(key, value_str))
return value_str == "1" || value_str == "true";
return defaultValue;
return dict_get_bool(data, key, defaultValue);
}
bool dictionary_has_keys(dictionary @data) {
if (@data == null)
return false;
string[] @keys = data.get_keys();
return keys.length() > 0;
return dict_has_keys(data);
}
bool has_number_key(dictionary @data, const string& in key) {
double value;
if (@data == null)
return false;
if (data.get(key, value))
return true;
int value_int;
if (data.get(key, value_int))
return true;
string value_str;
if (data.get(key, value_str))
return value_str.length() > 0;
return false;
return dict_has_number_key(data, key);
}
string[] get_string_list(dictionary @data, const string& in key) {
string[] result;
if (@data == null)
return result;
if (!data.get(key, result))
return result;
return result;
return dict_get_string_list(data, key);
}
string flatten_exception_text(const string& in text) {
string result = "";
bool lastWasSpace = false;
for (uint i = 0; i < text.length(); i++) {
string ch = text.substr(i, 1);
if (ch == "\r" || ch == "\n") {
if (!lastWasSpace) {
result += " | ";
lastWasSpace = true;
}
continue;
}
result += ch;
lastWasSpace = false;
}
return result;
return log_flatten_multiline(text);
}
string format_log_timestamp() {
datetime dt;
string stamp = dt.format(DATE_TIME_FORMAT_RFC1123);
return "[" + stamp + "]";
return log_format_timestamp();
}
void log_unhandled_exception(const string& in context) {
string info = get_exception_info();
string filePath = get_exception_file();
int line = get_exception_line();
string func = get_exception_function();
string stack = flatten_exception_text(last_exception_call_stack);
string message = "Unhandled exception";
if (context != "")
message += " (" + context + ")";
if (info != "")
message += ": " + info;
if (filePath != "")
message += " at " + filePath;
if (line > 0)
message += ":" + line;
if (func != "")
message += " in " + func;
if (stack != "")
message += " | stack: " + stack;
message += " " + format_log_timestamp();
file logFile;
if (logFile.open(get_crash_log_path(), "ab")) {
logFile.write(message + "\r\n");
logFile.close();
}
log_unhandled_exception_to_file(get_crash_log_path(), context);
}
string normalize_player_name(string name) {
string result = "";
bool lastWasSpace = true;
for (uint i = 0; i < name.length(); i++) {
string ch = name.substr(i, 1);
bool isSpace = (ch == " " || ch == "\t" || ch == "\r" || ch == "\n");
if (isSpace) {
if (!lastWasSpace) {
result += " ";
lastWasSpace = true;
}
continue;
}
result += ch;
lastWasSpace = false;
}
if (result.length() > 0 && result.substr(result.length() - 1) == " ") {
result = result.substr(0, result.length() - 1);
}
return result;
return normalize_name_whitespace(name);
}
bool is_windows_reserved_name(const string& in upperName) {
if (upperName == "CON" || upperName == "PRN" || upperName == "AUX" || upperName == "NUL")
return true;
if (upperName.length() == 4 && upperName.substr(0, 3) == "COM") {
int num = parse_int(upperName.substr(3));
if (num >= 1 && num <= 9)
return true;
}
if (upperName.length() == 4 && upperName.substr(0, 3) == "LPT") {
int num = parse_int(upperName.substr(3));
if (num >= 1 && num <= 9)
return true;
}
return false;
return is_windows_reserved_filename(upperName);
}
string sanitize_save_filename_base(string name) {
string normalized = normalize_player_name(name);
string result = "";
bool lastSeparator = false;
for (uint i = 0; i < normalized.length(); i++) {
string ch = normalized.substr(i, 1);
bool isUpper = (ch >= "A" && ch <= "Z");
bool isLower = (ch >= "a" && ch <= "z");
bool isDigit = (ch >= "0" && ch <= "9");
if (isUpper || isLower || isDigit) {
result += ch;
lastSeparator = false;
continue;
}
if (ch == " " || ch == "_" || ch == "-") {
if (!lastSeparator && result.length() > 0) {
result += "_";
lastSeparator = true;
}
}
}
while (result.length() > 0 && result.substr(result.length() - 1) == "_") {
result = result.substr(0, result.length() - 1);
}
if (result.length() == 0) {
result = "character";
}
if (result.length() > 40) {
result = result.substr(0, 40);
}
while (result.length() > 0 && result.substr(result.length() - 1) == "_") {
result = result.substr(0, result.length() - 1);
}
string upperName = result.upper();
if (upperName == "." || upperName == "..") {
result = "character";
upperName = result.upper();
}
if (is_windows_reserved_name(upperName)) {
result = "save_" + result;
}
return result;
return sanitize_filename_base_with_reserved_prefix(name, 40, "character", "save_");
}
string get_save_filename_for_name(const string& in name) {
return sanitize_save_filename_base(name) + SAVE_EXTENSION;
return build_filename_from_name_ex(name, SAVE_EXTENSION, 40, "character", "save_");
}
string strip_save_extension(const string& in filename) {
@@ -504,10 +331,16 @@ string pick_random_name_for_sex(int sex, const string[] @usedNames) {
return pick_random_name(male_name_pool, usedNames);
}
string get_localized_sex_label(int sexValue) {
if (sexValue == SEX_FEMALE)
return tr("system.sex.female");
return tr("system.sex.male");
}
bool select_player_sex(int& out sex) {
string[] options = {"Male", "Female"};
string[] options = {get_localized_sex_label(SEX_MALE), get_localized_sex_label(SEX_FEMALE)};
int selection = 0;
string prompt = "Choose your sex.";
string prompt = tr("system.new_character.choose_sex");
speak_with_history(prompt + " " + options[selection], true);
while (true) {
@@ -548,14 +381,16 @@ bool setup_new_character() {
string[] existingNames = get_existing_character_names();
while (true) {
string entered = ui_input_box("Draugnorak", "Enter your name or press Enter for random.", "");
string entered = ui_input_box(tr("system.ui.window_title"), tr("system.new_character.enter_name"), "");
string normalized = normalize_player_name(entered);
if (normalized.length() == 0) {
normalized = pick_random_name_for_sex(selectedSex, existingNames);
}
string saveFile = get_save_filename_for_name(normalized);
if (file_exists(resolve_save_path(saveFile))) {
int confirm = ui_question("", "Save found for " + normalized + ". Overwrite?");
dictionary overwriteArgs;
overwriteArgs.set("name", normalized);
int confirm = ui_question("", trf("system.new_character.save_exists_overwrite", overwriteArgs));
if (confirm != 1) {
continue;
}
@@ -588,8 +423,11 @@ bool select_save_file(string& out filename) {
displayName = strip_save_extension(files[i]);
options.insert_last(displayName);
} else {
string sex_label = (sex == SEX_FEMALE) ? "female" : "male";
options.insert_last(displayName + ", " + sex_label + ", day " + day);
dictionary optionArgs;
optionArgs.set("name", displayName);
optionArgs.set("sex", get_localized_sex_label(sex));
optionArgs.set("day", day);
options.insert_last(trf("system.load_game.option_with_metadata", optionArgs));
}
displayNames.insert_last(displayName);
sexValues.insert_last(sex);
@@ -624,12 +462,13 @@ bool select_save_file(string& out filename) {
return true;
}
if (key_pressed(KEY_DELETE)) {
string prompt = "Are you sure you want to delete the character " + displayNames[selection];
dictionary deleteArgs;
deleteArgs.set("name", displayNames[selection]);
string prompt = trf("system.load_game.delete_confirm_base", deleteArgs);
if (hasMetadata[selection]) {
string sex_label = (sexValues[selection] == SEX_FEMALE) ? "female" : "male";
prompt += " gender " + sex_label + " days " + dayValues[selection] + "?";
} else {
prompt += "?";
deleteArgs.set("sex", get_localized_sex_label(sexValues[selection]));
deleteArgs.set("day", dayValues[selection]);
prompt = trf("system.load_game.delete_confirm_with_metadata", deleteArgs);
}
int confirm = ui_question("", prompt);
if (confirm == 1) {
@@ -638,7 +477,8 @@ bool select_save_file(string& out filename) {
refreshList = true;
break;
} else {
ui_info_box("Draugnorak", "Delete Save", "Unable to delete save.");
ui_info_box(tr("system.ui.window_title"), tr("system.load_game.delete_save_heading"),
tr("system.load_game.delete_save_failed"));
speak_with_history(options[selection], true);
}
} else {
+3 -3
View File
@@ -1,13 +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);
return file_viewer(content, i18n_text(title), readOnly);
}
string text_reader_lines(string[] lines, string title = "Text Reader", bool readOnly = true) {
return file_viewer_lines(lines, title, readOnly);
return file_viewer_lines(lines, i18n_text(title), readOnly);
}
string text_reader_file(string filePath, string title = "", bool readOnly = true) {
return file_viewer_file(filePath, title, readOnly);
return file_viewer_file(filePath, i18n_text(title), readOnly);
}
+16 -8
View File
@@ -91,12 +91,14 @@ void clear_world_drops() {
bool try_pickup_small_game(string game_type) {
if (get_personal_count(ITEM_SMALL_GAME) >= get_personal_stack_limit()) {
speak_with_history("You can't carry any more small game.", true);
speak_cant_carry_any_more_item(ITEM_SMALL_GAME);
return false;
}
add_personal_count(ITEM_SMALL_GAME, 1);
personal_small_game_types.insert_last(game_type);
speak_with_history("Picked up " + game_type + ".", true);
dictionary args;
args.set("item", i18n_translate_speech_message(game_type));
speak_with_history(trf("system.pickup.item", args), true);
return true;
}
@@ -107,26 +109,32 @@ bool try_pickup_world_drop(WorldDrop @drop) {
if (drop.type == "arrow") {
int max_arrows = get_arrow_limit();
if (max_arrows <= 0) {
speak_with_history("You need a quiver to carry arrows.", true);
speak_need_quiver_for_arrows();
return false;
}
if (get_personal_count(ITEM_ARROWS) >= max_arrows) {
speak_with_history("You can't carry any more arrows.", true);
speak_cant_carry_any_more_item(ITEM_ARROWS);
return false;
}
add_personal_count(ITEM_ARROWS, 1);
speak_with_history("Picked up arrow.", true);
dictionary args;
args.set("item", get_item_label_singular(ITEM_ARROWS));
speak_with_history(trf("system.pickup.item", args), true);
return true;
}
if (drop.type == "boar carcass") {
if (get_personal_count(ITEM_BOAR_CARCASSES) >= get_personal_stack_limit()) {
speak_with_history("You can't carry any more boar carcasses.", true);
speak_cant_carry_any_more_item(ITEM_BOAR_CARCASSES);
return false;
}
add_personal_count(ITEM_BOAR_CARCASSES, 1);
speak_with_history("Picked up boar carcass.", true);
dictionary args;
args.set("item", get_item_label_singular(ITEM_BOAR_CARCASSES));
speak_with_history(trf("system.pickup.item", args), true);
return true;
}
speak_with_history("Picked up " + drop.type + ".", true);
dictionary args;
args.set("item", i18n_translate_speech_message(drop.type));
speak_with_history(trf("system.pickup.item", args), true);
return true;
}