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

131 lines
3.8 KiB
GDScript

# title_achievement_system.gd —— Metin2 40250 殿堂级头衔与成就系统 1:1
# 对照 40250 服务端 char.cpp, questmanager.cpp, locale_string.txt
class_name TitleAchievementSystem
extends RefCounted
signal achievement_unlocked(ach_id: String, ach_name: String, title: String)
signal title_equipped(title_name: String, bonuses: Dictionary)
signal title_unequipped()
const ACHIEVEMENTS: Dictionary = {
"ACH_DEMON_TOWER": {
"name": "恶魔之塔登顶",
"desc": "突破恶魔之塔 9 层并斩杀死神",
"title": "恶魔征服者",
"points": 50,
"bonuses": {"max_hp": 500}
},
"ACH_SPIDER_BARONESS": {
"name": "蛛皇陨落",
"desc": "在蜘蛛洞穴三层巢穴击败蜘蛛男爵夫人",
"title": "蛛巢克星",
"points": 50,
"bonuses": {"monster_dmg_pct": 5}
},
"ACH_BERAN_SETAOU": {
"name": "冰龙之诛",
"desc": "击破四大魔石并消灭冰龙贝兰-塞陶",
"title": "屠龙勇士",
"points": 100,
"bonuses": {"boss_dmg_pct": 10}
},
"ACH_AZRAEL": {
"name": "冥王末日",
"desc": "穿过地下墓穴七层击破冥王阿兹瑞尔",
"title": "深渊征服者",
"points": 100,
"bonuses": {"def": 50}
},
"ACH_FOUR_WORLD_BOSSES": {
"name": "四方平定",
"desc": "击杀野外九尾妖狐、沙漠龟、黄虎之魂与烈焰领主",
"title": "大陆守护者",
"points": 150,
"bonuses": {"critical_pct": 5}
},
"ACH_SKILL_GRANDMASTER": {
"name": "大巧不工",
"desc": "将任意核心战斗技能修炼至 P 级完美境界",
"title": "武学宗师",
"points": 80,
"bonuses": {"cast_speed": 10}
},
"ACH_EPIC_BLACKSMITH": {
"name": "神兵天成",
"desc": "成功完成修元平史诗武器或蓝钢神甲升阶转换",
"title": "神工锻造师",
"points": 80,
"bonuses": {"att": 50}
}
}
var unlocked_achievements: Dictionary = {} # ach_id -> bool
var unlocked_titles: Array = [] # list of title strings
var total_achievement_points: int = 0
var active_title: String = ""
# 追踪并达成成就
func complete_achievement(ach_id: String) -> Dictionary:
if not ACHIEVEMENTS.has(ach_id):
return {"ok": false, "reason": "INVALID_ACH_ID", "msg": "未知的成就标识!"}
if unlocked_achievements.get(ach_id, false):
return {"ok": false, "reason": "ALREADY_COMPLETED", "msg": "该成就已达成!"}
var cfg: Dictionary = ACHIEVEMENTS[ach_id]
unlocked_achievements[ach_id] = true
total_achievement_points += int(cfg["points"])
var title_name: String = cfg["title"]
if not unlocked_titles.has(title_name):
unlocked_titles.append(title_name)
achievement_unlocked.emit(ach_id, cfg["name"], title_name)
return {
"ok": true,
"ach_id": ach_id,
"name": cfg["name"],
"title": title_name,
"points_awarded": cfg["points"],
"total_points": total_achievement_points,
"msg": "达成里程碑成就【%s】!解锁荣耀称号【%s】!" % [cfg["name"], title_name]
}
# 佩戴称号
func equip_title(title_name: String) -> Dictionary:
if not unlocked_titles.has(title_name):
return {"ok": false, "reason": "TITLE_NOT_UNLOCKED", "msg": "你尚未解锁该头衔称号!"}
active_title = title_name
var bonuses := get_active_bonuses()
title_equipped.emit(title_name, bonuses)
return {
"ok": true,
"active_title": active_title,
"bonuses": bonuses,
"msg": "已成功佩戴头衔【%s】!战力属性已生效!" % title_name
}
# 卸下称号
func unequip_title() -> void:
active_title = ""
title_unequipped.emit()
# 获取当前佩戴称号的属性加成
func get_active_bonuses() -> Dictionary:
if active_title.is_empty():
return {}
for cfg in ACHIEVEMENTS.values():
if cfg["title"] == active_title:
return cfg["bonuses"].duplicate()
return {}
# 查询成就状态
func is_achievement_unlocked(ach_id: String) -> bool:
return unlocked_achievements.get(ach_id, false)