Files
aetherbound-guild/runtime/tests/fg3/campaign_runtime_touch_test.gd
T

209 lines
9.9 KiB
GDScript

extends SceneTree
const Contract1Battle = preload("res://fg3/contract1_battle_service.gd")
const Controller = preload("res://campaign/fg3_runtime_controller.gd")
const Fixture = preload("res://tests/fg3/contract1_fixture.gd")
const VIEWPORT := Vector2i(844, 390)
var failures: Array[String] = []
var experience
var parsed_touches := 0
var parsed_drags := 0
var observed_touches := 0
var observed_drags := 0
func _initialize() -> void:
call_deferred("run_test")
func run_test() -> void:
root.content_scale_mode = Window.CONTENT_SCALE_MODE_CANVAS_ITEMS
root.content_scale_aspect = Window.CONTENT_SCALE_ASPECT_EXPAND
root.content_scale_size = VIEWPORT
root.size = VIEWPORT
await verify_market_plan_battle()
await verify_first_boss_record_handoff()
await verify_second_boss_region_close()
if is_instance_valid(experience):
experience.queue_free()
for _index in 45:
await process_frame
if failures.is_empty():
print("ABG_FG3_CAMPAIGN_RUNTIME_TOUCH_OK viewport=844x390 states=market8,plan,battle,boss1,record1,handoff,contract2,boss2,record2,rg03 touch=%d drag=%d fallback=0 direct=0 world_layers=5" % [parsed_touches, parsed_drags])
quit(0)
else:
for failure in failures:
push_error(failure)
quit(1)
func verify_market_plan_battle() -> void:
await mount(Fixture.new().fg3_market_8_snapshot())
expect(experience.screen == Controller.SCREEN_MARKET and experience.view.mode == "market", "saved Market 8 opens in world-first layout")
expect(String(experience.get_meta("logical_grid", "")) == "422x195@2x", "reference viewport preserves accepted logical grid")
for layer_name in ["BackgroundLayer", "MidgroundLayer", "ActorsLayer", "ForegroundLayer", "VfxLayer"]:
expect(experience.find_child(layer_name, true, false) != null, "%s remains present" % layer_name)
var background = experience.find_child("BackgroundLayer", true, false)
expect(String(background.get_meta("environment_region_id", "")) == "RG-02", "Market 8 uses the Siltwake environment binding")
var first_company := String(experience.view.company[0].recruit_id)
var second_company := String(experience.view.company[1].recruit_id)
await drag("FG2CompanyTarget_%s" % first_company, "FG2CompanyTarget_%s" % second_company)
expect(experience.last_code == "formation_authority_frozen", "parsed drag never forges a formation transaction")
var route: Dictionary = experience.view.routes[0]
await tap("FG3Option_%s" % String(route.offer_id))
await tap("FG3ContinueButton")
expect(experience.view.mode == "plan" and experience.view.plans.size() == 4, "route confirmation opens four response plans")
var plan_id := String(Contract1Battle.PREFERRED_PLAN[String(route.route_id)])
await tap("FG3Option_%s" % plan_id)
await tap("FG3ContinueButton")
expect(experience.screen == Controller.SCREEN_BATTLE, "plan confirmation enters automatic battle")
await process_frame
var actors = experience.find_child("ActorsLayer", true, false)
var authored_ids := Array(actors.get_meta("authored_actor_ids", []))
for identity_id in ["PF-B09", "PF-B07", "EN-R2-01"]:
expect(identity_id in authored_ids, "%s uses its authored RG-02 identity" % identity_id)
experience.advance_battle_ticks(5)
await process_frame
var motion_state: Dictionary = experience.event_presenter.snapshot()
expect(not Dictionary(motion_state.actor_cues).is_empty(), "single-frame Company and enemies receive event-bound motion cues")
var guard := 0
while not experience.battle_result_visible and guard < 80:
experience.advance_battle_ticks(4)
guard += 1
await process_frame
expect(guard < 80 and experience.view.battle.outcome == "victory", "real Market-8 battle reaches visible victory")
for kind in ["wave_arrived", "damage", "outcome"]:
expect(int(experience.presented_event_counts.get(kind, 0)) > 0, "runtime consumes %s" % kind)
await tap("FG2SettleButton")
expect(experience.view.mode == "market" and experience.view.market_index == 9, "settlement returns to changed Market 9")
var selected_market_9 := String(experience.selected_route_id)
expect(experience.view.routes.any(func(route): return String(route.offer_id) == selected_market_9), "settlement replaces the prior plan with a legal Market-9 route")
await tap("FG3ContinueButton")
expect(experience.view.mode == "plan", "the next Market remains immediately playable after settlement")
func verify_first_boss_record_handoff() -> void:
await mount(Fixture.new().boss_muster_snapshot())
expect(experience.view.mode == "boss_muster" and experience.view.plans.size() == 2, "first Boss Muster renders two plans")
await tap("FG3Option_follow_index_clear_eggs")
await tap("FG3ContinueButton")
await process_frame
var actors = experience.find_child("ActorsLayer", true, false)
expect("BO-R2-01" in Array(actors.get_meta("authored_actor_ids", [])), "Sluiceback Mother uses the authored action atlas")
while not experience.battle_result_visible:
experience.advance_battle_ticks(8)
await process_frame
await tap("FG2SettleButton")
expect(experience.screen == Controller.SCREEN_REWARD and experience.view.mode == "contract_record", "first Boss opens first Record screen")
await tap("FG3RecordButton")
expect(experience.view.mode == "contract_handoff", "first Record advances to explicit handoff")
await tap("FG3RecordButton")
expect(experience.view.mode == "market" and experience.view.contract_id == "CTR-R2-02", "handoff opens second Contract Market")
func verify_second_boss_region_close() -> void:
await mount(Fixture.new().contract2_boss_muster_snapshot())
expect(experience.view.mode == "boss_muster" and experience.view.boss_id == "BO-R2-02", "Drowned Bell Muster is visible")
await tap("FG3Option_interrupt_pulse_family")
await tap("FG3ContinueButton")
await process_frame
var actors = experience.find_child("ActorsLayer", true, false)
expect("BO-R2-02" in Array(actors.get_meta("authored_actor_ids", [])), "Drowned Bell uses the authored action atlas")
while not experience.battle_result_visible:
experience.advance_battle_ticks(8)
await process_frame
expect(int(experience.presented_event_counts.get("linked_positions_disclosed", 0)) == 1 and int(experience.presented_event_counts.get("third_pulse_link", 0)) == 1, "pair and third-pulse facts reach presentation")
var presenter_state: Dictionary = experience.event_presenter.snapshot()
for kind in ["linked_positions_disclosed", "third_pulse_link", "pulse_family_interrupted"]:
expect(int(presenter_state.event_counts.get(kind, 0)) == 1, "%s reaches the event-bound presenter" % kind)
var audio_state: Dictionary = experience.audio_router.snapshot()
for kind in ["linked_positions_disclosed", "third_pulse_link", "pulse_family_interrupted"]:
expect(int(audio_state.event_counts.get(kind, 0)) == 1, "%s reaches the semantic audio router" % kind)
expect(Array(audio_state.playback_log).any(func(row): return String(row.get("kind", "")) == kind and String(row.get("result", "")) != "unmapped"), "%s owns a mapped audio decision" % kind)
var vfx = experience.find_child("VfxLayer", true, false)
expect(int(vfx.get_meta("fg3_disclosure_vfx_count", 0)) > 0, "linked-pulse disclosure owns visible world VFX")
await tap("FG2SettleButton")
expect(experience.view.mode == "region_record", "Drowned Bell opens second Record")
await tap("FG3RecordButton")
expect(experience.view.mode == "region_closed" and experience.view.unlocked_region_ids.count("RG-03") == 1, "final touch reveals stable RG-03 unlock")
expect(observed_touches + experience.input_touch_count == parsed_touches and observed_drags + experience.input_drag_count == parsed_drags, "all input is accounted as parsed touch/drag")
expect(experience.fallback_input_count == 0 and experience.direct_command_count == 0, "no fallback or direct player command path runs")
func mount(snapshot_value: Dictionary) -> void:
if is_instance_valid(experience):
observed_touches += int(experience.input_touch_count)
observed_drags += int(experience.input_drag_count)
experience.queue_free()
for _index in 12:
await process_frame
experience = Controller.new()
experience.battle_auto_advance = false
experience.locale = "zh_CN"
experience.text_scale = 1.0
experience.size = Vector2(VIEWPORT)
experience.configure_entry(snapshot_value)
root.add_child(experience)
for _index in 8:
await process_frame
func tap(node_name: String) -> void:
var control := experience.find_child(node_name, true, false) as Control
expect(control != null, "%s exists before parsed touch" % node_name)
if control == null:
return
var point := root.get_final_transform() * control.get_global_rect().get_center()
var down := InputEventScreenTouch.new()
down.index = 0
down.position = point
down.pressed = true
Input.parse_input_event(down)
parsed_touches += 1
await process_frame
var up := InputEventScreenTouch.new()
up.index = 0
up.position = point
up.pressed = false
Input.parse_input_event(up)
for _index in 5:
await process_frame
func drag(source_name: String, target_name: String) -> void:
var source := experience.find_child(source_name, true, false) as Control
var target := experience.find_child(target_name, true, false) as Control
expect(source != null and target != null, "company drag targets exist")
if source == null or target == null:
return
var start := root.get_final_transform() * source.get_global_rect().get_center()
var finish := root.get_final_transform() * target.get_global_rect().get_center()
var down := InputEventScreenTouch.new()
down.index = 1
down.position = start
down.pressed = true
Input.parse_input_event(down)
parsed_touches += 1
await process_frame
var motion := InputEventScreenDrag.new()
motion.index = 1
motion.position = finish
motion.relative = finish - start
Input.parse_input_event(motion)
parsed_drags += 1
await process_frame
var up := InputEventScreenTouch.new()
up.index = 1
up.position = finish
up.pressed = false
Input.parse_input_event(up)
for _index in 5:
await process_frame
func expect(condition: bool, message: String) -> void:
if not condition:
failures.append("FG-3 campaign runtime touch: %s" % message)