156 lines
5.6 KiB
GDScript
156 lines
5.6 KiB
GDScript
extends SceneTree
|
|
## Editor-Test (headless): Werkzeuge laufen ohne Maus. Dieselben Züge einmal als
|
|
## echte Maus-Ereignisse und einmal als PointerEvent müssen dieselbe Karte ergeben.
|
|
## Ausführen: godot --headless --path . --script res://tests/test_pointer_router.gd
|
|
|
|
const OID := "box_001"
|
|
|
|
var failures := 0
|
|
var checks := 0
|
|
|
|
|
|
func _initialize() -> void:
|
|
_run.call_deferred()
|
|
|
|
|
|
func _check(cond: bool, label: String) -> void:
|
|
checks += 1
|
|
if cond:
|
|
print(" ok: ", label)
|
|
else:
|
|
failures += 1
|
|
print(" FAIL: ", label)
|
|
|
|
|
|
func _run() -> void:
|
|
print("== Objekte: Maus gegen PointerEvent ==")
|
|
var maus := await _objekt_zuege(true)
|
|
var zeiger := await _objekt_zuege(false)
|
|
# Neue Karten spiegeln: jedes gesetzte Objekt bekommt einen Zwilling.
|
|
_check(int(zeiger["anzahl"]) == 4, "Zeiger: zwei Objekte mit Zwillingen gesetzt (%d)" % int(zeiger["anzahl"]))
|
|
_check(int(zeiger["verschoben"]) == 1, "Zeiger: Ziehen hat ein Objekt bewegt")
|
|
_check(int(zeiger["undo"]) == 3, "Zeiger: drei Undo-Schritte")
|
|
_check(str(maus["objekte"]) == str(zeiger["objekte"]), "Maus und Zeiger ergeben dieselben Objekte")
|
|
_check(int(maus["undo"]) == int(zeiger["undo"]), "Maus und Zeiger ergeben gleich viele Undo-Schritte")
|
|
|
|
print("== Terrain und Abbruch ==")
|
|
await _terrain_und_abbruch()
|
|
|
|
if failures == 0:
|
|
print("ALL OK: %d checks" % checks)
|
|
quit(0)
|
|
else:
|
|
print("FAILED: %d/%d checks" % [failures, checks])
|
|
quit(1)
|
|
|
|
|
|
func _neu() -> Control:
|
|
var main: Control = (load("res://core/scenes/main.tscn") as PackedScene).instantiate()
|
|
root.add_child(main)
|
|
await process_frame
|
|
await process_frame
|
|
main.camera.global_position = Vector3(0.0, 200.0, 0.0)
|
|
main.camera.look_at(Vector3.ZERO, Vector3.FORWARD)
|
|
main.call("_set_tool_mode", "objects")
|
|
await process_frame
|
|
return main
|
|
|
|
|
|
func _bild(main: Control, welt: Vector3) -> Vector2:
|
|
return main.camera.unproject_position(welt)
|
|
|
|
|
|
## Ein Zug: als Mausereignis durch den echten Viewport oder direkt über den Router.
|
|
func _zug(main: Control, ueber_maus: bool, kind: PointerEvent.Kind, pos: Vector2, shift := false) -> void:
|
|
if ueber_maus:
|
|
var ev: InputEvent
|
|
if kind == PointerEvent.Kind.MOVE:
|
|
var mm := InputEventMouseMotion.new()
|
|
mm.position = pos
|
|
ev = mm
|
|
else:
|
|
var mb := InputEventMouseButton.new()
|
|
mb.position = pos
|
|
mb.button_index = MOUSE_BUTTON_LEFT
|
|
mb.pressed = kind == PointerEvent.Kind.PRESS
|
|
mb.shift_pressed = shift
|
|
ev = mb
|
|
main.camera.get_viewport().push_input(ev)
|
|
else:
|
|
var p := PointerEvent.make(kind, pos)
|
|
p.shift = shift
|
|
PointerRouter.dispatch(main, p)
|
|
await process_frame
|
|
|
|
|
|
func _objekt_zuege(ueber_maus: bool) -> Dictionary:
|
|
var main := await _neu()
|
|
var davor: int = (main.doc.get("objects", []) as Array).size()
|
|
var a := Vector3(10.0, 0.0, 10.0)
|
|
var b := Vector3(-20.0, 0.0, 5.0)
|
|
var ziel := Vector3(30.0, 0.0, -15.0)
|
|
# Serienmodus: Umschalt setzt und behält das Objekt in der Hand.
|
|
main.interaction.start_placement(OID)
|
|
await _zug(main, ueber_maus, PointerEvent.Kind.MOVE, _bild(main, a))
|
|
await _zug(main, ueber_maus, PointerEvent.Kind.PRESS, _bild(main, a), true)
|
|
await _zug(main, ueber_maus, PointerEvent.Kind.RELEASE, _bild(main, a), true)
|
|
await _zug(main, ueber_maus, PointerEvent.Kind.MOVE, _bild(main, b))
|
|
await _zug(main, ueber_maus, PointerEvent.Kind.PRESS, _bild(main, b))
|
|
await _zug(main, ueber_maus, PointerEvent.Kind.RELEASE, _bild(main, b))
|
|
main.interaction.cancel_placement()
|
|
main.interaction.clear_selection()
|
|
await process_frame
|
|
var objekte: Array = main.doc.get("objects", [])
|
|
var vorher: Array = (objekte[0]["pos"] as Array).duplicate()
|
|
# Greifen, ziehen, loslassen. Die Mitte des Objekts statt des Gizmos treffen:
|
|
# ohne Auswahl ist das Gizmo unsichtbar.
|
|
var start := Vector3(float(vorher[0]), float(vorher[1]), float(vorher[2]))
|
|
await _zug(main, ueber_maus, PointerEvent.Kind.PRESS, _bild(main, start))
|
|
main.gizmo.visible = false
|
|
await _zug(main, ueber_maus, PointerEvent.Kind.MOVE, _bild(main, ziel))
|
|
await _zug(main, ueber_maus, PointerEvent.Kind.RELEASE, _bild(main, ziel))
|
|
var ergebnis := {
|
|
"anzahl": objekte.size() - davor,
|
|
"objekte": JSON.stringify(objekte),
|
|
"verschoben": 1 if str(objekte[0]["pos"]) != str(vorher) else 0,
|
|
"undo": (main.history.index + 1),
|
|
}
|
|
main.queue_free()
|
|
await process_frame
|
|
return ergebnis
|
|
|
|
|
|
func _terrain_und_abbruch() -> void:
|
|
var main := await _neu()
|
|
main.call("_set_tool_mode", "terrain")
|
|
await process_frame
|
|
var hoehen_vorher := str(main.doc["terrain"].get("heightmap_r16_b64", ""))
|
|
var undo_vorher: int = main.history.index + 1
|
|
var mitte := _bild(main, Vector3.ZERO)
|
|
_check(not PointerRouter.dispatch(main, PointerEvent.make(PointerEvent.Kind.MOVE, mitte)),
|
|
"Terrain: Bewegen wird nicht verschluckt")
|
|
_check(PointerRouter.dispatch(main, PointerEvent.make(PointerEvent.Kind.PRESS, mitte)),
|
|
"Terrain: Drücken nimmt der Pinsel")
|
|
for i in 6:
|
|
await process_frame
|
|
_check(PointerRouter.dispatch(main, PointerEvent.make(PointerEvent.Kind.RELEASE, mitte)),
|
|
"Terrain: Loslassen nimmt der Pinsel")
|
|
await process_frame
|
|
_check(str(main.doc["terrain"].get("heightmap_r16_b64", "")) != hoehen_vorher, "Terrain: Strich ändert die Höhen")
|
|
_check((main.history.index + 1) == undo_vorher + 1, "Terrain: ein Strich, ein Undo-Schritt")
|
|
|
|
main.call("_set_tool_mode", "objects")
|
|
await process_frame
|
|
_check(not PointerRouter.dispatch(main, PointerEvent.make(PointerEvent.Kind.CANCEL)),
|
|
"Esc ohne laufenden Zug bleibt frei fürs Hauptfenster")
|
|
var ohne := MousePointer.from_input(_rechts())
|
|
_check(ohne == null, "Rechte Maustaste bleibt beim Flug")
|
|
main.queue_free()
|
|
await process_frame
|
|
|
|
|
|
func _rechts() -> InputEvent:
|
|
var mb := InputEventMouseButton.new()
|
|
mb.button_index = MOUSE_BUTTON_RIGHT
|
|
mb.pressed = true
|
|
return mb
|