AzerothCore 自定义副本开发指南:从地图加载到 Boss 脚本的完整链路 原创
引言
AzerothCore 是基于 CMake 的开源 MMO 服务器模拟器,采用模块化架构。自定义副本(Instance)开发是服务端定制化中最常见也最容易踩坑的领域之一。本文从地图加载机制出发,覆盖 ScriptName 绑定、Boss AI 脚本、副本数据表到生成与掉落的完整链路。
一、AzerothCore 架构概览
AzerothCore 由三大核心组件构成:
- worldserver:世界服,处理地图、玩家、生物、战斗等核心逻辑
- authserver:认证服,处理登录鉴权
- Database:三库结构 — auth(账号)、characters(角色数据)、world(游戏数据)
副本系统涉及 worldserver 的 MapManager、InstanceScript,以及 world 数据库中的 instance_template、creature、gameobject 等表。
二、副本地图加载流程
当玩家进入副本传送门时,服务器执行以下流程:
- MapManager::CreateInstance:根据 mapId 创建或复用实例
- InstanceTemplate 查询:从 world.instance_template 读取副本配置(maxPlayers、resetDelay 等)
- InstanceScript 实例化:通过 ScriptName 字段关联到 C++ 脚本类
- BOSS/creature 生成:根据 creature 表和 instance_spawn_group 生成 NPC
-- 查看副本模板
SELECT entry, name, maxPlayers, resetDelay FROM instance_template WHERE entry=332;
三、ScriptName 绑定机制
每个副本在 instance_template 表中有一个 ScriptName 字段,指向 C++ ScriptMgr 注册的脚本名。以经典副本”影牙城堡”为例:
-- 数据库端
UPDATE instance_template
SET ScriptName='instance_shadowfang_keep'
WHERE entry=332;
C++ 端需要注册对应的 InstanceScript:
// InstanceShadowfangKeep.cpp
class instance_shadowfang_keep : public InstanceScript
{
public:
InstanceShadowfangKeep(InstanceMap* map) : InstanceScript(map)
{
// 初始化 BOSS 进度检查
SetBossNumber(4); // 影牙城堡有4个 BOSS
}
void OnCreatureCreate(Creature* creature) override
{
switch (creature->GetEntry())
{
case NPC_BARON_SILVERLAINE:
AddObject(creature, DATA_BARON_SILVERLAINE);
break;
case NPC_COMMANDER_SPRINGVALE:
AddObject(creature, DATA_COMMANDER_SPRINGVALE);
break;
}
}
void OnGameObjectCreate(GameObject* go) override
{
switch (go->GetEntry())
{
case GO_BARON_DOOR:
AddObject(go, DATA_BARON_DOOR);
if (GetBossState(DATA_BARON_SILVERLAINE) == DONE)
go->SetGoState(GO_STATE_ACTIVE);
break;
}
}
bool SetBossState(uint32 id, EncounterState state) override
{
if (!InstanceScript::SetBossState(id, state))
return false;
switch (id)
{
case DATA_BARON_SILVERLAINE:
if (state == DONE)
{
// 开门逻辑
if (GameObject* go = GetGameObject(DATA_BARON_DOOR))
go->SetGoState(GO_STATE_ACTIVE);
}
break;
}
return true;
}
};
void AddSC_instance_shadowfang_keep()
{
new InstanceMapScript<instance_shadowfang_keep>(
"instance_shadowfang_keep", 332);
}
四、Boss AI 脚本开发
Boss 脚本通过 ScriptName 关联到 creature 表:
-- 关联 BOSS 脚本
UPDATE creature_template
SET ScriptName='boss_baron_silverlaine'
WHERE entry=3887;
// BossBaronSilverlaine.cpp
class boss_baron_silverlaine : public CreatureScript
{
public:
boss_baron_silverlaine() : CreatureScript("boss_baron_silverlaine") {}
struct boss_baron_silverlaineAI : public BossAI
{
boss_baron_silverlaineAI(Creature* creature) : BossAI(creature)
{
// 初始化技能列表
}
void Reset() override
{
BossAI::Reset();
// 重置 BOSS 状态、召唤物等
}
void EnterCombat(Unit* victim) override
{
BossAI::EnterCombat(victim);
Talk(SAY_AGGRO);
events.ScheduleEvent(EVENT_CURSED_VEIL, 15s);
events.ScheduleEvent(EVENT_SUMMON_WORGEN_SPIRIT, 12s);
}
void ExecuteEvent(uint32 eventId) override
{
switch (eventId)
{
case EVENT_CURSED_VEIL:
DoCastVictim(SPELL_CURSED_VEIL);
events.ScheduleEvent(EVENT_CURSED_VEIL, 20s);
break;
case EVENT_SUMMON_WORGEN_SPIRIT:
DoCastSelf(SPELL_SUMMON_WORGEN_SPIRIT);
events.ScheduleEvent(EVENT_SUMMON_WORGEN_SPIRIT, 25s);
break;
}
}
void JustDied(Unit* killer) override
{
BossAI::JustDied(killer);
Talk(SAY_DEATH);
// 实例脚本通知
if (InstanceScript* instance = GetInstanceScript())
instance->SetBossState(DATA_BARON_SILVERLAINE, DONE);
}
};
CreatureAI* GetAI(Creature* creature) const override
{
return GetShadowfangKeepAI<boss_baron_silverlaineAI>(creature);
}
};
void AddSC_boss_baron_silverlaine()
{
new boss_baron_silverlaine();
}
五、副本进度与门控制
AzerothCore 的副本进度通过 EncounterState 枚举管理:
| 状态 | 值 | 含义 |
|---|---|---|
| NOT_STARTED | 0 | BOSS 未被触发 |
| IN_PROGRESS | 1 | 战斗中 |
| FAIL | 2 | 战斗失败(玩家逃跑/死亡) |
| DONE | 3 | BOSS 已击杀 |
| SPECIAL | 4 | 特殊状态(如跳过 BOSS) |
门控制通过 OnGameObjectCreate + SetBossState 联动:BOSS 击杀后自动开门,这是最常见的副本流程控制模式。
六、掉落表与掉落系统
Boss 掉落通过 creature_loot_template 表配置:
-- 为 Baron Silverlaine 配置掉落
INSERT INTO creature_loot_template
(Entry, Item, Reference, Chance, QuestRequired, LootMode, GroupId, MinCount, MaxCount)
VALUES
(3887, 60000, 0, 20, 0, 1, 0, 1, 1), -- 蓝装 20% 掉率
(3887, 60001, 0, 15, 0, 1, 0, 1, 1), -- 另一件蓝装
(3887, 60002, 0, 3, 0, 1, 0, 1, 1), -- 紫装 3% 掉率
(3887, 60003, 0, 100, 0, 1, 1, 1, 2), -- 组1: 100% 掉一个
(3887, 60004, 0, 100, 0, 1, 1, 1, 2), -- 组1: 二选一
(3887, 60005, 0, 0, 0, 1, 2, 1, 1); -- 组2: 引用模板
掉落系统核心概念:
- Chance:独立掉率。如果 GroupId=0,则每个物品按各自的 Chance 独立判定
- Group:同组物品共享 100% 概率池。Chance=100 表示必定掉落该组中某一件
- Reference:引用 reference_loot_template,实现掉落表嵌套
- QuestRequired:只有接了对应任务的玩家才能拾取
七、自定义副本的完整开发流程
假设要新增一个自定义 5 人副本,完整步骤:
1. 数据库配置
-- 副本模板
INSERT INTO instance_template (entry, name, ScriptName, maxPlayers, resetDelay)
VALUES (1000, '自定义副本', 'instance_custom_dungeon', 5, 3600);
-- 地图区域
INSERT INTO instance_template (entry, parent, mapType)
VALUES (1000, 0, 1);
-- BOSS creature_template
INSERT INTO creature_template
(entry, name, faction, rank, ScriptName, minLevel, maxLevel)
VALUES
(100000, '副本守门 BOSS', 14, 1, 'boss_custom_guardian', 80, 80),
(100001, '最终 BOSS', 14, 1, 'boss_custom_final', 82, 82);
-- 掉落表
INSERT INTO creature_loot_template (Entry, Item, Chance, GroupId, MinCount, MaxCount)
VALUES (100000, 60100, 25, 0, 1, 1);
2. C++ 脚本注册
// RegisterCustomDungeon.cpp
void AddSC_instance_custom_dungeon()
{
new InstanceMapScript<instance_custom_dungeon>(
"instance_custom_dungeon", 1000);
}
void AddSC_boss_custom_guardian()
{
new boss_custom_guardian();
}
void AddSC_boss_custom_final()
{
new boss_custom_final();
}
3. 编译与加载
# 确保脚本在 ScriptLoader 中注册
# 编辑 src/server/game/Scripting/ScriptLoader.cpp
// void AddCustomScripts()
// {
// AddSC_instance_custom_dungeon();
// AddSC_boss_custom_guardian();
// AddSC_boss_custom_final();
// }
# 重新编译
cd build
cmake .. -DSCRIPTS=ON
make -j$(nproc) worldserver
4. 测试与调试
# GM 命令测试
.go 1000 # 进入副本
.npc add 100000 # 召唤 BOSS 测试
.kill # 击杀测试掉落
.go creature 100000 # 定位 BOSS
# 查看实例状态
.instance list # 查看当前实例进度
八、常见坑与注意事项
- ScriptName 拼写:数据库中的 ScriptName 必须与 C++ 注册名完全一致(区分大小写)
- BossAI 事件系统:使用 events.ScheduleEvent 时,时间单位必须用 chrono 字面量(15s, 1min)
- InstanceScript 数据持久化:自定义进度数据用 SetData/GetData,BOSS 进度用 SetBossState
- creature 表 vs creature_template:template 是模板(spawn 原型),creature 是实际刷出点
- 地图 VMap/MMAP:自定义副本需要对应的 .vmap 和 .mmap 文件,否则玩家穿墙/卡墙
总结
AzerothCore 副本开发的核心链路:instance_template 绑定 ScriptName → C++ InstanceScript 管理进度 → BossAI 处理战斗逻辑 → creature_loot_template 配置掉落。掌握这条链路后,自定义副本的开发就是在这四个层面填内容。建议从修改现有副本开始练习,再尝试从头创建完整副本。