Add story beats to opening battle

This commit is contained in:
2026-06-19 15:37:31 +09:00
parent fb04cbda44
commit ffbe61a28e
4 changed files with 160 additions and 4 deletions

View File

@@ -1,5 +1,6 @@
extends SceneTree
const BattleStateScript := preload("res://scripts/core/battle_state.gd")
const BattleSceneScript := preload("res://scripts/scenes/battle_scene.gd")
const CHAPTER_ONE_SCENARIOS := [
@@ -21,6 +22,9 @@ func _init() -> void:
_check_edge_scroll_layout_contract(failures)
_check_readability_contract(failures)
_check_dialogue_localization_and_side(failures)
_check_event_dialogue_side_passthrough(failures)
_check_opening_battle_event_dialogue_structure(failures)
_check_opening_battle_story_beats(failures)
_check_pixel_unit_helpers(failures)
if failures.is_empty():
@@ -122,6 +126,98 @@ func _check_dialogue_localization_and_side(failures: Array[String]) -> void:
scene.free()
func _check_event_dialogue_side_passthrough(failures: Array[String]) -> void:
var state = BattleStateScript.new()
var scene = BattleSceneScript.new()
var raw_lines := state._dialogue_lines_from_action({
"type": "dialogue",
"lines": [
{"speaker": "Zhang Mancheng", "text": "성채를 지켜라."},
{"speaker": "Cao Cao", "text": "길을 열어라."}
]
})
if raw_lines.size() != 2:
failures.append("battle events should preserve valid dialogue lines")
scene.free()
return
if str((raw_lines[0] as Dictionary).get("side", "")) != "":
failures.append("battle state should not force event dialogue without an explicit side to the left")
var normalized := scene._normalized_dialogue_lines(raw_lines)
if normalized.size() != 2:
failures.append("scene should normalize battle event dialogue lines")
elif normalized[0].get("speaker", "") != "장만성" or normalized[0].get("side", "") != "right":
failures.append("event dialogue should localize Zhang Mancheng and infer right-side layout")
scene.free()
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"):
failures.append("could not load opening battle for event dialogue structure")
return
for event_id in [
"opening_dialogue",
"northern_woods_cache",
"turn_2_warning",
"turn_4_eastern_reserve",
"turn_7_southern_raiders",
"turn_9_western_reserve",
"zhang_mancheng_falls"
]:
var event := _event_by_id(state, event_id)
if event.is_empty():
failures.append("opening battle missing event: %s" % event_id)
continue
if not _event_has_dialogue_action(event):
failures.append("opening battle event should include contextual dialogue: %s" % event_id)
var boss_event := _event_by_id(state, "zhang_mancheng_falls")
if not boss_event.is_empty():
var when: Dictionary = boss_event.get("when", {})
var unit_ids: Array = when.get("unit_ids", [])
if not unit_ids.has("yellow_turban_1"):
failures.append("Zhang Mancheng defeat event should watch yellow_turban_1")
func _check_opening_battle_story_beats(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 story beat checks")
return
var dialogue_batches := []
state.dialogue_requested.connect(func(lines: Array) -> void:
dialogue_batches.append(lines)
)
state._run_events("turn_start", BattleStateScript.TEAM_ENEMY, 2)
state._run_events("turn_start", BattleStateScript.TEAM_ENEMY, 4)
state._run_events("turn_start", BattleStateScript.TEAM_ENEMY, 7)
state._run_events("turn_start", BattleStateScript.TEAM_ENEMY, 9)
if dialogue_batches.size() < 4:
failures.append("opening battle should add dialogue beats for each major reinforcement phase")
var victory_text := str(state.objectives.get("victory", ""))
if not victory_text.contains("마지막 황건대"):
failures.append("opening battle turn 9 event should update the victory objective after the final reserve arrives")
if not state._condition_gate_open({"after_event": "turn_9_western_reserve"}):
failures.append("opening battle final objective gate should open after the turn 9 story beat")
func _event_by_id(state, event_id: String) -> Dictionary:
for event in state.battle_events:
if typeof(event) == TYPE_DICTIONARY and str(event.get("id", "")) == event_id:
return event
return {}
func _event_has_dialogue_action(event: Dictionary) -> bool:
var actions: Array = event.get("actions", [])
for action in actions:
if typeof(action) == TYPE_DICTIONARY and str(action.get("type", "")) == "dialogue":
return true
return false
func _check_pixel_unit_helpers(failures: Array[String]) -> void:
var scene = BattleSceneScript.new()
for method_name in [