本文是主讲怪物方向的AI
①继承顺序
BossAI
和WorldBossAI
都继承来自ScriptedAI类
ScriptedAI
类继承来自CreatureAI类,
Creature
类继承来自UnitAI类
②个例举例(继承BossAI)
<1>科林·狄尔布鲁(Coren Direbrew)
struct boss_coren_direbrew : public BossAI
<2>烟网女王(Mother Smolderweb)
struct boss_mother_smolderweb : public BossAI
<3>巨龙之王维姆萨拉克(Overlord Wyrmthalak)
struct boss_overlord_wyrmthalak : public BossAI
③WorldBossAI继承举例
1)Azuregos(艾索雷葛斯)
struct boss_azuregos : public WorldBossAI
2)翡翠龙(emerald_dragon)
struct emerald_dragonAI : public WorldBossAI
①分别举例说明:
1)暮光志愿者AI
struct npc_twilight_volunteer : public ScriptedAI
2)Millhouse Manastorm(米尔豪斯·法力风暴)
struct npc_millhouse_manastorm : public ScriptedAI
②Valithria Dreamwalker (瓦莉瑟瑞娅·梦行者) 的全部AI
③Algalon the Observer(奥尔加隆,观察者之眼)
④Sister Svalna(斯瓦尔娜修女)
npc_twilight_volunteer和boss_jedoga_shadowseeker是作为两种怪物的AI注册在这个副本脚本里面
enum JedogaEvents
{
EVENT_INTRO_SAY = 1, --表示介绍对话事件。
EVENT_START_FIGHT_1, --表示战斗开始事件 1
EVENT_START_FIGHT_2, --表示战斗开始事件 2
EVENT_CYCLONE_STRIKE, --表示飓风打击事件
EVENT_LIGHTNING_BOLT, --表示闪电箭事件
EVENT_THUNDERSHOCK, --表示雷霆震击事件
EVENT_START_PHASE_TWO, --表示第二阶段开始事件
EVENT_FLY_DELAY, --表示飞行延迟事件
EVENT_END_PHASE_TWO, --表示第二阶段结束事件
EVENT_CHOOSE_VOLUNTEER, --表示选择志愿者事件
EVENT_SUMMON_VOLUNTEER --表示召唤志愿者事件
};
enum JedogaPhases
{
PHASE_INTRO = 0,
PHASE_ONE,
PHASE_TWO,
PHASE_THREE
};
enum EncounterState
{
NOT_STARTED = 0,
IN_PROGRESS = 1,
FAIL = 2,
DONE = 3, --副本机制结束
SPECIAL = 4,
TO_BE_DECIDED = 5
};
NOT_STARTED (值为0): 表示战斗或遭遇尚未开始的状态。
IN_PROGRESS (值为1): 表示战斗或遭遇正在进行中的状态。
FAIL (值为2): 表示战斗或遭遇失败的状态。
DONE (值为3): 表示战斗或遭遇成功完成的状态。
SPECIAL (值为4): 表示战斗或遭遇的特殊状态。
TO_BE_DECIDED (值为5): 表示战斗或遭遇的状态尚待决定
1)构造函数
boss_jedoga_shadowseeker(Creature* creature) : BossAI(creature, DATA_JEDOGA_SHADOWSEEKER), _volunteerWork(true) { }
2)重写reset函数
void Reset() override
3)重写JustEngagedWith函数
void JustEngagedWith(Unit* who) override
4)重写EnterEvadeMode函数
void EnterEvadeMode(EvadeReason /*why*/) override
5)有obj死亡
void KilledUnit(Unit* Victim) override
6)boss死亡事件
void JustDied(Unit* /*killer*/) override
7)获取是否志愿者是否死亡数据
uint32 GetData(uint32 type) const override
8)伤害发生事件
void DamageTaken(Unit* /*done_by*/, uint32& /*damage*/, DamageEffectType /*damageType*/, SpellInfo const* /*spellInfo = nullptr*/) override
9)行动函数-处理对应行动
void DoAction(int32 action) override
10)召唤事件
void JustSummoned(Creature* summon) override
11)召唤物死亡事件
void SummonedCreatureDies(Creature* summon, Unit* killer) override
12)移动事件
void MovementInform(uint32 type, uint32 pointId) override
13)处理事件(每帧处理)
void UpdateAI(uint32 diff) override
private:
bool _volunteerWork;
GuidVector _volunteerGUIDS;
ObjectGuid _selectedVolunteerGUID;
GuidUnorderedSet _initiateGUIDS;
①团灭重置或是boss转阶段函数-reset
//boss_jedoga_shadowseeker类的函数
void Reset() override
{
_Reset();
events.SetPhase(PHASE_INTRO);
me->SetReactState(REACT_PASSIVE);
std::list<TempSummon*> summoned;
me->SummonCreatureGroup(SUMMON_GROUP_INITIATES, &summoned);
for (TempSummon* value : summoned)
_initiateGUIDS.insert(value->GetGUID());
if (TempSummon* controller = me->SummonCreature(NPC_JEDOGA_CONTROLLER, JedogaControllerPositions[0], TEMPSUMMON_MANUAL_DESPAWN))
controller->CastSpell(me, SPELL_BEAM_VISUAL_JEDOGA);
if (TempSummon* controller = me->SummonCreature(NPC_JEDOGA_CONTROLLER, JedogaControllerPositions[1], TEMPSUMMON_MANUAL_DESPAWN))
controller->CastSpell(me, SPELL_BEAM_VISUAL_JEDOGA);
events.ScheduleEvent(EVENT_INTRO_SAY, Minutes(2), 0, PHASE_INTRO);
}
void BossAI::_Reset()
{
if (!me->IsAlive())
return;
me->SetCombatPulseDelay(0);
me->ResetLootMode();
events.Reset();
summons.DespawnAll();
scheduler.CancelAll();
if (instance && instance->GetBossState(_bossId) != DONE)
instance->SetBossState(_bossId, NOT_STARTED);
}
<1>std::list
<2>召唤两个怪物id为NPC_JEDOGA_CONTROLLER的怪物在位置0和位置1,然后这两个怪物释放光束技能(SPELL_BEAM_VISUAL_JEDOGA,这个两个怪物在调用unsummon后会被销毁)
if (TempSummon* controller = me->SummonCreature(NPC_JEDOGA_CONTROLLER, JedogaControllerPositions[0], TEMPSUMMON_MANUAL_DESPAWN))
controller->CastSpell(me, SPELL_BEAM_VISUAL_JEDOGA);
if (TempSummon* controller = me->SummonCreature(NPC_JEDOGA_CONTROLLER, JedogaControllerPositions[1], TEMPSUMMON_MANUAL_DESPAWN))
controller->CastSpell(me, SPELL_BEAM_VISUAL_JEDOGA);
<3> 启动定时器,每隔两分钟启动说话事件
-------事件注册
events.ScheduleEvent(EVENT_INTRO_SAY, Minutes(2), 0, PHASE_INTRO);
------事件触发(如果boss状态是结束状态,那么副本结束,给场景玩家发送布教)
case EVENT_INTRO_SAY:
if (instance->GetBossState(DATA_PRINCE_TALDARAM) == DONE)
Talk(SAY_PREACHING);
events.Repeat(Minutes(2));
break;
②这可能是玩家进入某块boss视野的区域,处理 BOSS 与玩家或其他单位刚刚发生接触时的行为调用的函数
void JustEngagedWith(Unit* who) override
{
me->RemoveAurasDueToSpell(SPELL_SPHERE_VISUAL);
me->RemoveAurasDueToSpell(SPELL_RANDOM_LIGHTNING_VISUAL);
me->SummonCreatureGroup(SUMMON_GROUP_WORSHIPPERS);
BossAI::JustEngagedWith(who);
Talk(SAY_AGGRO);
events.SetPhase(PHASE_ONE);
for (JedogaVolunteerPositionPair const& posPair : JedogaVolunteerSpotPositions)
{
if (TempSummon* volunteer = me->SummonCreature(NPC_TWILIGHT_VOLUNTEER, posPair.first, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 3s))
{
volunteer->GetMotionMaster()->MovePoint(POINT_INITIAL_POSITION, posPair.second);
_volunteerGUIDS.push_back(volunteer->GetGUID());
}
}
}
<1>移除与 SPELL_SPHERE_VISUAL 相关的光环效果。
<2>移除与 SPELL_RANDOM_LIGHTNING_VISUAL 相关的光环效果。
<3>召唤一组崇拜者(worshippers)的生物
<4>触发一个攻击的语音对话(aggro)。
<5>设置 BOSS 的战斗阶段(phase)为第一阶段(PHASE_ONE)
<6>按照配置读取怪物id为NPC_TWILIGHT_VOLUNTEER的坐标,将怪物移动到目标地点
③逃跑触发的函数(销毁所有召唤物,然后设置boss状态为挑战失败)
_EnterEvadeMode里面会冷却boss所有技能,停止战斗状态,设置攻击目标为空,去除身上的buff
void EnterEvadeMode(EvadeReason /*why*/) override
{
summons.DespawnAll();
_EnterEvadeMode();
_DespawnAtEvade(Seconds(15));
}
④有obj被杀掉调用这个,如果被杀的是玩家,那么boss就说点嚣张的话
void KilledUnit(Unit* Victim) override
{
if (Victim->GetTypeId() == TYPEID_PLAYER)
Talk(SAY_SLAY);
}
⑤boss被杀调用的事件
void JustDied(Unit* /*killer*/) override
{
_JustDied();
Talk(SAY_DEATH);
}
⑥获得特定数据(志愿者还在工作就返回1)
uint32 GetData(uint32 type) const override
{
if (type == DATA_VOLUNTEER_WORK)
return _volunteerWork ? 1 : 0;
return 0;
}
⑦伤害发生调用的函数
void DamageTaken(Unit* /*done_by*/, uint32& /*damage*/, DamageEffectType /*damageType*/, SpellInfo const* /*spellInfo = nullptr*/) override
{
if (HealthBelowPct(55) && events.IsInPhase(PHASE_ONE))
{
events.Reset();
events.SetPhase(PHASE_TWO);
events.ScheduleEvent(EVENT_START_PHASE_TWO, 1s);
}
}
---------------------------
阶段二事件:EVENT_START_PHASE_TWO
移动到特定地点,设置无敌状态,不能攻击玩家,移动到特定地点
case EVENT_START_PHASE_TWO:
me->SetReactState(REACT_PASSIVE);
me->AttackStop();
me->InterruptNonMeleeSpells(true);
me->SetUnitFlag(UNIT_FLAG_UNINTERACTIBLE);
me->GetMotionMaster()->MovePoint(POINT_PHASE_TWO, JedogaGroundPosition);
break;
⑧召唤事件
若召唤物是WORSHIPPER则让其处于跪倒状态
void JustSummoned(Creature* summon) override
{
if (summon->GetEntry() == NPC_TWILIGHT_WORSHIPPER)
{
summon->SetStandState(UNIT_STAND_STATE_KNEEL);
summons.Summon(summon);
return;
}
BossAI::JustSummoned(summon);
}
⑨召唤物死亡
<1>如果死亡的召唤物是初始的召唤物,boss释放SPELL_HOVER_FALL_1技能,1s后执行EVENT_START_FIGHT_1事件
<2>如果死亡的召唤物是志愿者召唤物,改变志愿者的工作状态,如果是阶段二则转变到阶段三,启动阶段二结束事件EVENT_END_PHASE_TWO
void SummonedCreatureDies(Creature* summon, Unit* killer) override
{
if (summon->GetEntry() == NPC_TWILIGHT_INITIATE)
{
_initiateGUIDS.erase(summon->GetGUID());
if (_initiateGUIDS.empty())
{
DoCastSelf(SPELL_HOVER_FALL_1);
me->SetAnimTier(AnimTier::Ground);
events.ScheduleEvent(EVENT_START_FIGHT_1, Seconds(1));
}
}
else if (summon->GetEntry() == NPC_TWILIGHT_VOLUNTEER)
{
_volunteerWork = false;
if (events.IsInPhase(PHASE_TWO))
{
events.SetPhase(PHASE_THREE);
events.RescheduleEvent(EVENT_END_PHASE_TWO, Seconds(1));
}
events.RescheduleEvent(EVENT_SUMMON_VOLUNTEER, Seconds(10));
}
BossAI::SummonedCreatureDies(summon, killer);
}
⑩移动事件
<1>若不是按照点移动或是按照特效移动(就是飞行特效)那就返回
<2>去除UNIT_FLAG_UNINTERACTIBLE标志
具体来说,当一个单位具有UNIT_FLAG_UNINTERACTIBLE
标志时,其他单位或玩家无法与该单位进行交互。这意味着玩家无法与该单位进行对话、攻击、施放法术等互动行为
<3>boss设置REACT_AGGRESSIVE
,当一个单位的行为反应状态设置为REACT_AGGRESSIVE时,该单位将主动攻击其他单位,即使这些单位没有主动攻击它。这意味着该单位会对附近的敌对单位发起攻击,并对它们进行战斗。
<4>循环释放技能
void MovementInform(uint32 type, uint32 pointId) override
{
if (type != POINT_MOTION_TYPE && type != EFFECT_MOTION_TYPE)
return;
switch (pointId)
{
case POINT_GROUND:
me->RemoveUnitFlag(UNIT_FLAG_UNINTERACTIBLE);
me->SetReactState(REACT_AGGRESSIVE);
DoZoneInCombat();
events.ScheduleEvent(EVENT_CYCLONE_STRIKE, 3s);
events.ScheduleEvent(EVENT_LIGHTNING_BOLT, 7s);
events.ScheduleEvent(EVENT_THUNDERSHOCK, 12s);
break;
case POINT_PHASE_TWO:
events.ScheduleEvent(EVENT_FLY_DELAY, 2s);
break;
case POINT_PHASE_TWO_FLY:
events.ScheduleEvent(EVENT_CHOOSE_VOLUNTEER, 2s);
break;
default:
break;
}
}
十一、主逻辑处理(每帧处理对应事件)
<1>每帧调用UpdateAI,这里会把事件都执行一遍
<2>事件执行结束会调用DoMeleeAttackIfReady进行正反手近战普通攻击
void UpdateAI(uint32 diff) override
{
if (!UpdateVictim() && !events.IsInPhase(PHASE_INTRO))
return;
events.Update(diff);
if (me->HasUnitState(UNIT_STATE_CASTING))
return;
while (uint32 eventId = events.ExecuteEvent())
{
switch (eventId)
{
case EVENT_INTRO_SAY:
if (instance->GetBossState(DATA_PRINCE_TALDARAM) == DONE)
Talk(SAY_PREACHING);
events.Repeat(Minutes(2));
break;
case EVENT_START_FIGHT_1:
me->RemoveAurasDueToSpell(SPELL_BEAM_VISUAL_JEDOGA);
events.ScheduleEvent(EVENT_START_FIGHT_2, Seconds(1));
break;
case EVENT_START_FIGHT_2:
summons.DespawnEntry(NPC_JEDOGA_CONTROLLER);
me->SetDisableGravity(false);
me->GetMotionMaster()->MoveLand(POINT_GROUND, JedogaGroundPosition);
break;
case EVENT_START_PHASE_TWO:
me->SetReactState(REACT_PASSIVE);
me->AttackStop();
me->InterruptNonMeleeSpells(true);
me->SetUnitFlag(UNIT_FLAG_UNINTERACTIBLE);
me->GetMotionMaster()->MovePoint(POINT_PHASE_TWO, JedogaGroundPosition);
break;
case EVENT_FLY_DELAY:
me->SetDisableGravity(true);
me->GetMotionMaster()->MoveTakeoff(POINT_PHASE_TWO_FLY, JedogaFlyPosition);
break;
case EVENT_CHOOSE_VOLUNTEER:
if (TempSummon* controller = me->SummonCreature(NPC_JEDOGA_CONTROLLER, JedogaControllerPositions[2], TEMPSUMMON_MANUAL_DESPAWN))
{
me->SetFacingToObject(controller);
controller->CastSpell(controller, SPELL_SACRIFICE_VISUAL);
}
Talk(SAY_CHOOSE);
if (_volunteerGUIDS.empty())
break;
_selectedVolunteerGUID = Trinity::Containers::SelectRandomContainerElement(_volunteerGUIDS);
if (Creature* volunteer = ObjectAccessor::GetCreature(*me, _selectedVolunteerGUID))
volunteer->AI()->DoAction(ACTION_CHOSEN);
break;
case EVENT_SUMMON_VOLUNTEER:
{
uint32 pos = std::distance(_volunteerGUIDS.begin(), std::find(_volunteerGUIDS.begin(), _volunteerGUIDS.end(), _selectedVolunteerGUID));
if (pos < JedogaVolunteerSpotPositions.size())
{
JedogaVolunteerPositionPair const& posPair = JedogaVolunteerSpotPositions.at(pos);
if (TempSummon* volunteer = me->SummonCreature(NPC_TWILIGHT_VOLUNTEER, posPair.first, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 3s))
volunteer->GetMotionMaster()->MovePoint(POINT_INITIAL_POSITION, posPair.second);
}
break;
}
case EVENT_END_PHASE_TWO:
summons.DespawnEntry(NPC_JEDOGA_CONTROLLER);
DoCastSelf(SPELL_HOVER_FALL_2);
me->SetDisableGravity(false);
me->GetMotionMaster()->MoveLand(POINT_GROUND, JedogaGroundPosition);
break;
case EVENT_CYCLONE_STRIKE:
DoCastSelf(SPELL_CYCLONE_STRIKE);
events.Repeat(Seconds(15), Seconds(30));
break;
case EVENT_LIGHTNING_BOLT:
if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 100.0f, true))
DoCast(target, SPELL_LIGHTNING_BOLT);
events.Repeat(Seconds(15), Seconds(30));
break;
case EVENT_THUNDERSHOCK:
if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 100.0f, true))
DoCast(target, SPELL_THUNDERSHOCK);
events.Repeat(Seconds(15), Seconds(30));
break;
default:
break;
}
if (me->HasUnitState(UNIT_STATE_CASTING))
return;
}
DoMeleeAttackIfReady();
}
struct npc_guard_generic : public GuardAI
1、构造函数
npc_guard_generic(Creature* creature) : GuardAI(creature)
2、
void Reset() override
3、
void DoReplyToTextEmote(uint32 emote)
4、
void ReceiveEmote(Player* player, uint32 textEmote) override
5、
void JustEngagedWith(Unit* who) override
6、
void UpdateAI(uint32 diff) override
private:
TaskScheduler _scheduler;
TaskScheduler _combatScheduler;
struct npc_guard_shattrath_faction: public GuardAI
1、构造函数
npc_guard_shattrath_faction(Creature* creature) : GuardAI(creature)
2、重置函数
void Reset() override
3、与单位交互(比如攻击、施法、交互等。)
void JustEngagedWith(Unit* /*who*/) override
4、每帧更新游戏逻辑
void UpdateAI(uint32 diff) override
5、
void ScheduleVanish()
private:
TaskScheduler _scheduler;