Route UI labels through the string table
Full build / Import + headless tests (push) Successful in 38s
Full build / Full desktop build + optional release (push) Skipped

Move start menu, settings, pause, Help, HUD, radio, shift review and campaign
labels/templates into content/strings.en.json, including placeholder-validated
dynamic status text. No player-facing hardcoded labels remain outside dynamic
data/debug output. Headless 250/250, docs check OK.
This commit is contained in:
zwitschi committed 2026-11-02 11:25:15 +01:00
1 parent 3a74cedee0
commit af923b1483
10 files changed
+179 -89

No files matched your search

+13 -12
View File
@@ -14,14 +14,14 @@ var _policy: String = ""
var _lines: Array[String] = []
const CONTROL_LINES := [
"Movement: W/A/S/D · mouse look · Shift precision walk",
"Focus: crosshair cone · Tab / mouse wheel cycles nearby people",
"Action wheel: Space or left click · A/D or arrows step · Space confirms",
"Inspect access: E · Wand: C · Polite direction: Q",
"Warning: 4 · Blocking: 5 · Security radio: 6",
"Emergency radio: R, hold the on-screen button to confirm",
"Body camera: B · Flashlight: G · Door lock: L",
"Pause: Esc · Debug overlay: F",
"help.control_move",
"help.control_focus",
"help.control_wheel",
"help.control_access",
"help.control_escalate",
"help.control_emergency",
"help.control_tools",
"help.control_misc",
]
func _init() -> void:
@@ -50,13 +50,13 @@ func _build() -> void:
center.add_child(column)
var title := Label.new()
title.text = "Help"
title.text = Strings.lookup("ui.help_title")
title.add_theme_font_size_override("font_size", 30)
title.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
column.add_child(title)
_search = LineEdit.new()
_search.placeholder_text = "Filter controls…"
_search.placeholder_text = Strings.lookup("ui.help_filter")
_search.text_changed.connect(_refresh)
column.add_child(_search)
@@ -67,7 +67,7 @@ func _build() -> void:
column.add_child(_body)
var close := Button.new()
close.text = "Close"
close.text = Strings.lookup("ui.help_close")
close.pressed.connect(func(): close_panel())
close.size_flags_horizontal = Control.SIZE_SHRINK_CENTER
column.add_child(close)
@@ -92,7 +92,8 @@ func _refresh(filter: String) -> void:
for action in InputBindings.REMAPPABLE:
var label := InputBindings.label_for(InputBindings.current_descriptor(action))
dynamic.append("%s: %s" % [str(action).replace("_", " ").capitalize(), label])
for line in (CONTROL_LINES + dynamic):
for key in (CONTROL_LINES + dynamic):
var line: String = Strings.lookup(key) if key.begins_with("help.") else key
if needle == "" or line.to_lower().contains(needle):
_lines.append(line)
var text := "\n".join(_lines)
+17 -17
View File
@@ -171,7 +171,7 @@ func _init() -> void:
# --- door / flashlight state ---
mode_label = Label.new()
mode_label.text = "Door: open · Light: off"
mode_label.text = Strings.fmt("hud.mode", [Strings.lookup("hud.door_open"), Strings.lookup("hud.light_off")])
mode_label.add_theme_font_size_override("font_size", 13)
mode_label.modulate = Color(0.85, 0.85, 0.9)
precision_label = Label.new()
@@ -184,7 +184,7 @@ func _init() -> void:
# --- escalation level indicator (color-coded) ---
escalation_label = Label.new()
escalation_label.text = "Escalation: Observation"
escalation_label.text = Strings.fmt("hud.escalation_prefix", [str(Rules.LEVEL_NAMES[0])])
escalation_label.add_theme_font_size_override("font_size", 13)
escalation_label.modulate = Color(0.6, 0.9, 0.6)
vbox.add_child(escalation_label)
@@ -231,12 +231,12 @@ func _init() -> void:
rec_icon.stretch_mode = TextureRect.STRETCH_KEEP_ASPECT_CENTERED
rec_row.add_child(rec_icon)
var rec_label := Label.new()
rec_label.text = "REC"
rec_label.text = Strings.lookup("hud.rec")
rec_label.add_theme_font_size_override("font_size", 15)
rec_label.modulate = Color(0.95, 0.35, 0.35)
rec_row.add_child(rec_label)
cam_clock = Label.new()
cam_clock.text = "SHIFT 00:00:00"
cam_clock.text = Strings.fmt("hud.cam_clock", [0, 0, 0])
cam_clock.add_theme_font_size_override("font_size", 12)
cam_clock.modulate = Color(0.95, 0.95, 0.95)
cam_clock.horizontal_alignment = HORIZONTAL_ALIGNMENT_RIGHT
@@ -372,15 +372,15 @@ func set_shift_state(coordinator: Object, scenario: Resource) -> void:
top_label.text = scenario.display_name if scenario else "Gig Line"
var mins := int(coordinator.elapsed_s) / 60
var secs := int(coordinator.elapsed_s) % 60
time_label.text = "shift %02d:%02d phase %s\nrep %.0f crowd %.0f%%" % [
time_label.text = Strings.fmt("hud.shift_status", [
mins, secs,
Rules.PHASE_NAMES.get(coordinator.current_phase, "Early"),
coordinator.reputation.rating,
coordinator.crowd.mood * 100.0,
]
])
if cam_clock:
var total := int(coordinator.elapsed_s)
cam_clock.text = "SHIFT %02d:%02d:%02d" % [total / 3600, (total / 60) % 60, total % 60]
cam_clock.text = Strings.fmt("hud.cam_clock", [total / 3600, (total / 60) % 60, total % 60])
_update_radio_cooldown(coordinator)
func _update_radio_cooldown(coordinator: Object) -> void:
@@ -413,7 +413,7 @@ func set_flashlight(on: bool) -> void:
_refresh_mode_label()
func _refresh_mode_label() -> void:
mode_label.text = "Door: %s · Light: %s" % ["LOCKED" if _door_locked else "open", "on" if _flashlight else "off"]
mode_label.text = Strings.fmt("hud.mode", [Strings.lookup("hud.door_locked") if _door_locked else Strings.lookup("hud.door_open"), Strings.lookup("hud.light_on") if _flashlight else Strings.lookup("hud.light_off")])
if icon_row:
flashlight_icon.visible = _flashlight
door_icon.visible = _door_locked
@@ -571,7 +571,7 @@ func show_inspection(controller: Object, artifacts: Array, result: Dictionary) -
var profile: Object = controller.profile
var npc_display: String = (profile.display_name if profile else "Person")
var granted: bool = result.get("granted", false)
inspect_text.text = "ACCESS INSPECTION\n\nAccess check — %s\nGranted: %s" % [npc_display, "YES" if granted else "NO"]
inspect_text.text = Strings.fmt("hud.inspect_text", [npc_display, Strings.lookup("hud.yes") if granted else Strings.lookup("hud.no")])
_clear_card_row()
var card_row := HBoxContainer.new()
card_row.name = "INSPECT_CARD_ROW"
@@ -599,9 +599,9 @@ func show_inspection(controller: Object, artifacts: Array, result: Dictionary) -
for artifact in artifacts:
lines.append(" • " + artifact.summary())
if result.get("reasons", []).size() > 0:
lines.append("Reasons: " + ", ".join(result.get("reasons", [])))
lines.append(Strings.fmt("hud.reasons", [", ".join(result.get("reasons", []))]))
if result.get("issues", []).size() > 0:
lines.append("Issues: " + "; ".join(result.get("issues", [])))
lines.append(Strings.fmt("hud.issues", ["; ".join(result.get("issues", []))]))
inspect_meta.text = "\n".join(lines)
inspect_panel.visible = true
if _inspect_art_layer:
@@ -626,18 +626,18 @@ func show_wand(controller: Object, result: Dictionary) -> void:
var profile: Object = controller.profile
var npc_display: String = (profile.display_name if profile else "Person")
var alert: bool = result.get("alert", false)
wand_text.text = "Wand check — %s\nAlert: %s" % [npc_display, "YES" if alert else "none"]
wand_text.text = Strings.fmt("hud.wand_text", [npc_display, Strings.lookup("hud.yes") if alert else Strings.lookup("hud.wand_none")])
var lines: Array[String] = []
if alert:
var item: String = result.get("item", "")
if result.get("prohibited", false):
lines.append("The wand alerts on a suspicious item (%s)." % item)
lines.append("Ask the guest to identify it before deciding.")
lines.append(Strings.fmt("hud.wand_suspicious", [item]))
lines.append(Strings.lookup("hud.wand_ask"))
else:
lines.append("The wand alerts on a harmless item (%s)." % item)
lines.append("Likely a false alarm — keys, belt, or phone.")
lines.append(Strings.fmt("hud.wand_harmless", [item]))
lines.append(Strings.lookup("hud.wand_false"))
else:
lines.append("No alert. The guest appears clear.")
lines.append(Strings.lookup("hud.wand_clear"))
wand_meta.text = "\n".join(lines)
wand_panel.visible = true
if _wand_art_layer:
+7 -6
View File
@@ -10,6 +10,7 @@ signal settings_requested()
signal quit_to_menu_requested()
const UiArt := preload("res://src/game/ui/art.gd")
const Strings := preload("res://src/core/strings.gd")
var _root: Control = null
var _resume_button: Button = null
@@ -45,18 +46,18 @@ func _build() -> void:
center.add_child(column)
var title := Label.new()
title.text = "Paused"
title.text = Strings.lookup("ui.pause_title")
title.add_theme_font_size_override("font_size", 36)
title.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
column.add_child(title)
_resume_button = _add_button(column, "Resume", "ResumeButton", func(): resume_requested.emit())
_add_button(column, "Restart shift", "RestartButton", func(): restart_requested.emit())
_add_button(column, "Settings", "SettingsButton", func(): settings_requested.emit())
_add_button(column, "Quit to menu", "MenuButton", func(): quit_to_menu_requested.emit())
_resume_button = _add_button(column, Strings.lookup("ui.pause_resume"), "ResumeButton", func(): resume_requested.emit())
_add_button(column, Strings.lookup("ui.pause_restart"), "RestartButton", func(): restart_requested.emit())
_add_button(column, Strings.lookup("ui.pause_settings"), "SettingsButton", func(): settings_requested.emit())
_add_button(column, Strings.lookup("ui.pause_menu"), "MenuButton", func(): quit_to_menu_requested.emit())
var hint := Label.new()
hint.text = "Esc resumes"
hint.text = Strings.lookup("ui.pause_hint")
hint.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
hint.add_theme_color_override("font_color", Color(0.55, 0.58, 0.65))
column.add_child(hint)
+8 -8
View File
@@ -68,22 +68,22 @@ func _init() -> void:
vbox.add_child(npc_name_label)
location_edit = LineEdit.new()
location_edit.placeholder_text = "Location (e.g. front steps)"
location_edit.text = "front entrance"
location_edit.placeholder_text = Strings.lookup("ui.radio_location_placeholder")
location_edit.text = Strings.lookup("ui.radio_location_default")
vbox.add_child(location_edit)
desc_edit = LineEdit.new()
desc_edit.placeholder_text = "Describe the person and behavior"
desc_edit.placeholder_text = Strings.lookup("ui.radio_description_placeholder")
vbox.add_child(desc_edit)
weapons_check = CheckBox.new()
weapons_check.text = "Weapon seen"
weapons_check.text = Strings.lookup("ui.radio_weapon")
vbox.add_child(weapons_check)
var danger_row := HBoxContainer.new()
vbox.add_child(danger_row)
var danger_label := Label.new()
danger_label.text = "Danger level: "
danger_label.text = Strings.lookup("ui.radio_danger")
danger_row.add_child(danger_label)
danger_option = OptionButton.new()
for i in 4:
@@ -109,7 +109,7 @@ func _init() -> void:
action_button.pressed.connect(_on_action_pressed)
buttons.add_child(action_button)
var cancel_btn := Button.new()
cancel_btn.text = "Cancel"
cancel_btn.text = Strings.lookup("ui.radio_cancel")
cancel_btn.pressed.connect(func(): cancelled.emit())
buttons.add_child(cancel_btn)
@@ -134,9 +134,9 @@ func open(controller: Object, emergency: bool, truth: Dictionary) -> void:
_paint_backdrop()
_truth = truth
_npc_id = controller.npc_id if controller else ""
npc_name_label.text = "Target: %s" % (controller.profile.display_name if controller else "?")
npc_name_label.text = Strings.fmt("ui.radio_target", [controller.profile.display_name if controller else "?"])
var title: Label = panel.find_child("RadioTitle", true, false)
title.text = "EMERGENCY RADIO" if emergency else "RADIO DISPATCH"
title.text = Strings.lookup("ui.radio_title_emergency") if emergency else Strings.lookup("ui.radio_title_assist")
weapons_check.button_pressed = truth.get("weapons_seen", false)
danger_option.select(truth.get("danger_level", 1))
hold_hint.text = Strings.fmt("ui.radio_hold_hint", [HOLD_NEEDED]) if emergency else ""
+12 -12
View File
@@ -68,7 +68,7 @@ func _build() -> void:
center.add_child(column)
var title := Label.new()
title.text = "Settings"
title.text = Strings.lookup("ui.settings_title")
title.add_theme_font_size_override("font_size", 32)
title.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
column.add_child(title)
@@ -86,19 +86,19 @@ func _build() -> void:
_font_scale.value_changed.connect(_on_font_scale_changed)
_invert_y = CheckButton.new()
_invert_y.text = "Invert vertical look"
_invert_y.text = Strings.lookup("ui.settings_invert")
_invert_y.toggled.connect(_on_invert_y_toggled)
column.add_child(_invert_y)
_head_bob = CheckButton.new()
_head_bob.text = "Head bob and camera motion"
_head_bob.text = Strings.lookup("ui.settings_head_bob")
_head_bob.toggled.connect(_on_head_bob_toggled)
column.add_child(_head_bob)
var diff_row := HBoxContainer.new()
column.add_child(diff_row)
var diff_label := Label.new()
diff_label.text = "Difficulty / pacing"
diff_label.text = Strings.lookup("ui.settings_difficulty")
diff_label.size_flags_horizontal = Control.SIZE_EXPAND_FILL
diff_row.add_child(diff_label)
_difficulty = OptionButton.new()
@@ -120,11 +120,11 @@ func _build() -> void:
_ambience.value_changed.connect(func(v): settings["ambience_volume"] = v)
_reduce_motion = CheckButton.new()
_reduce_motion.text = "Reduce motion"
_reduce_motion.text = Strings.lookup("ui.settings_reduce_motion")
_reduce_motion.toggled.connect(func(v): settings["reduce_motion"] = v)
column.add_child(_reduce_motion)
_captions = CheckButton.new()
_captions.text = "Captions / subtitles"
_captions.text = Strings.lookup("ui.settings_captions")
_captions.toggled.connect(func(v): settings["captions_enabled"] = v)
column.add_child(_captions)
@@ -133,17 +133,17 @@ func _build() -> void:
column.add_child(replay_row)
var replay := Button.new()
replay.name = "ReplayTutorialButton"
replay.text = "Replay tutorial"
replay.text = Strings.lookup("ui.settings_replay")
replay.pressed.connect(func(): replay_tutorial_requested.emit())
replay_row.add_child(replay)
var reset_campaign := Button.new()
reset_campaign.name = "ResetCampaignButton"
reset_campaign.text = "Reset campaign"
reset_campaign.text = Strings.lookup("ui.settings_reset_campaign")
reset_campaign.pressed.connect(_confirm_reset_campaign)
replay_row.add_child(reset_campaign)
var controls_title := Label.new()
controls_title.text = "Controls (click a binding, then press a key or pad button)"
controls_title.text = Strings.lookup("ui.settings_controls_help")
controls_title.add_theme_font_size_override("font_size", 13)
controls_title.modulate = Color(0.75, 0.8, 0.9)
column.add_child(controls_title)
@@ -176,7 +176,7 @@ func _build() -> void:
var cancel_button := Button.new()
cancel_button.name = "CancelButton"
cancel_button.text = "Cancel"
cancel_button.text = Strings.lookup("ui.settings_cancel")
cancel_button.pressed.connect(_on_cancel)
row.add_child(cancel_button)
@@ -270,7 +270,7 @@ func _on_difficulty_selected(index: int) -> void:
func _confirm_reset_campaign() -> void:
var dialog := ConfirmationDialog.new()
dialog.title = "Reset campaign"
dialog.dialog_text = "Clear campaign progress and start again from the Hollywood shift?"
dialog.dialog_text = Strings.lookup("ui.settings_reset_confirm")
dialog.confirmed.connect(func(): reset_campaign_requested.emit())
add_child(dialog)
dialog.popup_centered()
@@ -289,7 +289,7 @@ func _on_rebind(action: String) -> void:
_listening_action = action
var button: Button = _bind_buttons.get(action)
if button:
button.text = "Press…"
button.text = Strings.lookup("ui.settings_press")
if button.is_inside_tree():
button.grab_focus()
+16 -16
View File
@@ -38,7 +38,7 @@ func _init() -> void:
root.add_child(center)
var title := Label.new()
title.text = "Shift Review"
title.text = Strings.lookup("review.title")
title.add_theme_font_size_override("font_size", 28)
title.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
center.add_child(title)
@@ -55,7 +55,7 @@ func _init() -> void:
center.add_child(_campaign_label)
_continue_button = Button.new()
_continue_button.text = "Continue campaign"
_continue_button.text = Strings.lookup("review.continue_campaign")
_continue_button.pressed.connect(func(): continue_requested.emit())
_continue_button.size_flags_horizontal = Control.SIZE_SHRINK_CENTER
center.add_child(_continue_button)
@@ -126,28 +126,28 @@ func show_review(review: Dictionary, scenario: Resource, history: Array = [],
var g: String = rep.get("grade", "?")
if not past_grades.has(g):
past_grades.append(g)
history_line = "Shifts on record: %d · best score: %.0f · grades seen: %s" % [
history.size(), best, ", ".join(past_grades)]
history_line = Strings.fmt("review.history", [
history.size(), best, ", ".join(past_grades)])
var lines: Array[String] = [
"Venue: %s" % (scenario.display_name if scenario else review.get("scenario_id", "")),
"Policy: %s" % (scenario.policy_summary if scenario else "See venue policy."),
"Grade: %s Score: %.0f/100" % [reputation.get("grade", "?"), reputation.get("rating", 0.0)],
Strings.fmt("review.venue", [scenario.display_name if scenario else review.get("scenario_id", "")]),
Strings.fmt("review.policy", [scenario.policy_summary if scenario else "See venue policy."]),
Strings.fmt("review.grade", [reputation.get("grade", "?"), reputation.get("rating", 0.0)]),
history_line,
"Incidents handled: %d (peaceful %d)" % [incident_list.size(), peaceful],
"NPCs encountered: %d" % review.get("spawned_count", 0),
"Radio battery: %.0f%% emergency cooldown: %.0fs" % [radio.get("battery", 0.0), radio.get("emergency_cooldown_s", 0.0)],
"Crowd mood: %.0f%% (panic: %s)" % [crowd.get("mood", 0.0) * 100.0, str(crowd.get("panic", false))],
Strings.fmt("review.incidents", [incident_list.size(), peaceful]),
Strings.fmt("review.npcs", [review.get("spawned_count", 0)]),
Strings.fmt("review.radio", [radio.get("battery", 0.0), radio.get("emergency_cooldown_s", 0.0)]),
Strings.fmt("review.crowd", [crowd.get("mood", 0.0) * 100.0, str(crowd.get("panic", false))]),
Strings.fmt("review.responder", [", ".join(outcomes) if outcomes.size() > 0 else Strings.lookup("review.none")]),
Strings.fmt("review.investigations", [" | ".join(investigations) if investigations.size() > 0 else Strings.lookup("review.none")]),
"",
"Highlights:",
Strings.lookup("review.highlights"),
]
var mods: Array = reputation.get("modifiers", [])
var shown := 0
for mod in mods:
var delta: float = float(mod.get("delta", 0.0))
lines.append(" %s (%+.0f) %s" % [mod.get("category", ""), delta, mod.get("reason", "")])
lines.append(Strings.fmt("review.modifier", [str(mod.get("category", "")), "%+.0f" % delta, str(mod.get("reason", ""))]))
shown += 1
if shown >= 12:
break
@@ -156,11 +156,11 @@ func show_review(review: Dictionary, scenario: Resource, history: Array = [],
var campaign_complete := bool(campaign_summary.get("complete", false))
if _campaign_label:
if campaign_complete:
_campaign_label.text = "Campaign complete. Free-play venues remain available."
_campaign_label.text = Strings.lookup("review.campaign_complete")
elif next_venue != "":
_campaign_label.text = "Campaign shift %d complete · next: %s" % [
_campaign_label.text = Strings.fmt("review.campaign_next", [
int(campaign_summary.get("shift_number", 1)) - 1,
next_venue.replace("_", " ").capitalize()]
next_venue.replace("_", " ").capitalize()])
else:
_campaign_label.text = ""
if _continue_button:
+13 -14
View File
@@ -14,9 +14,9 @@ const UiArt := preload("res://src/game/ui/art.gd")
const Strings := preload("res://src/core/strings.gd")
const VENUES := [
{"id": "hollywood_building", "label": "Hollywood Boulevard", "blurb": "Building entrance, tourists and late-night trouble."},
{"id": "joke_shop", "label": "The Joke Shop", "blurb": "Guest list, IDs and wand screening."},
{"id": "tv_studio", "label": "Late Night TV Studio", "blurb": "Credentials, filming boundary, celebrity arrivals."},
{"id": "hollywood_building", "label_key": "venue.hollywood.label", "blurb_key": "venue.hollywood.blurb"},
{"id": "joke_shop", "label_key": "venue.joke.label", "blurb_key": "venue.joke.blurb"},
{"id": "tv_studio", "label_key": "venue.studio.label", "blurb_key": "venue.studio.blurb"},
]
var _index := 0
@@ -83,13 +83,13 @@ func _build() -> void:
center.add_child(column)
var title := Label.new()
title.text = "GIG LINE"
title.text = Strings.lookup("ui.start_title")
title.add_theme_font_size_override("font_size", 44)
title.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
column.add_child(title)
var subtitle := Label.new()
subtitle.text = "Pick a venue to start your shift."
subtitle.text = Strings.lookup("ui.start_subtitle")
subtitle.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
subtitle.add_theme_color_override("font_color", Color(0.72, 0.75, 0.82))
column.add_child(subtitle)
@@ -112,7 +112,7 @@ func _build() -> void:
var venue: Dictionary = VENUES[i]
var button := Button.new()
button.name = "Venue_%s" % venue["id"]
button.text = "%s\n%s" % [venue["label"], venue["blurb"]]
button.text = "%s\n%s" % [Strings.lookup(str(venue["label_key"])), Strings.lookup(str(venue["blurb_key"]))]
button.alignment = HORIZONTAL_ALIGNMENT_LEFT
button.custom_minimum_size = Vector2(460, 62)
button.pressed.connect(_on_venue_pressed.bind(i))
@@ -131,13 +131,13 @@ func _build() -> void:
var help_button := Button.new()
help_button.name = "HelpButton"
help_button.text = "Help"
help_button.text = Strings.lookup("ui.start_help")
help_button.pressed.connect(func(): help_requested.emit())
row.add_child(help_button)
var settings_button := Button.new()
settings_button.name = "SettingsButton"
settings_button.text = "Settings"
settings_button.text = Strings.lookup("ui.start_settings")
settings_button.pressed.connect(func(): settings_requested.emit())
row.add_child(settings_button)
@@ -148,7 +148,7 @@ func _build() -> void:
row.add_child(quit_button)
var hint := Label.new()
hint.text = "Up/Down to choose, Enter to start, Esc to quit"
hint.text = Strings.lookup("ui.start_hint")
hint.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
hint.add_theme_color_override("font_color", Color(0.55, 0.58, 0.65))
column.add_child(hint)
@@ -199,20 +199,19 @@ func open(campaign: Dictionary = {}) -> void:
var complete := bool(_campaign.get("complete", false))
if _status:
if complete:
_status.text = "Campaign complete — pick any venue for a free shift."
_status.text = Strings.lookup("ui.campaign_complete")
elif next_id != "":
_status.text = "Campaign shift %d · next: %s" % [
int(_campaign.get("shift_number", 1)), _venue_label(next_id)]
_status.text = Strings.fmt("ui.campaign_status", [int(_campaign.get("shift_number", 1)), _venue_label(next_id)])
if _continue_button:
var next_label := _venue_label(next_id)
_continue_button.text = "Continue shift — %s" % next_label if next_id != "" else "Campaign complete"
_continue_button.text = Strings.fmt("ui.continue_shift", [next_label]) if next_id != "" else Strings.lookup("ui.campaign_complete_short")
_continue_button.visible = not complete
_focus_index(_index)
func _venue_label(id: String) -> String:
for venue in VENUES:
if str(venue["id"]) == id:
return str(venue["label"])
return Strings.lookup(str(venue["label_key"]))
return id.replace("_", " ").capitalize()
func _on_continue_pressed() -> void: