163 lines
5.8 KiB
GDScript
163 lines
5.8 KiB
GDScript
extends SceneTree
|
|
|
|
var failures: Array[String] = []
|
|
var started: Array[StringName] = []
|
|
var rejected: Array[StringName] = []
|
|
var judged: Array[Dictionary] = []
|
|
|
|
|
|
func _init() -> void:
|
|
_run.call_deferred()
|
|
|
|
|
|
func _run() -> void:
|
|
var fixture := await _controller_fixture()
|
|
if fixture.is_empty():
|
|
_finish()
|
|
return
|
|
var controller: Node = fixture["controller"]
|
|
var combo: Node = fixture["combo"]
|
|
controller.connect("action_started", _on_action_started)
|
|
controller.connect("action_rejected", _on_action_rejected)
|
|
_event_bus().connect("judgement_made", _on_judgement_made)
|
|
|
|
controller.call("submit_intent", _intent(&"A", &"a", &"pressed", "perfect", 12))
|
|
_expect_array(combo.call("get_slots"), [&"A"], "A should enter ComboWindow")
|
|
_expect_string(str(started[started.size() - 1]), "ground_attack_left_1", "A should start left attack")
|
|
_expect_int(int(judged[judged.size() - 1].get("beat")), 12, "judgement_made should include nearest beat")
|
|
|
|
controller.call("_reset_to_idle")
|
|
controller.call("submit_intent", _intent(&"A", &"a", &"pressed", "perfect", 13))
|
|
_expect_array(combo.call("get_slots"), [&"A", &"A"], "second A should stay in ComboWindow")
|
|
_expect_string(str(started[started.size() - 1]), "ground_attack_left_2", "AA should start left second attack")
|
|
|
|
controller.call("_reset_to_idle")
|
|
combo.call("clear", &"test-reset")
|
|
started.clear()
|
|
controller.call("submit_intent", _intent(&"D", &"d", &"pressed", "miss", 14))
|
|
_expect_array(combo.call("get_slots"), [&"Ø"], "miss should record explicit miss placeholder")
|
|
_expect_bool(started.is_empty(), true, "miss should not start an action")
|
|
_expect_bool(rejected.has(&"miss"), true, "miss should reject with miss reason")
|
|
|
|
var context: Dictionary = controller.call("_resolver_context")
|
|
_expect(context.has("burst_action_id"), "resolver context should expose burst_action_id")
|
|
_expect(context.has("counter_action_id"), "resolver context should expose counter_action_id")
|
|
_expect(context.has("blade_chain_action_id"), "resolver context should expose blade_chain_action_id")
|
|
fixture["root"].free()
|
|
_finish()
|
|
|
|
|
|
func _controller_fixture() -> Dictionary:
|
|
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")
|
|
for item: Dictionary in [
|
|
{"script": combo_script, "label": "ComboWindow"},
|
|
{"script": resolver_script, "label": "ActionResolver"},
|
|
{"script": state_script, "label": "StateMachine"},
|
|
{"script": energy_script, "label": "EnergyComponent"},
|
|
{"script": controller_script, "label": "ActionController"},
|
|
]:
|
|
_expect(item["script"] != null, "%s script should load" % item["label"])
|
|
if combo_script == null or resolver_script == null or state_script == null or energy_script == null or controller_script == null:
|
|
return {}
|
|
|
|
var fixture_root := Node.new()
|
|
root.add_child(fixture_root)
|
|
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,
|
|
"combo": combo,
|
|
"controller": controller,
|
|
}
|
|
|
|
|
|
func _intent(symbol: StringName, rhythm_action: StringName, event_type: StringName, label: String, beat: int) -> RefCounted:
|
|
var intent_script: Script = load("res://scenes/components/input_intent.gd")
|
|
var intent: RefCounted = intent_script.call("create", symbol, rhythm_action, event_type, float(Time.get_ticks_msec()))
|
|
intent.set("judgement", {
|
|
"label": label,
|
|
"diff": 0.0,
|
|
"abs_diff": 0.0,
|
|
"nearest_beat": beat,
|
|
})
|
|
return intent
|
|
|
|
|
|
func _on_action_started(action: Resource, _intent) -> void:
|
|
started.append(StringName(str(action.get("id"))))
|
|
|
|
|
|
func _on_action_rejected(_intent, reason: StringName) -> void:
|
|
rejected.append(reason)
|
|
|
|
|
|
func _on_judgement_made(quality: StringName, offset_ms: float, beat_index: int) -> void:
|
|
judged.append({"quality": quality, "offset_ms": offset_ms, "beat": beat_index})
|
|
|
|
|
|
func _event_bus() -> Node:
|
|
var bus := root.get_node_or_null("EventBus")
|
|
if bus == null:
|
|
bus = load("res://autoload/event_bus.gd").new()
|
|
bus.name = "EventBus"
|
|
root.add_child(bus)
|
|
return bus
|
|
|
|
|
|
func _expect(condition: bool, label: String) -> void:
|
|
if not condition:
|
|
failures.append(label)
|
|
|
|
|
|
func _expect_bool(actual: bool, expected: bool, label: String) -> void:
|
|
if actual != expected:
|
|
failures.append("%s: expected %s, got %s" % [label, expected, actual])
|
|
|
|
|
|
func _expect_int(actual: int, expected: int, label: String) -> void:
|
|
if actual != expected:
|
|
failures.append("%s: expected %d, got %d" % [label, expected, actual])
|
|
|
|
|
|
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 action controller flow")
|
|
quit(0)
|
|
else:
|
|
for failure: String in failures:
|
|
push_error(failure)
|
|
quit(1)
|
|
|