Files
aetherbound-guild/runtime/tests/dev5/battle_runtime_test.gd
T
wushenghua 64c1c6fe7e feat(rsk002): implement risk commitment runtime
Reconstruct the approved contained 16:9 commitment page with engine-owned risk facts, frozen Party actors, transparent hit areas and explicit confirmation before the idempotent Battle Opening handoff. Add focused responsive behavior coverage and update the battle UI fixture for the confirmation boundary.
2026-08-23 17:45:07 +08:00

183 lines
7.4 KiB
GDScript

extends SceneTree
const EntryShell = preload("res://entry/entry_shell.gd")
const SettingsStore = preload("res://entry/settings_store.gd")
const DraftService = preload("res://entry/initial_company_draft_service.gd")
const ContractService = preload("res://entry/guild_contract_service.gd")
const FormationService = preload("res://entry/formation_risk_service.gd")
const DRAFT_PATH := "user://dev5_battle_ui_draft.json"
const COMPANY_PATH := "user://dev5_battle_ui_company.json"
const SEED := "fedcba98765432100123456789abcdef"
const CONTRACT_OP := "0123456789abcdef0123456789abcdef"
const HIRE_OP_1 := "11112222333344445555666677778888"
const HIRE_OP_2 := "88887777666655554444333322221111"
const ORDER_OP := "abcdefabcdefabcdefabcdefabcdefab"
const READY_OP := "fedcbafedcbafedcbafedcbafedcbafe"
var failures: Array[String] = []
var parsed_touches := 0
func _initialize() -> void:
call_deferred("run_test")
func run_test() -> void:
_cleanup()
root.content_scale_mode = Window.CONTENT_SCALE_MODE_CANVAS_ITEMS
root.content_scale_aspect = Window.CONTENT_SCALE_ASPECT_EXPAND
root.size = Vector2i(844, 390)
for locale in ["zh_CN", "en"]:
_cleanup()
var prepared := _create_prepared_company()
if not prepared.is_empty():
await verify_battle_ui(prepared, locale)
_cleanup()
if failures.is_empty():
print("ABG_DEV5_BATTLE_UI_OK risk_to_battle=true opening=true actors=4 enemies=true order=rear_left_front_right phase_telegraph=true event_rail=true pause_resume=true speed=1x,2x no_command_prompt=true restart=active_battle locales=zh_CN,en text_130=true parsed_touch=%d" % parsed_touches)
quit(0)
else:
for failure in failures:
push_error(failure)
quit(1)
func verify_battle_ui(company: Dictionary, locale: String) -> void:
var settings := SettingsStore.new().defaults()
settings.setup_completed = true
settings.locale = locale
settings.text_scale = 1.3
var shell := EntryShell.new()
shell.auto_start = false
shell.initial_company_service.path = DRAFT_PATH
shell.initial_company_service.company_path = COMPANY_PATH
root.add_child(shell)
await _frames(3)
shell.settings = settings
shell.setup_draft = settings.duplicate(true)
shell.risk_selected_id = String(company.active_contract.formation.risk_options[1].risk_option_id)
shell.show_risk_commitment(company)
await _frames(3)
var acknowledge := shell.find_child("RiskCommitmentAcknowledge", true, false) as Button
await _touch(acknowledge)
expect(shell.risk_commitment_acknowledged, "%s Risk Commitment requires explicit acknowledgement" % locale)
await _touch(shell.find_child("RiskBeginBattle", true, false))
expect(shell.current_screen == "battle", "%s Risk Commitment enters Battle Opening" % locale)
await _frames(3)
expect(shell.find_child("BattleActors", true, false).get_child_count() == 4 and shell.find_child("BattleEnemies", true, false).get_child_count() == 3, "%s Battle renders four actors and three enemy markers" % locale)
expect(shell.find_child("BattleEventContext", true, false) != null and shell.find_child("BattleCommittedRule", true, false) != null, "%s Battle exposes event context and committed rule" % locale)
expect(shell.find_child("BattleCommand", true, false) == null, "%s Battle has no combat command prompt" % locale)
var pause := shell.find_child("BattlePause", true, false) as Button
var speed := shell.find_child("BattleSpeed", true, false) as Button
expect(pause != null and speed != null, "%s pause and speed controls exist" % locale)
await _touch(pause)
expect(shell.battle_paused, "%s Pause stops automatic playback" % locale)
await _touch(shell.find_child("BattleSpeed", true, false))
expect(is_equal_approx(shell.battle_speed, 2.0), "%s Speed toggles to 2x" % locale)
for _phase in range(8):
shell.advance_battle_phase()
await _frames(2)
expect(shell.current_screen == "battle" and shell.find_child("BattleCurrentEvent", true, false) != null, "%s live timeline reaches the knockout event without result handoff" % locale)
var active: Dictionary = _draft_service().load_company()
expect(active.ok and active.company.active_contract.battle_state.status == "active", "%s active battle receipt persists during playback" % locale)
shell._invalidate_battle_playback()
shell.queue_free()
await _timer_seconds(1.0)
var resumed := EntryShell.new()
resumed.auto_start = false
resumed.initial_company_service.path = DRAFT_PATH
resumed.initial_company_service.company_path = COMPANY_PATH
root.add_child(resumed)
await _frames(2)
resumed.settings = settings
resumed.setup_draft = settings.duplicate(true)
resumed._open_company_destination(active.company)
await _frames(3)
expect(resumed.current_screen == "battle" and resumed.find_child("BattleActors", true, false) != null, "%s Continue restores the active Battle receipt" % locale)
resumed.battle_paused = true
resumed._invalidate_battle_playback()
resumed.queue_free()
await _timer_seconds(1.0)
func _create_prepared_company() -> Dictionary:
var draft: Dictionary = _draft_service().begin_or_load(SEED)
if not draft.ok:
return {}
_draft_service().set_selection([String(draft.draft.offers[0].offer_id), String(draft.draft.offers[1].offer_id)])
var committed: Dictionary = _draft_service().commit_company()
if not committed.ok:
return {}
var created: Dictionary = _contract_service().create_contract(CONTRACT_OP, "CTR-R1-01", "standard")
if not created.ok:
return {}
var offers: Array = created.company.active_contract.market_board.recruit_offers
_contract_service().hire_recruit(HIRE_OP_1, String(offers[0].source_offer_id), 14)
var hired: Dictionary = _contract_service().hire_recruit(HIRE_OP_2, String(offers[1].source_offer_id), 14)
if not hired.ok:
return {}
var begun: Dictionary = _formation_service().begin_formation()
var saved: Dictionary = _formation_service().save_order(ORDER_OP, begun.formation.base_front_to_rear_ids)
var ready: Dictionary = _formation_service().accept_readiness(READY_OP, String(saved.receipt.order_hash))
return ready.company if ready.ok else {}
func _draft_service() -> RefCounted:
var service := DraftService.new()
service.path = DRAFT_PATH
service.company_path = COMPANY_PATH
return service
func _contract_service() -> RefCounted:
var service := ContractService.new()
service.company_path = COMPANY_PATH
return service
func _formation_service() -> RefCounted:
var service := FormationService.new()
service.company_path = COMPANY_PATH
return service
func _touch(control: Control) -> void:
if control == null:
expect(false, "touch target exists")
return
var center := control.get_global_rect().get_center()
var press := InputEventScreenTouch.new()
press.position = center
press.pressed = true
press.index = 0
Input.parse_input_event(press)
await _frames(2)
var release := InputEventScreenTouch.new()
release.position = center
release.pressed = false
release.index = 0
Input.parse_input_event(release)
parsed_touches += 1
await _frames(5)
func _frames(count: int) -> void:
for _index in range(count):
await process_frame
func _timer_seconds(seconds: float) -> void:
await create_timer(seconds).timeout
func _cleanup() -> void:
for path in [DRAFT_PATH, "%s.bak" % DRAFT_PATH, "%s.candidate" % DRAFT_PATH, "%s.bak.candidate" % DRAFT_PATH, COMPANY_PATH, "%s.bak" % COMPANY_PATH, "%s.candidate" % COMPANY_PATH, "%s.bak.candidate" % COMPANY_PATH, "%s.corrupt" % COMPANY_PATH]:
if FileAccess.file_exists(path):
DirAccess.remove_absolute(path)
func expect(condition: bool, message: String) -> void:
if not condition:
failures.append("DEV-5 Battle UI: %s" % message)