# chat_hyperlink_dice_system.gd —— 40250 聊天物品超链接与 /dice 掷骰系统 1:1 # 严格对照: # ClientVS22/source/EterLib/TextTag.cpp:32 (Hyperlink |Hitem:vnum:flags:socket:attr|h[%s]|h) # Client/Eternexus/root/uichat.py & uitooltip.py (ItemHyperlink, SetHyperlinkItem) # metin2/src/server/game/src/cmd_general.cpp:2392 (do_dice) extends RefCounted const LINK_COLOR := "ffffc700" # 官方物品链接专用亮金黄色 # 1. 将物品数据编码为 40250 官方超链接格式 # 格式: |cffffc700|Hitem:vnum:flags:s0:s1:s2:a0_t:a0_v:a1_t:a1_v:...|h[物品名]|h|r static func encode_item_hyperlink(item_data: Dictionary) -> String: var vnum: int = item_data.get("vnum", 0) var name: String = item_data.get("name", "Unknown Item") var flags: int = item_data.get("flags", 0) var sockets: Array = item_data.get("sockets", [0, 0, 0]) while sockets.size() < 3: sockets.append(0) var attrs: Array = item_data.get("attributes", []) var attr_parts: Array = [] for a in attrs: attr_parts.append("%d:%d" % [a.get("type", 0), a.get("value", 0)]) var payload_parts := [ str(vnum), str(flags), str(sockets[0]), str(sockets[1]), str(sockets[2]) ] if not attr_parts.is_empty(): payload_parts.append((":").join(attr_parts)) var payload := (":").join(payload_parts) return "|c%s|Hitem:%s|h[%s]|h|r" % [LINK_COLOR, payload, name] # 2. 从超链接中解析提取物品完整数据结构 (1:1 TextTag::FindHyperlink) static func decode_item_hyperlink(hyperlink_text: String) -> Dictionary: var res := { "valid": false, "vnum": 0, "name": "", "flags": 0, "sockets": [0, 0, 0], "attributes": [] } # 正则或字符串匹配提取 |Hitem:(.+?)\|h\[(.*?)\]\|h var h_start := hyperlink_text.find("|Hitem:") if h_start == -1: return res var payload_start := h_start + 7 # len("|Hitem:") var payload_end := hyperlink_text.find("|h[", payload_start) if payload_end == -1: return res var payload := hyperlink_text.substr(payload_start, payload_end - payload_start) var name_start := payload_end + 3 # len("|h[") var name_end := hyperlink_text.find("]|h", name_start) if name_end == -1: return res var display_name := hyperlink_text.substr(name_start, name_end - name_start) res["name"] = display_name var tokens := payload.split(":") if tokens.size() < 5: return res res["vnum"] = tokens[0].to_int() res["flags"] = tokens[1].to_int() res["sockets"] = [tokens[2].to_int(), tokens[3].to_int(), tokens[4].to_int()] # 解析属性对 (type:value) var attrs: Array = [] var idx := 5 while idx + 1 < tokens.size(): var at_type := tokens[idx].to_int() var at_val := tokens[idx + 1].to_int() attrs.append({ "type": at_type, "value": at_val }) idx += 2 res["attributes"] = attrs res["valid"] = true return res # 3. 模拟 Tooltip 生成(悬停时渲染内容) static func build_tooltip_text(item_dict: Dictionary) -> String: if not item_dict.get("valid", false): return "无效的物品链接" var lines: Array = [] lines.append("=== %s (Vnum: %d) ===" % [item_dict["name"], item_dict["vnum"]]) # 宝石孔位信息 var sockets: Array = item_dict.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 = item_dict.get("attributes", []) for a in attrs: lines.append("附加属性: 类型 %d +%d" % [a["type"], a["value"]]) return ("\n").join(lines) # 4. 40250 官方 /dice 掷骰系统 (1:1 ACMD(do_dice)) static func execute_dice(player_name: String, args: Array = [], roll_override: int = -1) -> Dictionary: var start_val := 1 var end_val := 100 if args.size() >= 2: start_val = int(args[0]) end_val = int(args[1]) elif args.size() == 1: start_val = 1 end_val = int(args[0]) # 40250 规则: end = MAX(start, end); start = MIN(start, end); var real_start := mini(start_val, end_val) var real_end := maxi(start_val, end_val) var roll_result: int = 0 if roll_override >= 0: roll_result = clamp(roll_override, real_start, real_end) else: roll_result = randi() % (real_end - real_start + 1) + real_start var broadcast_msg := "%s 投掷了骰子,点数为 %d (%d-%d)" % [ player_name, roll_result, real_start, real_end ] return { "player": player_name, "roll": roll_result, "start": real_start, "end": real_end, "message": broadcast_msg }