Files
mtgodot-poc/project/item_split_system.gd
T
shenandshen c93894313a fix: 装备属性面板避让逻辑 + 多项功能更新
- item_tooltip_view.gd: 新增 avoid_rect 属性,tooltip 与装备窗口重叠时自动推到左侧
- inventory_ui.gd: 悬停装备时传入窗口矩形作为避让区域
- 包含其他累积的功能开发和测试文件
2026-09-21 16:38:59 -07:00

144 lines
5.0 KiB
GDScript

# item_split_system.gd —— Metin2 40250 物品堆叠精准拆分与快捷批量操作 1:1
# 对照 40250 服务端 char_item.cpp, input_main.cpp, uiinventory.py
class_name ItemSplitSystem
extends RefCounted
signal item_split(source_slot: int, target_slot: int, split_count: int)
signal item_quick_sold(slot_idx: int, gold_received: int, item_name: String)
signal item_split_requested(source_slot: int, target_slot: int, split_count: int)
signal item_quick_sell_requested(slot_idx: int, count: int, item_name: String)
# 堆叠精准拆分 (Shift + 左键)
func split_item(
source_slot: int,
split_count: int,
target_slot: int,
inventory: Array,
client: Node = null,
source_window: int = 1,
target_window: int = 1
) -> Dictionary:
if source_slot < 0 or source_slot >= inventory.size():
return {"ok": false, "reason": "INVALID_SOURCE_SLOT", "msg": "无效的源栏位!"}
var source_item = inventory[source_slot]
if source_item == null:
return {"ok": false, "reason": "EMPTY_SOURCE_SLOT", "msg": "源栏位上没有物品!"}
var current_count: int = int(source_item.get("count", 1))
if current_count <= 1:
return {"ok": false, "reason": "NOT_SPLITTABLE", "msg": "单件物品无法拆分!"}
if split_count < 1 or split_count >= current_count:
return {
"ok": false,
"reason": "INVALID_SPLIT_COUNT",
"max_allowed": current_count - 1,
"msg": "拆分数量必须在 1 到 %d 之间!" % (current_count - 1)
}
# 确定目标格子
var dest_slot := target_slot
if dest_slot < 0:
for i in range(inventory.size()):
if inventory[i] == null:
dest_slot = i
break
if dest_slot < 0 or dest_slot >= inventory.size():
return {"ok": false, "reason": "NO_DEST_SLOT", "msg": "背包没有可用空位放置拆分出的物品!"}
if inventory[dest_slot] != null:
return {"ok": false, "reason": "DEST_SLOT_NOT_EMPTY", "msg": "目标栏位已被占用!"}
# 40250 does not edit CPythonPlayer's item array optimistically. Splitting
# is an ordinary CG_ITEM_MOVE(source, destination, count); both resulting
# counts arrive later through GC_ITEM_UPDATE/SET. Keep this helper from
# becoming a second local authority when it is used by desktop or mobile UI.
if client == null or not client.has_method("move_item"):
return {"ok": false, "reason": "NETWORK_CLIENT_REQUIRED", "msg": "拆分需要连接服务器确认!"}
if not client.move_item(source_window, source_slot, target_window, dest_slot, split_count):
return {"ok": false, "reason": "SEND_FAILED", "msg": "拆分请求发送失败!"}
item_split_requested.emit(source_slot, dest_slot, split_count)
return {
"ok": true,
"pending": true,
"source_slot": source_slot,
"target_slot": dest_slot,
"remaining_source": current_count - split_count,
"split_count": split_count,
"msg": "已发送拆分请求,等待服务器确认!"
}
# 快捷批量出售给商人 (Ctrl + 右键)
func quick_sell_item(
slot_idx: int,
inventory: Array,
player_data: Dictionary,
client: Node = null,
window: int = 1
) -> Dictionary:
if slot_idx < 0 or slot_idx >= inventory.size():
return {"ok": false, "reason": "INVALID_SLOT", "msg": "无效的栏位!"}
var item = inventory[slot_idx]
if item == null:
return {"ok": false, "reason": "EMPTY_SLOT", "msg": "该格子上没有物品!"}
# 加锁保护
if item.get("is_locked", false):
return {"ok": false, "reason": "ITEM_LOCKED", "msg": "已加锁的物品严禁出售!"}
if window != 1:
return {"ok": false, "reason": "INVALID_SELL_WINDOW", "msg": "只能出售背包中的物品!"}
var count: int = int(item.get("count", 1))
# 40250's SELL/SELL2 response supplies the authoritative price and the
# subsequent item/gold updates. Do not use sale_price/player_data as a local
# substitute for the server result.
if client == null or not client.has_method("shop_sell"):
return {"ok": false, "reason": "NETWORK_CLIENT_REQUIRED", "msg": "出售需要连接服务器确认!"}
if not client.shop_sell(slot_idx, count):
return {"ok": false, "reason": "SEND_FAILED", "msg": "出售请求发送失败!"}
var iname: String = item.get("name", "物品")
item_quick_sell_requested.emit(slot_idx, count, iname)
return {
"ok": true,
"pending": true,
"slot_idx": slot_idx,
"count": count,
"item_name": iname,
"msg": "已发送出售请求,等待服务器确认!"
}
# 高价值物品丢弃/销毁防误触检查
func check_drop_safety(item: Dictionary) -> Dictionary:
if item.is_empty():
return {"can_drop": false}
if item.get("is_locked", false):
return {
"can_drop": false,
"require_confirm": false,
"reason": "ITEM_LOCKED",
"msg": "已加锁的贵重物品无法丢弃!"
}
var refine_level: int = int(item.get("refine_level", 0))
var has_attrs: bool = item.has("attributes") and not item["attributes"].is_empty()
if refine_level >= 7 or has_attrs:
return {
"can_drop": true,
"require_confirm": true,
"msg": "此装备强化等级较高或拥有附加属性,属于贵重装备!确定要丢弃吗?"
}
return {
"can_drop": true,
"require_confirm": false
}