# complex_item_drop_test —— 仓库 / 商城 / 交易 / NPC 商店的物品态回归。 # 验证增量 54 的全局 MouseController 可以把物品送到各窗口的明确目标槽, # 而不是只验证背包和快捷栏。 extends SceneTree const MouseController = preload("res://ui/mouse_controller.gd") const CursorManager = preload("res://ui/cursor_manager.gd") const SafeboxUI = preload("res://ui/safebox_ui.gd") const MallUI = preload("res://ui/mall_ui.gd") const ShopUI = preload("res://ui/shop_ui.gd") const ExchangeUI = preload("res://ui/exchange_ui.gd") class FakeClient extends Node: signal safebox_changed() signal mall_opened(size: int) signal mall_changed() signal shop_opened(vid: int) signal shop_closed() signal shop_error(kind: String) signal exchange_changed() var calls: Array = [] var safe_open := true var safe_size := 1 var safe_items: Array = [] var mall_open := true var mall_size := 1 var mall_items: Array = [] var shop_open := true var exchange := {"active": true, "self_items": [], "peer_items": []} func is_safebox_open() -> bool: return safe_open func get_safebox_size() -> int: return safe_size func get_safebox_gold() -> int: return 0 func get_safebox_items() -> Array: return safe_items func safebox_checkin(dst, window, cell) -> bool: calls.append(["safe_checkin", dst, window, cell]); return true func safebox_move(src, dst, count) -> bool: calls.append(["safe_move", src, dst, count]); return true func safebox_checkout(src, window, cell) -> bool: calls.append(["safe_checkout", src, window, cell]); return true func is_mall_open() -> bool: return mall_open func get_mall_size() -> int: return mall_size func get_mall_items() -> Array: return mall_items func mall_checkout(src, window, cell) -> bool: calls.append(["mall_checkout", src, window, cell]); return true func is_shop_open() -> bool: return shop_open func get_shop_items() -> Array: return [] func get_shop() -> Dictionary: return {"tabs": [{"name": "", "items": []}]} func shop_sell(cell, count) -> bool: calls.append(["shop_sell", cell, count]); return true func get_exchange() -> Dictionary: return exchange func exchange_add_item(window, cell, slot) -> bool: calls.append(["exchange_add_item", window, cell, slot]); return true var _fail := 0 func _ck(condition: bool, message: String) -> void: if not condition: _fail += 1 printerr("FAIL: " + message) func _has_call(client: FakeClient, name: String, predicate: Callable) -> bool: for call in client.calls: if call[0] == name and predicate.call(call): return true return false func _init() -> void: var host := Control.new() get_root().add_child(host) var cursor := CursorManager.new() get_root().add_child(cursor) var mouse := MouseController.new() get_root().add_child(mouse) mouse.setup(host, cursor) var client := FakeClient.new() get_root().add_child(client) var safe := SafeboxUI.new() safe.item_mouse = mouse get_root().add_child(safe) safe.setup(client, host) client.safe_items = [{"cell": 2, "vnum": 19, "count": 3}] safe.refresh() _ck(mouse._targets.size() >= 45, "safebox registers all 45 visible drop slots") _ck(safe._drop_to_slot({"window": 1, "cell": 7, "vnum": 19, "count": 1}, 4), "inventory item drops into empty safebox slot") _ck(_has_call(client, "safe_checkin", func(c): return c[1] == 4 and c[2] == 1 and c[3] == 7), "safebox drop uses explicit destination slot") var safe_item := safe._cells[2] as Button var safe_event := InputEventMouseButton.new() safe_event.button_index = MOUSE_BUTTON_LEFT safe_event.pressed = true safe._on_grid_input(2, safe_event) _ck(mouse.is_attached() and int(mouse.attached().get("window", -1)) == 3, "safebox item enters global item state") mouse.cancel() var mall := MallUI.new() mall.item_mouse = mouse get_root().add_child(mall) mall.setup(client, host) client.mall_items = [{"cell": 5, "vnum": 27, "count": 2}] mall.refresh() var mall_name := mall._list.get_child(0).get_child(0) as Label var mall_event := InputEventMouseButton.new() mall_event.button_index = MOUSE_BUTTON_LEFT mall_event.pressed = true mall_name.gui_input.emit(mall_event) _ck(mouse.is_attached() and int(mouse.attached().get("window", -1)) == 4 and int(mouse.attached().get("cell", -1)) == 5, "mall item enters global item state") mouse.cancel() var exchange := ExchangeUI.new() exchange.item_mouse = mouse get_root().add_child(exchange) exchange.setup(client, host) _ck(exchange._drop_to_slot({"window": 1, "cell": 8, "vnum": 29, "count": 1}, 6), "inventory item drops into empty exchange slot") _ck(_has_call(client, "exchange_add_item", func(c): return c[1] == 1 and c[2] == 8 and c[3] == 6), "exchange drop uses explicit display slot") var shop := ShopUI.new() shop.item_mouse = mouse get_root().add_child(shop) shop.setup(client, host) shop.open() _ck(shop._mode == 1 and not shop._sell_drop_target.visible, "shop opens in buy mode with sell target hidden") _ck(not shop._drop_to_sell({"window": 1, "cell": 9, "vnum": 31, "count": 4}), "buy mode rejects item drop to sell target") shop._set_mode(2) _ck(shop._mode == 2 and shop._sell_drop_target.visible, "sell mode shows sell target") _ck(shop._drop_to_sell({"window": 1, "cell": 9, "vnum": 31, "count": 4}), "inventory item drops into shop sell target") _ck(_has_call(client, "shop_sell", func(c): return c[1] == 9 and c[2] == 1), "shop drop uses selected sell quantity") if _fail == 0: print("PASS: complex_item_drop_test (safebox + mall + exchange + shop)") quit(0) else: printerr("%d check(s) failed" % _fail) quit(1)