# test_mob_drop_scatter_parity.gd —— 怪物死亡爆装与辐射散射 1:1 回归测试套件 # 运行方式: # /opt/homebrew/bin/godot --headless --path metin2-client/project --script test_mob_drop_scatter_parity.gd extends SceneTree const MobDropScatter = preload("res://mob_drop_scatter.gd") var _passed := 0 var _failed := 0 func _assert(cond: bool, msg: String) -> void: if cond: _passed += 1 print(" [PASS] %s" % msg) else: _failed += 1 printerr(" [FAIL] %s" % msg) func _init() -> void: print("\n=== Running test_mob_drop_scatter_parity (Direction 2: Drop Scatter & Ownership 1:1) ===") _test_single_drop_offset() _test_multi_drop_radial_scatter() _test_ownership_protection_window() _test_drop_sounds() _finish() func _test_single_drop_offset() -> void: print("\n--- Test 1: Single Drop Offset [-1.4m, +1.4m] (char_battle.cpp) ---") for seed_val in range(10): var offset := MobDropScatter.calculate_scatter_offset(0, 1, seed_val) _assert(offset.x >= -1.4 and offset.x <= 1.4, "Seed %d dx within [-1.4m, 1.4m]" % seed_val) _assert(offset.z >= -1.4 and offset.z <= 1.4, "Seed %d dz within [-1.4m, 1.4m]" % seed_val) _assert(offset.y == 0.0, "Y offset is 0.0 (ground plane)") func _test_multi_drop_radial_scatter() -> void: print("\n--- Test 2: Multi-Item Boss Drop Radial Scatter ---") var center := Vector3(100.0, 10.0, 50.0) var count := 6 var positions: Array[Vector3] = [] for i in range(count): var pos := MobDropScatter.calculate_drop_position(center, i, count, 42) positions.append(pos) var dist := (pos - center).length() _assert(dist >= 0.95 and dist <= 1.75, "Item %d scatter radius is %.2fm (expected 1.0~1.6m)" % [i, dist]) # Check that items do not overlap (pairwise distance > 0.5m) for i in range(count): for j in range(i + 1, count): var d := (positions[i] - positions[j]).length() _assert(d > 0.5, "Item %d and %d separated by %.2fm (no pile overlap)" % [i, j, d]) func _test_ownership_protection_window() -> void: print("\n--- Test 3: 30-Second Ownership Protection Window ---") var drop_time := 100.0 _assert(MobDropScatter.is_ownership_protected(drop_time, 105.0) == true, "Protected at 5 seconds") _assert(MobDropScatter.remaining_ownership_time(drop_time, 105.0) == 25.0, "25 seconds remaining at 5s") _assert(MobDropScatter.is_ownership_protected(drop_time, 129.5) == true, "Protected at 29.5 seconds") _assert(MobDropScatter.is_ownership_protected(drop_time, 130.1) == false, "Expired after 30 seconds") _assert(MobDropScatter.remaining_ownership_time(drop_time, 135.0) == 0.0, "Remaining time clamped to 0 after expiry") func _test_drop_sounds() -> void: print("\n--- Test 4: Drop Sound Assignment ---") _assert(MobDropScatter.get_drop_sound(true) == "sound/ui/money.wav", "Gold drop uses money.wav") _assert(MobDropScatter.get_drop_sound(false) == "sound/ui/drop.wav", "Item drop uses drop.wav") func _finish() -> void: print("\n=== SUMMARY: %d passed, %d failed ===" % [_passed, _failed]) if _failed == 0: print("ALL TESTS PASSED!\n") quit(0) else: printerr("SOME TESTS FAILED!\n") quit(1)