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
+43 -14
View File
@@ -6,6 +6,20 @@ const ItemSplitSystem = preload("res://item_split_system.gd")
var _passed := 0
var _failed := 0
class FakeClient extends Node:
var move_calls: Array = []
var sell_calls: Array = []
var move_ok := true
var sell_ok := true
func move_item(fw: int, fc: int, tw: int, tc: int, count: int) -> bool:
move_calls.append([fw, fc, tw, tc, count])
return move_ok
func shop_sell(cell: int, count: int) -> bool:
sell_calls.append([cell, count])
return sell_ok
func _assert(cond: bool, msg: String) -> void:
if cond:
_passed += 1
@@ -24,6 +38,7 @@ func _init() -> void:
func _test_item_stack_splitting() -> void:
print("\n--- Test 1: Stack Splitting (Shift + Left Click) ---")
var iss := ItemSplitSystem.new()
var client := FakeClient.new()
var inv: Array = [
{"vnum": 27003, "name": "红药水(大)", "count": 200}, # Slot 0
{"vnum": 10, "name": "木剑+0", "count": 1}, # Slot 1 (single)
@@ -48,22 +63,31 @@ func _test_item_stack_splitting() -> void:
split_signal["cnt"] = c
)
var r_split = iss.split_item(0, 50, 2, inv)
var r_split = iss.split_item(0, 50, 2, inv, client)
_assert(r_split["ok"] == true, "Split 50 potions succeeded")
_assert(inv[0]["count"] == 150, "Source slot 0 has 150 remaining")
_assert(inv[2] != null and inv[2]["count"] == 50, "Destination slot 2 has 50")
_assert(split_signal["fired"] == true and split_signal["cnt"] == 50, "item_split signal fired")
_assert(r_split["pending"] == true and client.move_calls == [[1, 0, 1, 2, 50]],
"Split sends CG_ITEM_MOVE with source, destination and count")
_assert(inv[0]["count"] == 200 and inv[2] == null,
"Split request does not mutate local inventory before GC confirmation")
_assert(not split_signal["fired"], "legacy item_split is not emitted before server confirmation")
# 4. Auto-find empty slot when target_slot is -1
var r_auto = iss.split_item(0, 30, -1, inv)
# Simulate the first request's destination after a server GC confirmation;
# the helper itself must still not mutate this snapshot.
inv[2] = {"vnum": 27003, "count": 50}
var r_auto = iss.split_item(0, 30, -1, inv, client)
_assert(r_auto["ok"] == true, "Auto-find split succeeded")
_assert(r_auto["target_slot"] == 3, "Auto-placed into slot 3")
_assert(inv[3] != null and inv[3]["count"] == 30, "Slot 3 has 30 count")
_assert(inv[0]["count"] == 120, "Slot 0 has 120 remaining")
_assert(client.move_calls[-1] == [1, 0, 1, 3, 30],
"Auto split sends the selected empty destination")
_assert(inv[3] == null and inv[0]["count"] == 200,
"Auto split also waits for server confirmation")
client.free()
func _test_quick_sell_to_shop() -> void:
print("\n--- Test 2: Quick Sell to NPC Shop (Ctrl + Right Click) ---")
var iss := ItemSplitSystem.new()
var client := FakeClient.new()
var player_data := {"gold": 1000}
var inv: Array = [
{"vnum": 10, "name": "木剑+0", "count": 1, "sale_price": 50, "is_locked": true}, # Locked
@@ -75,17 +99,22 @@ func _test_quick_sell_to_shop() -> void:
_assert(r_lock["ok"] == false and r_lock["reason"] == "ITEM_LOCKED", "Locked item quick sell blocked")
# 2. Valid quick sell
var sell_signal := {"fired": false, "gold": 0}
iss.item_quick_sold.connect(func(_s: int, g: int, _n: String):
var sell_signal := {"fired": false, "count": 0}
iss.item_quick_sell_requested.connect(func(_s: int, c: int, _n: String):
sell_signal["fired"] = true
sell_signal["gold"] = g
sell_signal["count"] = c
)
var r_sell = iss.quick_sell_item(1, inv, player_data)
var r_sell = iss.quick_sell_item(1, inv, player_data, client)
_assert(r_sell["ok"] == true, "Quick sell succeeded")
_assert(inv[1] == null, "Sold slot cleared to null")
_assert(player_data["gold"] == 3000, "Earned 2000 Yang (1000 -> 3000)")
_assert(sell_signal["fired"] == true and sell_signal["gold"] == 2000, "item_quick_sold signal fired")
_assert(r_sell["pending"] == true and client.sell_calls == [[1, 20]],
"Quick sell sends CG_SHOP SELL2 with cell and count")
_assert(inv[1] != null and inv[1]["count"] == 20,
"Quick sell does not clear local item before server confirmation")
_assert(player_data["gold"] == 1000, "Quick sell does not add local gold before confirmation")
_assert(sell_signal["fired"] and sell_signal["count"] == 20,
"item_quick_sell_requested signal records pending request")
client.free()
func _test_drop_safety_confirmation() -> void:
print("\n--- Test 3: Drop Safety Check for Valuable Equipment ---")