Files
mtgodot-poc/project/dragon_soul_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

216 lines
7.3 KiB
GDScript

# dragon_soul_system.gd —— Metin2 40250 龙神炼金石系统 1:1
# 对照 40250 服务端 DragonSoul.cpp, char_dragonsoul.cpp 及客户端 uidragonsoul.py
class_name DragonSoulSystem
extends RefCounted
signal deck_activated(deck_idx: int)
signal deck_deactivated(deck_idx: int)
# 6 种官方龙神石类型 (DragonSoul.cpp:EDSStoneTypes)
enum DragonStoneType {
NONE = 0,
DIAMOND = 1, # 钻石: INT, 魔法抗性
RUBY = 2, # 红宝石: STR, 物理攻击力, 防御力
JADE = 3, # 玉石: Max HP, HP 恢复
SAPPHIRE = 4,# 蓝宝石: DEX, 职业减伤
GARNET = 5, # 石榴石: CON, Max SP
ONYX = 6 # 缟玛瑙: 格挡, 反弹, 穿透抵抗
}
# 5 档品阶 (Grade)
enum Grade {
ROUGH = 0, # 粗糙
CUT = 1, # 切割
RARE = 2, # 稀有
ANCIENT = 3, # 古代
LEGENDARY = 4 # 传奇
}
# 5 档净度 (Clarity)
enum Clarity {
MATT = 0, # 哑光
CLEAR = 1, # 清晰
FLAWLESS = 2, # 无瑕
BRILLIANT = 3, # 辉煌
EXCELLENT = 4 # 卓越
}
const DECK_SKY := 0 # 天空卡槽
const DECK_EARTH := 1 # 大地卡槽
const DECK_COUNT := 2
const SLOTS_PER_DECK := 6 # 每卡槽 6 个神石槽位
# 双卡槽数据结构: decks[deck_idx][slot_idx] = stone_dict or null
var decks: Array[Array] = [[], []]
var active_deck: int = -1 # -1: 未激活, 0: 天空卡槽, 1: 大地卡槽
func _init() -> void:
for d in range(DECK_COUNT):
var slot_list: Array = []
for s in range(SLOTS_PER_DECK):
slot_list.append(null)
decks[d] = slot_list
# 创建一颗龙神石
static func create_dragon_stone(type: int, grade: int = 0, clarity: int = 0, refine: int = 0, duration: float = 86400.0) -> Dictionary:
var stone: Dictionary = {
"stone_type": type,
"grade": clampi(grade, 0, 4),
"clarity": clampi(clarity, 0, 4),
"refine": clampi(refine, 0, 6),
"duration": duration, # 激活消耗时间 (秒)
"total_duration": duration
}
stone["attributes"] = calculate_stone_attributes(stone)
return stone
# 计算单颗神石属性 (对齐 40250 DragonSoulTable.txt)
static func calculate_stone_attributes(stone: Dictionary) -> Dictionary:
var type: int = int(stone.get("stone_type", 0))
var gr: int = int(stone.get("grade", 0))
var cl: int = int(stone.get("clarity", 0))
var rf: int = int(stone.get("refine", 0))
var attrs: Dictionary = {}
match type:
DragonStoneType.DIAMOND:
attrs["int_bonus"] = 5 + (gr * 3) + (cl * 2) + rf
attrs["magic_defense"] = 2 + (gr * 2) + (cl * 2) + (rf * 2)
DragonStoneType.RUBY:
attrs["str_bonus"] = 5 + (gr * 3) + (cl * 2) + rf
attrs["att_grade"] = 20 + (gr * 15) + (cl * 10) + (rf * 5)
attrs["def_grade"] = 15 + (gr * 10) + (cl * 5) + (rf * 4)
DragonStoneType.JADE:
attrs["max_hp"] = 300 + (gr * 250) + (cl * 150) + (rf * 100)
attrs["hp_recovery_pct"] = 3 + gr + cl + rf
DragonStoneType.SAPPHIRE:
attrs["dex_bonus"] = 5 + (gr * 3) + (cl * 2) + rf
attrs["resist_warrior"] = 2 + gr + cl + rf
attrs["resist_assassin"] = 2 + gr + cl + rf
DragonStoneType.GARNET:
attrs["con_bonus"] = 5 + (gr * 3) + (cl * 2) + rf
attrs["max_sp"] = 200 + (gr * 150) + (cl * 100) + (rf * 50)
DragonStoneType.ONYX:
attrs["block_pct"] = 2 + (gr * 2) + cl + rf
attrs["reflect_pct"] = 2 + (gr * 2) + cl + rf
return attrs
# 装备神石到卡槽
func equip_stone(deck_idx: int, stone: Dictionary) -> Dictionary:
if deck_idx < 0 or deck_idx >= DECK_COUNT:
return {"ok": false, "reason": "INVALID_DECK"}
var type: int = int(stone.get("stone_type", 0))
if type < 1 or type > 6:
return {"ok": false, "reason": "INVALID_STONE_TYPE"}
var slot_idx: int = type - 1 # 1~6 映射到 0~5 号槽位
decks[deck_idx][slot_idx] = stone
return {
"ok": true,
"deck": deck_idx,
"slot": slot_idx,
"stone": stone
}
# 卸下神石
func unequip_stone(deck_idx: int, slot_idx: int) -> Dictionary:
if deck_idx < 0 or deck_idx >= DECK_COUNT:
return {"ok": false, "reason": "INVALID_DECK"}
if slot_idx < 0 or slot_idx >= SLOTS_PER_DECK:
return {"ok": false, "reason": "INVALID_SLOT"}
var removed = decks[deck_idx][slot_idx]
decks[deck_idx][slot_idx] = null
return {"ok": true, "stone": removed}
# 激活/停用卡槽
func toggle_deck(deck_idx: int) -> bool:
if active_deck == deck_idx:
active_deck = -1
deck_deactivated.emit(deck_idx)
return false
else:
if active_deck != -1:
deck_deactivated.emit(active_deck)
active_deck = deck_idx
deck_activated.emit(deck_idx)
return true
# 计算当前已激活卡槽的总战力属性
func get_active_bonuses() -> Dictionary:
var total_bonuses: Dictionary = {}
if active_deck < 0 or active_deck >= DECK_COUNT:
return total_bonuses
var deck: Array = decks[active_deck]
for slot in deck:
if slot != null and float(slot.get("duration", 0.0)) > 0.0:
var attrs: Dictionary = slot.get("attributes", {})
for key in attrs:
total_bonuses[key] = int(total_bonuses.get(key, 0)) + int(attrs[key])
return total_bonuses
# 逐帧消耗激活中的龙神石能量时间
func update(dt: float) -> Array:
var expired_stones: Array = []
if active_deck < 0 or active_deck >= DECK_COUNT:
return expired_stones
var deck: Array = decks[active_deck]
for i in range(deck.size()):
var stone = deck[i]
if stone != null:
var dur: float = float(stone.get("duration", 0.0))
dur -= dt
if dur <= 0.0:
dur = 0.0
expired_stones.append({"deck": active_deck, "slot": i, "stone": stone})
stone["duration"] = dur
return expired_stones
# 升阶 (Grade Refine): 2 颗同种类、同品阶神石融合升级
static func refine_grade(stone_a: Dictionary, stone_b: Dictionary, force_success: bool = false) -> Dictionary:
var ta: int = int(stone_a.get("stone_type", 0))
var tb: int = int(stone_b.get("stone_type", 0))
var ga: int = int(stone_a.get("grade", 0))
var gb: int = int(stone_b.get("grade", 0))
if ta != tb or ga != gb:
return {"ok": false, "reason": "MISMATCHED_STONES", "msg": "只有同种类且同品阶的神石才能融合升阶!"}
if ga >= Grade.LEGENDARY:
return {"ok": false, "reason": "MAX_GRADE", "msg": "该神石已达到最高传奇品阶!"}
# 成功率 60%
var success: bool = force_success or randf() < 0.60
if success:
var new_stone = create_dragon_stone(ta, ga + 1, 0, 0)
return {"ok": true, "success": true, "stone": new_stone, "msg": "神石品阶提升成功!"}
else:
return {"ok": true, "success": false, "msg": "升阶失败,融合材料已碎裂。"}
# 升净度 (Clarity Refine): 2 颗同种类、同品阶、同净度神石融合升级
static func refine_clarity(stone_a: Dictionary, stone_b: Dictionary, force_success: bool = false) -> Dictionary:
var ta: int = int(stone_a.get("stone_type", 0))
var tb: int = int(stone_b.get("stone_type", 0))
var ca: int = int(stone_a.get("clarity", 0))
var cb: int = int(stone_b.get("clarity", 0))
if ta != tb or ca != cb:
return {"ok": false, "reason": "MISMATCHED_STONES", "msg": "只有同种类且同净度的神石才能融合提升净度!"}
if ca >= Clarity.EXCELLENT:
return {"ok": false, "reason": "MAX_CLARITY", "msg": "该神石已达到最高卓越净度!"}
var success: bool = force_success or randf() < 0.50
if success:
var ga: int = int(stone_a.get("grade", 0))
var new_stone = create_dragon_stone(ta, ga, ca + 1, 0)
return {"ok": true, "success": true, "stone": new_stone, "msg": "神石净度提升成功!"}
else:
return {"ok": true, "success": false, "msg": "净度提升失败,辅助材料碎裂。"}