diff --git a/scripts/core/battle_state.gd b/scripts/core/battle_state.gd index a215eed..4d254f9 100644 --- a/scripts/core/battle_state.gd +++ b/scripts/core/battle_state.gd @@ -305,9 +305,9 @@ func _normalized_camp_conversations(source) -> Array: if lines.is_empty(): continue seen[conversation_id] = true - var label := str(entry.get("label", conversation_id.capitalize())).strip_edges() + var label := str(entry.get("label", "대화")).strip_edges() if label.is_empty(): - label = conversation_id.capitalize() + label = "대화" var speaker := str(entry.get("speaker", "")).strip_edges() var display_speaker := str(entry.get("display_speaker", speaker)).strip_edges() result.append({ @@ -563,7 +563,10 @@ func _prepare_unit(source_unit: Dictionary) -> Dictionary: push_error("Unit has malformed position: %s" % str(source_unit)) return {} unit["pos"] = Vector2i(int(pos_array[0]), int(pos_array[1])) - unit["class"] = str(unit.get("class", unit.get("class_id", "Unit"))).capitalize() + var resolved_class_name := str(unit.get("class", "")).strip_edges() + if resolved_class_name.is_empty(): + resolved_class_name = _fallback_class_name(str(unit.get("class_id", ""))) + unit["class"] = resolved_class_name unit["hp"] = int(unit.get("hp", unit.get("max_hp", 1))) unit["max_hp"] = int(unit.get("max_hp", unit["hp"])) unit["max_mp"] = int(unit.get("max_mp", unit.get("mp", 0))) @@ -1177,7 +1180,49 @@ func _weapon_effectiveness_target_type(target: Dictionary) -> String: func _format_move_type(move_type: String) -> String: - return move_type.replace("_", " ").capitalize() + match move_type.strip_edges().to_lower(): + "foot": + return "보병" + "cavalry", "mounted": + return "기병" + "archer": + return "궁병" + "water": + return "수군" + "flying": + return "비행" + return "행군" + + +func _fallback_class_name(class_id: String) -> String: + match class_id.strip_edges().to_lower(): + "hero": + return "영걸" + "commander": + return "도독" + "cavalry": + return "기병" + "elite_cavalry": + return "정예기병" + "infantry": + return "보병" + "guard_captain": + return "호위장" + "warrior": + return "무인" + "champion": + return "맹장" + "archer": + return "궁병" + "marksman": + return "강궁병" + "strategist": + return "책사" + "military_advisor": + return "군사" + "bandit": + return "산적" + return "부대" func _equipped_weapon(unit: Dictionary) -> Dictionary: @@ -1439,7 +1484,7 @@ func _status_display_name(status: String) -> String: return "무장해제" if normalized == "debuff": return "약화" - return normalized.replace("_", " ").capitalize() + return "상태" func _status_stat_display_name(stat: String) -> String: @@ -1663,7 +1708,7 @@ func get_threatening_unit_names(cell: Vector2i, source_team := TEAM_ENEMY) -> Ar for unit in get_living_units(source_team): if not _unit_threat_cells(unit).has(cell): continue - var unit_name := str(unit.get("name", unit.get("id", "Unit"))) + var unit_name := str(unit.get("name", unit.get("id", "부대"))) if not result.has(unit_name): result.append(unit_name) return result @@ -1686,7 +1731,7 @@ func get_physical_threat_previews(cell: Vector2i, source_team := TEAM_ENEMY) -> var directional_info := get_directional_attack_info(unit, target) result.append({ "source_id": str(unit.get("id", "")), - "source_name": str(unit.get("name", unit.get("id", "Unit"))), + "source_name": str(unit.get("name", unit.get("id", "부대"))), "damage": damage, "hit_chance": hit_chance, "target_hp_after": max(0, int(target.get("hp", 0)) - damage), @@ -2385,7 +2430,7 @@ func _team_status_text(team: String) -> String: return "아군" if team == TEAM_ENEMY: return "적군" - return team.capitalize() + return "전장" func get_turn_limit() -> int: @@ -2509,7 +2554,7 @@ func _build_skill_threat_preview(caster: Dictionary, skill_id: String, skill: Di var skill_kind := str(skill.get("kind", "damage")) var preview := { "source_id": str(caster.get("id", "")), - "source_name": str(caster.get("name", caster.get("id", "Unit"))), + "source_name": str(caster.get("name", caster.get("id", "부대"))), "skill_id": skill_id, "skill_name": str(skill.get("name", skill_id)), "kind": skill_kind, @@ -3053,7 +3098,7 @@ func _apply_class_runtime_fields(unit: Dictionary, class_id: String) -> void: var class_def := data_catalog.get_class_def(class_id) if class_def.is_empty(): return - unit["class"] = str(class_def.get("name", class_id.capitalize())) + unit["class"] = str(class_def.get("name", _fallback_class_name(class_id))) unit["move"] = int(class_def.get("move", unit.get("move", 3))) unit["move_type"] = str(class_def.get("move_type", unit.get("move_type", "foot"))) unit["growth"] = class_def.get("growth", {}).duplicate(true) @@ -3819,7 +3864,7 @@ func _level_up(unit: Dictionary) -> void: "type": "level_up", "unit_id": str(unit.get("id", "")), "officer_id": str(unit.get("officer_id", "")), - "name": str(unit.get("name", "Unit")), + "name": str(unit.get("name", "부대")), "from_level": previous_level, "to_level": int(unit.get("level", previous_level + 1)), "class_id": str(unit.get("class_id", "")), @@ -3844,7 +3889,7 @@ func _try_promote_unit(unit: Dictionary) -> void: if next_class_def.is_empty(): return var previous_class_id := class_id - var previous_class_name := str(class_def.get("name", class_id.capitalize())) + var previous_class_name := str(class_def.get("name", _fallback_class_name(class_id))) _apply_stat_bonus_delta(unit, class_def.get("base_bonus", {}), next_class_def.get("base_bonus", {})) unit["class_id"] = next_class_id _apply_class_runtime_fields(unit, next_class_id) @@ -3855,12 +3900,12 @@ func _try_promote_unit(unit: Dictionary) -> void: "type": "promotion", "unit_id": str(unit.get("id", "")), "officer_id": str(unit.get("officer_id", "")), - "name": str(unit.get("name", "Unit")), + "name": str(unit.get("name", "부대")), "level": int(unit.get("level", 1)), "from_class_id": previous_class_id, "from_class": previous_class_name, "to_class_id": next_class_id, - "to_class": str(unit.get("class", next_class_id.capitalize())) + "to_class": str(unit.get("class", _fallback_class_name(next_class_id))) }) diff --git a/scripts/core/data_catalog.gd b/scripts/core/data_catalog.gd index 4007bc4..e6e17b6 100644 --- a/scripts/core/data_catalog.gd +++ b/scripts/core/data_catalog.gd @@ -47,7 +47,7 @@ func hydrate_deployment(deployment: Dictionary) -> Dictionary: unit["sprite"] = str(sprite_info.get("path", "")) unit["uses_enemy_sprite"] = bool(sprite_info.get("uses_enemy_sprite", false)) unit["class_id"] = class_id - unit["class"] = str(class_def.get("name", class_id.capitalize())) + unit["class"] = str(class_def.get("name", _fallback_class_name(class_id))) unit["team"] = unit_team unit["pos"] = deployment.get("pos", [0, 0]) unit["requires_joined"] = bool(deployment.get("requires_joined", false)) @@ -194,6 +194,37 @@ func _get_dict(source: Dictionary, key) -> Dictionary: return {} +func _fallback_class_name(class_id: String) -> String: + match class_id.strip_edges().to_lower(): + "hero": + return "영걸" + "commander": + return "도독" + "cavalry": + return "기병" + "elite_cavalry": + return "정예기병" + "infantry": + return "보병" + "guard_captain": + return "호위장" + "warrior": + return "무인" + "champion": + return "맹장" + "archer": + return "궁병" + "marksman": + return "강궁병" + "strategist": + return "책사" + "military_advisor": + return "군사" + "bandit": + return "산적" + return "부대" + + func _normalized_speaker_name(value: String) -> String: return value.strip_edges().to_lower() diff --git a/scripts/scenes/battle_scene.gd b/scripts/scenes/battle_scene.gd index 052dc7a..5fdbf7d 100644 --- a/scripts/scenes/battle_scene.gd +++ b/scripts/scenes/battle_scene.gd @@ -9331,7 +9331,7 @@ func _team_hud_short_text(team: String) -> String: return "아군" if team == BattleState.TEAM_ENEMY: return "적군" - return team.capitalize() + return "전장" func _format_objective_hud_text() -> String: @@ -9575,7 +9575,7 @@ func _unit_team_text(team: String) -> String: return "아군" if team == BattleState.TEAM_ENEMY: return "적군" - return team.capitalize() + return "소속 미상" func _unit_class_display_name(unit: Dictionary) -> String: @@ -14045,7 +14045,7 @@ func _format_manual_checkpoint_text() -> String: return "수동 봉인: 비어 있음" var title := str(summary.get("current_scenario_title", "")) if title.is_empty(): - title = str(summary.get("current_scenario_id", "Unknown")) + title = "전기" var pending_suffix := " · 회의 미결" if bool(summary.get("pending_choice", false)) else "" return "수동 봉인: %s · %d/%d 봉인 · 군자금 %d냥%s" % [ title, @@ -15077,7 +15077,7 @@ func _shop_item_common_detail_parts(item: Dictionary, owned: int) -> Array: var effect_text := _format_shop_item_effect_text(item) if not effect_text.is_empty(): parts.append(effect_text) - var kind := str(item.get("kind", "item")).capitalize() + var kind := str(item.get("kind", "item")) if not kind.is_empty(): parts.append(_item_kind_text(kind)) parts.append("보유 %d점" % owned) @@ -15148,7 +15148,7 @@ func _item_kind_text(kind: String) -> String: return "보물" if normalized == "item": return "도구" - return kind.capitalize() + return "물자" func _equipment_type_text(equipment_type: String) -> String: @@ -15698,7 +15698,24 @@ func _officer_display_name_from_id(officer_id: String) -> String: func _format_identifier_label(value: String) -> String: - return value.strip_edges().replace("_", " ").capitalize() + var normalized := value.strip_edges().to_lower() + if normalized == "event": + return "사건" + if normalized == "weapon": + return "병장" + if normalized == "armor": + return "갑주" + if normalized == "accessory": + return "보물" + if normalized == "officer": + return "장수" + if normalized == "topic": + return "의제" + if normalized == "stat": + return "능력" + if normalized == "status": + return "상태" + return "항목" func _item_display_name(item_id: String) -> String: @@ -16808,7 +16825,7 @@ func _skill_kind_text(kind: String) -> String: return "보조책" if normalized == "skill": return "책략" - return kind.capitalize() + return "책략" func _on_tactic_skill_pressed(skill_id: String) -> void: @@ -17306,7 +17323,7 @@ func _format_move_type(move_type: String) -> String: return "수군" if normalized == "flying": return "비행" - return move_type.strip_edges().replace("_", " ").capitalize() + return "행군" func _terrain_display_name(terrain_name: String) -> String: diff --git a/tools/smoke_chapter_one_polish.gd b/tools/smoke_chapter_one_polish.gd index dd6afe8..06ab533 100644 --- a/tools/smoke_chapter_one_polish.gd +++ b/tools/smoke_chapter_one_polish.gd @@ -24,6 +24,7 @@ func _init() -> void: _check_map_focus_contract(failures) _check_battle_presentation_sequence_contract(failures) _check_readability_contract(failures) + _check_korean_fallback_labels(failures) _check_dialogue_localization_and_side(failures) _check_event_dialogue_side_passthrough(failures) _check_opening_battle_camp_context(failures) @@ -570,6 +571,54 @@ func _check_readability_contract(failures: Array[String]) -> void: scene.free() +func _check_korean_fallback_labels(failures: Array[String]) -> void: + var state = BattleStateScript.new() + var scene = BattleSceneScript.new() + var prepared := state._prepare_unit({ + "id": "fallback_unit", + "class_id": "mystery_unit", + "pos": [0, 0] + }) + var labels := [ + str(prepared.get("class", "")), + state._format_move_type("hover_cart"), + state._status_display_name("mystery_status"), + state._team_status_text("neutral_force"), + scene._team_hud_short_text("neutral_force"), + scene._unit_team_text("neutral_force"), + scene._item_kind_text("mystery"), + scene._skill_kind_text("mystery"), + scene._format_move_type("hover_cart"), + scene._format_identifier_label("mystery_value") + ] + var forbidden := ["Unit", "Unknown", "Player", "Enemy", "Hover Cart", "Mystery"] + for label in labels: + if label.strip_edges().is_empty(): + failures.append("fallback labels should not be blank") + for word in forbidden: + if label.contains(word): + failures.append("fallback label should stay Korean-facing, got %s" % label) + if prepared.get("class", "") != "부대": + failures.append("missing unit class should fall back to 부대: %s" % str(prepared.get("class", ""))) + if state._format_move_type("hover_cart") != "행군": + failures.append("unknown battle-state move type should fall back to 행군") + if state._status_display_name("mystery_status") != "상태": + failures.append("unknown battle-state status should fall back to 상태") + if state._team_status_text("neutral_force") != "전장": + failures.append("unknown battle-state team should fall back to 전장") + if scene._team_hud_short_text("neutral_force") != "전장": + failures.append("unknown HUD team should fall back to 전장") + if scene._unit_team_text("neutral_force") != "소속 미상": + failures.append("unknown unit team should fall back to 소속 미상") + if scene._item_kind_text("mystery") != "물자": + failures.append("unknown item kind should fall back to 물자") + if scene._skill_kind_text("mystery") != "책략": + failures.append("unknown skill kind should fall back to 책략") + if scene._format_move_type("hover_cart") != "행군": + failures.append("unknown scene move type should fall back to 행군") + scene.free() + + func _check_text_fit_contract(scene, failures: Array[String]) -> void: for method_name in [ "_fit_label_font_size_to_text",