TrinityCore 脚本系统深度解析:从基础到自定义 Boss 实战 原创
温馨提示:
本文最后更新于 2026-06-18,已超过 0 天没有更新。
若文章内的图片失效(无法正常加载),请留言反馈或直接 联系我。
一、TrinityCore 的脚本系统概览
TrinityCore 的脚本系统是其核心扩展机制,负责处理从 NPC 对话到副本 Boss 战的所有游戏逻辑。与 AzerothCore 的 Module 系统不同,TrinityCore 的脚本系统更加灵活,支持多种脚本类型和动态加载机制。
1.1 脚本类型
TrinityCore 提供了丰富的脚本基类,覆盖了游戏中的所有实体:
| 脚本基类 | 作用对象 | 典型用途 |
|---|---|---|
CreatureScript |
生物(NPC、怪物) | 对话、AI 行为、掉落逻辑 |
GameObjectScript |
游戏对象(宝箱、门、火盆) | 交互逻辑、事件触发 |
SpellScript |
法术 | 自定义法术效果、伤害计算 |
PlayerScript |
玩家 | 登录/登出、升级、成就 |
InstanceScript |
副本实例 | 副本状态管理、Boss 战流程 |
WorldScript |
世界服务器 | 全局事件、定时任务 |
ConditionScript |
条件系统 | 自定义条件判断 |
二、脚本注册机制
2.1 静态注册
TrinityCore 使用宏来注册脚本,这些宏会在编译时自动将脚本注册到全局脚本注册表中:
// 在 .cpp 文件末尾
void AddSC_boss_onyxia()
{
new boss_onyxia();
new mob_onyxia_whelp();
new npc_onyxia_lair_guard();
}
2.2 动态加载
从 TrinityCore 的后期版本开始,脚本支持动态加载(.so 文件):
// 编译为动态库
// CMakeLists.txt
add_library(mod_my_scripts SHARED
MyCreatureScript.cpp
MySpellScript.cpp
)
// 在服务器控制台加载
server reload scripts
// 或通过配置文件
ScriptLibs = "mod_my_scripts"
三、实战:自定义 Boss 脚本
下面我们从一个完整的 Boss 脚本入手,展示 TrinityCore 脚本开发的全流程。
3.1 Boss 设计
- Boss 名称:暗影领主莫甘
- 阶段:2 个阶段(P1 暗影形态 / P2 虚空形态)
- 技能:暗影箭雨、虚空裂隙、暗影护盾
- 狂暴机制:10 分钟后狂暴
3.2 头文件
// boss_shadow_lord_morgan.h
#ifndef BOSS_SHADOW_LORD_MORGAN_H
#define BOSS_SHADOW_LORD_MORGAN_H
#include "ScriptMgr.h"
#include "ScriptedCreature.h"
#include "Player.h"
#include "Spell.h"
#include "SpellAuras.h"
#include "SpellAuraEffects.h"
enum Spells
{
SPELL_SHADOW_BOLT_VOLLEY = 70001,
SPELL_VOID_RIFT = 70002,
SPELL_SHADOW_SHIELD = 70003,
SPELL_VOID_TRANSFORMATION = 70004,
SPELL_BERSERK = 70005,
SPELL_SHADOW_CRASH = 70006
};
enum Events
{
EVENT_SHADOW_BOLT_VOLLEY = 1,
EVENT_VOID_RIFT = 2,
EVENT_SHADOW_SHIELD = 3,
EVENT_PHASE_TWO = 4,
EVENT_BERSERK = 5,
EVENT_SHADOW_CRASH = 6
};
enum Phases
{
PHASE_ONE = 1,
PHASE_TWO = 2
};
// 位置常量
const Position voidRiftPositions[] =
{
{ 100.0f, 200.0f, 30.0f, 0.0f },
{ 150.0f, 250.0f, 30.0f, 0.0f },
{ 200.0f, 200.0f, 30.0f, 0.0f }
};
#endif
3.3 核心实现
// boss_shadow_lord_morgan.cpp
#include "boss_shadow_lord_morgan.h"
#include "GameObject.h"
#include "Map.h"
#include "MotionMaster.h"
#include "TemporarySummon.h"
struct boss_shadow_lord_morgan : public BossAI
{
boss_shadow_lord_morgan(Creature* creature) : BossAI(creature, DATA_SHADOW_LORD_MORGAN)
{
Initialize();
}
void Initialize()
{
_phase = PHASE_ONE;
}
void Reset() override
{
_Reset();
Initialize();
me->SetReactState(REACT_AGGRESSIVE);
}
void JustEngagedWith(Unit* who) override
{
BossAI::JustEngagedWith(who);
me->SetReactState(REACT_AGGRESSIVE);
// 注册事件
events.ScheduleEvent(EVENT_SHADOW_BOLT_VOLLEY, 8s);
events.ScheduleEvent(EVENT_VOID_RIFT, 15s);
events.ScheduleEvent(EVENT_SHADOW_SHIELD, 25s);
events.ScheduleEvent(EVENT_PHASE_TWO, 60s);
events.ScheduleEvent(EVENT_BERSERK, 10min);
}
void JustDied(Unit* /*killer*/) override
{
_JustDied();
Talk(SAY_DEATH);
}
void UpdateAI(uint32 diff) override
{
if (!UpdateVictim())
return;
events.Update(diff);
if (me->HasUnitState(UNIT_STATE_CASTING))
return;
while (uint32 eventId = events.ExecuteEvent())
{
switch (eventId)
{
case EVENT_SHADOW_BOLT_VOLLEY:
DoCastAOE(SPELL_SHADOW_BOLT_VOLLEY);
events.Repeat(10s, 15s);
break;
case EVENT_VOID_RIFT:
if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0))
{
DoCast(target, SPELL_VOID_RIFT);
}
events.Repeat(20s, 25s);
break;
case EVENT_SHADOW_SHIELD:
DoCast(me, SPELL_SHADOW_SHIELD);
Talk(SAY_SHIELD);
events.Repeat(30s);
break;
case EVENT_PHASE_TWO:
EnterPhaseTwo();
break;
case EVENT_BERSERK:
DoCast(me, SPELL_BERSERK);
Talk(SAY_BERSERK);
break;
case EVENT_SHADOW_CRASH:
if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0))
{
DoCast(target, SPELL_SHADOW_CRASH);
}
events.Repeat(8s, 12s);
break;
}
}
DoMeleeAttackIfReady();
}
void EnterPhaseTwo()
{
_phase = PHASE_TWO;
Talk(SAY_PHASE_TWO);
// 虚空转化
DoCast(me, SPELL_VOID_TRANSFORMATION);
// 清除 P1 事件
events.CancelEvent(EVENT_SHADOW_BOLT_VOLLEY);
events.CancelEvent(EVENT_SHADOW_SHIELD);
// 注册 P2 事件
events.ScheduleEvent(EVENT_SHADOW_CRASH, 5s);
events.ScheduleEvent(EVENT_VOID_RIFT, 10s);
}
private:
uint8 _phase;
};
// 注册脚本
void AddSC_boss_shadow_lord_morgan()
{
new boss_shadow_lord_morgan();
}
四、脚本调试技巧
4.1 日志系统
// TrinityCore 日志级别
TC_LOG_DEBUG("scripts", "Boss %s entered phase two", me->GetName().c_str());
TC_LOG_INFO("scripts.boss", "Shadow Lord Morgan engaged by %s", who->GetName().c_str());
TC_LOG_WARN("scripts.boss", "Unexpected phase transition detected");
// 在配置文件中控制日志级别
Logs.ScriptDebug = 1
Logs.ScriptInfo = 1
4.2 常用调试命令
# 游戏内调试命令
.debug loadmaps # 加载地图调试信息
.debug spell 70001 # 测试法术效果
.npc info # 查看 NPC 详细信息
.npc add 12345 # 刷出测试 NPC
.gobject near # 查看附近游戏对象
.gps # 获取当前位置坐标
五、数据库配置
脚本需要配合数据库配置才能正常工作:
5.1 creature_template 配置
INSERT INTO `creature_template` (`entry`, `name`, `ScriptName`, `minlevel`, `maxlevel`, `faction`, `rank`)
VALUES (700001, '暗影领主莫甘', 'boss_shadow_lord_morgan', 83, 83, 16, 3);
5.2 法术配置
INSERT INTO `spell_dbc` (`Id`, `SpellName`, `Description`, `Effect1`, `EffectBasePoints1`, `EffectDieSides1`)
VALUES
(70001, '暗影箭雨', '对附近所有敌人造成暗影伤害', 6, 5000, 1000),
(70002, '虚空裂隙', '在目标位置制造虚空裂隙', 3, 8000, 500),
(70003, '暗影护盾', '吸收伤害并反弹', 6, 0, 0);
六、性能优化
6.1 事件调度优化
- 使用
Seconds/Minutes字面量代替原始毫秒值 - 避免在
UpdateAI中执行耗时操作 - 使用
events.Repeat()替代手动重新调度 - 善用
events.CancelEvent()清理不需要的事件
6.2 内存管理
// 正确管理临时召唤物
if (Creature* summon = me->SummonCreature(NPC_VOID_WALKER, pos, TEMPSUMMON_CORPSE_DESPAWN, 0))
{
summon->SetOwnerGUID(me->GetGUID());
// 自动清理:副本结束后自动消失
}
// 避免内存泄漏
// 使用智能指针管理动态资源
std::unique_ptr<MyCustomData> data = std::make_unique<MyCustomData>();
七、总结
TrinityCore 的脚本系统为魔兽世界模拟器开发提供了强大的扩展能力。通过本文的实战,我们掌握了:
- 脚本类型:CreatureScript、SpellScript、InstanceScript 等
- 注册机制:静态注册与动态加载
- Boss 脚本开发:阶段转换、事件调度、技能施放
- 调试技巧:日志系统、游戏内调试命令
- 数据库配置:creature_template、spell_dbc 配置
掌握 TrinityCore 脚本开发,意味着你可以为服务器创建任何自定义内容——从简单的任务 NPC 到复杂的副本 Boss,一切皆有可能。