1202 lines
62 KiB
Python
1202 lines
62 KiB
Python
#!/usr/bin/env python3
|
|
from __future__ import annotations
|
|
|
|
import hashlib
|
|
import re
|
|
from pathlib import Path
|
|
from typing import Dict, List, Optional, Tuple
|
|
|
|
ROOT = Path(__file__).resolve().parents[1]
|
|
|
|
TARGET_FUNCTION_ARGS = {
|
|
"speak_with_history": [0],
|
|
"screen_reader_speak": [0],
|
|
"notify": [0],
|
|
"learn_sounds_speak": [0],
|
|
"learn_sounds_add_description": [1],
|
|
"notifications_speak": [0],
|
|
"ui_info_box": [0, 1, 2],
|
|
"ui_question": [0, 1],
|
|
"ui_input_box": [0, 1],
|
|
"virtual_info_box": [0, 1, 2],
|
|
"virtual_question": [0, 1],
|
|
"virtual_input_box": [0, 1],
|
|
"text_reader": [1],
|
|
"text_reader_lines": [1],
|
|
"text_reader_file": [1],
|
|
}
|
|
|
|
INSERT_LAST_CONTEXT_HINTS = (
|
|
"option",
|
|
"label",
|
|
"line",
|
|
"prompt",
|
|
"instruction",
|
|
"intro",
|
|
"reward",
|
|
"message",
|
|
"title",
|
|
"menu",
|
|
)
|
|
|
|
PASS_THROUGH_TEXT_FUNCTIONS = {
|
|
"i18n_text",
|
|
"i18n_translate_speech_message",
|
|
}
|
|
|
|
SKIP_DIR_NAMES = {".git", "bloodshed", "docs", "skills", "nvgt-git"}
|
|
SKIP_FILE_NAMES = {"crash.log"}
|
|
|
|
|
|
def iter_nvgt_files() -> List[Path]:
|
|
files: List[Path] = []
|
|
|
|
entrypoints = [ROOT / "draugnorak.nvgt", ROOT / "src" / "sound_settings.nvgt"]
|
|
for entry in entrypoints:
|
|
if entry.exists():
|
|
files.append(entry)
|
|
|
|
source_roots = [ROOT / "src", ROOT / "libstorm-nvgt"]
|
|
for source_root in source_roots:
|
|
if not source_root.exists():
|
|
continue
|
|
for path in source_root.rglob("*.nvgt"):
|
|
rel = path.relative_to(ROOT)
|
|
if any(part in SKIP_DIR_NAMES for part in rel.parts):
|
|
continue
|
|
if path.name in SKIP_FILE_NAMES:
|
|
continue
|
|
files.append(path)
|
|
|
|
return sorted(set(files))
|
|
|
|
|
|
def is_identifier_char(ch: str) -> bool:
|
|
return ch.isalnum() or ch == "_"
|
|
|
|
|
|
def read_identifier_backward(text: str, before_index: int) -> str:
|
|
i = before_index
|
|
while i >= 0 and text[i].isspace():
|
|
i -= 1
|
|
end = i
|
|
while i >= 0 and is_identifier_char(text[i]):
|
|
i -= 1
|
|
start = i + 1
|
|
if end < start:
|
|
return ""
|
|
return text[start : end + 1]
|
|
|
|
|
|
def find_matching_paren(text: str, open_index: int) -> int:
|
|
depth = 0
|
|
in_string = False
|
|
escape = False
|
|
|
|
for i in range(open_index, len(text)):
|
|
ch = text[i]
|
|
if in_string:
|
|
if escape:
|
|
escape = False
|
|
elif ch == "\\":
|
|
escape = True
|
|
elif ch == '"':
|
|
in_string = False
|
|
continue
|
|
|
|
if ch == '"':
|
|
in_string = True
|
|
continue
|
|
if ch == "(":
|
|
depth += 1
|
|
continue
|
|
if ch == ")":
|
|
depth -= 1
|
|
if depth == 0:
|
|
return i
|
|
continue
|
|
|
|
return -1
|
|
|
|
|
|
def split_top_level(expr: str, delimiter: str) -> List[str]:
|
|
parts: List[str] = []
|
|
depth_paren = 0
|
|
depth_bracket = 0
|
|
depth_brace = 0
|
|
in_string = False
|
|
escape = False
|
|
start = 0
|
|
|
|
for i, ch in enumerate(expr):
|
|
if in_string:
|
|
if escape:
|
|
escape = False
|
|
elif ch == "\\":
|
|
escape = True
|
|
elif ch == '"':
|
|
in_string = False
|
|
continue
|
|
|
|
if ch == '"':
|
|
in_string = True
|
|
continue
|
|
if ch == "(":
|
|
depth_paren += 1
|
|
continue
|
|
if ch == ")":
|
|
depth_paren = max(0, depth_paren - 1)
|
|
continue
|
|
if ch == "[":
|
|
depth_bracket += 1
|
|
continue
|
|
if ch == "]":
|
|
depth_bracket = max(0, depth_bracket - 1)
|
|
continue
|
|
if ch == "{":
|
|
depth_brace += 1
|
|
continue
|
|
if ch == "}":
|
|
depth_brace = max(0, depth_brace - 1)
|
|
continue
|
|
|
|
if ch == delimiter and depth_paren == 0 and depth_bracket == 0 and depth_brace == 0:
|
|
parts.append(expr[start:i])
|
|
start = i + 1
|
|
|
|
parts.append(expr[start:])
|
|
return parts
|
|
|
|
|
|
def unescape_string_literal(literal_body: str) -> str:
|
|
result: List[str] = []
|
|
i = 0
|
|
while i < len(literal_body):
|
|
ch = literal_body[i]
|
|
if ch != "\\":
|
|
result.append(ch)
|
|
i += 1
|
|
continue
|
|
|
|
if i + 1 >= len(literal_body):
|
|
result.append("\\")
|
|
break
|
|
|
|
nxt = literal_body[i + 1]
|
|
if nxt == "n":
|
|
result.append("\n")
|
|
elif nxt == "r":
|
|
result.append("\r")
|
|
elif nxt == "t":
|
|
result.append("\t")
|
|
else:
|
|
result.append(nxt)
|
|
i += 2
|
|
|
|
return "".join(result)
|
|
|
|
|
|
def parse_string_literal_sequence(expr: str) -> Optional[str]:
|
|
expr = expr.strip()
|
|
if not expr or expr[0] != '"':
|
|
return None
|
|
|
|
parts: List[str] = []
|
|
i = 0
|
|
length = len(expr)
|
|
|
|
while i < length:
|
|
while i < length and expr[i].isspace():
|
|
i += 1
|
|
|
|
if i >= length:
|
|
break
|
|
if expr[i] != '"':
|
|
return None
|
|
|
|
i += 1
|
|
literal_chars: List[str] = []
|
|
escape = False
|
|
|
|
while i < length:
|
|
ch = expr[i]
|
|
if escape:
|
|
literal_chars.append("\\" + ch)
|
|
escape = False
|
|
i += 1
|
|
continue
|
|
|
|
if ch == "\\":
|
|
escape = True
|
|
i += 1
|
|
continue
|
|
|
|
if ch == '"':
|
|
i += 1
|
|
break
|
|
|
|
literal_chars.append(ch)
|
|
i += 1
|
|
else:
|
|
return None
|
|
|
|
parts.append(unescape_string_literal("".join(literal_chars)))
|
|
|
|
if not parts:
|
|
return None
|
|
|
|
return "".join(parts)
|
|
|
|
|
|
def expression_to_template(expr: str) -> Optional[str]:
|
|
expr = expr.strip()
|
|
if not expr:
|
|
return None
|
|
|
|
parts = split_top_level(expr, "+")
|
|
template_parts: List[str] = []
|
|
placeholder_count = 0
|
|
|
|
for raw_part in parts:
|
|
part = raw_part.strip()
|
|
if not part:
|
|
continue
|
|
|
|
passthrough_expr = unwrap_passthrough_expression(part)
|
|
if passthrough_expr is not None:
|
|
passthrough_template = expression_to_template(passthrough_expr)
|
|
if passthrough_template is not None:
|
|
template_parts.append(passthrough_template)
|
|
continue
|
|
|
|
literal_sequence = parse_string_literal_sequence(part)
|
|
if literal_sequence is not None:
|
|
template_parts.append(literal_sequence)
|
|
else:
|
|
placeholder_count += 1
|
|
template_parts.append(f"{{arg{placeholder_count}}}")
|
|
|
|
if not template_parts:
|
|
return None
|
|
|
|
template = "".join(template_parts)
|
|
if template.strip() == "":
|
|
return None
|
|
if template_literal_length(template) == 0:
|
|
return None
|
|
return template
|
|
|
|
|
|
def unwrap_passthrough_expression(expr: str) -> Optional[str]:
|
|
expr = expr.strip()
|
|
match = re.match(r"^([A-Za-z_][A-Za-z0-9_]*)\((.*)\)$", expr, re.DOTALL)
|
|
if not match:
|
|
return None
|
|
|
|
function_name = match.group(1)
|
|
if function_name not in PASS_THROUGH_TEXT_FUNCTIONS:
|
|
return None
|
|
|
|
arg_text = match.group(2)
|
|
args = split_top_level(arg_text, ",")
|
|
if not args:
|
|
return None
|
|
|
|
return args[0].strip()
|
|
|
|
|
|
def template_literal_length(template_text: str) -> int:
|
|
literal_count = 0
|
|
in_placeholder = False
|
|
|
|
for ch in template_text:
|
|
if not in_placeholder and ch == "{":
|
|
in_placeholder = True
|
|
continue
|
|
if in_placeholder and ch == "}":
|
|
in_placeholder = False
|
|
continue
|
|
if not in_placeholder:
|
|
literal_count += 1
|
|
|
|
return literal_count
|
|
|
|
|
|
def line_number_for_index(text: str, index: int) -> int:
|
|
return text.count("\n", 0, index) + 1
|
|
|
|
|
|
def add_entry(entries: Dict[str, Dict[str, object]], template: str, source_ref: str) -> None:
|
|
base_key = f"msg.{hashlib.sha1(template.encode('utf-8')).hexdigest()[:12]}"
|
|
key = base_key
|
|
suffix = 1
|
|
while key in entries and entries[key]["value"] != template:
|
|
key = f"{base_key}_{suffix}"
|
|
suffix += 1
|
|
|
|
if key not in entries:
|
|
entries[key] = {"value": template, "refs": [source_ref]}
|
|
else:
|
|
refs: List[str] = entries[key]["refs"] # type: ignore[assignment]
|
|
if source_ref not in refs:
|
|
refs.append(source_ref)
|
|
|
|
|
|
def extract_from_call(
|
|
entries: Dict[str, Dict[str, object]],
|
|
receiver: str,
|
|
function_name: str,
|
|
args: List[str],
|
|
source_ref_base: str,
|
|
) -> None:
|
|
target_arg_indexes: List[int] = []
|
|
|
|
if function_name in TARGET_FUNCTION_ARGS:
|
|
target_arg_indexes = TARGET_FUNCTION_ARGS[function_name]
|
|
elif function_name == "insert_last":
|
|
receiver_lower = receiver.lower()
|
|
if not any(hint in receiver_lower for hint in INSERT_LAST_CONTEXT_HINTS):
|
|
return
|
|
target_arg_indexes = [0]
|
|
else:
|
|
return
|
|
|
|
for arg_index in target_arg_indexes:
|
|
if arg_index >= len(args):
|
|
continue
|
|
template = expression_to_template(args[arg_index])
|
|
if not template:
|
|
continue
|
|
source_ref = f"{source_ref_base}:{function_name}[{arg_index}]"
|
|
add_entry(entries, template, source_ref)
|
|
|
|
|
|
def scan_file(path: Path, entries: Dict[str, Dict[str, object]]) -> None:
|
|
text = path.read_text(encoding="utf-8", errors="replace")
|
|
i = 0
|
|
|
|
while i < len(text):
|
|
ch = text[i]
|
|
if not is_identifier_char(ch):
|
|
i += 1
|
|
continue
|
|
|
|
start = i
|
|
while i < len(text) and is_identifier_char(text[i]):
|
|
i += 1
|
|
name = text[start:i]
|
|
|
|
j = i
|
|
while j < len(text) and text[j].isspace():
|
|
j += 1
|
|
|
|
if j >= len(text) or text[j] != "(":
|
|
continue
|
|
|
|
receiver = ""
|
|
k = start - 1
|
|
while k >= 0 and text[k].isspace():
|
|
k -= 1
|
|
if k >= 0 and text[k] == ".":
|
|
receiver = read_identifier_backward(text, k - 1)
|
|
|
|
close = find_matching_paren(text, j)
|
|
if close < 0:
|
|
break
|
|
|
|
arg_text = text[j + 1 : close]
|
|
args = split_top_level(arg_text, ",")
|
|
|
|
line = line_number_for_index(text, start)
|
|
rel_path = path.relative_to(ROOT).as_posix()
|
|
source_ref_base = f"{rel_path}:{line}"
|
|
extract_from_call(entries, receiver, name, args, source_ref_base)
|
|
|
|
i = close + 1
|
|
|
|
|
|
def slugify_fragment(value: str) -> str:
|
|
slug = re.sub(r"[^a-z0-9]+", "_", value.lower())
|
|
slug = slug.strip("_")
|
|
return slug or "value"
|
|
|
|
|
|
def add_item_registry_fragments(entries: Dict[str, Dict[str, object]]) -> None:
|
|
item_registry_path = ROOT / "src" / "item_registry.nvgt"
|
|
if not item_registry_path.exists():
|
|
return
|
|
|
|
text = item_registry_path.read_text(encoding="utf-8", errors="replace")
|
|
pattern = re.compile(
|
|
r"ItemDefinition\([^,]+,\s*\"([^\"]+)\",\s*\"([^\"]+)\",\s*\"([^\"]+)\"",
|
|
re.MULTILINE,
|
|
)
|
|
|
|
for match in pattern.finditer(text):
|
|
plural = match.group(1)
|
|
singular = match.group(2)
|
|
display = match.group(3)
|
|
|
|
plural_slug = slugify_fragment(plural)
|
|
singular_slug = slugify_fragment(singular)
|
|
display_slug = slugify_fragment(display)
|
|
|
|
add_entry(entries, plural, f"src/item_registry.nvgt:item_plural:{plural_slug}")
|
|
add_entry(entries, singular, f"src/item_registry.nvgt:item_singular:{singular_slug}")
|
|
add_entry(entries, display, f"src/item_registry.nvgt:item_display:{display_slug}")
|
|
|
|
|
|
def add_seed_messages(entries: Dict[str, Dict[str, object]]) -> None:
|
|
seeds = [
|
|
"Load Game (no saves found)",
|
|
"No saves found.",
|
|
"Unable to load save.",
|
|
"Unhandled exception",
|
|
"Really exit?",
|
|
"Found a stone.",
|
|
"Found a reed.",
|
|
"Found clay.",
|
|
]
|
|
for value in seeds:
|
|
add_entry(entries, value, "seed:manual")
|
|
|
|
|
|
def normalize_learn_sounds_label(sound_path: Path) -> str:
|
|
name = sound_path.stem.lower()
|
|
name = name.replace("_", " ").replace("-", " ")
|
|
name = re.sub(r"\s+", " ", name).strip()
|
|
return name or "sound"
|
|
|
|
|
|
def add_learn_sounds_label_seeds(entries: Dict[str, Dict[str, object]]) -> None:
|
|
sounds_dir = ROOT / "sounds"
|
|
if not sounds_dir.exists():
|
|
return
|
|
|
|
for path in sorted(sounds_dir.rglob("*")):
|
|
if not path.is_file():
|
|
continue
|
|
if path.suffix.lower() not in {".ogg", ".wav"}:
|
|
continue
|
|
label = normalize_learn_sounds_label(path)
|
|
rel = path.relative_to(ROOT).as_posix()
|
|
add_entry(entries, label, f"seed:learn_sounds_label:{rel}")
|
|
|
|
|
|
def escape_for_ini(value: str) -> str:
|
|
escaped = value.replace("\\", "\\\\")
|
|
escaped = escaped.replace("\n", "\\n")
|
|
escaped = escaped.replace("\r", "\\r")
|
|
escaped = escaped.replace("\t", "\\t")
|
|
return escaped
|
|
|
|
|
|
def write_catalog(entries: Dict[str, Dict[str, object]], output_path: Path) -> None:
|
|
output_path.parent.mkdir(parents=True, exist_ok=True)
|
|
|
|
manual_entries: List[Tuple[str, str]] = [
|
|
("meta.code", "en"),
|
|
("meta.name", "English"),
|
|
("meta.native_name", "English"),
|
|
("system.language.select_prompt", "Select your language."),
|
|
("system.language.selected", "Language set to {language}."),
|
|
("system.language.english_label", "English (en)"),
|
|
("system.ui.window_title", "Draugnorak"),
|
|
("system.main_menu.prompt", "Draugnorak. Main menu."),
|
|
("system.game.new_game_started", "New game started."),
|
|
("system.game.loaded", "Game loaded."),
|
|
("system.game.paused_prompt", "Paused. Press backspace to resume."),
|
|
("system.game.resumed", "Resumed."),
|
|
("system.exit.confirm", "Really exit?"),
|
|
("system.direction.west", "west"),
|
|
("system.direction.east", "east"),
|
|
("system.sex.male", "Male"),
|
|
("system.sex.female", "Female"),
|
|
("system.new_character.choose_sex", "Choose your sex."),
|
|
("system.new_character.enter_name", "Enter your name or press Enter for random."),
|
|
("system.new_character.save_exists_overwrite", "Save found for {name}. Overwrite?"),
|
|
("system.load_game.option_with_metadata", "{name}, {sex}, day {day}"),
|
|
("system.load_game.delete_confirm_base", "Are you sure you want to delete the character {name}?"),
|
|
("system.load_game.delete_confirm_with_metadata",
|
|
"Are you sure you want to delete the character {name} gender {sex} days {day}?"),
|
|
("system.load_game.delete_save_heading", "Delete Save"),
|
|
("system.load_game.delete_save_failed", "Unable to delete save."),
|
|
("system.load_game.save_deleted", "Save deleted."),
|
|
("system.load_game.no_saves_found", "No saves found."),
|
|
("system.load_game.unable_to_load", "Unable to load save."),
|
|
("system.load_game.select_prompt", "Load game. Select character. Press delete to remove a save."),
|
|
("system.quick_slot.no_item_bound", "No item bound to slot {slot}."),
|
|
("system.menu.closed", "Closed."),
|
|
("system.menu.canceled", "Canceled."),
|
|
("system.menu.no_options", "No options."),
|
|
("system.menu.no_matches", "No matches for {arg1}."),
|
|
("system.menu.main.story", "The story"),
|
|
("system.menu.main.language", "Language"),
|
|
("system.option.no", "No"),
|
|
("system.option.yes", "Yes"),
|
|
("system.inventory.cant_carry_any_more_item", "You can't carry any more {item}."),
|
|
("system.inventory.menu.prompt", "Inventory menu. {option}"),
|
|
("system.inventory.menu.no_options", "Inventory menu. No options."),
|
|
("system.inventory.option.personal", "Personal inventory"),
|
|
("system.inventory.option.base_storage", "Base storage"),
|
|
("system.inventory.count_set_to_slot", "{name} count set to slot {slot}."),
|
|
("system.inventory.need_quiver_for_arrows", "You need a quiver to carry arrows."),
|
|
("system.inventory.max_arrows_with_quivers", "You can only carry {max} arrows with your current quivers."),
|
|
("system.inventory.item_not_available", "Item not available."),
|
|
("system.search.found_item", "Found {item}."),
|
|
("system.search.found_nothing", "Found nothing."),
|
|
("system.pickup.item", "Picked up {item}."),
|
|
("system.time.period.am", "am"),
|
|
("system.time.period.pm", "pm"),
|
|
("system.time.report", "{hour} oclock {period} day {day}"),
|
|
("system.time.report_with_conditions", "{time}. {conditions}"),
|
|
("system.time.condition.sunny", "Sunny"),
|
|
("system.time.condition.daylight", "Daylight"),
|
|
("system.time.condition.dark", "Dark"),
|
|
("system.time.condition.windy", "Windy"),
|
|
("system.time.condition.raining", "Raining"),
|
|
("system.time.condition.storming", "Storming"),
|
|
("system.time.condition.moon_and_stars", "Moon and stars"),
|
|
("system.time.event.area_expanded", "The area has expanded! New territory discovered to the east."),
|
|
("system.time.event.mountain_discovered", "A mountain range has been discovered to the east!"),
|
|
("system.time.event.invasion_source_mountains", "the mountains"),
|
|
("system.time.event.invasion_source_new_area", "the new area"),
|
|
("system.time.event.invasion_start", "{enemy_plural} are invading from {source}!"),
|
|
("system.time.event.zombie_swarm_spotted", "A swarm of zombies has been spotted."),
|
|
("system.time.event.survivor_joins_one", "A survivor joins your base."),
|
|
("system.time.event.survivor_joins_many", "{count} survivors join your base."),
|
|
("system.time.event.base_max_capacity", "Your base is at maximum capacity."),
|
|
("system.time.event.invasion_ended", "The {enemy} invasion has ended."),
|
|
("system.time.event.blessing_speed_fades", "The speed blessing fades."),
|
|
("system.time.event.blessing_resident_fades", "The residents' purpose fades."),
|
|
("system.time.event.blessing_search_fades", "The eagle's sight fades."),
|
|
("system.time.event.sun_setting", "The sun is setting."),
|
|
("system.time.event.sky_brightening", "The sky begins to brighten."),
|
|
("system.time.event.residents_repaired_barricade", "Residents repaired the barricade. +{health} health."),
|
|
("system.time.event.incense_burned_out", "The incense has burned out."),
|
|
("system.combat.burning", "Burning! {health} health remaining."),
|
|
("system.combat.health_status", "{health} health."),
|
|
("system.combat.you_died", "You have died."),
|
|
("system.enemies.residents_killed_attacker", "Residents killed an attacking {enemy}."),
|
|
("system.enemies.resident_taken", "A resident has been taken."),
|
|
("system.action.fire.intensity.giant_blaze", "Giant blaze"),
|
|
("system.action.fire.intensity.small_fire", "Small fire"),
|
|
("system.action.fire.intensity.glowing_coals", "A few glowing coals"),
|
|
("system.action.fire.estimate.less_than_one_hour", "less than 1 hour"),
|
|
("system.action.fire.estimate.approximately_one_hour", "approximately 1 hour"),
|
|
("system.action.fire.estimate.approximately_hours", "approximately {hours} hours"),
|
|
("system.action.fire.status", "{label}. {estimate} remaining."),
|
|
("system.action.no_fire_nearby", "No fire nearby."),
|
|
("system.action.no_snare_nearby", "No snare nearby."),
|
|
("system.action.snare_holds", "Snare holds a {catch_type}."),
|
|
("system.action.snare_set_inactive", "Snare is set but not active yet."),
|
|
("system.action.snare_set_empty", "Snare is set and empty."),
|
|
("system.action.fish_on_line", "Fish on the line."),
|
|
("system.action.no_fish_on_line", "No fish on the line."),
|
|
("system.action.cannot_place_snares_in_base", "Cannot place snares in the base area."),
|
|
("system.action.snare_already_here", "There is already a snare here."),
|
|
("system.action.snare_set", "Snare set."),
|
|
("system.action.no_snares_to_place", "No snares to place."),
|
|
("system.action.feed_fire.stick", "You dump an arm load of sticks into the fire."),
|
|
("system.action.feed_fire.vine", "You toss a few vines and leaves into the fire."),
|
|
("system.action.feed_fire.log", "You heave a log into the fire."),
|
|
("system.action.no_altar_built", "No altar built."),
|
|
("system.action.need_clay_pot_for_incense", "You need a clay pot to burn incense."),
|
|
("system.action.no_incense_to_burn", "No incense to burn."),
|
|
("system.action.incense_burning", "Incense burning. {hours} hours remaining."),
|
|
("system.action.no_sticks_to_feed_fire", "No sticks to feed fire."),
|
|
("system.action.dumped_sticks", "Dumped {amount} sticks into the fire."),
|
|
("system.action.no_vines_to_feed_fire", "No vines to feed fire."),
|
|
("system.action.dumped_vines", "Dumped {amount} vines into the fire."),
|
|
("system.action.no_logs_to_feed_fire", "No logs to feed fire."),
|
|
("system.action.dumped_logs", "Dumped {amount} logs into the fire."),
|
|
("system.action.burned_incense", "Burned {amount} incense. +{hours} hours."),
|
|
("system.action.option.place_snare", "Place Snare"),
|
|
("system.action.option.feed_fire_stick", "Feed fire with stick"),
|
|
("system.action.option.feed_fire_vine", "Feed fire with vine"),
|
|
("system.action.option.feed_fire_log", "Feed fire with log"),
|
|
("system.action.option.burn_incense", "Burn incense"),
|
|
("system.action.option.check_fire", "Check fire"),
|
|
("system.action.option.check_snare", "Check snare"),
|
|
("system.action.option.check_fishing_pole", "Check fishing pole"),
|
|
("system.action.menu.prompt", "Action menu. {option}"),
|
|
("system.action.cant_do_that", "Can't do that."),
|
|
("system.base_info.not_in_base", "You are not in the base."),
|
|
("system.base_info.menu.no_options", "Base info. No options."),
|
|
("system.base_info.menu.prompt", "Base info. {option}"),
|
|
("system.base.barricade_fallen", "The barricade has fallen!"),
|
|
("system.base.no_food_residents_hungry", "No food, residents are hungry."),
|
|
("system.base.resident_fishing_pole_broke_one", "A resident's fishing pole broke."),
|
|
("system.base.resident_fishing_pole_broke_many", "{count} fishing poles broke."),
|
|
("system.base.resident_caught_fish_one", "Resident caught a fish and added it to storage."),
|
|
("system.base.resident_caught_fish_many", "Residents caught {count} fish and added them to storage."),
|
|
("system.base.resident_smoked_fish", "Resident smoked a fish into {yield} smoked fish."),
|
|
("system.base.livestock_produced",
|
|
"Livestock produced {meat} meat, {skins} skins, and {feathers} feathers and added to storage."),
|
|
("system.base.resident_spear_broke_one", "A resident's spear broke from wear."),
|
|
("system.base.resident_spear_broke_many", "{count} spears broke from wear."),
|
|
("system.base.resident_sling_broke_one", "A resident's sling broke from wear."),
|
|
("system.base.resident_sling_broke_many", "{count} slings broke from wear."),
|
|
("system.base.resident_bow_broke_one", "A resident's bow broke from wear."),
|
|
("system.base.resident_bow_broke_many", "{count} bows broke from wear."),
|
|
("system.base.resident_clothing_repair_one",
|
|
"A resident is mending clothing. Used {vines} vines, {skins} skins, and {down} down."),
|
|
("system.base.resident_clothing_repair_many",
|
|
"Residents are mending clothing. Used {vines} vines, {skins} skins, and {down} down."),
|
|
("system.base.snare_escape_during_check",
|
|
"A {catch_type} escaped while a resident checked the snare at x {position}."),
|
|
("system.base.resident_retrieved_from_snare",
|
|
"Resident retrieved {game_type} from snare at x {position} y 0 and reset it."),
|
|
("system.base.resident_knife_broke_butchering", "A resident's knife broke while butchering."),
|
|
("system.base.resident_butchered_result",
|
|
"Resident butchered {game_type}. Added {meat} meat, {skins} skins, {feathers} feathers, {down} down, {sinew} sinew to storage."),
|
|
("system.base.resident_added_to_storage", "Resident added {item} to storage."),
|
|
("system.base.resident_basket_broke_one", "A resident's basket broke."),
|
|
("system.base.resident_basket_broke_many", "{count} baskets broke."),
|
|
("system.base.resident_basket_broke_foraging_one", "A resident's basket broke while foraging."),
|
|
("system.base.resident_basket_broke_foraging_many", "{count} baskets broke while foraging."),
|
|
("system.base.resident_gathered_basket_food_one", "Resident gathered a basket of fruits and nuts."),
|
|
("system.base.resident_gathered_basket_food_many",
|
|
"Residents gathered {count} baskets of fruits and nuts."),
|
|
("system.fishing.size.small", "small"),
|
|
("system.fishing.size.medium", "medium sized"),
|
|
("system.fishing.size.large", "large"),
|
|
("system.fishing.size.monster", "monster"),
|
|
("system.fishing.pole_broke.default", "Your fishing pole broke."),
|
|
("system.fishing.pole_broke.switched_weapons", "You switched weapons and your fishing pole broke."),
|
|
("system.fishing.pole_broke.moved_fish_got_away", "You moved and the fish got away. Your fishing pole broke."),
|
|
("system.fishing.pole_broke.moved", "You moved and your fishing pole broke."),
|
|
("system.fishing.require_pole_equipped", "You need a fishing pole equipped."),
|
|
("system.fishing.require_near_stream", "You need to be within 2 tiles of a stream."),
|
|
("system.fishing.fish_on_line", "A fish is on the line!"),
|
|
("system.fishing.caught", "Caught a {size} {fish}."),
|
|
("system.fishing.fish_slipped_off_water", "The fish slipped off in the water."),
|
|
("system.fishing.fish_got_away", "The fish got away."),
|
|
("system.fishing.fish_still_on_line", "The fish is still on the line."),
|
|
("system.fishing.stopped", "You stop fishing."),
|
|
("system.altar.must_be_in_base", "Must be in base to use altar."),
|
|
("system.altar.no_altar_built", "No altar built."),
|
|
("system.altar.runed_cannot_sacrifice", "Runed items cannot be sacrificed."),
|
|
("system.altar.nothing_to_sacrifice", "Nothing to sacrifice."),
|
|
("system.altar.sacrificed_one", "Sacrificed 1 {item}. Favor +{gain}. Total {total}."),
|
|
("system.altar.sacrificed_many", "Sacrificed {count} {items}. Favor +{gain}. Total {total}."),
|
|
("system.altar.menu.prompt", "Altar. Favor {favor}."),
|
|
("system.storage.window_title", "Inventory"),
|
|
("system.storage.transfer_prompt", "{prompt} (max {max})"),
|
|
("system.storage.deposit_how_many", "Deposit how many?"),
|
|
("system.storage.withdraw_how_many", "Withdraw how many?"),
|
|
("system.storage.nothing_to_deposit", "Nothing to deposit."),
|
|
("system.storage.nothing_to_withdraw", "Nothing to withdraw."),
|
|
("system.storage.runed_cannot_deposit", "Runed items cannot be deposited into storage."),
|
|
("system.storage.item_full", "Storage for that item is full."),
|
|
("system.storage.no_storage_built", "No storage built."),
|
|
("system.storage.menu_title", "Base storage."),
|
|
("system.storage.menu.prompt", "Base storage. {option}"),
|
|
("system.storage.menu.no_options", "Base storage. No options."),
|
|
("system.storage.runed_item_name", "Runed {equipment} of {rune}"),
|
|
("system.storage.deposited_one", "Deposited {name}."),
|
|
("system.storage.deposited_many", "Deposited {amount} {item}."),
|
|
("system.storage.withdrew_one", "Withdrew {name}."),
|
|
("system.storage.withdrew_many", "Withdrew {amount} {item}."),
|
|
("system.crafting.require.fire_within_three_clay_pot",
|
|
"You need a fire within 3 tiles to craft a clay pot."),
|
|
("system.crafting.require.fire_within_three_clay_pots",
|
|
"You need a fire within 3 tiles to craft clay pots."),
|
|
("system.crafting.require.fire_within_three_bowstring",
|
|
"You need a fire within 3 tiles to make bowstring."),
|
|
("system.crafting.require.altar_incense", "You need an altar to craft incense."),
|
|
("system.crafting.require.fire_within_three_smoke_fish",
|
|
"You need a fire within 3 tiles to smoke fish."),
|
|
("system.crafting.require.fire_within_three_butcher", "You need a fire within 3 tiles to butcher."),
|
|
("system.character.slot.head", "head"),
|
|
("system.character.slot.torso", "torso"),
|
|
("system.character.slot.arms", "arms"),
|
|
("system.character.slot.hands", "hands"),
|
|
("system.character.slot.legs", "legs"),
|
|
("system.character.slot.feet", "feet"),
|
|
("system.character.menu.prompt", "Character info. {option}"),
|
|
("system.character.menu.no_options", "Character info. No options."),
|
|
("system.character.pet.no_pet", "No pet."),
|
|
("system.character.pet.abandon_confirm", "Really abandon your pet? {option}"),
|
|
("system.character.pet.unconscious_cannot_abandon",
|
|
"Your {pet} is unconscious. You can't abandon it right now."),
|
|
("system.character.info.name_sex", "Name {name}, {sex}"),
|
|
("system.character.info.name_unknown", "Name unknown"),
|
|
("system.character.info.health", "Health {health} of {max}"),
|
|
("system.character.info.weapon", "Weapon {weapon}"),
|
|
("system.character.info.clothing_equipped", "Clothing equipped: {items}"),
|
|
("system.character.info.no_clothing", "No clothing equipped."),
|
|
("system.character.info.missing", "Missing: {slots}"),
|
|
("system.character.info.favor", "Favor {favor}"),
|
|
("system.character.info.speed", "Speed {status}"),
|
|
("system.character.info.pet", "Pet {pet}, health {health} of {max_health}, loyalty {loyalty} of {max_loyalty}"),
|
|
("system.character.info.pet_knocked_out", "Knocked out for {hours} {hour_label}."),
|
|
("system.character.info.pet_none", "No pet."),
|
|
("system.character.word.hour", "hour"),
|
|
("system.character.word.hours", "hours"),
|
|
("system.pet.default_name", "pet"),
|
|
("system.pet.returned", "A {pet} returns to you."),
|
|
("system.pet.knocked_out", "Your {pet} has been knocked out."),
|
|
("system.pet.knocked_out_with_remaining", "Your {pet} has been knocked out. {hours} {hour_label} remaining."),
|
|
("system.pet.leaves", "{pet} leaves."),
|
|
("system.pet.leaving", "Your {pet} is leaving."),
|
|
("system.pet.on_the_way", "Your pet is on its way."),
|
|
("system.pet.hungry_unresponsive", "A {pet} is hungry and unresponsive."),
|
|
("system.pet.joins_you", "A {pet} joins you."),
|
|
("system.pet.offer.prompt", "A friendly looking {pet} begs for food. Accept?"),
|
|
("system.pet.offer.declined", "Declined."),
|
|
("system.pet.retrieve.cant_carry_chew_on",
|
|
"You can't carry a {item}, so you give it to a {pet} to chew on."),
|
|
("system.pet.retrieve.retrieved_item", "Your {pet} retrieved {item}."),
|
|
("system.pet.retrieve.random_find", "Your {pet} retrieved {count} {item}."),
|
|
("system.pet.recovered_from_injuries", "Your {pet} has recovered from its injuries."),
|
|
("system.pet.getting_hungry", "A {pet} is getting hungry."),
|
|
("system.fylgja.already_used_today", "You have already used your Fylgja today."),
|
|
("system.fylgja.menu.prompt", "Fylgja menu."),
|
|
("system.fylgja.need_open_ground_to_charge", "You need open ground in front of you to charge."),
|
|
("system.fylgja.option", "{name} Fylgja"),
|
|
("system.fylgja.connection_with_target", "You have a {stage} connection with the {target}."),
|
|
("system.fylgja.unlocked", "You have unlocked the {name} Fylgja!"),
|
|
("system.fylgja.already_unlocked", "You have already unlocked the {name} Fylgja."),
|
|
("system.fylgja.name.unicorn", "Unicorn"),
|
|
("system.fylgja.target.unicorn", "unicorn"),
|
|
("system.fylgja.stage.tenuous", "tenuous"),
|
|
("system.fylgja.stage.faint", "faint"),
|
|
("system.fylgja.stage.stirring", "stirring"),
|
|
("system.fylgja.stage.budding", "budding"),
|
|
("system.fylgja.stage.kindled", "kindled"),
|
|
("system.fylgja.stage.bound", "bound"),
|
|
("system.fylgja.stage.sworn", "sworn"),
|
|
("system.fylgja.stage.ascendant", "ascendant"),
|
|
("system.fylgja.stage.ultimate", "ultimate"),
|
|
("system.quest.name.bat_invasion", "Bat Invasion"),
|
|
("system.quest.name.catch_boomerang", "Catch the Boomerang"),
|
|
("system.quest.name.enchanted_melody", "Enchanted Melody"),
|
|
("system.quest.name.escape_from_hel", "Escape from Hel"),
|
|
("system.quest.name.skeletal_bard", "Skeletal Bard"),
|
|
("system.quest.name.unknown", "Unknown Quest"),
|
|
("system.quest.description.bat_invasion",
|
|
"Bat Invasion. Giant killer bats are attacking. Press space to throw when the bat is centered."),
|
|
("system.quest.description.catch_boomerang",
|
|
"Catch the Boomerang. Throw and catch the boomerang as it returns for up to 4 points."),
|
|
("system.quest.description.enchanted_melody",
|
|
"Enchanted Melody. Repeat the pattern using E R D F or U I J K. Lowest to highest pitch."),
|
|
("system.quest.description.escape_from_hel",
|
|
"Escape from Hel. Press space to jump over open graves. The pace quickens."),
|
|
("system.quest.description.skeletal_bard",
|
|
"Skeletal Bard. A skeleton named Billy Bones is practicing to become a bard. Count the notes."),
|
|
("system.quest.description.unknown", "Unknown quest."),
|
|
("system.quest.new_available", "A new quest is available: {quest}."),
|
|
("system.quest.none_available", "No quests available."),
|
|
("system.quest.favor_phrase.displeased", "The gods are displeased with your performance."),
|
|
("system.quest.favor_phrase.pleased", "The gods are pleased with your performance."),
|
|
("system.quest.favor_phrase.very_pleased", "The gods are very pleased with your performance."),
|
|
("system.quest.favor_phrase.extremely_pleased", "The gods are extremely pleased with your performance."),
|
|
("system.quest.favor_phrase.ultimately_pleased", "The gods are ultimately pleased with your performance."),
|
|
("system.quest.rewards.title", "Quest Rewards"),
|
|
("system.quest.rewards.complete_heading", "Quest Complete!"),
|
|
("system.quest.rewards.heading", "Rewards:"),
|
|
("system.quest.rewards.favor_line", "Favor: +{favor}"),
|
|
("system.quest.rewards.item_line", "{item}: +{amount}"),
|
|
("system.quest.rewards.inventory_full_line", "Inventory full, could not receive {item}."),
|
|
("system.quest.rewards.no_items_awarded", "No items awarded."),
|
|
("system.quest.rewards.score_line", "Score: {score}"),
|
|
("system.quest.menu.prompt", "Quest menu."),
|
|
("system.quest.menu.count", "{index} of {total}"),
|
|
("system.quest.minigame.starting", "Starting."),
|
|
("system.quest.bat_invasion.complete_score", "Bat invasion complete. Score {score}."),
|
|
("system.quest.boomerang.press_space_to_throw", "Press Space to throw."),
|
|
("system.quest.boomerang.skill.almost_dropped", "almost dropped"),
|
|
("system.quest.boomerang.skill.managed_to_catch", "managed to catch"),
|
|
("system.quest.boomerang.skill.caught", "caught"),
|
|
("system.quest.boomerang.skill.expertly_caught", "expertly caught"),
|
|
("system.quest.boomerang.caught_result", "You {skill} the boomerang. {points} points.{suffix}"),
|
|
("system.quest.boomerang.missed", "You missed the boomerang.{suffix}"),
|
|
("system.quest.boomerang.hit_you", "The boomerang hit you.{suffix}"),
|
|
("system.quest.boomerang.summary", "You caught {count} boomerangs for a total of {score} points."),
|
|
("system.quest.enchanted_melody.practice_prompt", "Practice mode. Press Enter to begin, Escape to cancel."),
|
|
("system.quest.enchanted_melody.starting_prompt", "Starting. Repeat the pattern."),
|
|
("system.quest.enchanted_melody.score", "You matched {rounds} notes. Score {score}."),
|
|
("system.quest.skeletal_bard.select_notes", "Select number of notes. {selection}."),
|
|
("system.quest.skeletal_bard.round_result",
|
|
"You entered {guess} notes and the actual number was {actual}. {points} points. Press Enter to continue."),
|
|
("system.quest.skeletal_bard.complete_score", "Skeletal Bard complete. Your score for all five tunes is {score}."),
|
|
("system.quest.escape_from_hel.fell_in_score", "You fell in. Score {score}."),
|
|
("system.adventure.none_in_base", "No adventures available in the base."),
|
|
("system.adventure.already_attempted_today", "You have already attempted an adventure today."),
|
|
("system.adventure.option.unicorn_hunt", "Unicorn Hunt (Mountain Boss)"),
|
|
("system.adventure.option.bandit_hideout", "Bandit's Hideout"),
|
|
("system.adventure.none_in_area", "No adventures found in this area."),
|
|
("system.adventure.menu.prompt", "Adventure menu."),
|
|
("system.adventure.victory", "Victory!"),
|
|
("system.adventure.rewards.title", "=== Victory Rewards ==="),
|
|
("system.adventure.unicorn.flee", "You fled from the unicorn."),
|
|
("system.adventure.unicorn.position_report",
|
|
"X {x}, {terrain}, facing {direction}"),
|
|
("system.adventure.unicorn.player_trampled", "The unicorn trampled you!"),
|
|
("system.adventure.unicorn.victory_title", "Unicorn Victory"),
|
|
("system.adventure.unicorn.reward.favor_awarded",
|
|
"The gods are pleased with your victory! {favor} favor awarded."),
|
|
("system.adventure.unicorn.reward.heal_scrolls", "Heal Scrolls: +{count}."),
|
|
("system.adventure.unicorn.reward.learned_rune_swiftness", "Learned Rune of Swiftness!"),
|
|
("system.adventure.unicorn.reward.engrave_unlocked",
|
|
"You can now engrave equipment with this rune at the crafting menu."),
|
|
("system.adventure.unicorn.reward.mastered_rune_swiftness",
|
|
"You have already mastered the Rune of Swiftness."),
|
|
("system.adventure.unicorn.horses.no_stable_or_storage",
|
|
"Stable or storage not built. No horses were captured."),
|
|
("system.adventure.unicorn.horses.stable_full", "Stable is full. No horses were captured."),
|
|
("system.adventure.unicorn.horses.joined_stable", "A horse joins your stable."),
|
|
("system.adventure.unicorn.horses.none_captured", "No horses were captured."),
|
|
("system.adventure.bandit_hideout.flee", "You fled from the bandit hideout."),
|
|
("system.adventure.bandit_hideout.position_report",
|
|
"X {x}, {terrain}. Distance to base {distance}. Facing {direction}. Base health {health} of {max}."),
|
|
("system.adventure.bandit_hideout.player_defeated", "You were defeated in the bandit hideout."),
|
|
("system.character.report.position", "{direction}, x {x}, y {y}, terrain {terrain}"),
|
|
("system.character.report.terrain_mountains", "{terrain}. Mountains"),
|
|
("system.character.report.player", "{health} of {max} health"),
|
|
("system.character.report.pet",
|
|
"{pet}: {health} of {max} health, loyalty {loyalty} of {max_loyalty}"),
|
|
("system.item.label.items", "items"),
|
|
("system.item.label.item", "item"),
|
|
("system.item.label.unknown", "Unknown"),
|
|
("system.equipment.menu.prompt", "Equipment menu. {option}"),
|
|
("system.equipment.menu.nothing_to_equip", "Nothing to equip."),
|
|
("system.equipment.menu.equipped_suffix", " (equipped)"),
|
|
("system.equipment.set_to_slot", "{name} set to slot {slot}."),
|
|
("system.equipment.equipped", "{name} equipped."),
|
|
("system.equipment.unequipped", "{name} unequipped."),
|
|
("system.equipment.name.none", "None"),
|
|
("system.equipment.name.unknown", "Unknown"),
|
|
("system.equipment.name.spear", "Spear"),
|
|
("system.equipment.name.stone_axe", "Stone Axe"),
|
|
("system.equipment.name.sling", "Sling"),
|
|
("system.equipment.name.bow", "Bow"),
|
|
("system.equipment.name.skin_hat", "Skin Hat"),
|
|
("system.equipment.name.skin_gloves", "Skin Gloves"),
|
|
("system.equipment.name.skin_pants", "Skin Pants"),
|
|
("system.equipment.name.skin_tunic", "Skin Tunic"),
|
|
("system.equipment.name.moccasins", "Moccasins"),
|
|
("system.equipment.name.skin_pouch", "Skin Pouch"),
|
|
("system.equipment.name.backpack", "Backpack"),
|
|
("system.equipment.name.fishing_pole", "Fishing Pole"),
|
|
("system.equipment.name_plural.spears", "Spears"),
|
|
("system.equipment.name_plural.stone_axes", "Stone Axes"),
|
|
("system.equipment.name_plural.slings", "Slings"),
|
|
("system.equipment.name_plural.bows", "Bows"),
|
|
("system.equipment.name_plural.skin_hats", "Skin Hats"),
|
|
("system.equipment.name_plural.skin_gloves", "Skin Gloves"),
|
|
("system.equipment.name_plural.skin_pants", "Skin Pants"),
|
|
("system.equipment.name_plural.skin_tunics", "Skin Tunics"),
|
|
("system.equipment.name_plural.moccasins", "Moccasins"),
|
|
("system.equipment.name_plural.skin_pouches", "Skin Pouches"),
|
|
("system.equipment.name_plural.backpacks", "Backpacks"),
|
|
("system.equipment.name_plural.fishing_poles", "Fishing Poles"),
|
|
("system.equipment.name_plural.items", "Items"),
|
|
("system.environment.tree.fell", "Tree fell!"),
|
|
("system.environment.tree.fell_with_loot",
|
|
"Tree fell! Got {sticks} {sticks_label}, {vines} {vines_label}, and {logs} {logs_label}."),
|
|
("system.environment.tree.inventory_full", "Inventory full."),
|
|
("system.environment.fall.damage_report",
|
|
"Fell {height} feet! Took {damage} damage. {health} health remaining."),
|
|
("system.environment.snare.collected_with_catch", "Collected {catch_type} and snare."),
|
|
("system.environment.snare.collected_empty", "Collected snare."),
|
|
("system.environment.tree.cut_down", "This tree has been cut down."),
|
|
("system.environment.tree.empty", "This tree is empty."),
|
|
("system.environment.area.nothing_left", "This area has nothing left."),
|
|
("system.environment.tree.climb_started", "Started climbing tree. Height is {height} feet."),
|
|
("system.environment.tree.climb_reached_top", "Reached the top at {height} feet."),
|
|
("system.environment.tree.climb_reached_ground", "Safely reached the ground."),
|
|
("system.environment.tree.climb_down", "Climbing down."),
|
|
("system.environment.movement.need_rope", "You'll need a rope to climb there."),
|
|
("system.environment.movement.press_up_climb", "Press up to climb up."),
|
|
("system.environment.movement.press_down_climb", "Press down to climb down."),
|
|
("system.environment.movement.need_canoe", "You need a canoe to cross deep water."),
|
|
("system.environment.rope_climb.direction.up", "up"),
|
|
("system.environment.rope_climb.direction.down", "down"),
|
|
("system.environment.rope_climb.progress", "Climbing {direction}. {distance} feet."),
|
|
("system.environment.rope_climb.reached_elevation", "Reached elevation {elevation}."),
|
|
("system.snare.escape_at", "A {catch_type} escaped from your snare at x {position} y 0!"),
|
|
("system.snare.caught_at", "{catch_type} caught in snare at x {position} y 0!"),
|
|
("system.snare.stepped_on_with_escape", "You stepped on your snare! The {catch_type} escaped."),
|
|
("system.snare.stepped_on_broke", "You stepped on your snare and broke it!"),
|
|
("system.fire.low_fuel_at", "Fire at x {position} y {y} is getting low!"),
|
|
("system.fire.went_out_at", "Fire at x {position} y {y} has gone out."),
|
|
("system.crafting.menu.prompt", "Crafting menu. {option}"),
|
|
("system.crafting.category.weapons", "Weapons"),
|
|
("system.crafting.category.tools", "Tools"),
|
|
("system.crafting.category.materials", "Materials"),
|
|
("system.crafting.category.clothing", "Clothing"),
|
|
("system.crafting.category.buildings", "Buildings"),
|
|
("system.crafting.category.barricade", "Barricade"),
|
|
("system.crafting.category.runes", "Runes"),
|
|
("system.crafting.in_progress", "Crafting..."),
|
|
("system.crafting.weapons.prompt", "Weapons. {option}"),
|
|
("system.crafting.weapons.option.spear", "Spear (1 Stick, 1 Vine, 1 Stone) [Requires Knife]"),
|
|
("system.crafting.weapons.option.sling", "Sling (1 Skin, 2 Vines)"),
|
|
("system.crafting.weapons.option.bow", "Bow (1 Stick, 1 Bowstring)"),
|
|
("system.crafting.weapons.crafted.spear", "Crafted a Spear."),
|
|
("system.crafting.weapons.crafted.sling", "Crafted a Sling."),
|
|
("system.crafting.weapons.crafted.bow", "Crafted a Bow."),
|
|
("system.crafting.weapons.crafted.stone_axe", "Crafted a Stone Axe."),
|
|
("system.crafting.weapons.crafted_max.spears", "Crafted {count} Spears."),
|
|
("system.crafting.weapons.crafted_max.slings", "Crafted {count} Slings."),
|
|
("system.crafting.weapons.crafted_max.bows", "Crafted {count} Bows."),
|
|
("system.crafting.weapons.crafted_max.stone_axes", "Crafted {count} Stone Axes."),
|
|
("system.crafting.tools.prompt", "Tools. {option}"),
|
|
("system.crafting.tools.option.stone_knife", "Stone Knife (2 Stones)"),
|
|
("system.crafting.tools.option.snare", "Snare (1 Stick, 2 Vines)"),
|
|
("system.crafting.tools.option.stone_axe", "Stone Axe (1 Stick, 1 Vine, 2 Stones) [Requires Knife]"),
|
|
("system.crafting.tools.option.fishing_pole", "Fishing Pole (1 Stick, 2 Vines)"),
|
|
("system.crafting.tools.option.rope", "Rope (3 Vines)"),
|
|
("system.crafting.tools.option.quiver", "Quiver (2 Skins, 2 Vines)"),
|
|
("system.crafting.tools.option.canoe", "Canoe (4 Logs, 11 Sticks, 11 Vines, 6 Skins, 2 Rope, 6 Reeds)"),
|
|
("system.crafting.tools.option.reed_basket", "Reed Basket (3 Reeds)"),
|
|
("system.crafting.tools.option.clay_pot", "Clay Pot (3 Clay)"),
|
|
("system.crafting.tools.crafted.stone_knife", "Crafted a Stone Knife."),
|
|
("system.crafting.tools.crafted.snare", "Crafted a Snare."),
|
|
("system.crafting.tools.crafted.fishing_pole", "Crafted a Fishing Pole."),
|
|
("system.crafting.tools.crafted.rope", "Crafted rope."),
|
|
("system.crafting.tools.crafted.quiver", "Crafted a Quiver."),
|
|
("system.crafting.tools.crafted.canoe", "Crafted a Canoe."),
|
|
("system.crafting.tools.crafted.reed_basket", "Crafted a reed basket."),
|
|
("system.crafting.tools.crafted.clay_pot", "Crafted a clay pot."),
|
|
("system.crafting.tools.crafted_max.stone_knives", "Crafted {count} Stone Knives."),
|
|
("system.crafting.tools.crafted_max.snares", "Crafted {count} Snares."),
|
|
("system.crafting.tools.crafted_max.fishing_poles", "Crafted {count} Fishing Poles."),
|
|
("system.crafting.tools.crafted_max.ropes", "Crafted {count} Rope."),
|
|
("system.crafting.tools.crafted_max.quivers", "Crafted {count} Quivers."),
|
|
("system.crafting.tools.crafted_max.canoes", "Crafted {count} Canoes."),
|
|
("system.crafting.tools.crafted_max.reed_baskets", "Crafted {count} Reed Baskets."),
|
|
("system.crafting.tools.crafted_max.clay_pots", "Crafted {count} Clay Pots."),
|
|
("system.crafting.materials.prompt", "Materials. {option}"),
|
|
("system.crafting.materials.option.butcher_game", "Butcher Game [Requires Game, Knife, Fire nearby]"),
|
|
("system.crafting.materials.option.smoke_fish", "Smoke Fish (1 Fish, 1 Stick) [Requires Fire nearby]"),
|
|
("system.crafting.materials.option.arrows", "Arrows (2 Sticks, 4 Feathers, 2 Stones) [Requires Quiver]"),
|
|
("system.crafting.materials.option.bowstring", "Bowstring (3 Sinew) [Requires Fire nearby]"),
|
|
("system.crafting.materials.option.incense", "Incense (6 Sticks, 2 Vines, 1 Reed) [Requires Altar]"),
|
|
("system.crafting.materials.not_enough_quiver_capacity_for_arrows", "Not enough quiver capacity for arrows."),
|
|
("system.crafting.materials.crafted.arrows", "Crafted {count} arrows."),
|
|
("system.crafting.materials.crafted.bowstring", "Crafted a bowstring."),
|
|
("system.crafting.materials.crafted_max.bowstrings", "Crafted {count} Bowstrings."),
|
|
("system.crafting.materials.crafted.incense", "Crafted incense."),
|
|
("system.crafting.materials.crafted_max.incense", "Crafted {count} Incense."),
|
|
("system.crafting.materials.smoke_fish.crafted", "Smoked a fish into {yield} smoked fish."),
|
|
("system.crafting.materials.smoke_fish.crafted_max",
|
|
"Smoked {count} fish into {yield} smoked fish."),
|
|
("system.crafting.materials.butcher.goose", "Butchered goose. Got 1 meat, feathers, and down."),
|
|
("system.crafting.materials.butcher.turkey", "Butchered turkey. Got 1 meat and feathers."),
|
|
("system.crafting.materials.butcher.boar", "Butchered boar. Got meat, 3 skins, and 2 sinew."),
|
|
("system.crafting.materials.butcher.default", "Butchered {game}. Got 1 meat and 1 skin."),
|
|
("system.crafting.materials.butcher.no_space_outputs", "No space for outputs."),
|
|
("system.crafting.materials.butcher.max_result",
|
|
"Butchered {count} game. Got {meat} meat, {skins} skins, {feathers} feathers, {down} down, {sinew} sinew."),
|
|
("system.crafting.clothing.prompt", "Clothing. {option}"),
|
|
("system.crafting.clothing.option.skin_hat", "Skin Hat (1 Skin, 1 Vine)"),
|
|
("system.crafting.clothing.option.skin_gloves", "Skin Gloves (1 Skin, 1 Vine)"),
|
|
("system.crafting.clothing.option.skin_pants", "Skin Pants (6 Skins, 3 Vines)"),
|
|
("system.crafting.clothing.option.skin_tunic", "Skin Tunic (4 Skins, 2 Vines)"),
|
|
("system.crafting.clothing.option.moccasins", "Moccasins (2 Skins, 1 Vine)"),
|
|
("system.crafting.clothing.option.skin_pouch", "Skin Pouch (2 Skins, 1 Vine)"),
|
|
("system.crafting.clothing.option.backpack", "Backpack (11 Skins, 5 Vines, 4 Skin Pouches)"),
|
|
("system.crafting.clothing.crafted.skin_hat", "Crafted a Skin Hat."),
|
|
("system.crafting.clothing.crafted.skin_gloves", "Crafted Skin Gloves."),
|
|
("system.crafting.clothing.crafted.skin_pants", "Crafted Skin Pants."),
|
|
("system.crafting.clothing.crafted.skin_tunic", "Crafted a Skin Tunic."),
|
|
("system.crafting.clothing.crafted.moccasins", "Crafted moccasins."),
|
|
("system.crafting.clothing.crafted.skin_pouch", "Crafted a Skin Pouch."),
|
|
("system.crafting.clothing.crafted.backpack", "Crafted a Backpack."),
|
|
("system.crafting.clothing.crafted_max.skin_hats", "Crafted {count} Skin Hats."),
|
|
("system.crafting.clothing.crafted_max.skin_gloves", "Crafted {count} Skin Gloves."),
|
|
("system.crafting.clothing.crafted_max.skin_pants", "Crafted {count} Skin Pants."),
|
|
("system.crafting.clothing.crafted_max.skin_tunics", "Crafted {count} Skin Tunics."),
|
|
("system.crafting.clothing.crafted_max.moccasins", "Crafted {count} Moccasins."),
|
|
("system.crafting.clothing.crafted_max.skin_pouches", "Crafted {count} Skin Pouches."),
|
|
("system.crafting.clothing.crafted_max.backpacks", "Crafted {count} Backpacks."),
|
|
("system.crafting.buildings.prompt", "Buildings. {option}"),
|
|
("system.crafting.buildings.option.firepit", "Firepit (9 Stones)"),
|
|
("system.crafting.buildings.option.fire", "Fire (2 Sticks, 1 Log) [Requires Firepit]"),
|
|
("system.crafting.buildings.option.herb_garden", "Herb Garden (9 Stones, 3 Vines, 2 Logs) [Base Only]"),
|
|
("system.crafting.buildings.option.storage_upgrade_1",
|
|
"Upgrade Storage (6 Logs, 9 Stones, 8 Vines) [Base Only, 50 each]"),
|
|
("system.crafting.buildings.option.storage_upgrade_2",
|
|
"Upgrade Storage (12 Logs, 18 Stones, 16 Vines) [Base Only, 100 each]"),
|
|
("system.crafting.buildings.option.pasture", "Pasture (8 Logs, 18 Ropes) [Base Only, Requires Storage Upgrade]"),
|
|
("system.crafting.buildings.option.stable",
|
|
"Stable (10 Logs, 15 Stones, 10 Vines) [Base Only, Requires Storage Upgrade]"),
|
|
("system.crafting.buildings.option.altar", "Altar (9 Stones, 3 Sticks) [Base Only]"),
|
|
("system.crafting.buildings.none_available", "No buildings available."),
|
|
("system.crafting.buildings.firepit.already_in_base", "There is already a firepit in the base."),
|
|
("system.crafting.buildings.firepit.already_here", "There is already a firepit here."),
|
|
("system.crafting.buildings.firepit.built", "Firepit built here."),
|
|
("system.crafting.buildings.fire.requires_firepit_nearby", "You need a firepit within 2 tiles to build a fire."),
|
|
("system.crafting.buildings.fire.already_in_base", "There is already a fire in the base."),
|
|
("system.crafting.buildings.fire.built", "Fire built at firepit."),
|
|
("system.crafting.buildings.herb_garden.base_only", "Herb garden can only be built in the base area."),
|
|
("system.crafting.buildings.herb_garden.already_in_base", "There is already an herb garden in the base."),
|
|
("system.crafting.buildings.herb_garden.built", "Herb garden built. The base now heals faster."),
|
|
("system.crafting.buildings.storage.base_only", "Storage must be built in the base."),
|
|
("system.crafting.buildings.storage.fully_upgraded", "Storage is fully upgraded."),
|
|
("system.crafting.buildings.storage.upgraded", "Storage upgraded. Capacity is now {capacity} per item."),
|
|
("system.crafting.buildings.pasture.base_only", "Pasture must be built in the base."),
|
|
("system.crafting.buildings.pasture.requires_storage_upgrade", "Storage must be upgraded before a pasture."),
|
|
("system.crafting.buildings.pasture.already_built", "Pasture already built."),
|
|
("system.crafting.buildings.pasture.built", "Pasture built."),
|
|
("system.crafting.buildings.stable.base_only", "Stable must be built in the base."),
|
|
("system.crafting.buildings.stable.requires_storage_upgrade", "Storage must be upgraded before a stable."),
|
|
("system.crafting.buildings.stable.already_built", "Stable already built."),
|
|
("system.crafting.buildings.stable.built", "Stable built."),
|
|
("system.crafting.buildings.altar.base_only", "Altar must be built in the base."),
|
|
("system.crafting.buildings.altar.already_built", "Altar already built."),
|
|
("system.crafting.buildings.altar.built", "Altar built."),
|
|
("system.crafting.runes.base_only", "Rune engraving can only be done in the base area."),
|
|
("system.crafting.runes.option", "{rune} (1 Clay, 1 Favor) [Requires Knife]"),
|
|
("system.crafting.runes.none_unlocked", "No runes unlocked yet."),
|
|
("system.crafting.runes.prompt", "Runes. {option}"),
|
|
("system.crafting.runes.equipment_option", "{name} ({count} available)"),
|
|
("system.crafting.runes.no_equipment_available", "No equipment available to engrave."),
|
|
("system.crafting.runes.select_equipment_prompt",
|
|
"Select equipment to engrave with {rune}. {option}"),
|
|
("system.crafting.runes.no_item_available", "No {item} available."),
|
|
("system.crafting.runes.runed_name", "Runed {equipment} of {rune}"),
|
|
("system.crafting.runes.engraved", "Engraved {item}."),
|
|
("system.crafting.runes.engraved_max", "Engraved {count} {item} with {rune}."),
|
|
("system.crafting.barricade.prompt", "Barricade. {option}"),
|
|
("system.crafting.barricade.option.reinforce_sticks",
|
|
"Reinforce with sticks ({cost} sticks, +{health} health)"),
|
|
("system.crafting.barricade.option.reinforce_vines", "Reinforce with vines ({cost} vines, +{health} health)"),
|
|
("system.crafting.barricade.option.reinforce_log", "Reinforce with log ({cost} log, +{health} health)"),
|
|
("system.crafting.barricade.option.reinforce_stones",
|
|
"Reinforce with stones ({cost} stones, +{health} health)"),
|
|
("system.crafting.barricade.full_health", "Barricade is already at full health."),
|
|
("system.crafting.barricade.no_materials", "No materials to reinforce the barricade."),
|
|
("system.crafting.barricade.reinforced.sticks",
|
|
"Reinforced barricade with sticks. +{gained} health. Now {health} of {max}."),
|
|
("system.crafting.barricade.reinforced.vines",
|
|
"Reinforced barricade with vines. +{gained} health. Now {health} of {max}."),
|
|
("system.crafting.barricade.reinforced.log",
|
|
"Reinforced barricade with log. +{gained} health. Now {health} of {max}."),
|
|
("system.crafting.barricade.reinforced.stones",
|
|
"Reinforced barricade with stones. +{gained} health. Now {health} of {max}."),
|
|
("system.crafting.barricade.reinforced_max.sticks",
|
|
"Reinforced barricade {count} times with sticks. Health now {health}."),
|
|
("system.crafting.barricade.reinforced_max.vines",
|
|
"Reinforced barricade {count} times with vines. Health now {health}."),
|
|
("system.crafting.barricade.reinforced_max.log",
|
|
"Reinforced barricade {count} times with log. Health now {health}."),
|
|
("system.crafting.barricade.reinforced_max.stones",
|
|
"Reinforced barricade {count} times with stones. Health now {health}."),
|
|
("system.crafting.barricade.not_enough.sticks", "Not enough sticks."),
|
|
("system.crafting.barricade.not_enough.vines", "Not enough vines."),
|
|
("system.crafting.barricade.not_enough.logs", "Not enough logs."),
|
|
("system.crafting.barricade.not_enough.stones", "Not enough stones."),
|
|
("system.crafting.missing", "Missing: {requirements}"),
|
|
("system.crafting.requirement.stone_knife", "Stone Knife"),
|
|
("system.crafting.requirement.game", "Game"),
|
|
("system.crafting.requirement.favor", "favor"),
|
|
("system.crafting.requirement.resources", "resources"),
|
|
("system.story.voice.format", "{speaker}: {line}"),
|
|
("system.story.god.fallback", "A god"),
|
|
("system.story.god.odin", "Odin"),
|
|
("system.story.god.thor", "Thor"),
|
|
("system.story.god.freyja", "Freyja"),
|
|
("system.story.god.loki", "Loki"),
|
|
("system.story.god.tyr", "Tyr"),
|
|
("system.story.god.baldur", "Baldur"),
|
|
("system.story.god.frigg", "Frigg"),
|
|
("system.story.god.heimdall", "Heimdall"),
|
|
("system.story.god.hel", "Hel"),
|
|
("system.story.god.fenrir", "Fenrir"),
|
|
("system.story.god.freyr", "Freyr"),
|
|
("system.story.line01", "The world has fallen."),
|
|
("system.story.line02", "Once-great cities lie in ruin."),
|
|
("system.story.line03", "In most places, not two stones remain as mankind set them."),
|
|
("system.story.line04", "The gods, long thought sleeping, achieved the impossible."),
|
|
("system.story.line05", "Ragnarok was averted."),
|
|
("system.story.line06", "Great Fenrir was freed and did not strike down his captors."),
|
|
("system.story.line07", "Surtr did not lay waste to our world with his flaming greatsword."),
|
|
("system.story.line08", "For a fleeting age, the gods wrestled fate into silence."),
|
|
("system.story.line09", "But the end of the world was delayed, never denied."),
|
|
("system.story.line10", "One force lay beyond even divine command: mankind."),
|
|
("system.story.line11", "Hatred swelled."),
|
|
("system.story.line12", "Greed answered it."),
|
|
("system.story.line13", "Mercy withered."),
|
|
("system.story.line14", "Differences became crimes."),
|
|
("system.story.line15", "Folk no longer argued; they tore at one another like beasts."),
|
|
("system.story.line16", "Neighbors became enemies for trifles: a color, a song, a single word."),
|
|
("system.story.line17", "War followed."),
|
|
("system.story.line18", "Cities burned, roads broke, and monsters wearing human faces roamed the streets."),
|
|
("system.story.line19", "Then ruin deepened into terror."),
|
|
("system.story.line20", "The dead, enraged by wanton slaughter, rose from their graves."),
|
|
("system.story.line21", "Corpses woke and hunted the living."),
|
|
("system.story.line22", "Night-stalkers fed on blood."),
|
|
("system.story.line23", "Even ash and dust remembered form and rose again."),
|
|
("system.story.line24", "Soon all that remained was rubble and bone."),
|
|
("system.story.line25", "Your last memory is flight from a vast horde of nightmare creatures."),
|
|
("system.story.line26", "When you wake, you are somewhere strange."),
|
|
("system.story.line27",
|
|
"A crude wooden shelter stands against the wind, open to the elements, braced with sticks and logs."),
|
|
("system.story.line28", "As your senses return, faint voices stir in the dark."),
|
|
("system.story.line29", "After the death of this world, another shall rise."),
|
|
("system.story.line30", "You have been judged worthy to try and survive."),
|
|
("system.story.line31", "This world belongs to the dead."),
|
|
("system.story.line32", "In time, they will slay even us."),
|
|
("system.story.line33", "Prove yourself, warrior, and you may walk into the next world."),
|
|
("system.story.line34", "May your death, when it comes, be honorable."),
|
|
("system.story.line35", "We will aid you while we still have strength, but time is against us."),
|
|
("system.story.line36",
|
|
"Beyond your shelter lies grassland, and farther off, broken stone that may once have been a city."),
|
|
("system.story.line37", "In the total devastation, even memory has lost its shape."),
|
|
]
|
|
|
|
lines: List[str] = []
|
|
lines.append("; Draugnorak localization catalog")
|
|
lines.append("; Copy this file to lang/<code>.ini and translate only the right-hand values.")
|
|
lines.append("; Keep keys unchanged. Use placeholders like {arg1}, {count}, {language} exactly as written.")
|
|
lines.append("")
|
|
|
|
lines.append("[meta]")
|
|
for key, value in manual_entries[:3]:
|
|
lines.append(f"{key.split('.', 1)[1]}={escape_for_ini(value)}")
|
|
|
|
lines.append("")
|
|
lines.append("[system]")
|
|
for key, value in manual_entries[3:]:
|
|
lines.append(f"{key.split('.', 1)[1]}={escape_for_ini(value)}")
|
|
|
|
lines.append("")
|
|
lines.append("[messages]")
|
|
|
|
for key in sorted(entries.keys()):
|
|
value = entries[key]["value"]
|
|
refs: List[str] = entries[key]["refs"] # type: ignore[assignment]
|
|
for ref in refs[:3]:
|
|
lines.append(f"; {ref}")
|
|
lines.append(f"{key}={escape_for_ini(str(value))}")
|
|
|
|
output_path.write_text("\n".join(lines) + "\n", encoding="utf-8")
|
|
|
|
|
|
def main() -> None:
|
|
entries: Dict[str, Dict[str, object]] = {}
|
|
|
|
for file_path in iter_nvgt_files():
|
|
scan_file(file_path, entries)
|
|
|
|
add_item_registry_fragments(entries)
|
|
add_seed_messages(entries)
|
|
add_learn_sounds_label_seeds(entries)
|
|
|
|
en_path = ROOT / "lang" / "en.ini"
|
|
template_path = ROOT / "lang" / "en.template.ini"
|
|
|
|
write_catalog(entries, en_path)
|
|
write_catalog(entries, template_path)
|
|
|
|
print(f"Wrote {en_path.relative_to(ROOT)} with {len(entries)} generated entries")
|
|
print(f"Wrote {template_path.relative_to(ROOT)}")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|