mirror of
https://github.com/liyunfan1223/mod-playerbots.git
synced 2026-06-20 15:39:25 +02:00
Implement remaining racials* + minor modifications to racials strategy (#2456)
<!--
Thank you for contributing to mod-playerbots, please make sure that
you...
1. Submit your PR to the test-staging branch, not master.
2. Read the guidelines below before submitting.
3. Don't delete parts of this template.
DESIGN PHILOSOPHY: We prioritize STABILITY, PERFORMANCE, AND
PREDICTABILITY over behavioral realism.
Every action and decision executes PER BOT AND PER TRIGGER. Small
increases in logic complexity scale
poorly across thousands of bots and negatively affect all. We prioritize
a stable system over a smarter
one. Bots don't need to behave perfectly; believable behavior is the
goal, not human simulation.
Default behavior must be cheap in processing; expensive behavior must be
opt-in.
Before submitting, make sure your changes aligns with these principles.
-->
## Pull Request Description
<!-- Describe what this change does and why it is needed -->
*The title is a lie because I did not implement Cannibalize. I don't
think that one is ever going to be worth the processing cost to find
nearby corpses.
Anyway:
- Implemented Stoneform and PoisonDiseaseBleedTrigger.
- Implemented Escape Artist and MovementImpairedTrigger. The trigger
excludes Stealth and Prowl, like with Hand of Freedom. I realize Gnomes
cannot normally be Druids, but I have left Prowl in, in case somebody
uses a mod to make it happen. I could easily be persuaded to remove the
Prowl exclusion, though.
- Implemented Rogue version of Arcane Torrent, used based on the low
energy trigger.
- Implemented DK version of Arcane Torrent; I don't know shit about DKs,
and it doesn't look like there is any runic-energy-related trigger, so I
just made this a generic boost trigger. That means it's used on cooldown
while the boost strategy is active and the encounter trips the balance
that causes boost trigger to fire. It's not great, but it's better than
not using the ability at all.
- All racials, plus Lifeblood (which is also under the racials
strategy), are gated behind HasSpell() checks. This stops bots from
evaluating racials they don't have and reduces log spam.
- Removed the ActionNodeFactory for racials, which was used only for
Lifeblood with Gift of the Naaru alternative. I split those abilities
into their own TriggerNodes, behind their own spell checks.
- Increased threshold for Lifeblood and Gift of the Naaru use to medium
health instead of low health. These are not strong heals, plus they are
HoTs, so bots may as well get more use out of them. Gift of the Naaru
can be used on allies as well, but I did not implement it (because of
the effort and because I don't think it's worth the processing cost
anyway for such an insignificant heal).
- Removed spell checks from EMFH and WoTF IsPossible() since they are
checked before the trigger is evaluated.
- Removed IsUseful() overrides from EMFH and WoTF since the checks are
already in the triggers. If we should have the checks done twice anyway,
LMK.
## Feature Evaluation
<!--
If your PR is very minimal (comment typo, wrong ID reference, etc), and
it is very obvious it will not have
any impact on performance, you may skip these question. If necessary, a
maintainer may ask you for them later.
-->
<!-- Please answer the following: -->
- Describe the **minimum logic** required to achieve the intended
behavior.
- Describe the **processing cost** when this logic executes across many
bots.
## How to Test the Changes
<!--
- Step-by-step instructions to test the change.
- Any required setup (e.g. multiple players, number of bots, specific
configuration).
- Expected behavior and how to verify it.
-->
I tested:
- EMFH with Fear
- Escape Artist with Entangling Roots
- Stoneform with Rupture
- Arcane Torrent (Rogue version)
I did not test the DK version of Arcane Torrent. I did not check all
possible conditions for the CC breaks, but it can be done most easily by
self-botting and using the .aura GM command. The racials strategy is a
default combat strategy only so for testing at least you may want to add
nc +racials to make it easier.
## Impact Assessment
<!-- As a generic test, before and after measure of pmon (playerbot pmon
tick) can help you here. -->
- Does this change increase per-bot/per-tick processing or risk scaling
poorly with thousands of bots?
- - [ ] No, not at all
- - [x] Minimal impact (**explain below**)
- - [ ] Moderate impact (**explain below**)
New triggers always add more processing, but this stuff is
insignificant.
- Does this change modify default bot behavior?
- - [ ] No
- - [x] Yes (**explain why**)
Racials is a default combat strategy so new racials/changes to existing
racials modifies default behavior.
- Does this change add new decision branches or increase maintenance
complexity?
- - [x] No
- - [ ] Yes (**explain below**)
## AI Assistance
<!--
AI assistance is allowed, but all submitted code must be fully
understood, reviewed, and owned by the contributor.
We expect contributors to be honest about what they do and do not
understand.
-->
Was AI assistance used while working on this change?
- - [x] No
- - [ ] Yes (**explain below**)
<!--
If yes, please specify:
- Purpose of usage (e.g. brainstorming, refactoring, documentation, code
generation).
- Which parts of the change were influenced or generated, and whether it
was thoroughly reviewed.
-->
<!--
TRANSLATIONS:
Anything new that the bots say in chat must be in a translatable format.
This is done using GetBotTextOrDefault,
which you can search for in the codebase to find examples. Your code
needs to have English as the default fallback,
while the full translations need to be in an SQL update file. The
languages in the file are the nine language
options supported by AzerothCore: English, Korean, French, German,
Chinese, Taiwanese, Spanish, Spanish Mexico, and
Russian. See
data/sql/playerbots/updates/2025_12_27_ai_playerbot_fishing_text.sql as
an example of a translation SQL
update, whose content are called within the codebase at
src/strategy/actions/FishingAction.cpp
-->
## Final Checklist
- - [x] Stability is not compromised.
- - [x] Performance impact is understood, tested, and acceptable.
- - [x] Added logic complexity is justified and explained.
- - [x] Any new bot dialogue lines are translated.
- - [x] Documentation updated if needed (Conf comments, WiKi commands).
## Notes for Reviewers
<!-- Anything else that's helpful to review or test your pull request.
-->
This commit is contained in:
parent
8542e39c77
commit
7eff13a9f8
@ -175,6 +175,8 @@ public:
|
|||||||
creators["berserking"] = &ActionContext::berserking;
|
creators["berserking"] = &ActionContext::berserking;
|
||||||
creators["every man for himself"] = &ActionContext::every_man_for_himself;
|
creators["every man for himself"] = &ActionContext::every_man_for_himself;
|
||||||
creators["will of the forsaken"] = &ActionContext::will_of_the_forsaken;
|
creators["will of the forsaken"] = &ActionContext::will_of_the_forsaken;
|
||||||
|
creators["stoneform"] = &ActionContext::stoneform;
|
||||||
|
creators["escape artist"] = &ActionContext::escape_artist;
|
||||||
creators["use trinket"] = &ActionContext::use_trinket;
|
creators["use trinket"] = &ActionContext::use_trinket;
|
||||||
creators["auto talents"] = &ActionContext::auto_talents;
|
creators["auto talents"] = &ActionContext::auto_talents;
|
||||||
creators["auto share quest"] = &ActionContext::auto_share_quest;
|
creators["auto share quest"] = &ActionContext::auto_share_quest;
|
||||||
@ -380,6 +382,8 @@ private:
|
|||||||
static Action* berserking(PlayerbotAI* botAI) { return new CastBerserkingAction(botAI); }
|
static Action* berserking(PlayerbotAI* botAI) { return new CastBerserkingAction(botAI); }
|
||||||
static Action* every_man_for_himself(PlayerbotAI* botAI) { return new CastEveryManForHimselfAction(botAI); }
|
static Action* every_man_for_himself(PlayerbotAI* botAI) { return new CastEveryManForHimselfAction(botAI); }
|
||||||
static Action* will_of_the_forsaken(PlayerbotAI* botAI) { return new CastWillOfTheForsakenAction(botAI); }
|
static Action* will_of_the_forsaken(PlayerbotAI* botAI) { return new CastWillOfTheForsakenAction(botAI); }
|
||||||
|
static Action* stoneform(PlayerbotAI* botAI) { return new CastStoneformAction(botAI); }
|
||||||
|
static Action* escape_artist(PlayerbotAI* botAI) { return new CastEscapeArtistAction(botAI); }
|
||||||
static Action* use_trinket(PlayerbotAI* botAI) { return new UseTrinketAction(botAI); }
|
static Action* use_trinket(PlayerbotAI* botAI) { return new UseTrinketAction(botAI); }
|
||||||
static Action* auto_talents(PlayerbotAI* botAI) { return new AutoSetTalentsAction(botAI); }
|
static Action* auto_talents(PlayerbotAI* botAI) { return new AutoSetTalentsAction(botAI); }
|
||||||
static Action* auto_share_quest(PlayerbotAI* ai) { return new AutoShareQuestAction(ai); }
|
static Action* auto_share_quest(PlayerbotAI* ai) { return new AutoShareQuestAction(ai); }
|
||||||
|
|||||||
@ -491,32 +491,13 @@ bool CastVehicleSpellAction::Execute(Event /*event*/)
|
|||||||
bool CastEveryManForHimselfAction::isPossible()
|
bool CastEveryManForHimselfAction::isPossible()
|
||||||
{
|
{
|
||||||
uint32 spellId = AI_VALUE2(uint32, "spell id", spell);
|
uint32 spellId = AI_VALUE2(uint32, "spell id", spell);
|
||||||
return spellId && bot->HasSpell(spellId) && !HasSpellOrCategoryCooldown(bot, spellId);
|
return spellId && !HasSpellOrCategoryCooldown(bot, spellId);
|
||||||
}
|
|
||||||
|
|
||||||
bool CastEveryManForHimselfAction::isUseful()
|
|
||||||
{
|
|
||||||
return (bot->HasAuraType(SPELL_AURA_MOD_STUN) ||
|
|
||||||
bot->HasAuraType(SPELL_AURA_MOD_FEAR) ||
|
|
||||||
bot->HasAuraType(SPELL_AURA_MOD_ROOT) ||
|
|
||||||
bot->HasAuraType(SPELL_AURA_MOD_CONFUSE) ||
|
|
||||||
bot->HasAuraType(SPELL_AURA_MOD_CHARM))
|
|
||||||
&& CastSpellAction::isUseful();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CastWillOfTheForsakenAction::isPossible()
|
bool CastWillOfTheForsakenAction::isPossible()
|
||||||
{
|
{
|
||||||
uint32 spellId = AI_VALUE2(uint32, "spell id", spell);
|
uint32 spellId = AI_VALUE2(uint32, "spell id", spell);
|
||||||
return spellId && bot->HasSpell(spellId) && !HasSpellOrCategoryCooldown(bot, spellId);
|
return spellId && !HasSpellOrCategoryCooldown(bot, spellId);
|
||||||
}
|
|
||||||
|
|
||||||
bool CastWillOfTheForsakenAction::isUseful()
|
|
||||||
{
|
|
||||||
return (bot->HasAuraType(SPELL_AURA_MOD_FEAR) ||
|
|
||||||
bot->HasAuraType(SPELL_AURA_MOD_CHARM) ||
|
|
||||||
bot->HasAuraType(SPELL_AURA_AOE_CHARM) ||
|
|
||||||
bot->HasAuraWithMechanic(1 << MECHANIC_SLEEP))
|
|
||||||
&& CastSpellAction::isUseful();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool UseTrinketAction::Execute(Event /*event*/)
|
bool UseTrinketAction::Execute(Event /*event*/)
|
||||||
|
|||||||
@ -313,7 +313,6 @@ public:
|
|||||||
|
|
||||||
std::string const GetTargetName() override { return "self target"; }
|
std::string const GetTargetName() override { return "self target"; }
|
||||||
bool isPossible() override;
|
bool isPossible() override;
|
||||||
bool isUseful() override;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class CastWillOfTheForsakenAction : public CastSpellAction
|
class CastWillOfTheForsakenAction : public CastSpellAction
|
||||||
@ -323,7 +322,22 @@ public:
|
|||||||
|
|
||||||
std::string const GetTargetName() override { return "self target"; }
|
std::string const GetTargetName() override { return "self target"; }
|
||||||
bool isPossible() override;
|
bool isPossible() override;
|
||||||
bool isUseful() override;
|
};
|
||||||
|
|
||||||
|
class CastStoneformAction : public CastSpellAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
CastStoneformAction(PlayerbotAI* botAI) : CastSpellAction(botAI, "stoneform") {}
|
||||||
|
|
||||||
|
std::string const GetTargetName() override { return "self target"; }
|
||||||
|
};
|
||||||
|
|
||||||
|
class CastEscapeArtistAction : public CastSpellAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
CastEscapeArtistAction(PlayerbotAI* botAI) : CastSpellAction(botAI, "escape artist") {}
|
||||||
|
|
||||||
|
std::string const GetTargetName() override { return "self target"; }
|
||||||
};
|
};
|
||||||
|
|
||||||
class UseTrinketAction : public Action
|
class UseTrinketAction : public Action
|
||||||
|
|||||||
@ -4,45 +4,104 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "RacialsStrategy.h"
|
#include "RacialsStrategy.h"
|
||||||
|
#include "Playerbots.h"
|
||||||
|
|
||||||
class RacialsStrategyActionNodeFactory : public NamedObjectFactory<ActionNode>
|
namespace
|
||||||
{
|
{
|
||||||
public:
|
constexpr uint32 SPELL_ARCANE_TORRENT_ENERGY = 25046;
|
||||||
RacialsStrategyActionNodeFactory() { creators["lifeblood"] = &lifeblood; }
|
constexpr uint32 SPELL_ARCANE_TORRENT_MANA = 28730;
|
||||||
|
constexpr uint32 SPELL_ARCANE_TORRENT_RUNIC_POWER = 50613;
|
||||||
private:
|
constexpr uint32 SPELL_WAR_STOMP = 20549;
|
||||||
static ActionNode* lifeblood(PlayerbotAI* /*botAI*/)
|
constexpr uint32 SPELL_BERSERKING = 26297;
|
||||||
{
|
constexpr uint32 SPELL_EVERY_MAN_FOR_HIMSELF = 59752;
|
||||||
return new ActionNode("lifeblood",
|
constexpr uint32 SPELL_WILL_OF_THE_FORSAKEN = 7744;
|
||||||
/*P*/ {},
|
constexpr uint32 SPELL_STONEFORM = 20594;
|
||||||
/*A*/ { NextAction("gift of the naaru") },
|
constexpr uint32 SPELL_ESCAPE_ARTIST = 20589;
|
||||||
/*C*/ {});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
void RacialsStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
|
||||||
{
|
|
||||||
triggers.push_back(
|
|
||||||
new TriggerNode("low health", { NextAction("lifeblood", ACTION_NORMAL + 5) }));
|
|
||||||
triggers.push_back(
|
|
||||||
new TriggerNode("medium aoe", { NextAction("war stomp", ACTION_NORMAL + 5) }));
|
|
||||||
triggers.push_back(new TriggerNode(
|
|
||||||
"low mana", { NextAction("arcane torrent", ACTION_NORMAL + 5) }));
|
|
||||||
|
|
||||||
triggers.push_back(new TriggerNode(
|
|
||||||
"generic boost", { NextAction("blood fury", ACTION_NORMAL + 5),
|
|
||||||
NextAction("berserking", ACTION_NORMAL + 5),
|
|
||||||
NextAction("use trinket", ACTION_NORMAL + 4) }));
|
|
||||||
|
|
||||||
triggers.push_back(new TriggerNode(
|
|
||||||
"loss of control", { NextAction("every man for himself", ACTION_EMERGENCY + 1) }));
|
|
||||||
|
|
||||||
triggers.push_back(new TriggerNode(
|
|
||||||
"fear charm sleep", { NextAction("will of the forsaken", ACTION_EMERGENCY + 1) }));
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
RacialsStrategy::RacialsStrategy(PlayerbotAI* botAI) : Strategy(botAI)
|
RacialsStrategy::RacialsStrategy(PlayerbotAI* botAI) : Strategy(botAI)
|
||||||
{
|
{
|
||||||
actionNodeFactories.Add(new RacialsStrategyActionNodeFactory());
|
// No custom ActionNodeFactory needed
|
||||||
|
}
|
||||||
|
|
||||||
|
void RacialsStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
||||||
|
{
|
||||||
|
Player* bot = botAI->GetBot();
|
||||||
|
|
||||||
|
if (bot->HasSpell(SPELL_ARCANE_TORRENT_MANA))
|
||||||
|
{
|
||||||
|
triggers.push_back(new TriggerNode(
|
||||||
|
"low mana", { NextAction("arcane torrent", ACTION_NORMAL + 5) }));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bot->HasSpell(SPELL_ARCANE_TORRENT_ENERGY))
|
||||||
|
{
|
||||||
|
triggers.push_back(new TriggerNode(
|
||||||
|
"low energy", { NextAction("arcane torrent", ACTION_NORMAL + 5) }));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bot->HasSpell(SPELL_ARCANE_TORRENT_RUNIC_POWER))
|
||||||
|
{
|
||||||
|
// No low runic power trigger exists; this trigger should be modified if one is added
|
||||||
|
triggers.push_back(new TriggerNode(
|
||||||
|
"generic boost", { NextAction("arcane torrent", ACTION_NORMAL + 5) }));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bot->HasSpell(SPELL_WAR_STOMP))
|
||||||
|
{
|
||||||
|
triggers.push_back(new TriggerNode(
|
||||||
|
"medium aoe", { NextAction("war stomp", ACTION_NORMAL + 5) }));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bot->HasSpell(SPELL_BERSERKING))
|
||||||
|
{
|
||||||
|
triggers.push_back(new TriggerNode(
|
||||||
|
"generic boost", { NextAction("berserking", ACTION_NORMAL + 5) }));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bot->HasSpell(SPELL_EVERY_MAN_FOR_HIMSELF))
|
||||||
|
{
|
||||||
|
triggers.push_back(new TriggerNode(
|
||||||
|
"loss of control", { NextAction("every man for himself", ACTION_EMERGENCY + 1) }));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bot->HasSpell(SPELL_WILL_OF_THE_FORSAKEN))
|
||||||
|
{
|
||||||
|
triggers.push_back(new TriggerNode(
|
||||||
|
"fear charm sleep", { NextAction("will of the forsaken", ACTION_EMERGENCY + 1) }));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bot->HasSpell(SPELL_STONEFORM))
|
||||||
|
{
|
||||||
|
triggers.push_back(new TriggerNode(
|
||||||
|
"poison disease bleed", { NextAction("stoneform", ACTION_DISPEL) }));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bot->HasSpell(SPELL_ESCAPE_ARTIST))
|
||||||
|
{
|
||||||
|
triggers.push_back(new TriggerNode(
|
||||||
|
"movement impaired", { NextAction("escape artist", ACTION_EMERGENCY + 1) }));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (botAI->HasSpell("blood fury"))
|
||||||
|
{
|
||||||
|
triggers.push_back(new TriggerNode(
|
||||||
|
"generic boost", { NextAction("blood fury", ACTION_NORMAL + 5) }));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (botAI->HasSpell("gift of the naaru"))
|
||||||
|
{
|
||||||
|
// Currently targets self only
|
||||||
|
triggers.push_back(new TriggerNode(
|
||||||
|
"medium health", { NextAction("gift of the naaru", ACTION_LIGHT_HEAL + 5) }));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (botAI->HasSpell("lifeblood"))
|
||||||
|
{
|
||||||
|
triggers.push_back(new TriggerNode(
|
||||||
|
"medium health", { NextAction("lifeblood", ACTION_LIGHT_HEAL + 5) }));
|
||||||
|
}
|
||||||
|
|
||||||
|
triggers.push_back(new TriggerNode(
|
||||||
|
"generic boost", { NextAction("use trinket", ACTION_NORMAL + 4) }));
|
||||||
}
|
}
|
||||||
|
|||||||
@ -491,6 +491,19 @@ bool FearSleepSapTrigger::IsActive()
|
|||||||
bot->HasAuraWithMechanic(1 << MECHANIC_SAPPED);
|
bot->HasAuraWithMechanic(1 << MECHANIC_SAPPED);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool PoisonDiseaseBleedTrigger::IsActive()
|
||||||
|
{
|
||||||
|
return botAI->HasAuraToDispel(bot, DISPEL_POISON) ||
|
||||||
|
botAI->HasAuraToDispel(bot, DISPEL_DISEASE) ||
|
||||||
|
bot->HasAuraWithMechanic(1 << MECHANIC_BLEED);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MovementImpairedTrigger::IsActive()
|
||||||
|
{
|
||||||
|
return botAI->IsMovementImpaired(bot) &&
|
||||||
|
!botAI->HasAnyAuraOf(bot, "stealth", "prowl", nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
bool HasAuraStackTrigger::IsActive()
|
bool HasAuraStackTrigger::IsActive()
|
||||||
{
|
{
|
||||||
return botAI->GetAura(getName(), GetTarget(), false, true, stack);
|
return botAI->GetAura(getName(), GetTarget(), false, true, stack);
|
||||||
|
|||||||
@ -752,6 +752,22 @@ public:
|
|||||||
bool IsActive() override;
|
bool IsActive() override;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class PoisonDiseaseBleedTrigger : public Trigger
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
PoisonDiseaseBleedTrigger(PlayerbotAI* botAI) : Trigger(botAI, "poison disease bleed", 1) {}
|
||||||
|
|
||||||
|
bool IsActive() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class MovementImpairedTrigger : public Trigger
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
MovementImpairedTrigger(PlayerbotAI* botAI) : Trigger(botAI, "movement impaired", 1) {}
|
||||||
|
|
||||||
|
bool IsActive() override;
|
||||||
|
};
|
||||||
|
|
||||||
class IsSwimmingTrigger : public Trigger
|
class IsSwimmingTrigger : public Trigger
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|||||||
@ -66,6 +66,8 @@ public:
|
|||||||
creators["loss of control"] = &TriggerContext::loss_of_control;
|
creators["loss of control"] = &TriggerContext::loss_of_control;
|
||||||
creators["fear charm sleep"] = &TriggerContext::fear_charm_sleep;
|
creators["fear charm sleep"] = &TriggerContext::fear_charm_sleep;
|
||||||
creators["fear sleep sap"] = &TriggerContext::fear_sleep_sap;
|
creators["fear sleep sap"] = &TriggerContext::fear_sleep_sap;
|
||||||
|
creators["poison disease bleed"] = &TriggerContext::poison_disease_bleed;
|
||||||
|
creators["movement impaired"] = &TriggerContext::movement_impaired;
|
||||||
|
|
||||||
creators["protect party member"] = &TriggerContext::protect_party_member;
|
creators["protect party member"] = &TriggerContext::protect_party_member;
|
||||||
|
|
||||||
@ -382,6 +384,8 @@ private:
|
|||||||
static Trigger* loss_of_control(PlayerbotAI* botAI) { return new LossOfControlTrigger(botAI); }
|
static Trigger* loss_of_control(PlayerbotAI* botAI) { return new LossOfControlTrigger(botAI); }
|
||||||
static Trigger* fear_charm_sleep(PlayerbotAI* botAI) { return new FearCharmSleepTrigger(botAI); }
|
static Trigger* fear_charm_sleep(PlayerbotAI* botAI) { return new FearCharmSleepTrigger(botAI); }
|
||||||
static Trigger* fear_sleep_sap(PlayerbotAI* botAI) { return new FearSleepSapTrigger(botAI); }
|
static Trigger* fear_sleep_sap(PlayerbotAI* botAI) { return new FearSleepSapTrigger(botAI); }
|
||||||
|
static Trigger* poison_disease_bleed(PlayerbotAI* botAI) { return new PoisonDiseaseBleedTrigger(botAI); }
|
||||||
|
static Trigger* movement_impaired(PlayerbotAI* botAI) { return new MovementImpairedTrigger(botAI); }
|
||||||
static Trigger* PartyMemberCriticalHealth(PlayerbotAI* botAI)
|
static Trigger* PartyMemberCriticalHealth(PlayerbotAI* botAI)
|
||||||
{
|
{
|
||||||
return new PartyMemberCriticalHealthTrigger(botAI);
|
return new PartyMemberCriticalHealthTrigger(botAI);
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user