The copy team rewrote ui-strings.yaml with multi-level nesting, renamed sections, and inline comments. Update the YAML parser to use a stack-based approach for arbitrary nesting depth, update HUD and interaction prompt to reference the new key names, and align tests with the new structure. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
93 lines
2.8 KiB
GDScript
93 lines
2.8 KiB
GDScript
extends Node
|
|
|
|
# UIStrings — loads UI display text from data/ui-strings.yaml.
|
|
# Access: UIStrings.get_text("hud.mode_label") -> "Mode"
|
|
# Falls back to the key itself if not found, with a push_warning.
|
|
|
|
const STRINGS_PATH: String = "res://data/ui-strings.yaml"
|
|
|
|
var _strings: Dictionary = {}
|
|
var _loaded: bool = false
|
|
|
|
func _ready() -> void:
|
|
_load_strings()
|
|
|
|
func _load_strings() -> void:
|
|
if not FileAccess.file_exists(STRINGS_PATH):
|
|
push_warning("UIStrings: file not found: %s" % STRINGS_PATH)
|
|
return
|
|
var file := FileAccess.open(STRINGS_PATH, FileAccess.READ)
|
|
if file == null:
|
|
push_warning("UIStrings: failed to open: %s" % STRINGS_PATH)
|
|
return
|
|
var text := file.get_as_text()
|
|
file.close()
|
|
_strings = _parse_yaml(text)
|
|
_loaded = true
|
|
print("UIStrings: loaded %d strings from %s" % [_strings.size(), STRINGS_PATH])
|
|
|
|
## Get a UI string by dotted key. Returns the key itself if not found.
|
|
func get_text(key: String) -> String:
|
|
if _strings.has(key):
|
|
return _strings[key]
|
|
push_warning("UIStrings: missing key '%s'" % key)
|
|
return key
|
|
|
|
## Check if a key exists.
|
|
func has_key(key: String) -> bool:
|
|
return _strings.has(key)
|
|
|
|
## Get all keys.
|
|
func get_all_keys() -> PackedStringArray:
|
|
return PackedStringArray(_strings.keys())
|
|
|
|
## Reload from disk (useful for hot-reload during development).
|
|
func reload() -> void:
|
|
_strings.clear()
|
|
_loaded = false
|
|
_load_strings()
|
|
|
|
## Parse YAML with arbitrary nesting depth.
|
|
## Returns flat Dictionary with dotted keys: { "section.sub.key": "value" }.
|
|
static func _parse_yaml(text: String) -> Dictionary:
|
|
var strings := {}
|
|
var stack: Array = [] # [[indent, key], ...]
|
|
for line in text.split("\n"):
|
|
var stripped := line.strip_edges(false, true)
|
|
if stripped.is_empty() or stripped.begins_with("#"):
|
|
continue
|
|
var indent := line.length() - line.lstrip(" ").length()
|
|
var content := stripped.strip_edges()
|
|
var colon_pos := content.find(":")
|
|
if colon_pos < 0:
|
|
continue
|
|
var key := content.substr(0, colon_pos).strip_edges()
|
|
var val := content.substr(colon_pos + 1).strip_edges()
|
|
# Trailing comment without a value — treat as section header
|
|
if val.begins_with("#"):
|
|
val = ""
|
|
# Pop sections at same or deeper indent
|
|
while stack.size() > 0 and stack.back()[0] >= indent:
|
|
stack.pop_back()
|
|
if val.is_empty():
|
|
# Section header — push onto stack
|
|
stack.push_back([indent, key])
|
|
else:
|
|
# Leaf value — extract from quotes or strip inline comment
|
|
if val.begins_with("\""):
|
|
var end_quote := val.find("\"", 1)
|
|
if end_quote > 0:
|
|
val = val.substr(1, end_quote - 1)
|
|
else:
|
|
val = val.substr(1)
|
|
else:
|
|
var comment_pos := val.find(" #")
|
|
if comment_pos >= 0:
|
|
val = val.substr(0, comment_pos).strip_edges()
|
|
var dotted_key := ""
|
|
for entry in stack:
|
|
dotted_key += entry[1] + "."
|
|
dotted_key += key
|
|
strings[dotted_key] = val
|
|
return strings
|