- 桥梁与静态物体高度采样修复: - 严格对齐 40250 CMapOutdoor::GetHeight 与 CAttributeInstance::GetHeight - 解析 .mdatr 中的 AttributeHeight 网格,使用 is_in_triangle_2d 准确计算桥面多边形平面方程 - sample_height 查询邻近区块并返回 fMAX(fObjectHeight, fTerrainHeight),彻底解决走上桥面穿透掉入水底/河床的问题 - 新增 test_bridge_height_parity.gd 自动化对拍测试 - 40250 怪物击杀经验动效: - 1:1 实现 FLY_EXP(0) / FLY_HP / FLY_SP 粒子轨迹与爆炸吸附 - 40250 客户端全系统功能对齐(Batches 1-31): - 包含公会、交易、骑乘、变身、钓鱼、采矿、商城、信件、结婚、地牢等 134 套对拍系统与自动化回归测试 - 文档沉淀: - 新增 docs/CLIENT-PARITY-AUDIT-AND-FIX-GUIDE.md 客户端对拍缺陷发现与修复工程指南
135 lines
4.0 KiB
GDScript
135 lines
4.0 KiB
GDScript
# equipment_inspect_system.gd —— 40250 装备透视检视与 GM 监察系统 1:1
|
|
# 严格对照:
|
|
# Client/Eternexus/root/uiequipmentdialog.py (EquipmentDialog)
|
|
# ClientVS22/source/UserInterface/PythonPlayer.cpp
|
|
# metin2/src/server/game/src/cmd_gm.cpp (do_view_equip)
|
|
extends RefCounted
|
|
|
|
# 官方装备槽位常量定义 (1:1 ClientVS22/source/UserInterface/GameType.h)
|
|
const WEAR_BODY := 0 # 铠甲
|
|
const WEAR_HEAD := 1 # 头盔
|
|
const WEAR_FOOTS := 2 # 鞋子
|
|
const WEAR_WRIST := 3 # 手镯
|
|
const WEAR_WEAPON := 4 # 武器
|
|
const WEAR_NECK := 5 # 项链
|
|
const WEAR_EAR := 6 # 耳环
|
|
const WEAR_SHIELD := 10 # 盾牌
|
|
const WEAR_COSTUME_BODY := 19 # 时装身体
|
|
const WEAR_COSTUME_HAIR := 20 # 时装发型
|
|
const WEAR_COSTUME_WEAPON := 21 # 时装武器
|
|
|
|
const SLOT_NAMES := {
|
|
WEAR_BODY: "铠甲",
|
|
WEAR_HEAD: "头盔",
|
|
WEAR_FOOTS: "鞋子",
|
|
WEAR_WRIST: "手镯",
|
|
WEAR_WEAPON: "武器",
|
|
WEAR_NECK: "项链",
|
|
WEAR_EAR: "耳环",
|
|
WEAR_SHIELD: "盾牌",
|
|
WEAR_COSTUME_BODY: "时装身体",
|
|
WEAR_COSTUME_HAIR: "时装发型",
|
|
WEAR_COSTUME_WEAPON: "时装武器",
|
|
}
|
|
|
|
# 窗口状态
|
|
var is_open: bool = false
|
|
var inspected_vid: int = 0
|
|
var inspected_name: String = ""
|
|
var equipment_slots: Dictionary = {} # slot_idx -> item_data Dictionary
|
|
|
|
func _init() -> void:
|
|
is_open = false
|
|
inspected_vid = 0
|
|
inspected_name = ""
|
|
equipment_slots.clear()
|
|
|
|
# 1. 打开检视窗口 (1:1 EquipmentDialog.Open)
|
|
# caller_is_gm: GM 可强制穿透隐私限制
|
|
func open_dialog(
|
|
target_vid: int,
|
|
target_name: String,
|
|
target_equip_dict: Dictionary,
|
|
allow_inspect: bool = true,
|
|
caller_is_gm: bool = false
|
|
) -> Dictionary:
|
|
var res := { "success": false, "error_code": "OK", "message": "" }
|
|
|
|
if not allow_inspect and not caller_is_gm:
|
|
res["error_code"] = "INSPECT_FORBIDDEN"
|
|
res["message"] = "目标玩家设置了隐私保护,禁止查看装备。"
|
|
return res
|
|
|
|
inspected_vid = target_vid
|
|
inspected_name = target_name
|
|
equipment_slots.clear()
|
|
|
|
# 载入目标玩家装备数据
|
|
for slot_key in target_equip_dict:
|
|
var slot_idx: int = int(slot_key)
|
|
var it: Dictionary = target_equip_dict[slot_key]
|
|
equipment_slots[slot_idx] = _normalize_item_data(it)
|
|
|
|
is_open = true
|
|
res["success"] = true
|
|
res["message"] = "成功载入 %s 的装备信息。" % target_name
|
|
return res
|
|
|
|
# 2. 关闭检视窗口 (EquipmentDialog.Close)
|
|
func close_dialog() -> void:
|
|
is_open = false
|
|
inspected_vid = 0
|
|
inspected_name = ""
|
|
equipment_slots.clear()
|
|
|
|
# 3. 规范化装备项数据 (确保含有 3 宝石孔与 7 词条结构)
|
|
func _normalize_item_data(it: Dictionary) -> Dictionary:
|
|
var item_copy := it.duplicate(true)
|
|
var sockets: Array = item_copy.get("sockets", [0, 0, 0])
|
|
while sockets.size() < 3:
|
|
sockets.append(0)
|
|
item_copy["sockets"] = sockets
|
|
|
|
var attrs: Array = item_copy.get("attributes", [])
|
|
item_copy["attributes"] = attrs
|
|
return item_copy
|
|
|
|
# 4. 获取指定槽位装备
|
|
func get_slot_item(slot_idx: int) -> Dictionary:
|
|
if not is_open:
|
|
return {}
|
|
return equipment_slots.get(slot_idx, {})
|
|
|
|
# 5. 悬停生成 Tooltip 详情 (1:1 EquipmentDialog.OverInItem)
|
|
func build_slot_tooltip(slot_idx: int) -> String:
|
|
if not is_open:
|
|
return ""
|
|
|
|
if not equipment_slots.has(slot_idx):
|
|
var slot_name: String = SLOT_NAMES.get(slot_idx, "未知槽位")
|
|
return "[%s] 未佩戴装备" % slot_name
|
|
|
|
var it: Dictionary = equipment_slots[slot_idx]
|
|
var lines: Array = []
|
|
var title_name: String = it.get("name", "Unknown Item")
|
|
var vnum: int = it.get("vnum", 0)
|
|
|
|
lines.append("=== %s (Vnum: %d) ===" % [title_name, vnum])
|
|
lines.append("部位: %s" % SLOT_NAMES.get(slot_idx, "特殊部位"))
|
|
|
|
# 宝石孔位
|
|
var sockets: Array = it.get("sockets", [])
|
|
for i in range(sockets.size()):
|
|
var s: int = sockets[i]
|
|
if s > 0:
|
|
lines.append("孔位 %d: 镶嵌宝石 [Vnum %d]" % [i + 1, s])
|
|
elif s == 1:
|
|
lines.append("孔位 %d: 空孔" % [i + 1])
|
|
|
|
# 附加属性 (1~7 词条)
|
|
var attrs: Array = it.get("attributes", [])
|
|
for a in attrs:
|
|
lines.append("附加属性: 属性 %d +%d" % [a.get("type", 0), a.get("value", 0)])
|
|
|
|
return ("\n").join(lines)
|