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.
183 lines
7.4 KiB
GDScript
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)
|