478 lines
18 KiB
GDScript
478 lines
18 KiB
GDScript
extends SceneTree
|
||
## Editor-Test (headless): Einmal-Platzieren — Karte t_5e3e97c9 (U5).
|
||
## Ausführen: godot4 --headless --path . --script res://tests/test_place_once.gd
|
||
## Exit 0 = alles grün.
|
||
##
|
||
## Verhalten (Spec KARTEN_UI_UNITY_vc27, Karte U5):
|
||
## 1. EIN Klick setzt EIN Objekt → Platzier-Werkzeug AUS, Ghost/Panel-
|
||
## Auswahl zurück, das NEUE Objekt ist ausgewählt (Index korrekt,
|
||
## Baum + Inspektor aktualisiert), Statuszeile „Objekt gesetzt…“.
|
||
## 2. Shift beim Klick = Serienmodus: Werkzeug bleibt AN, weitere Klicks
|
||
## setzen mehrfach; die Statuszeile zeigt „Serienmodus (Shift)“.
|
||
## 3. ESC / Rechtsklick bricht ab OHNE zu setzen.
|
||
## 4. EIN Undo entfernt genau das eine gesetzte Objekt.
|
||
## 5. Auswahl bleibt stabil: kein zweites Auswahl-Ereignis für dasselbe
|
||
## Objekt (Baum-Markierung nur 1×).
|
||
##
|
||
## Instanziert die ECHTE Main-UI (Muster tests/test_rmb_vorrang.gd) und
|
||
## schickt echte Maus-/Tastatur-Events durch den SubViewport; die direkten
|
||
## interaction-Pfade laufen gegen ein Probe-Main wie tests/test_shortcuts.gd.
|
||
|
||
var failures := 0
|
||
var checks := 0
|
||
var main: Control
|
||
var subvp: SubViewport
|
||
var selected_count := 0
|
||
|
||
# Zähler für die Auswahl-Stabilität (Baum-Markierungen).
|
||
var tree_marks := 0
|
||
|
||
|
||
func _initialize() -> void:
|
||
_run_all.call_deferred()
|
||
|
||
|
||
func _run_all() -> void:
|
||
_clear_library_cfg_section()
|
||
var scene: PackedScene = load("res://core/scenes/main.tscn")
|
||
_check(scene != null, "scenes/main.tscn laedt")
|
||
if scene == null:
|
||
_finish()
|
||
return
|
||
main = scene.instantiate()
|
||
root.add_child(main)
|
||
await process_frame
|
||
await process_frame
|
||
var container := _find_viewport_container(main)
|
||
_check(container != null, "SubViewportContainer gefunden")
|
||
if container == null:
|
||
_finish()
|
||
return
|
||
subvp = container.get_child(0) as SubViewport
|
||
_check(subvp != null, "SubViewport gefunden")
|
||
|
||
await _test_ein_klick_setzt_einmal()
|
||
await _test_serienmodus_shift()
|
||
await _test_esc_bricht_ab()
|
||
await _test_rechtsklick_laesst_nicht_fallen()
|
||
_test_undo_genau_eins()
|
||
_test_probe_auswahl_stabil()
|
||
|
||
_finish()
|
||
|
||
|
||
func _finish() -> void:
|
||
if failures == 0:
|
||
print("ALL OK: %d checks" % checks)
|
||
quit(0)
|
||
else:
|
||
print("FAILED: %d/%d checks" % [failures, checks])
|
||
quit(1)
|
||
|
||
|
||
func _check(cond: bool, label: String) -> void:
|
||
checks += 1
|
||
if cond:
|
||
print(" ok: %s" % label)
|
||
else:
|
||
failures += 1
|
||
print(" FAIL: %s" % label)
|
||
|
||
|
||
func _find_viewport_container(node: Node) -> SubViewportContainer:
|
||
if node is SubViewportContainer:
|
||
return node as SubViewportContainer
|
||
for child in node.get_children():
|
||
var found := _find_viewport_container(child)
|
||
if found != null:
|
||
return found
|
||
return null
|
||
|
||
|
||
## Event-Position: Mitte des 3D-Viewports (SubViewport-Koordinaten) — trifft
|
||
## garantiert die Karte (K1d-Startansicht: 80 m über der Kartenmitte).
|
||
func _vp_center() -> Vector2:
|
||
return subvp.size * 0.5
|
||
|
||
|
||
func _mouse(button: MouseButton, pressed: bool, pos: Vector2, shift := false) -> void:
|
||
var mb := InputEventMouseButton.new()
|
||
mb.button_index = button
|
||
mb.pressed = pressed
|
||
mb.position = pos
|
||
mb.global_position = pos
|
||
mb.shift_pressed = shift
|
||
subvp.push_input(mb)
|
||
|
||
|
||
func _motion(pos: Vector2, shift := false) -> void:
|
||
var mm := InputEventMouseMotion.new()
|
||
mm.position = pos
|
||
mm.global_position = pos
|
||
mm.relative = Vector2.ZERO
|
||
mm.shift_pressed = shift
|
||
subvp.push_input(mm)
|
||
|
||
|
||
func _press_key(keycode: Key) -> InputEventKey:
|
||
var ev := InputEventKey.new()
|
||
ev.keycode = keycode
|
||
ev.physical_keycode = keycode
|
||
ev.pressed = true
|
||
return ev
|
||
|
||
|
||
func _objects() -> Array:
|
||
return main.doc.get("objects", []) as Array
|
||
|
||
|
||
func _first_oid() -> String:
|
||
var lib: Dictionary = main.manifest.get("objects", {})
|
||
for key in lib:
|
||
return str(key)
|
||
return ""
|
||
|
||
|
||
# ------------------------------------------------------------ 1 Ein Klick ----
|
||
|
||
func _test_ein_klick_setzt_einmal() -> void:
|
||
print("Testgruppe: Ein Klick setzt EIN Objekt und beendet das Werkzeug")
|
||
var oid := _first_oid()
|
||
_check(oid != "", "Manifest hat ein Objekt zum Platzieren")
|
||
if oid == "":
|
||
return
|
||
var interaction: Node = main.interaction
|
||
var assets: Node = main.assets_mod
|
||
selected_count = 0
|
||
assets.object_selected.connect(func(_s: String) -> void: selected_count += 1)
|
||
main.call("_set_tool_mode", "objects")
|
||
_check(int(_objects().size()) == 0, "Startzustand: leere Map (%d Objekte)" % _objects().size())
|
||
|
||
# Doppelklick auf die Kachel = Werkzeug-Start (U4-Vertrag).
|
||
assets._activate_tile(oid)
|
||
_check(bool(interaction.get("placing")), "Werkzeug läuft (Objekt in der Hand)")
|
||
_check(str(interaction.get("place_oid")) == oid, "place_oid stimmt (%s)" % str(interaction.get("place_oid")))
|
||
var placed_index := _objects().size()
|
||
_check(placed_index == 0, "nichts gesetzt vor dem Klick (Index des Neuen = %d)" % placed_index)
|
||
|
||
# EIN echter Linksklick in die Kartenmitte (ohne Shift).
|
||
var pos := _vp_center()
|
||
_motion(pos)
|
||
_mouse(MOUSE_BUTTON_LEFT, true, pos)
|
||
await process_frame
|
||
_mouse(MOUSE_BUTTON_LEFT, false, pos)
|
||
await process_frame
|
||
|
||
_check(_objects().size() == 1, "EIN Klick = EIN Objekt im doc (ist %d)" % _objects().size())
|
||
_check(not bool(interaction.get("placing")), "Werkzeug nach dem Setzen AUS")
|
||
_check(str(interaction.get("place_oid")) == "", "Ghost-Auftrag geleert (place_oid leer)")
|
||
_check(int(interaction.get("selected_index")) == placed_index,
|
||
"das neue Objekt ist ausgewählt (Index %d, ist %d)" % [placed_index, int(interaction.get("selected_index"))])
|
||
var indices_arr: Variant = interaction.get("selected_indices")
|
||
var indices: Array = indices_arr if indices_arr is Array else []
|
||
_check(indices == ([placed_index] as Array), "Auswahl-Liste = [neues Objekt] (ist %s)" % str(indices))
|
||
_check(str(assets.get("selected_oid")) == "", "Panel-Auswahl zurückgesetzt (Kachel leuchtet nicht mehr)")
|
||
_check(str(main.status_label.text).contains("ausgewählt"), "Statuszeile meldet Klartext („…ausgewählt“): %s" % str(main.status_label.text))
|
||
# Inspektor zeigt das neue Objekt.
|
||
var inspector: Node = main.get("inspector")
|
||
_check(inspector != null, "Inspektor vorhanden")
|
||
if inspector != null:
|
||
var head_txt: String = str(inspector.get("_head").text)
|
||
_check(not str(head_txt).is_empty() and head_txt != "Objekt-Inspektor",
|
||
"Inspektor zeigt das neue Objekt (%s)" % head_txt)
|
||
|
||
# Zweiter Klick NACH dem Ende: wählt nichts / setzt nichts (Werkzeug ist aus).
|
||
var before := _objects().size()
|
||
_mouse(MOUSE_BUTTON_LEFT, true, pos)
|
||
await process_frame
|
||
_mouse(MOUSE_BUTTON_LEFT, false, pos)
|
||
await process_frame
|
||
_check(_objects().size() == before, "Klick danach setzt NICHTS (Werkzeug beendet)")
|
||
_check(selected_count == 1, "Auswahl bleibt stabil: object_selected genau 1× (ist %d)" % selected_count)
|
||
await _cleanup_objects()
|
||
|
||
|
||
# ----------------------------------------------------------- 2 Serienmodus ----
|
||
|
||
func _test_serienmodus_shift() -> void:
|
||
print("Testgruppe: Shift = Serienmodus — Werkzeug bleibt AN, setzt mehrfach")
|
||
var oid := _first_oid()
|
||
var interaction: Node = main.interaction
|
||
var assets: Node = main.assets_mod
|
||
main.call("_set_tool_mode", "objects")
|
||
_check(int(_objects().size()) == 0, "Startzustand: leere Map (%d Objekte)" % _objects().size())
|
||
assets._activate_tile(oid)
|
||
_check(bool(interaction.get("placing")), "Werkzeug läuft")
|
||
|
||
var pos0 := _vp_center()
|
||
# Klickpunkte leicht streuen: der vp-Mittelpunkt liegt exakt auf der
|
||
# Symmetrie-Mitte (Standard Punktspiegelung) — dort wird absichtlich NICHT
|
||
# gedoppelt, der nächste Klick liegt daneben und zeigt den Zwilling.
|
||
# Für U5 unerheblich (beide Varianten setzen), aber die Zählung bleibt
|
||
# eindeutig, wenn jeder Klick off-center landet.
|
||
var pos := Vector2(pos0.x + 17.0, pos0.y + 11.0)
|
||
# Klick MIT Shift: Serienmodus — Werkzeug bleibt an, setzt trotzdem.
|
||
_motion(pos, true)
|
||
_mouse(MOUSE_BUTTON_LEFT, true, pos, true)
|
||
await process_frame
|
||
_mouse(MOUSE_BUTTON_LEFT, false, pos, true)
|
||
await process_frame
|
||
_check(_objects().size() == 1, "1. Klick (Shift) setzt genau eins (ist %d)" % _objects().size())
|
||
_check(bool(interaction.get("placing")), "SERIENMODUS: Werkzeug bleibt AN")
|
||
_check(str(main.status_label.text).contains("Serienmodus"), "Statuszeile nennt den Serienmodus: %s" % str(main.status_label.text))
|
||
_check(int(interaction.get("selected_index")) == 0, "Serienmodus: das neue Objekt ist ausgewählt (Index 0)")
|
||
|
||
# Weiterer Klick MIT Shift: setzt das zweite Objekt, Werkzeug bleibt an.
|
||
pos = Vector2(pos0.x - 23.0, pos0.y + 7.0)
|
||
_motion(pos, true)
|
||
_mouse(MOUSE_BUTTON_LEFT, true, pos, true)
|
||
await process_frame
|
||
_mouse(MOUSE_BUTTON_LEFT, false, pos, true)
|
||
await process_frame
|
||
_check(_objects().size() >= 2 and _objects().size() % 2 == 0, "1./2. Klick (Shift) setzen (mit Zwillingen: gerade Zahl — ist %d)" % _objects().size())
|
||
_check(bool(interaction.get("placing")), "Serienmodus läuft weiter (Werkzeug noch an)")
|
||
|
||
# Klick OHNE Shift in der Serie: beendet das Werkzeug (normales Ende).
|
||
pos = Vector2(pos0.x + 9.0, pos0.y - 13.0)
|
||
_motion(pos)
|
||
_mouse(MOUSE_BUTTON_LEFT, true, pos)
|
||
await process_frame
|
||
_mouse(MOUSE_BUTTON_LEFT, false, pos)
|
||
await process_frame
|
||
_check(_objects().size() == 3, "3. Klick (ohne Shift) setzt das dritte (ist %d)" % _objects().size())
|
||
_check(not bool(interaction.get("placing")), "ohne Shift: Werkzeug beendet sich")
|
||
await _cleanup_objects()
|
||
|
||
|
||
# ------------------------------------------------------------- 3 ESC bricht ----
|
||
|
||
func _test_esc_bricht_ab() -> void:
|
||
print("Testgruppe: ESC bricht ab — nichts wird gesetzt")
|
||
var oid := _first_oid()
|
||
var interaction: Node = main.interaction
|
||
var assets: Node = main.assets_mod
|
||
main.call("_set_tool_mode", "objects")
|
||
_check(int(_objects().size()) == 0, "Startzustand: leere Map (%d Objekte)" % _objects().size())
|
||
assets._activate_tile(oid)
|
||
_check(bool(interaction.get("placing")), "Werkzeug läuft")
|
||
|
||
var pos := _vp_center()
|
||
_motion(pos)
|
||
main._unhandled_key_input(_press_key(KEY_ESCAPE))
|
||
await process_frame
|
||
_check(not bool(interaction.get("placing")), "ESC beendet das Werkzeug")
|
||
_check(_objects().size() == 0, "ESC hat NICHTS gesetzt (ist %d)" % _objects().size())
|
||
_check(str(interaction.get("place_oid")) == "", "Ghost-Auftrag weg")
|
||
_check(str(main.status_label.text).contains("abgebrochen") or str(main.status_label.text).contains("Bereit"),
|
||
"Statuszeile meldet den Abbruch (%s)" % str(main.status_label.text))
|
||
await _cleanup_objects()
|
||
|
||
|
||
# ------------------------------------------------------- 4 Rechtsklick = Flug ----
|
||
|
||
func _test_rechtsklick_laesst_nicht_fallen() -> void:
|
||
print("Testgruppe: Rechtsklick beim Platzieren = Flug (Objekt bleibt, RMB setzt NICHT)")
|
||
var oid := _first_oid()
|
||
var interaction: Node = main.interaction
|
||
var assets: Node = main.assets_mod
|
||
var camera: Camera3D = main.get("camera")
|
||
main.call("_set_tool_mode", "objects")
|
||
assets._activate_tile(oid)
|
||
_check(bool(interaction.get("placing")), "Werkzeug läuft")
|
||
|
||
var pos := _vp_center()
|
||
_motion(pos)
|
||
# Rechte Maustaste hat absoluten Vorrang (t_2470de89): Flug, kein Setzen,
|
||
# kein Abbruch.
|
||
_mouse(MOUSE_BUTTON_RIGHT, true, pos)
|
||
await process_frame
|
||
_check(bool(camera.call("fly_active")), "RMB → Flug an (Vorrang)")
|
||
_check(bool(interaction.get("placing")), "Objekt bleibt in der Hand (kein Abbruch, kein Setzen)")
|
||
_mouse(MOUSE_BUTTON_RIGHT, false, pos)
|
||
await process_frame
|
||
_check(not bool(camera.call("fly_active")), "RMB los → Flug aus")
|
||
_check(bool(interaction.get("placing")), "Werkzeug läuft nach dem Flug weiter")
|
||
|
||
# ESC wirft ab (bekannter Vertrag).
|
||
main._unhandled_key_input(_press_key(KEY_ESCAPE))
|
||
await process_frame
|
||
_check(not bool(interaction.get("placing")), "ESC wirft danach ab")
|
||
_check(_objects().size() == 0, "kein Objekt gesetzt (ist %d)" % _objects().size())
|
||
await _cleanup_objects()
|
||
|
||
|
||
# ------------------------------------------------------------- 5 EIN Undo ----
|
||
|
||
func _test_undo_genau_eins() -> void:
|
||
print("Testgruppe: Ein Undo nimmt genau das eine gesetzte Objekt zurück")
|
||
var oid := _first_oid()
|
||
var interaction: Node = main.interaction
|
||
var assets: Node = main.assets_mod
|
||
main.call("_set_tool_mode", "objects")
|
||
await _cleanup_objects()
|
||
_check(int(_objects().size()) == 0, "Startzustand: leere Map (%d Objekte)" % _objects().size())
|
||
assets._activate_tile(oid)
|
||
var pos := _vp_center()
|
||
_motion(pos)
|
||
_mouse(MOUSE_BUTTON_LEFT, true, pos)
|
||
await process_frame
|
||
_mouse(MOUSE_BUTTON_LEFT, false, pos)
|
||
await process_frame
|
||
_check(_objects().size() == 1, "Objekt gesetzt (ist %d)" % _objects().size())
|
||
_check(main.history.can_undo(), "History hat den Platzier-Schritt")
|
||
_check(str(main.history.undo_desc()).contains("platzieren"), "Undo-Beschreibung nennt das Platzieren (%s)" % str(main.history.undo_desc()))
|
||
|
||
main._do_undo()
|
||
await process_frame
|
||
_check(_objects().size() == 0, "EIN Undo: das eine Objekt ist weg (ist %d)" % _objects().size())
|
||
_check(not main.history.can_undo(), "History ist leer (kein weiterer Schritt)")
|
||
_check(int(interaction.get("selected_index")) == -1, "Auswahl nach dem Undo geleert (Index -1)")
|
||
await _cleanup_objects()
|
||
|
||
|
||
# --------------------------------------------------- 6 Auswahl-Stabilität ----
|
||
|
||
## Direkter Pfad (Probe-Main wie tests/test_shortcuts.gd): nach _place_object
|
||
## wird die Baum-Markierung für das neue Objekt GENAU EINMAL gesetzt — kein
|
||
## doppeltes Auswahl-Ereignis (1× in der Platzier-Meldung, 1× im Werkzeug-Ende).
|
||
func _test_probe_auswahl_stabil() -> void:
|
||
print("Testgruppe: Auswahl stabil — Baum-Markierung genau 1× je Platzierung")
|
||
var doc := SvmapDocument.create_new("U5Test", 1024.0, "mittag")
|
||
var fake := ProbeMain.new()
|
||
fake.doc = doc
|
||
fake.manifest = {"objects": {"box_001": {"poly": 12, "collision": {"shape": "box", "size": [1.2, 1.2, 1.2]}}}}
|
||
fake.sym_mode = SymmetryCore.Mode.OFF # Auswahl-Stabilität, nicht M3
|
||
fake.history.begin(doc)
|
||
var vp := SubViewport.new()
|
||
vp.size = Vector2i(1280, 720)
|
||
root.add_child(vp)
|
||
var cam := Camera3D.new()
|
||
vp.add_child(cam)
|
||
cam.current = true
|
||
cam.look_at_from_position(Vector3(-95.0, 115.0, 135.0), Vector3.ZERO, Vector3.UP)
|
||
var builder := ProbeBuilder.new()
|
||
var interaction := Node3D.new()
|
||
interaction.set_script(load("res://core/editor/interaction.gd"))
|
||
vp.add_child(interaction)
|
||
interaction.setup(fake, cam, builder)
|
||
|
||
# Werkzeug an (wie start_placement, ohne GLB-Abhängigkeit).
|
||
interaction.set("placing", true)
|
||
interaction.set("place_oid", "box_001")
|
||
var h := SvmapDocument.terrain_height_at(doc, 100.0, 40.0)
|
||
interaction.call("_place_object", cam.unproject_position(Vector3(100.0, h, 40.0)))
|
||
var objs: Array = doc.get("objects", [])
|
||
_check(objs.size() == 1, "Probe: 1 Objekt gesetzt (ist %d)" % objs.size())
|
||
_check(not bool(interaction.get("placing")), "Probe: Werkzeug nach dem Setzen AUS")
|
||
_check(int(interaction.get("selected_index")) == 0, "Probe: neues Objekt ausgewählt (Index 0)")
|
||
_check(fake.tree_marks == 1, "Probe: Baum-Markierung GENAU 1× (ist %d)" % fake.tree_marks)
|
||
_check(fake.statuses.size() > 0 and str(fake.statuses[fake.statuses.size() - 1]).contains("ausgewählt"),
|
||
"Probe: letzte Meldung sagt „ausgewählt“ (%s)" % str(fake.statuses.back() if not fake.statuses.is_empty() else ""))
|
||
# Serienmodus-Gegenprobe: mit Shift bleibt das Werkzeug an.
|
||
var inter2 := Node3D.new()
|
||
inter2.set_script(load("res://core/editor/interaction.gd"))
|
||
vp.add_child(inter2)
|
||
var doc2 := SvmapDocument.create_new("U5Test2", 1024.0, "mittag")
|
||
var fake2 := ProbeMain.new()
|
||
fake2.doc = doc2
|
||
fake2.manifest = {"objects": {"box_001": {"poly": 12, "collision": {"shape": "box", "size": [1.2, 1.2, 1.2]}}}}
|
||
fake2.sym_mode = SymmetryCore.Mode.OFF # Serienmodus-Zählung ohne Zwillinge
|
||
fake2.history.begin(doc2)
|
||
inter2.setup(fake2, cam, builder)
|
||
inter2.set("placing", true)
|
||
inter2.set("place_oid", "box_001")
|
||
inter2.call("_place_object", cam.unproject_position(Vector3(100.0, h, 40.0)), true)
|
||
_check(bool(inter2.get("placing")), "Probe Serienmodus: Werkzeug bleibt AN (Shift)")
|
||
_check((doc2["objects"] as Array).size() == 1, "Probe Serienmodus: Objekt gesetzt")
|
||
_check(str(fake2.statuses.back() if not fake2.statuses.is_empty() else "").contains("Serienmodus"),
|
||
"Probe: Statuszeile nennt „Serienmodus (Shift)“")
|
||
# Zweiter serieller Klick: zweites Objekt, immer noch AN.
|
||
inter2.set("placing", true)
|
||
inter2.set("place_oid", "box_001")
|
||
inter2.call("_place_object", cam.unproject_position(Vector3(-100.0, h, -40.0)), true)
|
||
_check((doc2["objects"] as Array).size() == 2, "Probe Serienmodus: 2 Objekte gesetzt")
|
||
_check(bool(inter2.get("placing")), "Probe Serienmodus: immer noch AN")
|
||
vp.queue_free()
|
||
|
||
|
||
# -------------------------------------------------------------- Helfer ----
|
||
|
||
## Test-Map leeren (Undo-History bleibt bewusst stehen — die nächste Gruppe
|
||
## baut ihren eigenen Zustand auf). Alle Objekte hart entfernen.
|
||
func _cleanup_objects() -> void:
|
||
# Symmetrie aus (Standard ist Punkt) — die Serien-/Einmal-Zählung soll
|
||
# nicht von automatischen Zwillingen abhängen (M3, separate Karte).
|
||
main.sym_mode = SymmetryCore.Mode.OFF
|
||
var objects: Array = main.doc.get("objects", [])
|
||
objects.clear()
|
||
main.view_builder.call("rebuild", main.doc, main.manifest)
|
||
var interaction: Node = main.interaction
|
||
interaction.call("clear_selection", true)
|
||
main.history.clear()
|
||
await process_frame
|
||
|
||
|
||
func _clear_library_cfg_section() -> void:
|
||
# Nur UNSEREN Abschnitt leeren — [test]/[update]/[fenster] bleiben.
|
||
var cfg := ConfigFile.new()
|
||
cfg.load("user://scopedvr_editor.cfg")
|
||
if cfg.has_section("library"):
|
||
cfg.erase_section("library")
|
||
cfg.save("user://scopedvr_editor.cfg")
|
||
|
||
|
||
## Probe-main-Doppel (wie tests/test_shortcuts.gd ProbeMain): zählt
|
||
## Baum-Markierungen mit, damit die Auswahl-Stabilität beweisbar ist.
|
||
class ProbeMain:
|
||
var doc: Dictionary = {}
|
||
var manifest: Dictionary = {}
|
||
var grid_snap := false
|
||
var dirty := false
|
||
var sym_mode: int = SymmetryCore.Mode.OFF # U5-Probe: Symmetrie bewusst aus
|
||
var history: ActionHistory = ActionHistory.new()
|
||
var statuses: Array = []
|
||
var tree_marks := 0
|
||
|
||
func object_label(_oid: String) -> String:
|
||
return "Kiste"
|
||
|
||
func _fmt_int(v: int) -> String:
|
||
return str(v)
|
||
|
||
func _set_status(text: String, _err := false) -> void:
|
||
statuses.append(text)
|
||
|
||
func _highlight_library(_oid: String) -> void:
|
||
pass
|
||
|
||
func _selection_status(_idx: int) -> void:
|
||
pass
|
||
|
||
func _sync_selection_panel() -> void:
|
||
pass
|
||
|
||
func _mark_tree_selection(_idx: int) -> void:
|
||
tree_marks += 1
|
||
|
||
func _mark_tree_selection_multi(_indices: Array) -> void:
|
||
pass
|
||
|
||
func _after_structure_change() -> void:
|
||
pass
|
||
|
||
func _refresh_history_buttons() -> void:
|
||
pass
|
||
|
||
func _refresh_stats() -> void:
|
||
pass
|
||
|
||
func _refresh_file_label() -> void:
|
||
pass
|
||
|
||
|
||
class ProbeBuilder extends Node3D:
|
||
var object_markers: Array = []
|
||
var last_selection: Array = []
|
||
|
||
func set_selected_multi(indices: Array) -> void:
|
||
last_selection = indices.duplicate()
|