# metin_socket_system.gd —— 魔石镶嵌与打孔清除系统(40250 官方 1:1 对齐) # 对齐源码: # - 40250 char_item.cpp:5004-5080 (case ITEM_METIN: 镶嵌魔石、30% 成功率、碎石 28960) # - 40250 char_item.cpp:4510-4540 (case USE_CLEAN_SOCKET: 卷轴 71006 清除 28960 碎石残渣) # - 40250 char_item.cpp:53 (const int ITEM_BROKEN_METIN_VNUM = 28960) # - 40250 ItemData.h (ITEM_METIN, ITEM_WEAPON, ITEM_ARMOR) class_name MetinSocketSystem extends RefCounted const ITEM_BROKEN_METIN_VNUM := 28960 # 碎石残片 Vnum (40250 官方标准) const VNUM_CLEAN_SOCKET_SCROLL := 71006 # 提石卷轴 / 清除残渣卷轴 (USE_CLEAN_SOCKET) const ITEM_TYPE_WEAPON := 1 const ITEM_TYPE_ARMOR := 2 const ITEM_TYPE_METIN := 10 const WEARABLE_WEAPON := 1 << 0 const WEARABLE_BODY := 1 << 1 # 魔石基础分类(根据 40250 官方属性定义,同一分类不可在同一装备上重复镶嵌) # 例如:突击石、穿透石、致命石、冷却石、怪物石、保护石、防御石、回避石、生命石等 const METIN_GROUPS := { # 穿透石 (28030..28430) "PENETRATE": [28030, 28130, 28230, 28330, 28430], # 致命石 / 暴击石 (28031..28431) "CRITICAL": [28031, 28131, 28231, 28331, 28431], # 冷却石 / 施法石 (28032..28432) "COOLDOWN": [28032, 28132, 28232, 28332, 28432], # 战士突击石 (28033..28433) "WARRIOR": [28033, 28133, 28233, 28333, 28433], # 刺客突击石 (28034..28434) "ASSASSIN": [28034, 28134, 28234, 28334, 28434], # 武士突击石 (28035..28435) "SURA": [28035, 28135, 28235, 28335, 28435], # 萨满突击石 (28036..28436) "SHAMAN": [28036, 28136, 28236, 28336, 28436], # 怪物石 (28037..28437) "MONSTER": [28037, 28137, 28237, 28337, 28437], # 躲闪石 / 回避石 (28038..28438) "EVASION": [28038, 28138, 28238, 28338, 28438], # 抵挡石 / 格挡石 (28039..28439) "BLOCK": [28039, 28139, 28239, 28339, 28439], # 坚韧石 / 魔法石 (28040..28440) "MAGIC": [28040, 28140, 28240, 28340, 28440], # 活力石 / 生命石 (28041..28441) "VITALITY": [28041, 28141, 28241, 28341, 28441], # 防御石 (28042..28442) "DEFENSE": [28042, 28142, 28242, 28342, 28442], # 速度石 (28043..28443) "SPEED": [28043, 28143, 28243, 28343, 28443], } # 官方默认镶嵌成功率:30%(40250 char_item.cpp:5070 number(1, 100) <= 30) const DEFAULT_METIN_SUCCESS_RATE := 30 ## 检查是否为魔石 static func is_metin_stone(vnum: int, proto: Dictionary = {}) -> bool: if proto and int(proto.get("type", 0)) == ITEM_TYPE_METIN: return true return vnum >= 28000 and vnum <= 28450 ## 检查是否为清除魔石残渣卷轴 static func is_clean_socket_scroll(vnum: int) -> bool: return vnum == VNUM_CLEAN_SOCKET_SCROLL ## 获取魔石的类别分组(用于防同类同插槽重复) static func get_metin_group(vnum: int) -> String: for grp in METIN_GROUPS.keys(): if vnum in METIN_GROUPS[grp]: return grp return str(vnum / 100) ## 检查装备能否镶嵌该魔石(武器魔石 vs 防具魔石) static func can_attach_metin(stone_vnum: int, stone_proto: Dictionary, target_proto: Dictionary) -> Dictionary: var target_type := int(target_proto.get("type", 0)) if target_type not in [ITEM_TYPE_WEAPON, ITEM_TYPE_ARMOR]: return {"ok": false, "code": "CANNOT_SOCKET_TYPE", "msg": "此物品类型无法镶嵌魔石。"} var stone_wear := int(stone_proto.get("wear_flag", 0)) if target_type == ITEM_TYPE_ARMOR: # 防具魔石必须匹配 WEARABLE_BODY (衣服) var sub := int(target_proto.get("sub_type", 0)) if sub != 0: # 0 = ARMOR_BODY return {"ok": false, "code": "ARMOR_NOT_BODY", "msg": "防具类魔石只能镶嵌在盔甲上。"} elif target_type == ITEM_TYPE_WEAPON: var sub := int(target_proto.get("sub_type", 0)) if sub == 5: # WEAPON_ARROW return {"ok": false, "code": "WEAPON_ARROW", "msg": "箭矢无法镶嵌魔石。"} return {"ok": true} ## 执行魔石镶嵌 (40250 char_item.cpp:5004-5080) static func socket_metin_stone(stone_vnum: int, stone_proto: Dictionary, target_item: Dictionary, target_proto: Dictionary, force_success := false, force_fail := false) -> Dictionary: var check := can_attach_metin(stone_vnum, stone_proto, target_proto) if not bool(check.get("ok", false)): return check var sockets: Array = target_item.get("sockets", [1, 1, 1]).duplicate() while sockets.size() < 3: sockets.append(1) var stone_grp := get_metin_group(stone_vnum) # 检查装备上是否已有同类魔石 (40250 char_item.cpp:5035 item->GetValue(5) == p->alValues[5]) for s in sockets: var sv := int(s) if sv > 2 and sv != ITEM_BROKEN_METIN_VNUM: if get_metin_group(sv) == stone_grp: return { "ok": false, "code": "DUPLICATE_METIN", "msg": "装备上已经镶嵌了同类型的魔石,无法重复镶嵌!" } # 寻找可用孔位 (40250: socket >= 1 && socket <= 2 表示开孔可用状态) var target_slot := -1 for i in sockets.size(): var sv := int(sockets[i]) if sv >= 1 and sv <= 2: target_slot = i break if target_slot == -1: return { "ok": false, "code": "NO_AVAILABLE_SOCKET", "msg": "此装备没有可用的空魔石插槽(已插满或被碎石堵塞)。" } # 判定成功/失败概率(40250 官方 30% 成功) var success := false if force_success: success = true elif force_fail: success = false else: success = (randi() % 100 + 1) <= DEFAULT_METIN_SUCCESS_RATE if success: sockets[target_slot] = stone_vnum target_item["sockets"] = sockets return { "ok": true, "code": "SUCCESS", "msg": "魔石成功镶嵌到了装备上!", "consumed": true, "socket_index": target_slot, "result_vnum": stone_vnum, } else: # 镶嵌失败:碎石堵塞孔位 (ITEM_BROKEN_METIN_VNUM = 28960) sockets[target_slot] = ITEM_BROKEN_METIN_VNUM target_item["sockets"] = sockets return { "ok": false, "code": "BROKEN_METIN", "msg": "魔石破碎了,碎石残渣堵塞了插槽!", "consumed": true, "socket_index": target_slot, "result_vnum": ITEM_BROKEN_METIN_VNUM, } ## 执行提石卷轴 / 清除碎石残渣 (40250 char_item.cpp:4510 USE_CLEAN_SOCKET) static func clean_broken_sockets(target_item: Dictionary) -> Dictionary: var sockets: Array = target_item.get("sockets", [1, 1, 1]).duplicate() while sockets.size() < 3: sockets.append(1) var has_broken := false for s in sockets: if int(s) == ITEM_BROKEN_METIN_VNUM: has_broken = true break if not has_broken: return { "ok": false, "code": "NO_BROKEN_STONE", "msg": "该装备插槽内没有碎石残渣,无需清除。" } # 40250 char_item.cpp:4526 算法: # 1. 把所有有效魔石(不等于 28960 且不为 0)按序向前压实 # 2. 剩余的孔位重新置为 1(可用空孔) var cleaned: Array = [] for s in sockets: var sv := int(s) if sv != ITEM_BROKEN_METIN_VNUM and sv > 2: cleaned.append(sv) while cleaned.size() < sockets.size(): cleaned.append(1) # 恢复为空孔位 1 target_item["sockets"] = cleaned return { "ok": true, "code": "SUCCESS", "msg": "成功清除了装备上的碎石残渣,插槽已恢复可用!", "consumed": true, "sockets": cleaned, }