extends Node ## Old Tam's voice: the recorded line for a tutorial step, his goodbye or one ## of his tips (assets/audio/voice, made by tools/voice/make_voice.sh). A line ## only plays if it was made from exactly the text on screen (lines.json), so ## after an edit it stays quiet rather than saying the old words. A new line ## cuts off the one before; the music dips while he talks. The Voice volume ## (pause menu) turns him down or off. It pauses with the game. const DIR := "res://assets/audio/voice/" var lines: Dictionary = {} ## line id -> the key of the text it was made from var spoken: Array = [] ## ids said (tests; headless nothing is heard) var speaking := "" var _p: AudioStreamPlayer var _quiet := false func _ready() -> void: var d: Variant = JSON.parse_string(FileAccess.get_file_as_string(DIR + "lines.json")) \ if FileAccess.file_exists(DIR + "lines.json") else null if d is Dictionary: lines = d _quiet = DisplayServer.get_name() == "headless" if _quiet: return _p = AudioStreamPlayer.new() _p.bus = "Voice" add_child(_p) _p.finished.connect(_done) ## The same key make_voice.py writes: the first 16 hex digits of the text's SHA-1. static func text_key(text: String) -> String: return text.sha1_text().substr(0, 16) ## Whether there's a recording of `text` for `id`. func has_line(id: String, text: String) -> bool: return String(lines.get(id, "")) == text_key(text) ## Says line `id` if it's the recording of `text`. True if it's being said. func say(id: String, text: String) -> bool: if not has_line(id, text): return false spoken.append(id) speaking = id if _quiet: return true var st: Variant = load(DIR + id + ".ogg") if not (st is AudioStream): speaking = "" return false _p.stop() _p.stream = st _p.play() Music.duck = true return true func stop() -> void: if _p != null: _p.stop() _done() func is_speaking() -> bool: return _p != null and _p.playing func _done() -> void: speaking = "" Music.duck = false