extends SceneTree var _failed := false class CursorStub extends Node: var current := "NORMAL" func set_cursor(shape: String) -> void: current = shape func reset() -> void: current = "NORMAL" class AudioStub extends Node: var calls: Array[String] = [] func play_ui(name: String) -> void: calls.append(name) func _init() -> void: var host := Node.new() root.add_child(host) var cur := CursorStub.new() host.add_child(cur) var audio := AudioStub.new() host.add_child(audio) var mouse := preload("res://ui/mouse_controller.gd").new() host.add_child(mouse) mouse.setup(host, cur, audio) _check(not mouse.is_attached(), "starts detached") _check(mouse.attach_item(1, 7, 1001, 3), "attach inventory item") _check(audio.calls == ["pick.wav"], "attach plays pick sound") _check(mouse.is_attached(), "attached state visible") _check(int(mouse.attached().get("cell", -1)) == 7, "attached source cell") _check(cur.current == "ITEM", "item cursor state") var target := Control.new() target.position = Vector2.ZERO target.size = Vector2(32, 32) host.add_child(target) mouse.register_target(target, func(payload: Dictionary): host.set_meta("dropped_cell", int(payload.get("cell", -1))) return int(payload.get("cell", -1)) == 7) mouse._drop_at(target.get_global_rect().position + Vector2(4, 4)) _check(int(host.get_meta("dropped_cell", -1)) == 7, "drop callback receives source payload") _check(not mouse.is_attached(), "successful drop clears attached state") _check(audio.calls == ["pick.wav", "drop.wav"], "successful drop plays drop sound") mouse.attach_money(123) _check(mouse.is_attached() and int(mouse.attached().get("vnum", 0)) == -1 and int(mouse.attached().get("count", 0)) == 123, "money has independent attached state") mouse.cancel() mouse.cancel() _check(not mouse.is_attached(), "cancel clears attached state") _check(cur.current == "NORMAL", "cancel resets cursor") mouse.attach_item(1, 7, 1001, 1) mouse._drop_at(Vector2(999, 999)) _check(not mouse.is_attached(), "invalid drop cancels attached state") mouse._notification(Node.NOTIFICATION_WM_WINDOW_FOCUS_OUT) mouse.attach_item(1, 7, 1001, 1) mouse._notification(Node.NOTIFICATION_WM_WINDOW_FOCUS_OUT) _check(not mouse.is_attached(), "focus loss cancels drag") _check(audio.calls == ["pick.wav", "drop.wav", "pick.wav", "pick.wav", "loginfail.wav", "pick.wav"], "invalid drop plays cancel sound and focus loss cancels") host.queue_free() print("PASS: mouse_controller (global item drag state + cancel)") quit(1 if _failed else 0) func _check(ok: bool, label: String) -> void: if ok: return _failed = true push_error("FAIL: " + label)