extends RefCounted ## Tests for ComboState — positional validation and combo breaks. var _test_failures := 0 func test_combo_advances_on_correct_positional() -> void: var hits := _make_warden_combo() var combo := ComboState.new(hits) # Hit 1 requires Front (positional = 1) var result := combo.advance(ComboState.Positional.FRONT) assert_true(result["success"], "Hit 1 should succeed with Front") assert_false(result["broke"], "Should not break") assert_eq(combo.current_hit, 1, "Should advance to hit 2") func test_combo_breaks_on_wrong_positional() -> void: var hits := _make_warden_combo() var combo := ComboState.new(hits) # Hit 1 requires Front (positional = 1), but player is at Back (2) var result := combo.advance(ComboState.Positional.BACK) assert_false(result["success"], "Hit 1 should fail with wrong positional") assert_true(result["broke"], "Combo should break") assert_eq(combo.current_hit, 0, "Should reset to hit 1 (index 0)") func test_combo_wraps_around() -> void: var hits := _make_warden_combo() var combo := ComboState.new(hits) # Go through all 3 hits combo.advance(ComboState.Positional.FRONT) # Hit 1 → advance to 2 combo.advance(ComboState.Positional.BACK) # Hit 2 → advance to 3 combo.advance(ComboState.Positional.NONE) # Hit 3 (any pos) → wrap to 1 assert_eq(combo.current_hit, 0, "Should wrap back to hit 1 after final hit") func test_no_positional_required_hits_any() -> void: var hits := _make_warden_combo() var combo := ComboState.new(hits) # Advance to Hit 3 (index 2) which has positional = NONE combo.advance(ComboState.Positional.FRONT) combo.advance(ComboState.Positional.BACK) # Hit 3 accepts any positional var result := combo.advance(ComboState.Positional.LEFT) assert_true(result["success"], "Hit 3 should succeed with any positional") func test_consecutive_breaks() -> void: var hits := _make_warden_combo() var combo := ComboState.new(hits) # Break twice in a row combo.advance(ComboState.Positional.BACK) # Wrong for hit 1 (needs Front) → break assert_eq(combo.current_hit, 0, "Reset to 0 after first break") combo.advance(ComboState.Positional.RIGHT) # Wrong again → break assert_eq(combo.current_hit, 0, "Still at 0 after second break") func test_ranged_combo_no_positionals() -> void: var hits := _make_arcanist_combo() var combo := ComboState.new(hits) # Arcanist combo has no positional requirements (all NONE) var result := combo.advance(ComboState.Positional.NONE) assert_true(result["success"], "Ranged hit 1 should succeed with no positional") result = combo.advance(ComboState.Positional.NONE) assert_true(result["success"], "Ranged hit 2 should succeed with no positional") # Wraps around assert_eq(combo.current_hit, 0, "Should wrap to start") # --- Helpers --- func _make_warden_combo() -> Array[Dictionary]: return [ {"name": "Shield Bash", "positional": ComboState.Positional.FRONT, "damage_mult": 0.8}, {"name": "Guard Break", "positional": ComboState.Positional.BACK, "damage_mult": 0.9}, {"name": "Avalanche Strike", "positional": ComboState.Positional.NONE, "damage_mult": 1.4} ] func _make_arcanist_combo() -> Array[Dictionary]: return [ {"name": "Firebolt", "positional": ComboState.Positional.NONE, "damage_mult": 1.0}, {"name": "Arcane Lance", "positional": ComboState.Positional.NONE, "damage_mult": 1.4} ] # --- Assertions --- func assert_true(condition: bool, msg: String = "") -> void: if not condition: _test_failures += 1 push_error("ASSERT FAILED [true]: " + msg) func assert_false(condition: bool, msg: String = "") -> void: if condition: _test_failures += 1 push_error("ASSERT FAILED [false]: " + msg) func assert_eq(value: int, expected: int, msg: String = "") -> void: if value != expected: _test_failures += 1 push_error("ASSERT FAILED [%d != %d]: %s" % [value, expected, msg])