Antique-localize battle briefings and camp UI
This commit is contained in:
@@ -1,9 +1,9 @@
|
||||
{
|
||||
"id": "001_yellow_turbans",
|
||||
"name": "Yingchuan Skirmish",
|
||||
"name": "영천 소전",
|
||||
"objectives": {
|
||||
"victory": "Defeat all Yellow Turban units.",
|
||||
"defeat": "Cao Cao is defeated or the battle reaches Turn 8."
|
||||
"victory": "황건적 전군을 격파하라.",
|
||||
"defeat": "조조가 퇴각하거나 제8턴에 이르면 패한다."
|
||||
},
|
||||
"conditions": {
|
||||
"victory": { "type": "all_units_defeated", "team": "enemy" },
|
||||
@@ -13,62 +13,64 @@
|
||||
]
|
||||
},
|
||||
"briefing": {
|
||||
"title": "Yingchuan Skirmish",
|
||||
"location": "Yingchuan, 184 CE",
|
||||
"title": "영천 소전",
|
||||
"location": "영천, 중평 원년",
|
||||
"lines": [
|
||||
"Yellow Turban rebels have gathered near Yingchuan.",
|
||||
"Cao Cao and Xiahou Dun must break their formation before the revolt spreads."
|
||||
"황건의 무리가 영천 근방에 깃발을 세웠다.",
|
||||
"조조와 하후돈은 난세의 불길이 번지기 전에 그 진형을 꺾어야 한다."
|
||||
],
|
||||
"camp_dialogue": [
|
||||
{ "speaker": "Cao Cao", "side": "left", "text": "Yingchuan is a spark in dry grass. If we move decisively, the court may still believe order can return." },
|
||||
{ "speaker": "Xiahou Dun", "side": "right", "text": "The men are ready. Give me the front, and I will keep the rebels fixed while you cut their nerve." }
|
||||
{ "speaker": "Cao Cao", "display_speaker": "조조", "side": "left", "text": "영천은 마른 풀밭의 불씨와 같다. 결단이 빠르면 조정도 아직 질서가 돌아올 수 있음을 믿을 것이다." },
|
||||
{ "speaker": "Xiahou Dun", "display_speaker": "하후돈", "side": "right", "text": "군사는 이미 창끝을 세웠습니다. 선봉을 맡겨 주시면 적의 앞줄을 묶어 두겠습니다." }
|
||||
],
|
||||
"camp_conversations": [
|
||||
{
|
||||
"id": "cao_cao_strategy",
|
||||
"group": "officer",
|
||||
"officer_id": "cao_cao",
|
||||
"label": "Cao Cao - Strategy",
|
||||
"label": "조조 - 군략",
|
||||
"speaker": "Cao Cao",
|
||||
"summary": "Read the revolt before the first move.",
|
||||
"display_speaker": "조조",
|
||||
"summary": "첫 행군 전에 난세의 형세를 살핀다.",
|
||||
"lines": [
|
||||
{ "speaker": "Cao Cao", "side": "left", "text": "Yingchuan is a spark in dry grass. If we move decisively, the court may still believe order can return." },
|
||||
{ "speaker": "Cao Cao", "side": "left", "text": "Their center is loose. Break the front, then deny them time to gather more banners." }
|
||||
{ "speaker": "Cao Cao", "display_speaker": "조조", "side": "left", "text": "영천은 마른 풀밭의 불씨와 같다. 결단이 빠르면 조정도 아직 질서가 돌아올 수 있음을 믿을 것이다." },
|
||||
{ "speaker": "Cao Cao", "display_speaker": "조조", "side": "left", "text": "적의 가운데가 허술하다. 앞줄을 깨고 깃발을 다시 모을 시간을 주지 마라." }
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "xiahou_dun_vanguard",
|
||||
"group": "officer",
|
||||
"officer_id": "xiahou_dun",
|
||||
"label": "Xiahou Dun - Vanguard",
|
||||
"label": "하후돈 - 선봉",
|
||||
"speaker": "Xiahou Dun",
|
||||
"summary": "Discuss the first clash.",
|
||||
"display_speaker": "하후돈",
|
||||
"summary": "첫 접전에 앞서 선봉의 길을 정한다.",
|
||||
"lines": [
|
||||
{ "speaker": "Xiahou Dun", "side": "right", "text": "The men are ready. Give me the front, and I will keep the rebels fixed while you cut their nerve." },
|
||||
{ "speaker": "Cao Cao", "side": "left", "text": "Hold them in place, but do not chase too deep. We still need eyes on the woods." }
|
||||
{ "speaker": "Xiahou Dun", "display_speaker": "하후돈", "side": "right", "text": "군사는 이미 창끝을 세웠습니다. 선봉을 맡겨 주시면 적의 앞줄을 묶어 두겠습니다." },
|
||||
{ "speaker": "Cao Cao", "display_speaker": "조조", "side": "left", "text": "붙잡되 깊이 쫓지는 마라. 숲 쪽에도 눈을 두어야 한다." }
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "northern_woods_cache",
|
||||
"group": "topic",
|
||||
"label": "Northern Woods Cache",
|
||||
"summary": "Follow up on the merchant's rumor.",
|
||||
"label": "북쪽 숲 보급고",
|
||||
"summary": "상인이 흘린 보급 소문을 살핀다.",
|
||||
"lines": [
|
||||
{ "speaker": "Cao Cao", "side": "left", "text": "The merchant's rumor may be useful. If the line holds, send a scout through the northern woods." },
|
||||
{ "speaker": "Xiahou Dun", "side": "right", "text": "A hidden cache in rebel country? Then we take it before they remember where they left it." }
|
||||
{ "speaker": "Cao Cao", "display_speaker": "조조", "side": "left", "text": "상인의 말이 허황되지만은 않을 것이다. 전열이 버티면 북쪽 숲으로 척후를 보내라." },
|
||||
{ "speaker": "Xiahou Dun", "display_speaker": "하후돈", "side": "right", "text": "적지에 숨은 보급이라면, 저들이 기억해 내기 전에 우리가 거두겠습니다." }
|
||||
],
|
||||
"effects": [
|
||||
{ "type": "grant_item", "item_id": "bean", "count": 1, "text": "The scout detail adds a Bean to the field supplies." }
|
||||
{ "type": "grant_item", "item_id": "bean", "count": 1, "text": "척후가 북쪽 숲에서 콩 한 줌을 거두어 군수에 보탰다." }
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"shop": {
|
||||
"merchant": {
|
||||
"name": "Camp Merchant",
|
||||
"name": "군막 상인",
|
||||
"lines": [
|
||||
"Roadside stores are thin, my lord, but I saved beans and antidotes for your column.",
|
||||
"The northern woods have rumors of a hidden cache, if your scouts can reach it."
|
||||
"길가의 물자는 박하오나, 장군의 행렬을 위해 콩과 해독약만은 남겨 두었습니다.",
|
||||
"북쪽 숲에는 묻어 둔 보급이 있다는 말도 도니, 척후가 닿으면 살펴보십시오."
|
||||
]
|
||||
},
|
||||
"items": ["bean", "antidote"]
|
||||
@@ -110,7 +112,7 @@
|
||||
},
|
||||
{
|
||||
"unit_id": "yellow_turban_1",
|
||||
"name": "Zhang Mancheng",
|
||||
"name": "장만성",
|
||||
"class_id": "bandit",
|
||||
"team": "enemy",
|
||||
"level": 1,
|
||||
@@ -119,7 +121,7 @@
|
||||
},
|
||||
{
|
||||
"unit_id": "yellow_turban_2",
|
||||
"name": "Peasant Rebel",
|
||||
"name": "농민 반군",
|
||||
"class_id": "infantry",
|
||||
"team": "enemy",
|
||||
"level": 1,
|
||||
@@ -128,7 +130,7 @@
|
||||
},
|
||||
{
|
||||
"unit_id": "yellow_turban_3",
|
||||
"name": "Rebel Archer",
|
||||
"name": "황건 궁수",
|
||||
"class_id": "archer",
|
||||
"team": "enemy",
|
||||
"level": 1,
|
||||
@@ -142,8 +144,8 @@
|
||||
"once": true,
|
||||
"when": { "type": "battle_start" },
|
||||
"actions": [
|
||||
{ "type": "log", "text": "Cao Cao engages the Yellow Turbans." },
|
||||
{ "type": "log", "text": "A small supply cache is hidden in the northern woods." }
|
||||
{ "type": "log", "text": "조조군이 황건적과 맞붙었다." },
|
||||
{ "type": "log", "text": "북쪽 숲 어딘가에 작은 보급고가 숨겨져 있다." }
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -154,8 +156,8 @@
|
||||
{
|
||||
"type": "dialogue",
|
||||
"lines": [
|
||||
{ "speaker": "Cao Cao", "side": "left", "text": "The rebels are scattered, but their numbers will swell if we hesitate." },
|
||||
{ "speaker": "Xiahou Dun", "side": "right", "text": "Then we strike now. I will open a path through their front line." }
|
||||
{ "speaker": "Cao Cao", "display_speaker": "조조", "side": "left", "text": "적은 흩어져 있으나, 머뭇거리면 그 수가 불어날 것이다." },
|
||||
{ "speaker": "Xiahou Dun", "display_speaker": "하후돈", "side": "right", "text": "그렇다면 지금 치겠습니다. 제가 앞줄을 열어 길을 내겠습니다." }
|
||||
]
|
||||
}
|
||||
]
|
||||
@@ -165,7 +167,7 @@
|
||||
"once": true,
|
||||
"when": { "type": "unit_reaches_tile", "team": "player", "pos": [3, 2] },
|
||||
"actions": [
|
||||
{ "type": "log", "text": "The northern woods cache is recovered." },
|
||||
{ "type": "log", "text": "북쪽 숲 보급고를 거두었다." },
|
||||
{ "type": "grant_item", "item_id": "bean" }
|
||||
]
|
||||
},
|
||||
@@ -174,7 +176,7 @@
|
||||
"once": true,
|
||||
"when": { "type": "turn_start", "team": "enemy", "turn": 2 },
|
||||
"actions": [
|
||||
{ "type": "log", "text": "The Yellow Turbans are looking for an opening." }
|
||||
{ "type": "log", "text": "황건적이 빈틈을 엿보고 있다." }
|
||||
]
|
||||
}
|
||||
],
|
||||
@@ -184,9 +186,9 @@
|
||||
"join_officers": ["xiahou_yuan", "cao_ren"]
|
||||
},
|
||||
"post_battle_dialogue": [
|
||||
{ "speaker": "Xiahou Dun", "side": "right", "text": "The rebels are scattered. Yingchuan should breathe easier tonight." },
|
||||
{ "speaker": "Cao Cao", "side": "left", "text": "This is only the first fire. If the court cannot move quickly, we must." },
|
||||
{ "speaker": "Xiahou Yuan", "side": "right", "text": "Then call on us for the next campaign. We will ride with you." },
|
||||
{ "speaker": "Cao Ren", "side": "right", "text": "The clan stands ready, my lord." }
|
||||
{ "speaker": "Xiahou Dun", "display_speaker": "하후돈", "side": "right", "text": "적이 흩어졌습니다. 오늘 밤 영천은 한숨 돌릴 수 있겠습니다." },
|
||||
{ "speaker": "Cao Cao", "display_speaker": "조조", "side": "left", "text": "이는 첫 불길일 뿐이다. 조정이 빨리 움직이지 못한다면 우리가 움직여야 한다." },
|
||||
{ "speaker": "Xiahou Yuan", "display_speaker": "하후연", "side": "right", "text": "그렇다면 다음 출정에도 저희를 부르십시오. 함께 달리겠습니다." },
|
||||
{ "speaker": "Cao Ren", "display_speaker": "조인", "side": "right", "text": "일족은 명을 기다리고 있습니다, 주공." }
|
||||
]
|
||||
}
|
||||
|
||||
@@ -271,12 +271,14 @@ func _normalized_camp_conversations(source) -> Array:
|
||||
var label := str(entry.get("label", conversation_id.capitalize())).strip_edges()
|
||||
if label.is_empty():
|
||||
label = conversation_id.capitalize()
|
||||
var speaker := str(entry.get("speaker", "")).strip_edges()
|
||||
var display_speaker := str(entry.get("display_speaker", speaker)).strip_edges()
|
||||
result.append({
|
||||
"id": conversation_id,
|
||||
"group": str(entry.get("group", "topic")).strip_edges(),
|
||||
"officer_id": str(entry.get("officer_id", "")).strip_edges(),
|
||||
"label": label,
|
||||
"speaker": str(entry.get("speaker", "")).strip_edges(),
|
||||
"speaker": display_speaker,
|
||||
"summary": str(entry.get("summary", "")).strip_edges(),
|
||||
"lines": lines,
|
||||
"effects": _normalized_camp_conversation_effects(entry.get("effects", []))
|
||||
@@ -1309,7 +1311,7 @@ func end_player_turn() -> void:
|
||||
|
||||
selected_unit_id = ""
|
||||
current_team = TEAM_ENEMY
|
||||
_emit_log("Enemy phase begins.")
|
||||
_emit_log("적군 차례가 열렸습니다.")
|
||||
_reset_team_actions(TEAM_ENEMY)
|
||||
if battle_status == STATUS_ACTIVE:
|
||||
_run_events("turn_start", current_team, turn_number)
|
||||
@@ -2068,13 +2070,21 @@ func can_player_act() -> bool:
|
||||
|
||||
func get_status_text() -> String:
|
||||
if battle_status == STATUS_VICTORY:
|
||||
return "Victory"
|
||||
return "승전"
|
||||
if battle_status == STATUS_DEFEAT:
|
||||
return "Defeat"
|
||||
return "패전"
|
||||
var turn_limit := get_turn_limit()
|
||||
if turn_limit > 0:
|
||||
return "Turn %d/%d - %s" % [turn_number, turn_limit, current_team.capitalize()]
|
||||
return "Turn %d - %s" % [turn_number, current_team.capitalize()]
|
||||
return "제%d턴/%d · %s" % [turn_number, turn_limit, _team_status_text(current_team)]
|
||||
return "제%d턴 · %s" % [turn_number, _team_status_text(current_team)]
|
||||
|
||||
|
||||
func _team_status_text(team: String) -> String:
|
||||
if team == TEAM_PLAYER:
|
||||
return "아군"
|
||||
if team == TEAM_ENEMY:
|
||||
return "적군"
|
||||
return team.capitalize()
|
||||
|
||||
|
||||
func get_turn_limit() -> int:
|
||||
@@ -2793,7 +2803,7 @@ func _run_enemy_turn() -> void:
|
||||
if battle_status == STATUS_ACTIVE:
|
||||
turn_number += 1
|
||||
current_team = TEAM_PLAYER
|
||||
_emit_log("Player phase begins.")
|
||||
_emit_log("아군 차례가 열렸습니다.")
|
||||
_reset_team_actions(TEAM_PLAYER)
|
||||
if battle_status == STATUS_ACTIVE:
|
||||
_run_events("turn_start", current_team, turn_number)
|
||||
@@ -3494,9 +3504,9 @@ func _append_all_units_progress_line(result: Array[String], team: String, mode:
|
||||
return
|
||||
var living := _count_living_deployed_units(team)
|
||||
if mode == "defeat":
|
||||
_append_progress_line(result, "%s remaining %d/%d" % [_condition_team_label(team), living, total], pending)
|
||||
_append_progress_line(result, "%s 잔존 %d/%d" % [_condition_team_label(team), living, total], pending)
|
||||
return
|
||||
_append_progress_line(result, "%s defeated %d/%d" % [_condition_team_label(team), total - living, total], pending)
|
||||
_append_progress_line(result, "%s 격파 %d/%d" % [_condition_team_label(team), total - living, total], pending)
|
||||
|
||||
|
||||
func _append_target_units_progress_line(result: Array[String], unit_ids, mode: String, pending := false) -> void:
|
||||
@@ -3507,11 +3517,11 @@ func _append_target_units_progress_line(result: Array[String], unit_ids, mode: S
|
||||
var defeated := int(counts.get("defeated", 0))
|
||||
if mode == "defeat" and total == 1:
|
||||
var unit_name := _condition_unit_display_name(unit_ids[0])
|
||||
var status := "defeated" if defeated > 0 else "safe"
|
||||
var status := "퇴각" if defeated > 0 else "건재"
|
||||
_append_progress_line(result, "%s %s" % [unit_name, status], pending)
|
||||
return
|
||||
var label := "Protected losses" if mode == "defeat" else "Target units defeated"
|
||||
var suffix := " (defeat at 1)" if mode == "defeat" else ""
|
||||
var label := "호위 손실" if mode == "defeat" else "격파 표식"
|
||||
var suffix := " (1 이상이면 패전)" if mode == "defeat" else ""
|
||||
_append_progress_line(result, "%s %d/%d%s" % [label, defeated, total, suffix], pending)
|
||||
|
||||
|
||||
@@ -3529,11 +3539,11 @@ func _append_target_officers_progress_line(
|
||||
var defeated := int(counts.get("defeated", 0))
|
||||
if mode == "defeat" and total == 1:
|
||||
var officer_name := _condition_officer_display_name(str(officer_ids[0]), team)
|
||||
var status := "defeated" if defeated > 0 else "safe"
|
||||
var status := "퇴각" if defeated > 0 else "건재"
|
||||
_append_progress_line(result, "%s %s" % [officer_name, status], pending)
|
||||
return
|
||||
var label := "Officer losses" if mode == "defeat" else "Target officers defeated"
|
||||
var suffix := " (defeat at 1)" if mode == "defeat" else ""
|
||||
var label := "장수 손실" if mode == "defeat" else "대상 장수 격파"
|
||||
var suffix := " (1 이상이면 패전)" if mode == "defeat" else ""
|
||||
_append_progress_line(result, "%s %d/%d%s" % [label, defeated, total, suffix], pending)
|
||||
|
||||
|
||||
@@ -3541,8 +3551,8 @@ func _append_destination_progress_line(result: Array[String], condition: Diction
|
||||
var targets := _condition_cells(condition)
|
||||
if targets.is_empty():
|
||||
return
|
||||
var status := "reached" if _unit_reaches_tile(condition) else "not reached"
|
||||
_append_progress_line(result, "Destination %s: %s" % [_format_cells(targets), status], pending)
|
||||
var status := "도달" if _unit_reaches_tile(condition) else "미도달"
|
||||
_append_progress_line(result, "목표 지점 %s: %s" % [_format_cells(targets), status], pending)
|
||||
|
||||
|
||||
func _append_turn_limit_progress_line(result: Array[String]) -> void:
|
||||
@@ -3550,7 +3560,7 @@ func _append_turn_limit_progress_line(result: Array[String]) -> void:
|
||||
if limit <= 0:
|
||||
return
|
||||
var turns_left: int = maxi(0, limit - turn_number + 1)
|
||||
_append_progress_line(result, "Turns left %d" % turns_left)
|
||||
_append_progress_line(result, "남은 턴 %d" % turns_left)
|
||||
|
||||
|
||||
func _append_after_event_progress_line(result: Array[String], event_id: String) -> void:
|
||||
@@ -3566,15 +3576,15 @@ func _append_after_event_progress_line(result: Array[String], event_id: String)
|
||||
elif trigger_type == "turn_start":
|
||||
var target_turn := int(when.get("turn", 0))
|
||||
if target_turn > 0:
|
||||
_append_progress_line(result, "Objective unlocks on Turn %d" % target_turn)
|
||||
_append_progress_line(result, "제%d턴에 전공 갱신" % target_turn)
|
||||
|
||||
|
||||
func _append_event_destination_progress_line(result: Array[String], when: Dictionary) -> void:
|
||||
var targets := _condition_cells(when)
|
||||
if targets.is_empty():
|
||||
return
|
||||
var status := "reached" if _unit_reaches_tile(when) else "not reached"
|
||||
_append_progress_line(result, "Reach %s: %s" % [_format_cells(targets), status])
|
||||
var status := "도달" if _unit_reaches_tile(when) else "미도달"
|
||||
_append_progress_line(result, "목표 지점 %s: %s" % [_format_cells(targets), status])
|
||||
|
||||
|
||||
func _append_turn_condition_progress_line(result: Array[String], condition: Dictionary, mode: String, pending := false) -> void:
|
||||
@@ -3583,7 +3593,7 @@ func _append_turn_condition_progress_line(result: Array[String], condition: Dict
|
||||
var limit := int(condition.get("turn", condition.get("limit", 0)))
|
||||
if limit <= 0:
|
||||
return
|
||||
_append_progress_line(result, "Turns left %d" % maxi(0, limit - turn_number + 1), pending)
|
||||
_append_progress_line(result, "남은 턴 %d" % maxi(0, limit - turn_number + 1), pending)
|
||||
elif condition_type == "turn_reached":
|
||||
var target_turn := int(condition.get("turn", 0))
|
||||
if target_turn <= 0:
|
||||
@@ -3591,16 +3601,16 @@ func _append_turn_condition_progress_line(result: Array[String], condition: Dict
|
||||
if mode == "defeat":
|
||||
var target_team := str(condition.get("team", ""))
|
||||
var limit_turn := target_turn - 1 if target_team == TEAM_PLAYER else target_turn
|
||||
_append_progress_line(result, "Turns left %d" % maxi(0, limit_turn - turn_number + 1), pending)
|
||||
_append_progress_line(result, "남은 턴 %d" % maxi(0, limit_turn - turn_number + 1), pending)
|
||||
return
|
||||
_append_progress_line(result, "Turn %d/%d" % [min(turn_number, target_turn), target_turn], pending)
|
||||
_append_progress_line(result, "제%d턴/%d" % [min(turn_number, target_turn), target_turn], pending)
|
||||
|
||||
|
||||
func _append_progress_line(result: Array[String], text: String, pending := false) -> void:
|
||||
if text.is_empty():
|
||||
return
|
||||
if pending:
|
||||
text += " (pending)"
|
||||
text += " (대기)"
|
||||
if not result.has(text):
|
||||
result.append(text)
|
||||
|
||||
@@ -3700,10 +3710,10 @@ func _condition_officer_display_name(officer_id: String, team := "") -> String:
|
||||
|
||||
func _condition_team_label(team: String) -> String:
|
||||
if team == TEAM_ENEMY:
|
||||
return "Enemies"
|
||||
return "적군"
|
||||
if team == TEAM_PLAYER:
|
||||
return "Allies"
|
||||
return "Units"
|
||||
return "아군"
|
||||
return "부대"
|
||||
|
||||
|
||||
func _event_by_id(event_id: String) -> Dictionary:
|
||||
@@ -4157,11 +4167,12 @@ func _normalize_dialogue_line(line) -> Dictionary:
|
||||
if text.is_empty():
|
||||
return {}
|
||||
var speaker := str(line.get("speaker", ""))
|
||||
var display_speaker := str(line.get("display_speaker", speaker))
|
||||
var portrait := str(line.get("portrait", ""))
|
||||
if portrait.is_empty():
|
||||
portrait = data_catalog.get_portrait_for_speaker(speaker)
|
||||
return {
|
||||
"speaker": speaker,
|
||||
"speaker": display_speaker,
|
||||
"text": text,
|
||||
"portrait": portrait,
|
||||
"side": _normalize_dialogue_side(line.get("side", "left"))
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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
@@ -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)
|
||||
|
||||
@@ -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]))
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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()
|
||||
|
||||
|
||||
|
||||
@@ -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("")
|
||||
|
||||
@@ -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) {
|
||||
|
||||
Reference in New Issue
Block a user