extends RefCounted ## Tests for DropTable — flat 25% drop chance, pool selection. var _test_failures := 0 func test_no_pool_no_drop() -> void: var dt := DropTable.new() var result := dt.roll_drop("unknown_boss") assert_false(result["dropped"], "No pool = no drop") assert_eq_str(result["artifact_id"], "", "Artifact id should be empty") func test_zero_chance_never_drops() -> void: var dt := DropTable.new() dt.register_boss_pool("boss_a", ["art_1", "art_2"]) dt.set_drop_chance(0.0) for i in 100: var result := dt.roll_drop("boss_a") assert_false(result["dropped"], "0%% chance should never drop (iter %d)" % i) func test_full_chance_always_drops() -> void: var dt := DropTable.new() dt.register_boss_pool("boss_a", ["art_1", "art_2"]) dt.set_drop_chance(1.0) for i in 100: var result := dt.roll_drop("boss_a") assert_true(result["dropped"], "100%% chance should always drop (iter %d)" % i) assert_false(result["artifact_id"].is_empty(), "Dropped artifact should have an id") func test_drops_only_from_registered_pool() -> void: var dt := DropTable.new() var pool: Array[String] = ["art_1", "art_2", "art_3"] dt.register_boss_pool("boss_a", pool) dt.set_drop_chance(1.0) for i in 50: var result := dt.roll_drop("boss_a") assert_true(pool.has(result["artifact_id"]), "Dropped artifact should be from the pool (got %s)" % result["artifact_id"]) func test_default_chance_is_25_percent() -> void: var dt := DropTable.new() assert_almost(dt.get_drop_chance(), 0.25, "Default drop chance should be 25%") func test_statistical_rate_near_25_percent() -> void: # With a seeded RNG the rate should land in a reasonable band around 25%. var dt := DropTable.new() dt.register_boss_pool("boss_a", ["art_1"]) var rng := RandomNumberGenerator.new() rng.seed = 12345 var drops := 0 var trials := 10000 for i in trials: if dt.roll_drop("boss_a", rng)["dropped"]: drops += 1 var rate := float(drops) / float(trials) assert_true(rate > 0.2 and rate < 0.3, "Drop rate over %d trials should be near 25%% (got %.1f%%)" % [trials, rate * 100.0]) # --- 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_str(value: String, expected: String, msg: String = "") -> void: if value != expected: _test_failures += 1 push_error("ASSERT FAILED ['%s' != '%s']: %s" % [value, expected, msg]) func assert_almost(value: float, expected: float, msg: String = "", tolerance: float = 0.01) -> void: if abs(value - expected) > tolerance: _test_failures += 1 push_error("ASSERT FAILED [%f != %f]: %s" % [value, expected, msg])