Antique-localize battle briefings and camp UI

This commit is contained in:
2026-06-19 05:48:39 +09:00
parent dc3dc06eb0
commit 30ac2479c4
11 changed files with 717 additions and 540 deletions

View File

@@ -65,7 +65,7 @@ try {
$jsonFiles = Get-ChildItem -Path (Join-Path $Root "data") -Filter "*.json" -Recurse
foreach ($file in $jsonFiles) {
try {
Get-Content $file.FullName -Raw | ConvertFrom-Json | Out-Null
Get-Content $file.FullName -Raw -Encoding UTF8 | ConvertFrom-Json | Out-Null
} catch {
Add-Failure "invalid JSON: $($file.FullName) - $($_.Exception.Message)"
}

File diff suppressed because it is too large Load Diff

View File

@@ -11,49 +11,49 @@ func _init() -> void:
failures,
"001 HUD progress",
_progress_text("res://data/scenarios/001_yellow_turbans.json", false),
"Enemies defeated 0/"
"적군 격파 0/"
)
_check_contains(
failures,
"001 commander defeat risk",
_defeat_text("res://data/scenarios/001_yellow_turbans.json"),
"Cao Cao safe"
"Cao Cao 건재"
)
_check_contains(
failures,
"001 turn pressure risk",
_defeat_text("res://data/scenarios/001_yellow_turbans.json"),
"Turns left 7"
"남은 턴 7"
)
_check_contains(
failures,
"002 gated victory progress",
_progress_text("res://data/scenarios/002_sishui_gate.json", false),
"Objective unlocks on Turn 2"
"제2턴에 전공 갱신"
)
_check_contains(
failures,
"002 pending enemy progress",
_progress_text("res://data/scenarios/002_sishui_gate.json", false),
"Enemies defeated 0/"
"적군 격파 0/"
)
_check_contains(
failures,
"007 destination progress",
_progress_text("res://data/scenarios/007_xian_emperor_escort.json", false),
"Destination (14, 5): not reached"
"목표 지점 (14, 5): 미도달"
)
_check_contains(
failures,
"007 protected-unit risk progress",
_defeat_text("res://data/scenarios/007_xian_emperor_escort.json"),
"Imperial Envoy safe"
"Imperial Envoy 건재"
)
_check_contains(
failures,
"045 gated destination progress",
_progress_text("res://data/scenarios/045_fancheng_relief.json", false),
"Reach (10, 5): not reached"
"목표 지점 (10, 5): 미도달"
)
_check_event_gated_objective_markers(failures)
_check_event_gated_multi_cell_objective_markers(failures)

View File

@@ -327,7 +327,7 @@ func _check_scene_post_move_tactic_picker_flow(failures: Array[String]) -> void:
if tactic_markers.size() != 1 or tactic_markers[0].get("cell", Vector2i.ZERO) != Vector2i(3, 3):
failures.append("tactic target markers should include only the Spark target: %s" % str(tactic_markers))
else:
if str(tactic_markers[0].get("text", "")) != "KO":
if str(tactic_markers[0].get("text", "")) != "퇴각":
failures.append("tactic target marker should preview the deterministic KO: %s" % str(tactic_markers[0]))
if str(tactic_markers[0].get("kind", "")) != "damage":
failures.append("tactic target marker should use damage color: %s" % str(tactic_markers[0]))

View File

@@ -37,15 +37,15 @@ func _init() -> void:
var result_text: String = scene._format_battle_result_summary()
_check_contains(failures, "reward section", result_text, "전리품")
_check_contains(failures, "gold reward", result_text, "군자금 +100")
_check_contains(failures, "item reward", result_text, "Bean x2")
_check_contains(failures, "roster section", result_text, "합류: Dian Wei")
_check_contains(failures, "item reward", result_text, " x2")
_check_contains(failures, "roster section", result_text, "합류: 전위")
_check_contains(failures, "growth section", result_text, "Cao Cao Lv.2")
_check_contains(failures, "campaign section", result_text, "다음 행군: Sishui Gate")
_check_contains(failures, "next camp section", result_text, "다음 군막")
_check_contains(failures, "next camp location", result_text, "Sishui Gate, 190 CE")
_check_contains(failures, "next camp talk", result_text, "군막 회의 3")
_check_contains(failures, "next camp shop", result_text, "군상 물품 6")
_check_contains(failures, "next camp deploy", result_text, "출진 명부 2/3")
_check_contains(failures, "next camp talk", result_text, "군막 회의 · 3건")
_check_contains(failures, "next camp shop", result_text, "군상 물품 · 6종")
_check_contains(failures, "next camp deploy", result_text, "출진 명부 · 2/3")
_check_contains(failures, "next camp formation", result_text, "진형 배치 6칸")
scene.battle_result_summary = {
@@ -104,8 +104,8 @@ func _init() -> void:
"join_officers": ["dian_wei"]
})
_check_contains(failures, "choice gold", choice_text, "+50G")
_check_contains(failures, "choice item", choice_text, "War Axe")
_check_contains(failures, "choice join", choice_text, "합류 Dian Wei")
_check_contains(failures, "choice item", choice_text, "전부")
_check_contains(failures, "choice join", choice_text, "합류 전위")
if failures.is_empty():
scene.free()

