Files
Blockline/tests/map_weapon_corrections.gd
T

87 lines
4.0 KiB
GDScript
Raw Normal View History

2026-09-12 11:01:37 +02:00
extends SceneTree
var failed := 0
var total := 0
func _initialize() -> void: call_deferred("run")
func check(ok: bool, title: String) -> void:
total += 1
if not ok: failed += 1
print("PASS " if ok else "FAIL ", title)
func run() -> void:
var game: Node3D = load("res://scenes/main.tscn").instantiate()
root.add_child(game)
game.port = 27986
game.host({"server_name":"Foundation", "mode":"FFA", "max_players":2, "bots":0, "score_limit":100, "time_limit":600})
game.set_physics_process(false)
var p: Fighter = game.players[1]
p.set_physics_process(false)
p.set_process(false)
p.reset_at(Vector3(0, 0.1, 28))
check(p.shape.shape is CapsuleShape3D and p.find_children("*", "CollisionShape3D", true, false).size() == 1, "fighter exclusively uses one capsule")
var scans: Node = game.arena.get_node("QuarryRouteRocks")
var bodies: Array[Node] = scans.find_children("*", "StaticBody3D", true, false)
check(bodies.size() == 2, "two scan mesh colliders restored on headless host")
var boxes := true
for body in bodies: boxes = boxes and body.get_child(0).shape is ConcavePolygonShape3D
check(boxes, "route scans use original triangle collision")
var motion := InputEventMouseMotion.new()
motion.screen_relative = Vector2(20, -10)
motion.relative = Vector2(999,999)
FpsCamera.mouse(p, motion, 0.001)
check(is_equal_approx(p.yaw, -0.02) and is_equal_approx(p.pitch, 0.01), "raw screen-relative mouse input applied without smoothing")
p.view_controller.begin_physics(p)
var body_position := p.global_position
for rate in [60, 120, 144, 240]:
p.view_controller.render(p, 1.0 / rate)
check(p.global_position == body_position and is_equal_approx(p.camera.global_rotation.y, p.yaw), "camera render preserves physics and direct yaw at %s Hz" % rate)
game.headless = false
game.hud.hit_time = 0
game.shot_event(p.eye(), p.peer_id, p.weapon, false, false)
check(p.gun.muzzle.visible and p.gun.recoil_velocity.length() > 0 and game.hud.shot_time > 0, "single shot event immediately starts flash, recoil and crosshair pulse")
check(game.hud.hit_time == 0, "miss does not fake a hitmarker")
var recoil_before: Vector2 = p.gun.recoil_velocity
p.magazines[p.weapon] -= 1
p.shot_serial += 1
p.gun.animate(p, 0.0)
check(p.gun.recoil_velocity.is_equal_approx(recoil_before), "snapshot ammo change does not duplicate recoil")
game.shot_event(p.eye(), p.peer_id, p.weapon, true, true)
check(game.hud.hit_time > 0 and game.hud.head_hit, "confirmed headshot adds hit feedback in same event")
for index in 10:
p.weapon = index
p.skin_designs[str(index)] = {"attachments": {"optic":1}}
p.gun.select_weapon(index)
p.gun.previous_life = p.life
WeaponSkins.apply(p.gun.model, p.skin_designs[str(index)])
p.aim_blend = 1
p.gun.position = Vector3(0.18,-0.22,-0.43)
p.gun.animate(p, 0.016)
var lens: Node3D = p.gun.model.get_node("Bolt/SlideAttachments/Reflex/Lens" if index == 4 else "Attachments/Reflex/Lens")
var center := p.camera.to_local(lens.global_position)
check(Vector2(center.x, center.y).length() < 0.0001, "reflex center on optical axis weapon %s" % index)
var guard_root := Node3D.new()
root.add_child(guard_root)
StairGuard.build(guard_root, 0, 0, 1, 0, 3.7)
var guard: StaticBody3D = guard_root.get_child(0)
check(guard.get_child_count() == 1 and guard.get_child(0).shape is ConvexPolygonShape3D, "guard is one continuous convex slope")
var walker := CharacterBody3D.new()
var capsule_shape := CollisionShape3D.new()
var capsule := CapsuleShape3D.new()
capsule.radius = 0.36
capsule.height = 1.8
capsule_shape.shape = capsule
walker.add_child(capsule_shape)
guard_root.add_child(walker)
await physics_frame
await physics_frame
var smooth := true
for step in 70:
var z := step * 0.1
walker.position = Vector3(0.425, 0.95 + z * 3.7 / 7.6, z)
smooth = smooth and not walker.test_move(walker.global_transform, Vector3(0, 0.1 * 3.7 / 7.6, 0.1))
check(smooth, "capsule sweeps along guard without catching seams")
guard_root.queue_free()
game.leave()
game.queue_free()
await process_frame
print("CORRECTIONS RESULT ", total - failed, "/", total)
quit(1 if failed else 0)