TrinityCore 成就与称号系统开发:从 DBC 定义到完成判定的完整链路 原创
引言
成就(Achievement)给玩家提供了”探索、收集、挑战”的长期目标,称号(Title)则是把成就外显在名字上的荣誉标识——”完成某史诗伟业的玩家”会被全服一眼认出。TrinityCore 中这套系统大多数据驱动:成就定义引用客户端数据,完成条件由核心逻辑或脚本判定,称号作为奖励发放。本文拆解成就数据结构、完成判定链路、自定义成就与称号的完整开发方法。
二、成就的数据基础
成就的元数据(名称、描述、图标、点数、分类)来自客户端的 Achievement.dbc,服务端 achievement_criteria_data 等表补充判定条件。这意味着:
- 完全复刻官方成就:客户端 DBC 已带定义,服务端只负责判定与记录
- 自定义新成就:除了服务端逻辑,还需修改客户端 Achievement.dbc(通过私服补丁),否则客户端无法显示该成就
相关表
| 表 | 作用 |
|---|---|
| achievement_criteria_data | 为成就的判定标准(criteria)附加额外条件(如限定地图、阵营、目标) |
| achievement_reward | 非 DBC 定义时的服务端奖励补充(物品、称号、金钱等) |
| character_achievement | 角色已完成的成就(运行时写入) |
| character_achievement_progress | 角色某条 criteria 的当前进度(如击杀计数) |
成就 ID 和 criteria ID 的主定义在 DBC,服务端表通过这些 ID 关联。Achievement.dbc 中一条成就包含若干 criteria(子条件),可能需要全部满足或满足其一,取决于 DBC 配置。
二、成就完成的判定类型
成就判定标准有多种类型(criteria type),核心内置了常见类型的自动统计:
| 类型 | 触发场景 | 示例成就 |
|---|---|---|
| Kill Creature | 击杀指定生物累计 | 击杀某副本 BOSS |
| Win Battleground / Arena | 战场/竞技场获胜 | 战场百胜 |
| Reach Level | 达到等级 | 升到 80 级 |
| Complete Quests | 完成任务数量/指定任务 | 任务大师 |
| Complete Achievement | 以其他成就为前提 | 综合成就(英雄的荣耀) |
| Explore Area | 探索区域 | 地图探索者 |
| Own Item / Equip Item | 拥有/装备物品 | 坐骑/宠物收集 |
| Use Item / Cast Spell | 使用物品/施放法术 | 节日动作成就 |
| Death / Fall / Die | 特定方式死亡 | 坠落、特殊死亡成就 |
| Money / Reputation / Skill | 金钱/声望/技能等级 | 声望崇拜、专业满点 |
| Criteria Complete (Script) | 脚本自定义判定 | 复杂的自定义成就 |
这些内置类型由核心在对应事件发生时自动更新进度,不需要写脚本——比如击杀 BOSS 时,成就系统检查是否有相关 criteria 并累加。
三、achievement_criteria_data 附加条件
同一条 criteria 想加限制(必须在某地图、某难度、某阵营下完成),用 achievement_criteria_data:
-- type 字段表示附加条件类型,value 为参数
-- 常见:限定地图(map)、副本难度、阵营、击杀的目标 entry 等
SELECT criteria_id, type, value1, value2
FROM achievement_criteria_data
WHERE criteria_id IN (SELECT ID FROM achievement_criteria WHERE achievement IN (...));
例如”在英雄难度下击杀某 BOSS”这种成就,DBC criteria 指向击杀事件,再由 criteria_data 附加”难度=英雄”的条件,普通难度击杀就不计数。这样复用同一击杀事件,靠附加条件区分多个成就。
四、运行时完成链路
玩家达成某个条件时的完整链路:
- 游戏事件发生(击杀/升级/完成任务/获得物品…)
- AchievementMgr 收到通知,找到所有匹配的 criteria
- 校验 achievement_criteria_data 的附加条件(地图/难度/阵营)
- 更新
character_achievement_progress的计数 - 进度达标且成就全部 criteria 满足 → 写入
character_achievement - 向客户端发送成就完成包,弹出成就提示、播放音效、加成就点数
- 发放奖励:物品、坐骑、称号,以及成就点数统计
进度是增量保存的,所以”击杀 100 次”这类成就玩家分多次登录也能累积,不用一次完成。
五、称号系统
称号本质是一个可显示在角色名前后的文本标识,数据来自 CharTitles.dbc,字段含称号文本、性别差异、显示位置(名前/名后)。获得途径通常是成就奖励或特殊条件。
角色已获得的称号
角色拥有的称号记录在 characters 库的角色数据中(known titles 位掩码字段,如 known_titles / 相关字段)。每获得一个称号就置对应位,客户端据此在称号选择列表里显示。当前选用的称号也记录在角色字段上(如 title 字段保存当前 title id)。
称号与成就挂钩
官方设计里,很多称号是某成就的 DBC 奖励:成就完成时自动解锁关联称号。自定义称号奖励时:
- 称号文本必须在 CharTitles.dbc 中存在(私服自定义称号需改 DBC + 客户端补丁)
- 服务端在成就完成时调用授予称号的接口置位
六、脚本自定义成就
内置类型表达不了的复杂判定(多条件组合、动态阶段、自定义事件),用 PlayerScript / AchievementScript 在事件发生时手动更新 criteria:
// 在自定义事件完成时,推进指定成就的 criteria
// criteriaId 为 Achievement.dbc 中定义的判定标准 ID
player->AchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_BE_SPELL_TARGET, spellId);
// 或直接完成脚本类型的 criteria
player->AchievementMgr().UpdateAchievementCriteria(
ACHIEVEMENT_CRITERIA_TYPE_SCRIPT, customScriptId);
// 也可在满足完全自定义的条件后,直接授予
// (具体接口随版本,核心是 CriteriaProgress + CompletedAchievement)
常用触发入口:
class player_achv_custom : public PlayerScript
{
public:
player_achv_custom() : PlayerScript("player_achv_custom") {}
void OnPVPKill(Player* killer, Unit* killed) override
{
// 自定义 PVP 成就:如在指定地点击杀
if (killer->GetMapId() == 489)
killer->GetAchievementMgr().UpdateAchievementCriteria(
ACHIEVEMENT_CRITERIA_TYPE_KILL_CREATURE, /*entry*/ 0);
}
void OnCreatureKill(Player* player, Creature* creature) override
{
// 击杀特定 BOSS 推进自定义成就
if (creature->GetEntry() == 90001)
player->GetAchievementMgr().UpdateAchievementCriteria(
ACHIEVEMENT_CRITERIA_TYPE_KILL_CREATURE, 90001);
}
};
要点:脚本只负责”在正确的时机调用 UpdateAchievementCriteria”,进度累计、是否达标、发奖仍由 AchievementMgr 统一处理,不要自己直接写 character_achievement 表,否则容易和正常链路不一致。
七、GM 调试
# 查看/完成成就
.achievement add <achievementId> # 直接赋予某成就(测试奖励/称号)
.achievement remove <id> # 移除
# 部分版本:
.cheat achievement 1 # 成就作弊辅助(依服务端版本)
# 称号测试
.titles # 部分服务端有称号相关命令,或直接给成就解锁
# 修改后重载
.reload achievement_criteria_data
调试技巧:复杂成就先用 GM 命令直接 .achievement add 验证奖励(物品/称号/点数)是否正确,再反过来调试”自然达成”的判定脚本。排查”进度不涨”时,重点看事件钩子是否触发、criteria type 是否匹配、criteria_data 附加条件(地图/难度/阵营)是否把这次行动过滤掉了。
八、自定义成就与称号完整流程
- 客户端侧:在 Achievement.dbc 加新成就和 criteria,在 CharTitles.dbc 加新称号(私服补丁),定义名称、图标、点数、奖励关联
- 服务端侧:需要附加限制就写 achievement_criteria_data;内置类型不覆盖的判定,写 PlayerScript 在事件中 UpdateAchievementCriteria
- 奖励:需要服务端补奖励(物品/金钱)时写 achievement_reward,称号通过 DBC 关联或脚本授予
- 验证:.achievement add 验证奖励,再实测自然触发,检查进度表和角色称号位
九、常见坑
- 客户端不显示:纯服务端加的成就/称号 DBC 里没有,客户端看不到,必须配合客户端补丁
- 进度不累计:criteria type 选错(事件类型不匹配),或 criteria_data 的地图/难度/阵营条件把行动过滤掉
- 直接写表绕过管理器:手动 INSERT character_achievement 会跳过发奖、点数、客户端通知,应走 AchievementMgr 接口
- 称号位掩码错位:CharTitles.dbc 的 bit index 和服务端置位不一致,导致解锁了错误称号或选不中
- 多 criteria 逻辑理解错:有的成就要求所有 criteria 满足,有的任一即可,以 DBC 配置为准,别想当然
- 重置/转服残留:角色迁移或成就重置时要同时处理 progress 和 completed 两张表,避免”已完成但进度还在”的脏状态
- 阵营限制遗漏:联盟/部落专属成就忘了加阵营条件,导致对立阵营也能完成
总结
TrinityCore 成就与称号系统的设计是”客户端 DBC 定义呈现,服务端 AchievementMgr 统一判定与发奖”。内置的十几类 criteria 覆盖了击杀、升级、任务、探索、收集、PVP 等绝大多数场景并自动统计,achievement_criteria_data 用附加条件区分地图/难度/阵营,脚本只在复杂自定义判定时调用 UpdateAchievementCriteria,绝不直接写完成表。称号作为成就奖励,其文本来自 CharTitles.dbc,授予靠置位。做自定义内容时务必记住”成就和称号都依赖客户端 DBC”,服务端逻辑和客户端补丁要同步,再用 GM 命令先验奖励、后验判定。