AzerothCore 战场系统开发:从状态机、旗帜机制到奖励结算的完整链路 原创
温馨提示:
本文最后更新于 2026-10-22,已超过 0 天没有更新。
若文章内的图片失效(无法正常加载),请留言反馈或直接 联系我。
战场是 AzerothCore 中最复杂的 PvP 系统:状态机管理比赛流程、积分判定决定胜负、奖励结算涉及荣誉/声望/物品。这篇以战歌峡谷(Warsong Gulch)为例,从 Battleground 基类出发,讲清楚旗帜机制、积分系统和奖励发放的实现原理。
一、Battleground 基类:状态机是核心
所有战场都继承自 Battleground 基类,核心是状态机:
enum BattlegroundStatus
{
STATUS_NONE = 0,
STATUS_WAIT_QUEUE = 1, // 排队中
STATUS_WAIT_JOIN = 2, // 等待玩家进入
STATUS_IN_PROGRESS = 3, // 进行中
STATUS_WAIT_LEAVE = 4, // 等待离开
};
状态切换由 BattlegroundMgr 驱动,关键时间点:
- STATUS_WAIT_QUEUE → STATUS_WAIT_JOIN:人数凑齐,发邀请
- STATUS_WAIT_JOIN → STATUS_IN_PROGRESS:倒计时结束,开门
- STATUS_IN_PROGRESS → STATUS_WAIT_LEAVE:一方获胜,结算
二、战歌峡谷:旗帜机制的完整实现
战歌的核心是「夺旗-交旗」循环。关键类 BattlegroundWSG:
class BattlegroundWSG : public Battleground
{
// 旗帜状态
enum WSGFlagState
{
WSG_FLAG_STATE_ON_BASE = 0, // 在基地
WSG_FLAG_STATE_ON_PLAYER = 1, // 被玩家携带
WSG_FLAG_STATE_ON_GROUND = 2, // 掉在地上
};
// 积分
uint32 m_TeamScores[2]; // 联盟/部落得分
uint32 m_FlagState[2]; // 双方旗帜状态
ObjectGuid m_FlagKeepers[2]; // 携带旗帜的玩家
};
2.1 夺旗判定
玩家点击敌方旗帜时触发:
void BattlegroundWSG::EventPlayerClickedOnFlag(Player* player, GameObject* go)
{
// 1. 校验:不能是自己队伍的旗帜
if (GetTeamIndexByTeamId(player->GetTeamId()) == GetFlagTeamIndex(go))
return;
// 2. 校验:旗帜必须在基地或地上
if (m_FlagState[flagTeam] != WSG_FLAG_STATE_ON_BASE &&
m_FlagState[flagTeam] != WSG_FLAG_STATE_ON_GROUND)
return;
// 3. 捡起旗帜
m_FlagState[flagTeam] = WSG_FLAG_STATE_ON_PLAYER;
m_FlagKeepers[flagTeam] = player->GetGUID();
player->CastSpell(player, SPELL_WSG_FLAG, true); // 给玩家上旗帜 buff
// 4. 广播消息
SendMessageToAll(LANG_BG_WS_FLAG_TAKEN, ...);
}
2.2 交旗得分
玩家把旗帜带回自己基地时:
void BattlegroundWSG::EventPlayerCapturedFlag(Player* player)
{
// 1. 校验:必须携带敌方旗帜
if (!player->HasAura(SPELL_WSG_FLAG))
return;
// 2. 校验:己方旗帜必须在基地(不能也被夺走)
if (m_FlagState[playerTeam] != WSG_FLAG_STATE_ON_BASE)
{
SendMessageToPlayer(player, LANG_BG_WS_FLAG_CANT_CAP, ...);
return;
}
// 3. 得分
m_TeamScores[playerTeam]++;
UpdateWorldState(WSG_WORLD_STATE_ALLIANCE_SCORE, m_TeamScores[ALLIANCE]);
// 4. 重置敌方旗帜
m_FlagState[enemyTeam] = WSG_FLAG_STATE_ON_BASE;
RespawnFlag(enemyTeam);
// 5. 检查胜利
if (m_TeamScores[playerTeam] >= 3)
EndBattleground(playerTeam);
}
三、积分系统:不仅仅是比分
战歌的积分不只是 3 分制,还有荣誉击杀、助攻、时间加成:
| 行为 | 奖励 |
|---|---|
| 夺旗成功 | +198 荣誉 |
| 击杀敌方 | 基础荣誉 × 战场系数 |
| 协助击杀 | 50% 荣誉 |
| 获胜方 | 额外 3 个战歌徽章 |
| 失败方 | 1 个战歌徽章 |
这些数值在 Battleground.h 的 BGHonorReward 结构体里定义,可以通过 worldserver.conf 调整:
Battleground.HonorWinner = 198
Battleground.HonorLoser = 50
Battleground.HonorKill = 20.8
四、奖励发放:不只是荣誉
战场结束时,Battleground::EndBattleground 触发奖励结算:
void Battleground::EndBattleground(uint32 winner)
{
// 1. 计算荣誉
uint32 honorWinner = sWorld->getIntConfig(CONFIG_BG_HONOR_WINNER);
uint32 honorLoser = sWorld->getIntConfig(CONFIG_BG_HONOR_LOSER);
// 2. 发放荣誉
for (auto& [guid, player] : GetPlayers())
{
uint32 honor = (GetTeamIndexByTeamId(player->GetTeamId()) == winner)
? honorWinner : honorLoser;
player->ModifyHonorPoints(honor);
}
// 3. 发放徽章
uint32 markWinner = 3, markLoser = 1;
RewardMarkOfHonor(winner, markWinner);
RewardMarkOfHonor(GetOtherTeam(winner), markLoser);
// 4. 声望(战歌峡谷是银翼要塞/战歌氏族)
if (GetTypeID() == BATTLEGROUND_WS)
{
RewardReputation(winner, FACTION_SILVERWING_SENTINELS, 35);
RewardReputation(winner, FACTION_WARSONG_OUTRIDERS, 35);
}
// 5. 每日首胜额外奖励
if (IsFirstWinOfDay(player))
RewardDailyFirstWin(player);
}
五、自定义:做一个「双倍荣誉周末」
结合前面知识,实现每周五-周日双倍荣誉:
// 在 Battleground::EndBattleground 里加时间判断
void Battleground::EndBattleground(uint32 winner)
{
// ... 原有逻辑 ...
// 检查是否周末
time_t now = time(nullptr);
tm* local = localtime(&now);
bool isWeekend = (local->tm_wday == 5 || local->tm_wday == 6 || local->tm_wday == 0);
uint32 honorMultiplier = isWeekend ? 2 : 1;
for (auto& [guid, player] : GetPlayers())
{
uint32 baseHonor = (GetTeamIndexByTeamId(player->GetTeamId()) == winner)
? honorWinner : honorLoser;
player->ModifyHonorPoints(baseHonor * honorMultiplier);
if (isWeekend)
ChatHandler(player->GetSession()).SendSysMessage("周末双倍荣誉已生效!");
}
}
结语
战场系统的精髓在于状态机驱动 + 事件响应。基类管理排队/进出/结算,子类实现具体玩法(夺旗/占塔/资源争夺)。理解了 BattlegroundWSG 的旗帜状态切换和积分判定,再看阿拉希盆地、奥特兰克山谷就会发现是同一套框架的不同实现。下一篇讲怎么做一个全新的自定义战场。