extends RefCounted ## Tests for DamageCalculator — mitigation stacking formula. var _test_failures := 0 func test_no_mitigation_full_damage() -> void: var result := DamageCalculator.calculate(100.0, []) assert_almost(result["final_damage"], 100.0, "No mitigation = full damage") func test_single_mitigation() -> void: var mits: Array = [DamageCalculator.Mitigation.new(0.5, 0.0, "Test Mit")] var result := DamageCalculator.calculate(100.0, mits) assert_almost(result["final_damage"], 50.0, "50% mit on 100 = 50") func test_sequential_mitigation_highest_first() -> void: # 100 damage with 60%, 40%, 20% mitigations # Sorted: 60%, 40%, 20% # 100 * 0.4 = 40 # 40 * 0.6 = 24 # 24 * 0.8 = 19.2 var mits: Array = [ DamageCalculator.Mitigation.new(0.2, 0.0, "Weaken"), DamageCalculator.Mitigation.new(0.6, 0.0, "Mitigate"), DamageCalculator.Mitigation.new(0.4, 0.0, "Sanctuary") ] var result := DamageCalculator.calculate(100.0, mits) assert_almost(result["final_damage"], 19.2, "Sequential: 100→40→24→19.2", 0.1) func test_mitigation_order_independent_of_input() -> void: # Same mitigations, different input order — should get same result var mits_a: Array = [ DamageCalculator.Mitigation.new(0.6, 0.0, "A"), DamageCalculator.Mitigation.new(0.4, 0.0, "B"), DamageCalculator.Mitigation.new(0.2, 0.0, "C") ] var mits_b: Array = [ DamageCalculator.Mitigation.new(0.2, 0.0, "C"), DamageCalculator.Mitigation.new(0.4, 0.0, "B"), DamageCalculator.Mitigation.new(0.6, 0.0, "A") ] var result_a := DamageCalculator.calculate(100.0, mits_a) var result_b := DamageCalculator.calculate(100.0, mits_b) assert_almost(result_a["final_damage"], result_b["final_damage"], "Order should not matter (sorted internally)") func test_absorb_applied_after_percent() -> void: # 100 damage, 50% mit, then 20 flat absorb # 100 * 0.5 = 50, then 50 - 20 = 30 var mits: Array = [ DamageCalculator.Mitigation.new(0.5, 20.0, "Combined") ] var result := DamageCalculator.calculate(100.0, mits) assert_almost(result["final_damage"], 30.0, "50% then -20 absorb = 30") func test_never_reaches_zero_from_percent_stacking() -> void: # Even with multiple high % mitigations, should not hit 0 var mits: Array = [ DamageCalculator.Mitigation.new(0.9, 0.0, "M1"), DamageCalculator.Mitigation.new(0.9, 0.0, "M2"), DamageCalculator.Mitigation.new(0.9, 0.0, "M3") ] var result := DamageCalculator.calculate(100.0, mits) assert_true(result["final_damage"] > 0.0, "Percent stacking should never reach 0 (got %f)" % result["final_damage"]) func test_absorb_can_reach_zero() -> void: # Absorb CAN bring damage to 0 var mits: Array = [ DamageCalculator.Mitigation.new(0.0, 100.0, "Full Absorb") ] var result := DamageCalculator.calculate(50.0, mits) assert_almost(result["final_damage"], 0.0, "Absorb > damage = 0") func test_vulnerability_increases_damage() -> void: var damaged := DamageCalculator.apply_vulnerability(100.0, 0.15) assert_almost(damaged, 115.0, "+15% vulnerability on 100 = 115") func test_weakness_reduces_damage() -> void: var damaged := DamageCalculator.apply_weakness(100.0, 0.25) assert_almost(damaged, 75.0, "-25% weakness on 100 = 75") # --- Assertions --- func assert_true(condition: bool, msg: String = "") -> void: if not condition: _test_failures += 1 push_error("ASSERT FAILED [true]: " + msg) func assert_almost(value: float, expected: float, msg: String = "", tolerance: float = 0.1) -> void: if abs(value - expected) > tolerance: _test_failures += 1 push_error("ASSERT FAILED [%f != %f (tol %f)]: %s" % [value, expected, tolerance, msg])