View File

@@ -66,8 +66,8 @@ func _check_hud_reuses_status_summary(failures: Array[String]) -> void:
scene.state.selected_unit_id = "cao_cao"
scene.hover_cell = Vector2i(1, 3)
scene._update_hud()
_check_contains(failures, "selected HUD status", scene.selected_label.text, "Conditions: DEF -2 (2 phases)")
_check_contains(failures, "hover tile status", scene.cell_info_label.text, "Conditions: DEF -2 (2 phases)")
_check_contains(failures, "selected HUD status", scene.selected_label.text, "상태: DEF -2 (2 phases)")
_check_contains(failures, "hover tile status", scene.cell_info_label.text, "상태: DEF -2 (2 phases)")
scene.free()

View File

@@ -1,4 +1,4 @@
extends SceneTree
extends SceneTree
const BattleStateScript := preload("res://scripts/core/battle_state.gd")
const BattleSceneScript := preload("res://scripts/scenes/battle_scene.gd")
@@ -2518,50 +2518,50 @@ func _check_scene_texture_loading(failures: Array[String]) -> void:
var strategy := scene._camp_conversation_by_id("cao_cao_strategy")
if strategy.is_empty():
failures.append("battle scene should find Cao Cao camp conversation by id")
elif not scene._format_camp_conversation_button_text(strategy).contains("Cao Cao"):
elif not scene._format_camp_conversation_button_text(strategy).contains("조조"):
failures.append("camp conversation button text should include label/speaker")
elif scene._camp_conversation_preview_portrait_path(strategy).is_empty():
failures.append("camp conversation preview should resolve Cao Cao portrait")
elif scene._load_art_texture(scene._camp_conversation_preview_portrait_path(strategy)) == null:
failures.append("camp conversation preview portrait should load")
elif not scene._format_camp_conversation_row_title(strategy).contains("Officer"):
elif not scene._format_camp_conversation_row_title(strategy).contains("장수"):
failures.append("camp conversation row title should include group")
elif not scene._format_camp_conversation_preview_text(strategy).contains("Yingchuan"):
elif not scene._format_camp_conversation_preview_text(strategy).contains("영천"):
failures.append("camp conversation preview should include first dialogue text")
var cache := scene._camp_conversation_by_id("northern_woods_cache")
if cache.is_empty():
failures.append("battle scene should find cache camp conversation by id")
elif not scene._format_camp_conversation_button_text(cache).contains("보급 Bean"):
elif not scene._format_camp_conversation_button_text(cache).contains("보급 "):
failures.append("camp conversation button text should preview supply effect")
var merchant_notice := scene._format_shop_merchant_notice_text(scene.state.get_shop_merchant())
if not merchant_notice.contains("Camp Merchant") or not merchant_notice.contains("Roadside stores"):
if not merchant_notice.contains("군막 상인") or not merchant_notice.contains("길가의 물자"):
failures.append("shop merchant notice should include name and flavor lines")
var camp_overview := scene._format_briefing_camp_overview_text(scene.state.get_briefing(), false)
for expected in ["Yingchuan, 184 CE", "군자금 0G", "군막 회의 3", "보급 1", "군상 물품 2", "출진 명부 2/2", "진형 배치 4칸", "병장고 비축"]:
for expected in ["영천, 중평 원년", "군자금 0G", "군막 회의 · 3건", "보급 대기 1건", "군상 물품 · 2종", "출진 명부 2/2", "진형 배치 4칸", "병장고 비축"]:
if not camp_overview.contains(expected):
failures.append("camp overview should include %s: %s" % [expected, camp_overview])
var bean: Dictionary = scene.state.get_item_def("bean")
var bean_buy_detail := scene._format_shop_buy_item_detail_text("bean", bean, int(bean.get("price", 0)), 0)
if not bean_buy_detail.contains("Heal 20") or not bean_buy_detail.contains("Owned x0"):
if not bean_buy_detail.contains("병력 +20") or not bean_buy_detail.contains("보유 x0"):
failures.append("shop buy detail should summarize item effect and owned count")
scene.campaign_state.gold = 10
var need_gold_detail := scene._format_shop_buy_item_detail_text("bean", bean, int(bean.get("price", 0)), 0)
if not need_gold_detail.contains("Need 40G"):
if not need_gold_detail.contains("부족 40G"):
failures.append("shop buy detail should explain missing gold")
var sold_out_detail := scene._format_shop_buy_item_detail_text("bean", bean, int(bean.get("price", 0)), 2, 3, 0)
if not sold_out_detail.contains("Stock 0/3") or not sold_out_detail.contains("Sold out"):
if not sold_out_detail.contains("재고 0/3") or not sold_out_detail.contains("품절"):
failures.append("shop buy detail should explain exhausted finite stock")
var sell_detail := scene._format_shop_sell_item_detail_text("bean", bean, 25, 2)
if not sell_detail.contains("Half-price sale") or not sell_detail.contains("Owned x2"):
if not sell_detail.contains("반값 매각") or not sell_detail.contains("보유 x2"):
failures.append("shop sell detail should summarize sale rule and owned count")
var bean_action := scene._format_shop_buy_action_text("bean", bean, int(bean.get("price", 0)))
if not bean_action.contains("Acquire Bean") or not bean_action.contains("50G"):
if not bean_action.contains("매입 콩") or not bean_action.contains("50G"):
failures.append("shop buy action should be concise and priced")
var bean_tooltip := scene._format_shop_item_tooltip_text(
scene._format_shop_item_button_text("bean", bean, int(bean.get("price", 0)), 0),
bean_buy_detail
)
if not bean_tooltip.contains("\n") or not bean_tooltip.contains("Owned x0"):
if not bean_tooltip.contains("\n") or not bean_tooltip.contains("보유 x0"):
failures.append("shop item tooltip should preserve full item detail")
scene.free()
@@ -2598,32 +2598,32 @@ func _check_hud_focus_text(failures: Array[String]) -> void:
return
var cao_cao: Dictionary = scene.state.get_unit("cao_cao")
var cao_text := scene._format_unit_focus_text(cao_cao, "Selected")
if not cao_text.contains("Command authority"):
if not cao_text.contains("군령 지휘"):
failures.append("Cao Cao HUD focus should describe command role: %s" % cao_text)
if not cao_text.contains("March 4 (Foot)") or not cao_text.contains("Strike reach 1-1"):
if not cao_text.contains("행군 4 (보병)") or not cao_text.contains("타격 1-1"):
failures.append("Cao Cao HUD focus should clarify move/attack affordance: %s" % cao_text)
if not cao_text.contains("군령:") or not cao_text.contains("March") or not cao_text.contains("Strike") or not cao_text.contains("책략") or not cao_text.contains("병장") or not cao_text.contains("대기"):
if not cao_text.contains("군령:") or not cao_text.contains("행군") or not cao_text.contains("타격") or not cao_text.contains("책략") or not cao_text.contains("병장") or not cao_text.contains("대기"):
failures.append("Cao Cao HUD focus should summarize available actions: %s" % cao_text)
if not cao_text.contains("Ground 2,4"):
if not cao_text.contains("지형 2,4"):
failures.append("Cao Cao HUD focus should include current tile: %s" % cao_text)
if not cao_text.contains("March 1"):
if not cao_text.contains("행군 1"):
failures.append("Cao Cao HUD focus should include terrain move cost: %s" % cao_text)
if not cao_text.contains("Guard +0"):
if not cao_text.contains("방비 +0"):
failures.append("Cao Cao HUD focus should include terrain defense: %s" % cao_text)
var xiahou_dun: Dictionary = scene.state.get_unit("xiahou_dun")
xiahou_dun["pos"] = Vector2i(3, 1)
var forest_text := scene._unit_current_terrain_text(xiahou_dun)
if not forest_text.contains("Forest") or not forest_text.contains("March 3"):
if not forest_text.contains("수풀") or not forest_text.contains("행군 3"):
failures.append("Cavalry movement should use mounted move_type cost on forest: %s" % forest_text)
var archer: Dictionary = scene.state.get_unit("yellow_turban_3")
var archer_text := scene._format_unit_focus_text(archer, "Hover")
if not archer_text.contains("Bow pressure"):
if not archer_text.contains("원거리 견제"):
failures.append("Archer HUD focus should describe ranged role: %s" % archer_text)
if not archer_text.contains("March 4 (Archer)") or not archer_text.contains("Strike reach 2-2"):
if not archer_text.contains("행군 4 (궁병)") or not archer_text.contains("타격 2-2"):
failures.append("Archer HUD focus should clarify ranged attack affordance: %s" % archer_text)
if not archer_text.contains("군령: 적군 군기"):
failures.append("Enemy archer HUD focus should describe enemy control: %s" % archer_text)
if not scene._unit_current_terrain_text(archer).contains("Plain"):
if not scene._unit_current_terrain_text(archer).contains("평지"):
failures.append("Archer terrain text should reflect its current plain tile")
scene.free()
@@ -2894,17 +2894,17 @@ func _check_hover_intent_badges(failures: Array[String]) -> void:
scene.hover_cell = Vector2i(2, 3)
var move_badge := scene._target_preview_badge()
if str(move_badge.get("text", "")) != "MOVE" or str(move_badge.get("kind", "")) != "move":
if str(move_badge.get("text", "")) != "행군" or str(move_badge.get("kind", "")) != "move":
failures.append("reachable empty tile should show MOVE badge: %s" % str(move_badge))
scene.hover_cell = Vector2i(1, 4)
var select_badge := scene._target_preview_badge()
if str(select_badge.get("text", "")) != "SELECT" or str(select_badge.get("kind", "")) != "select":
if str(select_badge.get("text", "")) != "선택" or str(select_badge.get("kind", "")) != "select":
failures.append("friendly selectable unit should show SELECT badge: %s" % str(select_badge))
scene.hover_cell = Vector2i(7, 2)
var attack_badge := scene._target_preview_badge()
if attack_badge.is_empty() or str(attack_badge.get("kind", "")) == "move" or str(attack_badge.get("text", "")) == "MOVE":
if attack_badge.is_empty() or str(attack_badge.get("kind", "")) == "move" or str(attack_badge.get("text", "")) == "행군":
failures.append("enemy hover should keep attack badge priority: %s" % str(attack_badge))
var cao_cao: Dictionary = scene.state.get_unit("cao_cao")
@@ -2912,7 +2912,7 @@ func _check_hover_intent_badges(failures: Array[String]) -> void:
scene._refresh_ranges()
scene.hover_cell = Vector2i(2, 3)
var moved_badge := scene._target_preview_badge()
if str(moved_badge.get("kind", "")) == "move" or str(moved_badge.get("text", "")) == "MOVE":
if str(moved_badge.get("kind", "")) == "move" or str(moved_badge.get("text", "")) == "행군":
failures.append("moved unit should not show MOVE badge: %s" % str(moved_badge))
var default_color := scene._target_preview_badge_color("")

View File

@@ -7,7 +7,7 @@ function Read-Json($RelativePath) {
if (-not (Test-Path $path)) {
throw "Missing file: $RelativePath"
}
return Get-Content $path -Raw | ConvertFrom-Json
return Get-Content $path -Raw -Encoding UTF8 | ConvertFrom-Json
}
function Has-Prop($Object, [string]$Name) {