fix: 装备属性面板避让逻辑 + 多项功能更新

- item_tooltip_view.gd: 新增 avoid_rect 属性,tooltip 与装备窗口重叠时自动推到左侧
- inventory_ui.gd: 悬停装备时传入窗口矩形作为避让区域
- 包含其他累积的功能开发和测试文件
This commit is contained in:
shen
2026-09-21 16:38:59 -07:00
parent 1522a8a1a1
commit c93894313a
5498 changed files with 4558004 additions and 1442 deletions
+34 -22
View File
@@ -5,15 +5,18 @@ 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)
const DEFAULT_ITEM_SALE_UNIT_PRICE := 100 # 默认单件基础回收金币
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
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": "无效的源栏位!"}
@@ -48,29 +51,34 @@ func split_item(
if inventory[dest_slot] != null:
return {"ok": false, "reason": "DEST_SLOT_NOT_EMPTY", "msg": "目标栏位已被占用!"}
# 执行拆分
source_item["count"] = current_count - split_count
# 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": "拆分请求发送失败!"}
var new_stack = source_item.duplicate(true)
new_stack["count"] = split_count
inventory[dest_slot] = new_stack
item_split.emit(source_slot, dest_slot, split_count)
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": source_item["count"],
"remaining_source": current_count - split_count,
"split_count": split_count,
"msg": "成功拆分出 %d 件物品!" % split_count
"msg": "已发送拆分请求,等待服务器确认!"
}
# 快捷批量出售给商人 (Ctrl + 右键)
func quick_sell_item(
slot_idx: int,
inventory: Array,
player_data: Dictionary
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": "无效的栏位!"}
@@ -82,24 +90,28 @@ func quick_sell_item(
# 加锁保护
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))
var unit_price: int = int(item.get("sale_price", DEFAULT_ITEM_SALE_UNIT_PRICE))
var total_gold := unit_price * count
# 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": "出售请求发送失败!"}
# 增加金币并清空格子
player_data["gold"] = int(player_data.get("gold", 0)) + total_gold
var iname: String = item.get("name", "物品")
inventory[slot_idx] = null
item_quick_sold.emit(slot_idx, total_gold, iname)
item_quick_sell_requested.emit(slot_idx, count, iname)
return {
"ok": true,
"pending": true,
"slot_idx": slot_idx,
"gold_received": total_gold,
"count": count,
"item_name": iname,
"msg": "已快捷出售【%s x%d】,获得 %d 金币!" % [iname, count, total_gold]
"msg": "已发送出售请求,等待服务器确认!"
}
# 高价值物品丢弃/销毁防误触检查