- 桥梁与静态物体高度采样修复: - 严格对齐 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 客户端对拍缺陷发现与修复工程指南
211 lines
5.9 KiB
GDScript
211 lines
5.9 KiB
GDScript
# epic_refine_system.gd —— Metin2 40250 修元平史诗级神装升阶转换锻造 1:1
|
|
# 对照 40250 服务端 refine.cpp, char_item.cpp, NPC 20091 (修元平 Seon-Pyeong)
|
|
class_name EpicRefineSystem
|
|
extends RefCounted
|
|
|
|
signal epic_refine_succeeded(old_vnum: int, new_vnum: int, item_data: Dictionary)
|
|
signal epic_refine_failed(reason: String)
|
|
|
|
const NPC_SEON_PYEONG := 20091
|
|
|
|
const ITEM_DRAGON_SCALE := 71123 # 龙鳞
|
|
const ITEM_DRAGON_CLAW := 71129 # 龙爪
|
|
const REQUIRED_SCALE_COUNT := 3
|
|
const REQUIRED_CLAW_COUNT := 4
|
|
const REQUIRED_YANG := 200000
|
|
|
|
# 官方 40250 经典史诗升阶配方表
|
|
const EPIC_RECIPES: Dictionary = {
|
|
# 武器篇:65 级 +9 武器 -> 80~90 级 +0 史诗神兵
|
|
169: {
|
|
"target_vnum": 279,
|
|
"name": "崔顿之剑+0",
|
|
"desc": "战神之剑+9 升阶为 崔顿之剑+0"
|
|
},
|
|
3159: {
|
|
"target_vnum": 3209,
|
|
"name": "神圣之剑+0",
|
|
"desc": "鬼牙刃+9 升阶为 神圣之剑+0"
|
|
},
|
|
1109: {
|
|
"target_vnum": 1139,
|
|
"name": "太阳之刃+0",
|
|
"desc": "党人斧+9 升阶为 太阳之刃+0"
|
|
},
|
|
1119: {
|
|
"target_vnum": 1129,
|
|
"name": "龙神刀+0",
|
|
"desc": "龙刀+9 升阶为 龙神刀+0"
|
|
},
|
|
2149: {
|
|
"target_vnum": 2159,
|
|
"name": "巨鸦弓+0",
|
|
"desc": "黄龙弓+9 升阶为 巨鸦弓+0"
|
|
},
|
|
5109: {
|
|
"target_vnum": 5119,
|
|
"name": "恶魔之杖+0",
|
|
"desc": "救赎之杖+9 升阶为 恶魔之杖+0"
|
|
},
|
|
# 铠甲篇:66 级 +9 黑钢铠甲 -> 90 级 +0 蓝钢铠甲
|
|
11299: {
|
|
"target_vnum": 12019,
|
|
"name": "蓝钢甲+0",
|
|
"desc": "黑钢甲+9 升阶为 蓝钢甲+0"
|
|
},
|
|
11499: {
|
|
"target_vnum": 12029,
|
|
"name": "蓝风甲+0",
|
|
"desc": "黑风甲+9 升阶为 蓝风甲+0"
|
|
},
|
|
11699: {
|
|
"target_vnum": 12039,
|
|
"name": "蓝仙袍+0",
|
|
"desc": "黑仙袍+9 升阶为 蓝仙袍+0"
|
|
},
|
|
11899: {
|
|
"target_vnum": 12049,
|
|
"name": "蓝龙袍+0",
|
|
"desc": "黑龙袍+9 升阶为 蓝龙袍+0"
|
|
}
|
|
}
|
|
|
|
# 判定物品是否可进行史诗升阶
|
|
func can_epic_refine(item: Dictionary) -> bool:
|
|
if item.is_empty():
|
|
return false
|
|
var vnum: int = int(item.get("vnum", 0))
|
|
return EPIC_RECIPES.has(vnum)
|
|
|
|
# 获取升阶配方详情
|
|
func get_recipe(vnum: int) -> Dictionary:
|
|
return EPIC_RECIPES.get(vnum, {})
|
|
|
|
# 执行史诗装备升阶转换锻造
|
|
func do_epic_refine(
|
|
target_slot: int,
|
|
inventory: Array,
|
|
player_data: Dictionary
|
|
) -> Dictionary:
|
|
if target_slot < 0 or target_slot >= inventory.size():
|
|
return {"ok": false, "reason": "INVALID_SLOT", "msg": "无效的装备栏位!"}
|
|
|
|
var base_item = inventory[target_slot]
|
|
if base_item == null:
|
|
return {"ok": false, "reason": "EMPTY_SLOT", "msg": "该格子上没有装备!"}
|
|
|
|
# 安全锁拦截
|
|
if base_item.get("is_locked", false):
|
|
return {"ok": false, "reason": "ITEM_LOCKED", "msg": "已加锁的装备无法进行史诗锻造!"}
|
|
|
|
var base_vnum: int = int(base_item.get("vnum", 0))
|
|
if not EPIC_RECIPES.has(base_vnum):
|
|
return {"ok": false, "reason": "NOT_AN_EPIC_BASE_ITEM", "msg": "该物品无法进行修元平史诗升阶!"}
|
|
|
|
var recipe: Dictionary = EPIC_RECIPES[base_vnum]
|
|
|
|
# 1. 检查金币 (Yang)
|
|
var current_yang: int = int(player_data.get("gold", 0))
|
|
if current_yang < REQUIRED_YANG:
|
|
return {
|
|
"ok": false,
|
|
"reason": "INSUFFICIENT_YANG",
|
|
"required_yang": REQUIRED_YANG,
|
|
"current_yang": current_yang,
|
|
"msg": "史诗锻造需要 200,000 金币!你的金币不足。"
|
|
}
|
|
|
|
# 2. 统计并收集龙鳞与龙爪
|
|
var scale_slots: Array = []
|
|
var claw_slots: Array = []
|
|
var total_scales: int = 0
|
|
var total_claws: int = 0
|
|
|
|
for i in range(inventory.size()):
|
|
var item = inventory[i]
|
|
if item == null:
|
|
continue
|
|
var v: int = int(item.get("vnum", 0))
|
|
var cnt: int = int(item.get("count", 1))
|
|
if v == ITEM_DRAGON_SCALE:
|
|
total_scales += cnt
|
|
scale_slots.append({"slot": i, "count": cnt})
|
|
elif v == ITEM_DRAGON_CLAW:
|
|
total_claws += cnt
|
|
claw_slots.append({"slot": i, "count": cnt})
|
|
|
|
if total_scales < REQUIRED_SCALE_COUNT:
|
|
return {
|
|
"ok": false,
|
|
"reason": "MISSING_DRAGON_SCALES",
|
|
"required": REQUIRED_SCALE_COUNT,
|
|
"current": total_scales,
|
|
"msg": "史诗锻造需要 3 个龙鳞!当前只有 %d 个。" % total_scales
|
|
}
|
|
|
|
if total_claws < REQUIRED_CLAW_COUNT:
|
|
return {
|
|
"ok": false,
|
|
"reason": "MISSING_DRAGON_CLAWS",
|
|
"required": REQUIRED_CLAW_COUNT,
|
|
"current": total_claws,
|
|
"msg": "史诗锻造需要 4 个龙爪!当前只有 %d 个。" % total_claws
|
|
}
|
|
|
|
# 3. 扣除金币
|
|
player_data["gold"] = current_yang - REQUIRED_YANG
|
|
|
|
# 4. 扣除材料 (龙鳞 3, 龙爪 4)
|
|
_consume_materials(inventory, scale_slots, REQUIRED_SCALE_COUNT)
|
|
_consume_materials(inventory, claw_slots, REQUIRED_CLAW_COUNT)
|
|
|
|
# 5. 生成新史诗装备,1:1 完整继承 5 条附加属性与已镶嵌灵石!
|
|
var new_vnum: int = recipe["target_vnum"]
|
|
var inherited_attrs: Array = []
|
|
if base_item.has("attributes"):
|
|
for attr in base_item["attributes"]:
|
|
inherited_attrs.append(attr.duplicate())
|
|
|
|
var inherited_sockets: Array = []
|
|
if base_item.has("sockets"):
|
|
for sock in base_item["sockets"]:
|
|
inherited_sockets.append(sock)
|
|
|
|
var epic_item: Dictionary = {
|
|
"vnum": new_vnum,
|
|
"name": recipe["name"],
|
|
"count": 1,
|
|
"attributes": inherited_attrs,
|
|
"sockets": inherited_sockets,
|
|
"socket_count": base_item.get("socket_count", 3),
|
|
"is_locked": false
|
|
}
|
|
|
|
# 替换背包中原位置的装备
|
|
inventory[target_slot] = epic_item
|
|
|
|
epic_refine_succeeded.emit(base_vnum, new_vnum, epic_item)
|
|
|
|
return {
|
|
"ok": true,
|
|
"old_vnum": base_vnum,
|
|
"new_vnum": new_vnum,
|
|
"item": epic_item,
|
|
"msg": "恭喜!修元平大师成功将装备升阶锻造为【%s】!" % recipe["name"]
|
|
}
|
|
|
|
# 辅助函数:按需从多个格子中扣除材料数量
|
|
func _consume_materials(inventory: Array, slots_info: Array, count_needed: int) -> void:
|
|
var needed := count_needed
|
|
for info in slots_info:
|
|
var s: int = info["slot"]
|
|
var have: int = info["count"]
|
|
if have <= needed:
|
|
needed -= have
|
|
inventory[s] = null
|
|
else:
|
|
inventory[s]["count"] = have - needed
|
|
needed = 0
|
|
if needed <= 0:
|
|
break
|