Files
mtgodot-poc/project/jewelry_socket_system.gd
T
shenandshen 66d217b313 feat(client): 完成40250客户端核心功能1:1对齐与桥梁高度采样修复
- 桥梁与静态物体高度采样修复:
  - 严格对齐 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 客户端对拍缺陷发现与修复工程指南
2026-09-19 08:51:25 -07:00

176 lines
6.3 KiB
GDScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# jewelry_socket_system.gd —— Metin2 40250 首饰饰品金刚石打孔与精炼宝石镶嵌 1:1
# 对照 40250 服务端 char_item.cpp (PutGemInAccessory), item_attribute.cpp
class_name JewelrySocketSystem
extends RefCounted
const ITEM_DIAMOND := 50621 # 精炼金刚石
const MAX_SOCKETS := 3
const GEM_LIFETIME_SECS := 86400.0 # 24 小时战斗佩戴寿命
# 饰品支持打孔的分类 (14:手镯, 16:项链, 17:耳环)
const VALID_ACCESSORY_PREFIXES := [14, 16, 17]
# 饰品材质与对应精炼宝石映射 (base_vnum_pattern -> gem_vnum)
const ACCESSORY_GEM_MAPPING: Dictionary = {
"copper": 50624, # 铜首饰 -> 铜 50624
"silver": 50625, # 银首饰 -> 银 50625
"gold": 50626, # 金首饰 -> 金 50626
"jade": 50627, # 玉首饰 -> 玉 50627
"ebony": 50628, # 黑檀首饰 -> 黑檀 50628
"pearl": 50629, # 珍珠首饰 -> 珍珠 50629
"white_gold": 50630, # 白金首饰 -> 白金 50630
"crystal": 50631, # 水晶首饰 -> 水晶 50631
"amethyst": 50632, # 紫晶首饰 -> 紫晶 50632
"heavens_tear": 50633,# 天泪首饰 -> 天泪石 50633
"soul_crystal": 50634,# 灵魂水晶首饰 -> 灵魂水晶 50634
"ruby": 50635, # 红宝石首饰 -> 红宝石 50635
"garnet": 50636, # 石榴石首饰 -> 石榴石 50636
"emerald": 50637, # 绿宝石首饰 -> 绿宝石 50637
"sapphire": 50638 # 蓝宝石首饰 -> 蓝宝石 50638
}
# 打孔成功率 (0孔打第1孔: 70%, 1孔打第2孔: 50%, 2孔打第3孔: 30%)
const DRILL_CHANCES: Array[float] = [0.70, 0.50, 0.30]
# 宝石阶梯增益百分比 (第1孔: +10%, 第2孔: +10%, 第3孔: +20%)
const GEM_BONUS_TIERS: Array[float] = [0.10, 0.10, 0.20]
static func is_accessory(item: Dictionary) -> bool:
var vnum: int = int(item.get("vnum", 0))
var prefix: int = int(floor(float(vnum) / 1000.0))
return prefix in VALID_ACCESSORY_PREFIXES
# 检查是否可用金刚石打孔
static func can_drill_socket(item: Dictionary) -> Dictionary:
if not is_accessory(item):
return {"ok": false, "reason": "NOT_ACCESSORY", "msg": "金刚石只能用于手镯、项链或耳环打孔!"}
var socket_count: int = int(item.get("socket_count", 0))
if socket_count >= MAX_SOCKETS:
return {"ok": false, "reason": "SOCKETS_FULL", "msg": "该首饰已拥有最大的 3 个宝石孔槽!"}
return {"ok": true, "current_sockets": socket_count}
# 使用金刚石 50621 打孔
static func drill_socket(item: Dictionary, diamond_slot_item: Dictionary, force_success: bool = false) -> Dictionary:
var check = can_drill_socket(item)
if not check["ok"]:
return check
var dvnum: int = int(diamond_slot_item.get("vnum", 0))
if dvnum != ITEM_DIAMOND:
return {"ok": false, "reason": "NOT_DIAMOND", "msg": "打孔必须使用精炼金刚石!"}
var current_sockets: int = int(item.get("socket_count", 0))
var chance: float = DRILL_CHANCES[current_sockets]
var roll: float = 0.0 if force_success else randf()
var success: bool = roll < chance
if not item.has("sockets"):
item["sockets"] = [0, 0, 0]
if not item.has("gem_times"):
item["gem_times"] = [0.0, 0.0, 0.0]
if success:
item["socket_count"] = current_sockets + 1
return {
"ok": true,
"success": true,
"socket_count": item["socket_count"],
"msg": "打孔成功!已开辟出第 %d 个宝石孔槽!" % item["socket_count"]
}
else:
return {
"ok": true,
"success": false,
"socket_count": current_sockets,
"msg": "打孔失败!金刚石破碎,未能开出孔槽。"
}
# 镶嵌精炼矿石宝石
static func insert_gem(item: Dictionary, gem_item: Dictionary, required_gem_vnum: int = 0) -> Dictionary:
if not is_accessory(item):
return {"ok": false, "reason": "NOT_ACCESSORY"}
var socket_count: int = int(item.get("socket_count", 0))
if socket_count <= 0:
return {"ok": false, "reason": "NO_SOCKETS_DRILLED", "msg": "该首饰尚未打孔,请先使用金刚石开孔!"}
if not item.has("sockets"):
item["sockets"] = [0, 0, 0]
if not item.has("gem_times"):
item["gem_times"] = [0.0, 0.0, 0.0]
# 查找第一个空孔
var empty_slot := -1
for i in range(socket_count):
if int(item["sockets"][i]) == 0:
empty_slot = i
break
if empty_slot == -1:
return {"ok": false, "reason": "NO_EMPTY_SOCKET", "msg": "所有已开辟的孔槽都已镶嵌满宝石!"}
var gem_vnum: int = int(gem_item.get("vnum", 0))
if gem_vnum < 50622 or gem_vnum > 50638:
return {"ok": false, "reason": "INVALID_GEM", "msg": "这不是有效的精炼宝石!"}
if required_gem_vnum > 0 and gem_vnum != required_gem_vnum:
return {"ok": false, "reason": "GEM_MISMATCH", "msg": "此宝石与该首饰材质不匹配!"}
# 镶嵌成功
item["sockets"][empty_slot] = gem_vnum
item["gem_times"][empty_slot] = GEM_LIFETIME_SECS
var active_gems := count_active_gems(item)
var bonus_pct := calculate_bonus_percentage(active_gems)
return {
"ok": true,
"slot": empty_slot,
"gem_vnum": gem_vnum,
"active_gems": active_gems,
"total_bonus_pct": bonus_pct,
"lifetime": GEM_LIFETIME_SECS,
"msg": "宝石成功镶入第 %d 个孔槽!固有属性提升 %d%%" % [empty_slot + 1, int(round(bonus_pct * 100))]
}
# 统计当前生效的宝石数量
static func count_active_gems(item: Dictionary) -> int:
var count := 0
var sockets: Array = item.get("sockets", [])
var times: Array = item.get("gem_times", [])
for i in range(sockets.size()):
if int(sockets[i]) > 0 and float(times[i]) > 0.0:
count += 1
return count
# 计算宝石提供的总属性加成百分比 (0~3 颗宝石)
static func calculate_bonus_percentage(gem_count: int) -> float:
var pct: float = 0.0
for i in range(mini(gem_count, GEM_BONUS_TIERS.size())):
pct += GEM_BONUS_TIERS[i]
return pct
# 佩戴战斗时间自然衰减 (dt: 秒)
static func update_gem_endurance(item: Dictionary, dt: float) -> Dictionary:
var expired_gems: Array[int] = []
if not item.has("sockets") or not item.has("gem_times"):
return {"expired": expired_gems}
var sockets: Array = item["sockets"]
var times: Array = item["gem_times"]
for i in range(sockets.size()):
if int(sockets[i]) > 0 and float(times[i]) > 0.0:
times[i] = maxf(0.0, float(times[i]) - dt)
if float(times[i]) <= 0.0:
expired_gems.append(int(sockets[i]))
sockets[i] = 0 # 宝石破碎,孔槽重新清空
return {
"expired": expired_gems,
"active_gems": count_active_gems(item),
"total_bonus_pct": calculate_bonus_percentage(count_active_gems(item))
}