Implement 40250 classic client port

This commit is contained in:
shenlei
2026-09-01 18:49:28 +09:00
parent 2277b1dd60
commit 8f61990a83
481 changed files with 169826 additions and 293 deletions
+58 -6
View File
@@ -13,9 +13,23 @@
# `OnSelectItemSlot` / `DelPrivateShopItemStock`)。OkButton(`BuildPrivateShop`)stock 按
# 格号排序、`display_pos = 格号`、上限 39`PRIVATE_SHOP_ITEM_MAX_NUM`)打包成
# `M2Client.open_private_shop(sign, [{vnum,count,inv_cell,price,display_pos}])`
# = `TPacketCGMyShop` + `TShopItemTable`×N)。철수 `close_private_shop()` = SHOP_CG_END。
# = `TPacketCGMyShop` + `TShopItemTable`×N)。40250 收摊用空的 `CG_MYSHOP`
# (服务端 `OpenMyShop` 会先关闭已有的个人摊位);`close_private_shop()` 已在
# M2Client classic 分支按该语义发送。
extends Node
class PrivateShopDragButton extends Button:
var drag_entry: Dictionary = {}
func _get_drag_data(_at_position: Vector2):
if drag_entry.is_empty():
return null
var preview := Label.new()
preview.text = String(text)
preview.add_theme_color_override("font_color", Color(1, 0.9, 0.55))
set_drag_preview(preview)
return {"private_shop_entry": drag_entry.duplicate(true)}
const PRIVATE_SHOP_ITEM_MAX := 39 # PRIVATE_SHOP_ITEM_MAX_NUM (TPacketCGMyShop::bCount 上限)
const SIGN_MAX := 25 # NameLine input_limit
@@ -33,6 +47,7 @@ var _status: Label
var _stock: Dictionary = {} # 格号:int -> {cell, vnum, count, price}
var _picked = null # {cell, vnum, count} 等待落位
var _price_dialog: Control = null
var _server_prices: Dictionary = {} # vnum -> last price returned by MyShopPriceList
func setup(m2client: Node, ui_manager: CanvasLayer, proto_node: Node = null, assets := "") -> void:
client = m2client
@@ -46,6 +61,10 @@ func setup(m2client: Node, ui_manager: CanvasLayer, proto_node: Node = null, ass
uiscript_dir = assets_root.path_join("uiscript")
if client and client.has_signal("inventory_changed"):
client.inventory_changed.connect(func(_w, _c): if is_open(): _refresh())
if client and client.has_signal("my_shop_price_list"):
client.my_shop_price_list.connect(_on_server_price)
if client and client.has_signal("private_shop_open_requested"):
client.private_shop_open_requested.connect(open)
# --- open / close --------------------------------------------------
@@ -101,6 +120,10 @@ func _index_cells(root: Node) -> void:
n.gui_input.connect(func(e: InputEvent) -> void:
if e is InputEventMouseButton and e.button_index == MOUSE_BUTTON_LEFT and e.pressed:
_on_slot_clicked(idx))
# 同时支持从右侧背包候选区拖到空货位;点击选取仍保留给触屏/无鼠标场景。
n.set_drag_forwarding(Callable(self, "_no_drag_data"),
Callable(self, "_can_drop_slot").bind(idx),
Callable(self, "_drop_slot").bind(idx))
# NameLine 在 uiscript 里是 textLabel)—— 盖一个 LineEdit 上去
func _overlay_name_edit(root: Control) -> void:
@@ -194,12 +217,13 @@ func _refresh_inv() -> void:
continue
var vnum := int(it.get("vnum", 0))
var count := int(it.get("count", 1))
var b := Button.new()
var b := PrivateShopDragButton.new()
b.text = "%s ×%d" % [_name_of(vnum), count]
b.alignment = HORIZONTAL_ALIGNMENT_LEFT
b.custom_minimum_size = Vector2(196, 22)
b.toggle_mode = true
var entry := {"cell": cell, "vnum": vnum, "count": count}
b.drag_entry = entry
b.pressed.connect(func() -> void: _pick(entry, b))
if _picked != null and int(_picked["cell"]) == cell:
b.button_pressed = true
@@ -246,6 +270,20 @@ func _pick(entry: Dictionary, _btn: Button) -> void:
_picked = entry
_refresh_grid()
func _no_drag_data(_at_position: Vector2):
return null
func _can_drop_slot(_at_position: Vector2, data, slot: int) -> bool:
return slot >= 0 and slot < 40 and not _stock.has(slot) \
and data is Dictionary and data.has("private_shop_entry")
func _drop_slot(_at_position: Vector2, data, slot: int) -> void:
if not _can_drop_slot(Vector2.ZERO, data, slot):
return
var entry: Dictionary = data["private_shop_entry"] as Dictionary
_picked = entry.duplicate(true)
_ask_price(slot)
func _on_slot_clicked(slot: int) -> void:
if _stock.has(slot):
_stock.erase(slot)
@@ -276,7 +314,10 @@ func _ask_price(slot: int) -> void:
spin.min_value = 1
spin.max_value = 2000000000
spin.step = 1
spin.value = int(_stock.get(slot, {}).get("price", 1))
var default_price := int(_server_prices.get(int(_picked["vnum"]), 1))
if _stock.has(slot):
default_price = int(_stock[slot].get("price", default_price))
spin.value = default_price
spin.position = Vector2(12, 40)
spin.size = Vector2(216, 28)
dlg.add_child(spin)
@@ -299,7 +340,7 @@ func _dismiss_price_dialog() -> void:
_price_dialog = null
func _place(slot: int, price: int) -> void:
if price <= 0 or _picked == null:
if slot < 0 or slot >= 40 or price <= 0 or price > 2000000000 or _picked == null:
_dismiss_price_dialog()
return
_stock[slot] = {"cell": int(_picked["cell"]), "vnum": int(_picked["vnum"]),
@@ -308,7 +349,16 @@ func _place(slot: int, price: int) -> void:
_dismiss_price_dialog()
_refresh()
func _on_server_price(vnum: int, price: int) -> void:
if vnum > 0 and price >= 0:
_server_prices[vnum] = price
func _ok() -> void:
var sign := _sign.text.strip_edges()
if sign.is_empty() or sign.length() > SIGN_MAX:
if is_instance_valid(_status):
_status.text = "请输入 1%d 个字符的摊位名称" % SIGN_MAX
return
if _stock.is_empty():
if is_instance_valid(_status):
_status.text = "판매할 아이템을 올리세요"
@@ -323,8 +373,10 @@ func _ok() -> void:
items.append({"vnum": s["vnum"], "count": s["count"], "inv_cell": s["cell"],
"price": s["price"], "display_pos": slot})
if client and client.has_method("open_private_shop"):
client.open_private_shop(_sign.text.strip_edges(), items)
close()
if client.open_private_shop(sign, items):
close()
elif is_instance_valid(_status):
_status.text = "摆摊请求发送失败"
func _close_shop() -> void:
if client and client.has_method("close_private_shop"):