diff --git a/README.md b/README.md index 88d0e0e..17b0ff1 100644 --- a/README.md +++ b/README.md @@ -27,6 +27,7 @@ Godot 4 tactical RPG prototype inspired by classic turn-based Romance of the Thr - Pre-battle shop rows emphasize item artwork, compact price/status badges, and hover details instead of dense inline text. - Pre-battle Talk can list scenario camp conversations by officer or topic with portrait rows, compact topic/supply badges, hover dialogue previews, saved-branch filtering, one-time pre-battle supplies, and merchant flavor lines before a battle. - The opening battle's camp now includes a villager route report that frames the castle, village recovery point, and enemy approach before combat starts. +- The opening victory dialogue now bridges Xiahou Yuan and Cao Ren's join into the Sishui Gate march against Hua Xiong. - Pre-battle Armory equipment changes, including unequipping gear back into stock, save immediately and sync roster equipment plus inventory stock, with portrait and item-icon rows plus hover change details. - Pre-battle Roster can move optional officers between sortie and reserve within scenario limits, using portrait rows, compact status badges, and hover details. - Pre-battle Formation lets deployed officers swap starting positions inside scenario-defined cells, with portrait rows, coordinate badges, and hover placement instructions. diff --git a/data/scenarios/001_yellow_turbans.json b/data/scenarios/001_yellow_turbans.json index acbb583..99bac14 100644 --- a/data/scenarios/001_yellow_turbans.json +++ b/data/scenarios/001_yellow_turbans.json @@ -1343,17 +1343,23 @@ "side": "left", "text": "이는 첫 불길일 뿐이다. 조정이 빨리 움직이지 못한다면 우리가 움직여야 한다." }, - { - "speaker": "Xiahou Yuan", - "display_speaker": "하후연", - "side": "right", - "text": "그렇다면 다음 출정에도 저희를 부르십시오. 함께 달리겠습니다." - }, { "speaker": "Cao Ren", "display_speaker": "조인", "side": "right", - "text": "일족은 명을 기다리고 있습니다, 주공." + "text": "사수관 아래로 제후들의 군기가 모이고 있습니다. 동탁의 장수 화웅이 관문 앞을 막았다는 전갈도 왔습니다." + }, + { + "speaker": "Xiahou Yuan", + "display_speaker": "하후연", + "side": "right", + "text": "성채를 넘은 기세가 식기 전에 선봉을 맡겨 주십시오. 관문 길이라면 제가 먼저 달려 보겠습니다." + }, + { + "speaker": "Cao Cao", + "display_speaker": "조조", + "side": "left", + "text": "좋다. 영천의 백성을 쉬게 하고, 우리는 사수관으로 간다. 이제 작은 의병이 아니라 난세의 길을 여는 군세가 되어야 한다." } ] } diff --git a/tools/smoke_chapter_one_polish.gd b/tools/smoke_chapter_one_polish.gd index b0ecceb..a76ffd7 100644 --- a/tools/smoke_chapter_one_polish.gd +++ b/tools/smoke_chapter_one_polish.gd @@ -27,6 +27,7 @@ func _init() -> void: _check_dialogue_localization_and_side(failures) _check_event_dialogue_side_passthrough(failures) _check_opening_battle_camp_context(failures) + _check_opening_battle_post_battle_bridge(failures) _check_opening_battle_event_dialogue_structure(failures) _check_sishui_gate_event_dialogue_structure(failures) _check_xingyang_ambush_event_dialogue_structure(failures) @@ -661,6 +662,20 @@ func _check_opening_battle_camp_context(failures: Array[String]) -> void: failures.append("villager route report should mention %s: %s" % [expected, report_text]) +func _check_opening_battle_post_battle_bridge(failures: Array[String]) -> void: + var state = BattleStateScript.new() + if not state.load_battle("res://data/scenarios/001_yellow_turbans.json"): + failures.append("could not load opening battle for post-battle bridge") + return + var lines: Array = state.get_post_battle_dialogue() + if lines.size() < 5: + failures.append("opening battle should have a multi-beat post-battle bridge") + var bridge_text := _dialogue_lines_text(lines) + for expected in ["사수관", "화웅", "하후연", "조인", "난세의 길"]: + if not bridge_text.contains(expected): + failures.append("opening post-battle bridge should mention %s: %s" % [expected, bridge_text]) + + func _check_opening_battle_event_dialogue_structure(failures: Array[String]) -> void: var state = BattleStateScript.new() if not state.load_battle("res://data/scenarios/001_yellow_turbans.json"): @@ -1606,6 +1621,16 @@ func _camp_conversation_text(conversation: Dictionary) -> String: return "\n".join(parts) +func _dialogue_lines_text(lines: Array) -> String: + var parts: Array[String] = [] + for line in lines: + if typeof(line) != TYPE_DICTIONARY: + continue + parts.append(str((line as Dictionary).get("display_speaker", (line as Dictionary).get("speaker", "")))) + parts.append(str((line as Dictionary).get("text", ""))) + return "\n".join(parts) + + func _event_has_set_objective_action(event: Dictionary) -> bool: var actions: Array = event.get("actions", []) for action in actions: