Initial project sync
This commit is contained in:
@@ -0,0 +1,169 @@
|
||||
extends SceneTree
|
||||
|
||||
var failures: Array[String] = []
|
||||
var started: Array[StringName] = []
|
||||
|
||||
|
||||
func _init() -> void:
|
||||
_run.call_deferred()
|
||||
|
||||
|
||||
func _run() -> void:
|
||||
var fixture := await _fixture()
|
||||
if fixture.is_empty():
|
||||
_finish()
|
||||
return
|
||||
var input_component: Node = fixture["input"]
|
||||
var controller: Node = fixture["controller"]
|
||||
var combo: Node = fixture["combo"]
|
||||
controller.connect("action_started", _on_action_started)
|
||||
input_component.connect("intent_created", controller.submit_intent)
|
||||
|
||||
# 每次按键前把节奏钟钉到一个新的拍心:判定确定为 perfect(消除旧的
|
||||
# 墙钟 flake),且不与 new1 的"节奏点一次性消耗"规则冲突。
|
||||
_pin_beat(100)
|
||||
input_component.call("handle_input_event", _key_event(KEY_A))
|
||||
_expect_bool(started.is_empty(), false, "physical A should start an action")
|
||||
_expect_string(_last_started(), "ground_attack_left_1", "physical A should start left attack")
|
||||
controller.call("_reset_to_idle")
|
||||
_pin_beat(101)
|
||||
input_component.call("handle_input_event", _key_event(KEY_A))
|
||||
_expect_array(combo.call("get_slots"), [&"A", &"A"], "physical A A should fill two slots")
|
||||
_expect_string(_last_started(), "ground_attack_left_2", "physical A A should start left second attack")
|
||||
|
||||
controller.call("_reset_to_idle")
|
||||
combo.call("clear", &"test-reset")
|
||||
started.clear()
|
||||
_pin_beat(102)
|
||||
input_component.call("handle_input_event", _key_event(KEY_D))
|
||||
controller.call("_reset_to_idle")
|
||||
_pin_beat(103)
|
||||
input_component.call("handle_input_event", _key_event(KEY_D))
|
||||
_expect_array(combo.call("get_slots"), [&"D", &"D"], "physical D D should fill two slots")
|
||||
_expect_string(_last_started(), "ground_attack_right_2", "physical D D should start right second attack")
|
||||
|
||||
controller.call("_reset_to_idle")
|
||||
combo.call("clear", &"test-reset")
|
||||
started.clear()
|
||||
_pin_beat(104)
|
||||
input_component.call("handle_input_event", _key_event(KEY_W))
|
||||
_expect_string(_last_started(), "launcher_up", "physical W should start launcher")
|
||||
_expect_array(combo.call("get_slots"), [&"W"], "physical W should enter ComboWindow")
|
||||
|
||||
controller.call("_reset_to_idle")
|
||||
combo.call("clear", &"test-reset")
|
||||
started.clear()
|
||||
_pin_beat(105)
|
||||
input_component.call("handle_input_event", _key_event(KEY_S))
|
||||
_expect_string(_last_started(), "block_start", "physical S should start temporary block animation")
|
||||
_expect_array(combo.call("get_slots"), [&"S"], "physical S should enter ComboWindow")
|
||||
|
||||
controller.call("_reset_to_idle")
|
||||
started.clear()
|
||||
_pin_beat(106)
|
||||
input_component.call("handle_input_event", _key_event(KEY_A))
|
||||
controller.call("_reset_to_idle")
|
||||
_pin_beat(107)
|
||||
input_component.call("handle_input_event", _key_event(KEY_S))
|
||||
_expect_string(_last_started(), "block_start", "physical S should still block after a previous basic input remains in ComboWindow")
|
||||
|
||||
fixture["root"].free()
|
||||
_finish()
|
||||
|
||||
|
||||
func _last_started() -> String:
|
||||
if started.is_empty():
|
||||
return ""
|
||||
return str(started[started.size() - 1])
|
||||
|
||||
|
||||
## 把 RhythmManager 的时钟偏移钉到指定拍心:紧随其后的实时按键判定为 perfect。
|
||||
func _pin_beat(beat: int) -> void:
|
||||
var rhythm := root.get_node_or_null("RhythmManager")
|
||||
if rhythm == null:
|
||||
failures.append("RhythmManager autoload should exist for beat pinning")
|
||||
return
|
||||
rhythm.set("running", true)
|
||||
var beat_time := float(rhythm.get("beat_time"))
|
||||
var beat_offset := float(rhythm.get("beat_offset"))
|
||||
rhythm.set("_clock_offset", float(beat) * beat_time - beat_offset - Time.get_ticks_msec() / 1000.0)
|
||||
|
||||
|
||||
func _expect_bool(actual: bool, expected: bool, label: String) -> void:
|
||||
if actual != expected:
|
||||
failures.append("%s: expected %s, got %s" % [label, expected, actual])
|
||||
|
||||
|
||||
func _fixture() -> Dictionary:
|
||||
var input_script: Script = load("res://scenes/components/input_component.gd")
|
||||
var combo_script: Script = load("res://scenes/components/combo_window.gd")
|
||||
var resolver_script: Script = load("res://scenes/combat/action_resolver.gd")
|
||||
var state_script: Script = load("res://scenes/components/state_machine.gd")
|
||||
var energy_script: Script = load("res://scenes/components/energy_component.gd")
|
||||
var controller_script: Script = load("res://scenes/components/action_controller.gd")
|
||||
if input_script == null or combo_script == null or resolver_script == null or state_script == null or energy_script == null or controller_script == null:
|
||||
failures.append("Phase 3 input/action scripts should load")
|
||||
return {}
|
||||
var fixture_root := Node.new()
|
||||
root.add_child(fixture_root)
|
||||
var input_component: Node = input_script.new()
|
||||
input_component.name = "InputComponent"
|
||||
fixture_root.add_child(input_component)
|
||||
var combo: Node = combo_script.new()
|
||||
combo.name = "ComboWindow"
|
||||
fixture_root.add_child(combo)
|
||||
var resolver: Node = resolver_script.new()
|
||||
resolver.name = "ActionResolver"
|
||||
fixture_root.add_child(resolver)
|
||||
var state: Node = state_script.new()
|
||||
state.name = "StateMachine"
|
||||
fixture_root.add_child(state)
|
||||
var energy: Node = energy_script.new()
|
||||
energy.name = "EnergyComponent"
|
||||
fixture_root.add_child(energy)
|
||||
var controller: Node = controller_script.new()
|
||||
controller.name = "ActionController"
|
||||
controller.set("combo_window_path", NodePath("../ComboWindow"))
|
||||
controller.set("action_resolver_path", NodePath("../ActionResolver"))
|
||||
controller.set("state_machine_path", NodePath("../StateMachine"))
|
||||
fixture_root.add_child(controller)
|
||||
await process_frame
|
||||
energy.call("set_values", 99, 99)
|
||||
return {
|
||||
"root": fixture_root,
|
||||
"input": input_component,
|
||||
"combo": combo,
|
||||
"controller": controller,
|
||||
}
|
||||
|
||||
|
||||
func _key_event(key: Key) -> InputEventKey:
|
||||
var event := InputEventKey.new()
|
||||
event.keycode = key
|
||||
event.physical_keycode = key
|
||||
event.pressed = true
|
||||
return event
|
||||
|
||||
|
||||
func _on_action_started(action: Resource, _intent) -> void:
|
||||
started.append(StringName(str(action.get("id"))))
|
||||
|
||||
|
||||
func _expect_array(actual: Array, expected: Array, label: String) -> void:
|
||||
if actual != expected:
|
||||
failures.append("%s: expected %s, got %s" % [label, expected, actual])
|
||||
|
||||
|
||||
func _expect_string(actual: String, expected: String, label: String) -> void:
|
||||
if actual != expected:
|
||||
failures.append("%s: expected %s, got %s" % [label, expected, actual])
|
||||
|
||||
|
||||
func _finish() -> void:
|
||||
if failures.is_empty():
|
||||
print("PASS player combo input")
|
||||
quit(0)
|
||||
else:
|
||||
for failure: String in failures:
|
||||
push_error(failure)
|
||||
quit(1)
|
||||
Reference in New Issue
Block a user