Merge pull request #1468 from ThePenguinMan96/Mage-Overhaul

Mage Overhaul
This commit is contained in:
kadeshar
2025-07-27 08:46:23 +02:00
committed by GitHub
19 changed files with 996 additions and 544 deletions

View File

@@ -1428,8 +1428,8 @@ AiPlayerbot.PremadeSpecLink.7.5.80 = -023222301004-05032331331013501120331251
AiPlayerbot.PremadeSpecName.8.0 = arcane pve
AiPlayerbot.PremadeSpecGlyph.8.0 = 42735,43339,44955,43364,43361,42751
AiPlayerbot.PremadeSpecLink.8.0.60 = 23000503110033014032310150532
AiPlayerbot.PremadeSpecLink.8.0.80 = 23000523310033015032310250532-03-203203001
AiPlayerbot.PremadeSpecLink.8.0.60 = 230005231100330150323102500321
AiPlayerbot.PremadeSpecLink.8.0.80 = 230005231100330150323102505321-03-203303001
AiPlayerbot.PremadeSpecName.8.1 = fire pve
AiPlayerbot.PremadeSpecGlyph.8.1 = 42739,43339,45737,43364,44920,42751
AiPlayerbot.PremadeSpecLink.8.1.60 = -0055030011302231053120321341
@@ -1441,7 +1441,7 @@ AiPlayerbot.PremadeSpecLink.8.2.80 = 23002303110003--053303031320310003015223135
AiPlayerbot.PremadeSpecName.8.3 = frostfire pve
AiPlayerbot.PremadeSpecGlyph.8.3 = 44684,44920,42751,43339,43364,45737
AiPlayerbot.PremadeSpecLink.8.3.60 = -2305032012303331053120300051
AiPlayerbot.PremadeSpecLink.8.3.80 = -2305032012303331053120311351-023303031
AiPlayerbot.PremadeSpecLink.8.3.80 = -2305032012303331053120321351-023302031
AiPlayerbot.PremadeSpecName.8.4 = arcane pvp
AiPlayerbot.PremadeSpecGlyph.8.4 = 42735,43364,42738,43360,43357,42752
AiPlayerbot.PremadeSpecLink.8.4.60 = 205323200122032103303102015221

View File

@@ -305,22 +305,22 @@ void AiFactory::AddDefaultCombatStrategies(Player* player, PlayerbotAI* const fa
break;
case CLASS_MAGE:
if (tab == 0)
engine->addStrategiesNoInit("arcane", "arcane aoe", nullptr);
engine->addStrategiesNoInit("arcane", nullptr);
else if (tab == 1)
{
if (player->HasSpell(44614) /*Frostfire Bolt*/ && player->HasAura(15047) /*Ice Shards*/)
{
engine->addStrategiesNoInit("frostfire", "frostfire aoe", nullptr);
engine->addStrategiesNoInit("frostfire", nullptr);
}
else
{
engine->addStrategiesNoInit("fire", "fire aoe", nullptr);
engine->addStrategiesNoInit("fire", nullptr);
}
}
else
engine->addStrategiesNoInit("frost", "frost aoe", nullptr);
engine->addStrategiesNoInit("frost", nullptr);
engine->addStrategiesNoInit("dps", "dps assist", "cure", nullptr);
engine->addStrategiesNoInit("dps", "dps assist", "cure", "aoe", nullptr);
break;
case CLASS_WARRIOR:
if (tab == 2)

View File

@@ -2949,14 +2949,19 @@ bool PlayerbotAI::CanCastSpell(uint32 spellid, Unit* target, bool checkHasSpell,
if (!itemTarget)
{
// Exception for Deep Freeze (44572) - allow cast for damage on immune targets (e.g., bosses)
if (target->IsImmunedToSpell(spellInfo))
{
if (!sPlayerbotAIConfig->logInGroupOnly || (bot->GetGroup() && HasRealPlayerMaster()))
if (spellid != 44572) // Deep Freeze
{
LOG_DEBUG("playerbots", "target is immuned to spell - target name: {}, spellid: {}, bot name: {}",
target->GetName(), spellid, bot->GetName());
if (!sPlayerbotAIConfig->logInGroupOnly || (bot->GetGroup() && HasRealPlayerMaster()))
{
LOG_DEBUG("playerbots", "target is immuned to spell - target name: {}, spellid: {}, bot name: {}",
target->GetName(), spellid, bot->GetName());
}
return false;
}
return false;
// Otherwise, allow Deep Freeze even if immune
}
if (bot != target && sServerFacade->GetDistance2d(bot, target) > sPlayerbotAIConfig->sightDistance)

View File

@@ -4,9 +4,9 @@
*/
#include "ArcaneMageStrategy.h"
#include "Playerbots.h"
// ===== Action Node Factory =====
class ArcaneMageStrategyActionNodeFactory : public NamedObjectFactory<ActionNode>
{
public:
@@ -15,69 +15,44 @@ public:
creators["arcane blast"] = &arcane_blast;
creators["arcane barrage"] = &arcane_barrage;
creators["arcane missiles"] = &arcane_missiles;
// creators["firebolt"] = &firebolt;
creators["fire blast"] = &fire_blast;
creators["frostbolt"] = &frostbolt;
creators["arcane power"] = &arcane_power;
creators["icy veins"] = &icy_veins;
}
private:
static ActionNode* arcane_blast([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode("arcane blast",
/*P*/ nullptr,
/*A*/ NextAction::array(0, new NextAction("arcane missiles"), nullptr),
/*C*/ nullptr);
}
static ActionNode* arcane_barrage([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode("arcane barrage",
/*P*/ nullptr,
/*A*/ nullptr,
/*C*/ nullptr);
}
static ActionNode* arcane_missiles([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode("arcane missiles",
/*P*/ nullptr,
/*A*/ NextAction::array(0, new NextAction("fireball"), nullptr),
/*C*/ nullptr);
}
// static ActionNode* firebolt([[maybe_unused]] PlayerbotAI* botAI)
// {
// return new ActionNode ("firebolt",
// /*P*/ nullptr,
// /*A*/ NextAction::array(0, new NextAction("shoot"), nullptr),
// /*C*/ nullptr);
// }
static ActionNode* arcane_blast(PlayerbotAI*) { return new ActionNode("arcane blast", nullptr, nullptr, nullptr); }
static ActionNode* arcane_barrage(PlayerbotAI*) { return new ActionNode("arcane barrage", nullptr, nullptr, nullptr); }
static ActionNode* arcane_missiles(PlayerbotAI*) { return new ActionNode("arcane missiles", nullptr, nullptr, nullptr); }
static ActionNode* fire_blast(PlayerbotAI*) { return new ActionNode("fire blast", nullptr, nullptr, nullptr); }
static ActionNode* frostbolt(PlayerbotAI*) { return new ActionNode("frostbolt", nullptr, nullptr, nullptr); }
static ActionNode* arcane_power(PlayerbotAI*) { return new ActionNode("arcane power", nullptr, nullptr, nullptr); }
static ActionNode* icy_veins(PlayerbotAI*) { return new ActionNode("icy veins", nullptr, nullptr, nullptr); }
};
// ===== Single Target Strategy =====
ArcaneMageStrategy::ArcaneMageStrategy(PlayerbotAI* botAI) : GenericMageStrategy(botAI)
{
actionNodeFactories.Add(new ArcaneMageStrategyActionNodeFactory());
}
// ===== Default Actions =====
NextAction** ArcaneMageStrategy::getDefaultActions()
{
return NextAction::array(0, new NextAction("arcane blast", ACTION_DEFAULT + 0.3f),
new NextAction("frostbolt", ACTION_DEFAULT + 0.2f), // arcane immune target
new NextAction("fire blast", ACTION_DEFAULT + 0.1f), // cast during movement
new NextAction("shoot", ACTION_DEFAULT), nullptr);
return NextAction::array(0, new NextAction("arcane blast", 5.6f),
new NextAction("arcane missiles", 5.5f),
new NextAction("arcane barrage", 5.4f), // cast while moving
new NextAction("fire blast", 5.3f), // cast while moving if arcane barrage isn't available/learned
new NextAction("frostbolt", 5.2f), // for arcane immune targets
new NextAction("shoot", 5.1f), nullptr);
}
// ===== Trigger Initialization ===
void ArcaneMageStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
{
GenericMageStrategy::InitTriggers(triggers);
triggers.push_back(
new TriggerNode("arcane blast stack", NextAction::array(0, new NextAction("arcane missiles", 15.0f), NULL)));
// Proc Trigger
triggers.push_back(new TriggerNode("arcane blast 4 stacks and missile barrage", NextAction::array(0, new NextAction("arcane missiles", 15.0f), nullptr)));
}
void ArcaneMageAoeStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
{
// triggers.push_back(new TriggerNode(
// "high aoe",
// NextAction::array(0, new NextAction("arcane explosion", 39.0f), NULL)));
triggers.push_back(new TriggerNode("medium aoe", NextAction::array(0, new NextAction("blizzard", 40.0f), NULL)));
}

View File

@@ -20,13 +20,4 @@ public:
NextAction** getDefaultActions() override;
};
class ArcaneMageAoeStrategy : public CombatStrategy
{
public:
ArcaneMageAoeStrategy(PlayerbotAI* ai) : CombatStrategy(ai) {}
public:
virtual void InitTriggers(std::vector<TriggerNode*>& triggers) override;
std::string const getName() override { return "arcane aoe"; }
};
#endif

View File

@@ -4,42 +4,71 @@
*/
#include "FireMageStrategy.h"
#include "Playerbots.h"
#include "Strategy.h"
NextAction** FireMageStrategy::getDefaultActions()
// ===== Action Node Factory =====
class FireMageStrategyActionNodeFactory : public NamedObjectFactory<ActionNode>
{
return NextAction::array(0, new NextAction("fireball", ACTION_DEFAULT + 0.3f),
new NextAction("frostbolt", ACTION_DEFAULT + 0.2f), // fire immune target
new NextAction("fire blast", ACTION_DEFAULT + 0.1f), // cast during movement
new NextAction("shoot", ACTION_DEFAULT), NULL);
public:
FireMageStrategyActionNodeFactory()
{
creators["fireball"] = &fireball;
creators["frostbolt"] = &frostbolt;
creators["fire blast"] = &fire_blast;
creators["pyroblast"] = &pyroblast;
creators["scorch"] = &scorch;
creators["living bomb"] = &living_bomb;
creators["combustion"] = &combustion;
}
private:
static ActionNode* fireball(PlayerbotAI*) { return new ActionNode("fireball", nullptr, nullptr, nullptr); }
static ActionNode* frostbolt(PlayerbotAI*) { return new ActionNode("frostbolt", nullptr, nullptr, nullptr); }
static ActionNode* fire_blast(PlayerbotAI*) { return new ActionNode("fire blast", nullptr, nullptr, nullptr); }
static ActionNode* pyroblast(PlayerbotAI*) { return new ActionNode("pyroblast", nullptr, nullptr, nullptr); }
static ActionNode* scorch(PlayerbotAI*) { return new ActionNode("scorch", nullptr, nullptr, nullptr); }
static ActionNode* living_bomb(PlayerbotAI*) { return new ActionNode("living bomb", nullptr, nullptr, nullptr); }
static ActionNode* combustion(PlayerbotAI*) { return new ActionNode("combustion", nullptr, nullptr, nullptr); }
};
// ===== Single Target Strategy =====
FireMageStrategy::FireMageStrategy(PlayerbotAI* botAI) : GenericMageStrategy(botAI)
{
actionNodeFactories.Add(new FireMageStrategyActionNodeFactory());
}
// ===== Default Actions =====
NextAction** FireMageStrategy::getDefaultActions()
{
return NextAction::array(0, new NextAction("fireball", 5.3f),
new NextAction("frostbolt", 5.2f), // fire immune target
new NextAction("fire blast", 5.1f), // cast during movement
new NextAction("shoot", 5.0f), nullptr);
}
// ===== Trigger Initialization =====
void FireMageStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
{
GenericMageStrategy::InitTriggers(triggers);
// triggers.push_back(new TriggerNode("pyroblast", NextAction::array(0, new NextAction("pyroblast", 10.0f),
// nullptr)));
triggers.push_back(
new TriggerNode("hot streak", NextAction::array(0, new NextAction("pyroblast", 25.0f), nullptr)));
triggers.push_back(
new TriggerNode("combustion", NextAction::array(0, new NextAction("combustion", 50.0f), nullptr)));
triggers.push_back(
new TriggerNode("living bomb", NextAction::array(0, new NextAction("living bomb", 19.0f), nullptr)));
// triggers.push_back(new TriggerNode("enemy too close for spell", NextAction::array(0, new NextAction("dragon's
// breath", 70.0f), nullptr)));
// Debuff Triggers
triggers.push_back(new TriggerNode("improved scorch", NextAction::array(0, new NextAction("scorch", 19.0f), nullptr)));
triggers.push_back(new TriggerNode("living bomb", NextAction::array(0, new NextAction("living bomb", 18.5f), nullptr)));
// Proc Trigger
triggers.push_back(new TriggerNode("hot streak", NextAction::array(0, new NextAction("pyroblast", 25.0f), nullptr)));
}
void FireMageAoeStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
// Combat strategy to run to melee for Dragon's Breath and Blast Wave
// Disabled by default for the Fire/Frostfire spec
// To enable, type "co +firestarter"
// To disable, type "co -firestarter"
FirestarterStrategy::FirestarterStrategy(PlayerbotAI* botAI) : CombatStrategy(botAI) {}
void FirestarterStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
{
// higher priority to cast before move away
triggers.push_back(
new TriggerNode("medium aoe", NextAction::array(0,
new NextAction("dragon's breath", ACTION_MOVE + 9),
new NextAction("flamestrike", ACTION_MOVE + 8),
new NextAction("blast wave", ACTION_MOVE + 7),
new NextAction("living bomb on attackers", 21.0f),
new NextAction("blizzard", 20.0f), nullptr)));
triggers.push_back(new TriggerNode(
"blast wave off cd and medium aoe",
NextAction::array(0, new NextAction("reach melee", 25.5f), nullptr)));
}

View File

@@ -13,20 +13,19 @@ class PlayerbotAI;
class FireMageStrategy : public GenericMageStrategy
{
public:
FireMageStrategy(PlayerbotAI* botAI) : GenericMageStrategy(botAI) {}
FireMageStrategy(PlayerbotAI* botAI);
void InitTriggers(std::vector<TriggerNode*>& triggers) override;
std::string const getName() override { return "fire"; }
NextAction** getDefaultActions() override;
};
class FireMageAoeStrategy : public CombatStrategy
class FirestarterStrategy : public CombatStrategy
{
public:
FireMageAoeStrategy(PlayerbotAI* botAI) : CombatStrategy(botAI) {}
FirestarterStrategy(PlayerbotAI* botAI);
void InitTriggers(std::vector<TriggerNode*>& triggers) override;
std::string const getName() override { return "fire aoe"; }
std::string const getName() override { return "firestarter"; }
};
#endif

View File

@@ -4,31 +4,56 @@
*/
#include "FrostFireMageStrategy.h"
#include "Playerbots.h"
NextAction** FrostFireMageStrategy::getDefaultActions()
// ===== Action Node Factory =====
class FrostFireMageStrategyActionNodeFactory : public NamedObjectFactory<ActionNode>
{
return NextAction::array(0, new NextAction("frostfire bolt", ACTION_DEFAULT + 0.1f),
new NextAction("shoot", ACTION_DEFAULT), NULL);
public:
FrostFireMageStrategyActionNodeFactory()
{
creators["frostfire bolt"] = &frostfire_bolt;
creators["fire blast"] = &fire_blast;
creators["pyroblast"] = &pyroblast;
creators["combustion"] = &combustion;
creators["icy veins"] = &icy_veins;
creators["scorch"] = &scorch;
creators["living bomb"] = &living_bomb;
}
private:
static ActionNode* frostfire_bolt(PlayerbotAI*) { return new ActionNode("frostfire bolt", nullptr, nullptr, nullptr); }
static ActionNode* fire_blast(PlayerbotAI*) { return new ActionNode("fire blast", nullptr, nullptr, nullptr); }
static ActionNode* pyroblast(PlayerbotAI*) { return new ActionNode("pyroblast", nullptr, nullptr, nullptr); }
static ActionNode* combustion(PlayerbotAI*) { return new ActionNode("combustion", nullptr, nullptr, nullptr); }
static ActionNode* icy_veins(PlayerbotAI*) { return new ActionNode("icy veins", nullptr, nullptr, nullptr); }
static ActionNode* scorch(PlayerbotAI*) { return new ActionNode("scorch", nullptr, nullptr, nullptr); }
static ActionNode* living_bomb(PlayerbotAI*) { return new ActionNode("living bomb", nullptr, nullptr, nullptr); }
};
// ===== Single Target Strategy =====
FrostFireMageStrategy::FrostFireMageStrategy(PlayerbotAI* botAI) : GenericMageStrategy(botAI)
{
actionNodeFactories.Add(new FrostFireMageStrategyActionNodeFactory());
}
// ===== Default Actions =====
NextAction** FrostFireMageStrategy::getDefaultActions()
{
return NextAction::array(0, new NextAction("frostfire bolt", 5.2f),
new NextAction("fire blast", 5.1f), // cast during movement
new NextAction("shoot", 5.0f), nullptr);
}
// ===== Trigger Initialization =====
void FrostFireMageStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
{
GenericMageStrategy::InitTriggers(triggers);
triggers.push_back(
new TriggerNode("hot streak", NextAction::array(0, new NextAction("pyroblast", 25.0f), nullptr)));
triggers.push_back(
new TriggerNode("combustion", NextAction::array(0, new NextAction("combustion", 50.0f), nullptr)));
triggers.push_back(
new TriggerNode("icy veins", NextAction::array(0, new NextAction("icy veins", 60.0f), nullptr)));
}
// Debuff Triggers
triggers.push_back(new TriggerNode("improved scorch", NextAction::array(0, new NextAction("scorch", 19.0f), nullptr)));
triggers.push_back(new TriggerNode("living bomb", NextAction::array(0, new NextAction("living bomb", 18.5f), nullptr)));
void FrostFireMageAoeStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
{
triggers.push_back(
new TriggerNode("medium aoe", NextAction::array(0, new NextAction("flamestrike", 20.0f), nullptr)));
triggers.push_back(
new TriggerNode("living bomb", NextAction::array(0, new NextAction("living bomb", 25.0f), nullptr)));
// Proc Trigger
triggers.push_back(new TriggerNode("hot streak", NextAction::array(0, new NextAction("pyroblast", 25.0f), nullptr)));
}

View File

@@ -13,20 +13,11 @@ class PlayerbotAI;
class FrostFireMageStrategy : public GenericMageStrategy
{
public:
FrostFireMageStrategy(PlayerbotAI* botAI) : GenericMageStrategy(botAI) {}
FrostFireMageStrategy(PlayerbotAI* botAI);
void InitTriggers(std::vector<TriggerNode*>& triggers) override;
std::string const getName() override { return "frostfire"; }
NextAction** getDefaultActions() override;
};
class FrostFireMageAoeStrategy : public CombatStrategy
{
public:
FrostFireMageAoeStrategy(PlayerbotAI* botAI) : CombatStrategy(botAI) {}
void InitTriggers(std::vector<TriggerNode*>& triggers) override;
std::string const getName() override { return "frostfire aoe"; }
};
#endif

View File

@@ -7,6 +7,7 @@
#include "Playerbots.h"
// ===== Action Node Factory =====
class FrostMageStrategyActionNodeFactory : public NamedObjectFactory<ActionNode>
{
public:
@@ -16,96 +17,66 @@ public:
creators["ice barrier"] = &ice_barrier;
creators["summon water elemental"] = &summon_water_elemental;
creators["deep freeze"] = &deep_freeze;
creators["icy veins"] = &icy_veins;
creators["frostbolt"] = &frostbolt;
creators["ice lance"] = &ice_lance;
creators["fire blast"] = &fire_blast;
creators["fireball"] = &fireball;
creators["frostfire bolt"] = &frostfire_bolt;
}
private:
static ActionNode* cold_snap([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode("cold snap",
/*P*/ nullptr,
/*A*/ nullptr,
/*C*/ nullptr);
}
static ActionNode* ice_barrier([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode("ice barrier",
/*P*/ nullptr,
/*A*/ nullptr,
/*C*/ nullptr);
}
static ActionNode* summon_water_elemental([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode("summon water elemental",
/*P*/ nullptr,
/*A*/ nullptr,
/*C*/ nullptr);
}
static ActionNode* deep_freeze([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode("deep freeze",
/*P*/ nullptr,
/*A*/ NextAction::array(0, new NextAction("ice lance"), nullptr),
/*C*/ nullptr);
}
static ActionNode* cold_snap(PlayerbotAI*) { return new ActionNode("cold snap", nullptr, nullptr, nullptr); }
static ActionNode* ice_barrier(PlayerbotAI*) { return new ActionNode("ice barrier", nullptr, nullptr, nullptr); }
static ActionNode* summon_water_elemental(PlayerbotAI*) { return new ActionNode("summon water elemental", nullptr, nullptr, nullptr); }
static ActionNode* deep_freeze(PlayerbotAI*) { return new ActionNode("deep freeze", nullptr, nullptr, nullptr); }
static ActionNode* icy_veins(PlayerbotAI*) { return new ActionNode("icy veins", nullptr, nullptr, nullptr); }
static ActionNode* frostbolt(PlayerbotAI*) { return new ActionNode("frostbolt", nullptr, nullptr, nullptr); }
static ActionNode* ice_lance(PlayerbotAI*) { return new ActionNode("ice lance", nullptr, nullptr, nullptr); }
static ActionNode* fire_blast(PlayerbotAI*) { return new ActionNode("fire blast", nullptr, nullptr, nullptr); }
static ActionNode* fireball(PlayerbotAI*) { return new ActionNode("fireball", nullptr, nullptr, nullptr); }
static ActionNode* frostfire_bolt(PlayerbotAI*) { return new ActionNode("frostfire bolt", nullptr, nullptr, nullptr); }
};
// ===== Single Target Strategy =====
FrostMageStrategy::FrostMageStrategy(PlayerbotAI* botAI) : GenericMageStrategy(botAI)
{
actionNodeFactories.Add(new FrostMageStrategyActionNodeFactory());
}
// ===== Default Actions =====
NextAction** FrostMageStrategy::getDefaultActions()
{
return NextAction::array(0, new NextAction("frostbolt", ACTION_DEFAULT + 0.3f),
new NextAction("fire blast", ACTION_DEFAULT + 0.2f), // cast during movement
new NextAction("shoot", ACTION_DEFAULT + 0.1f),
new NextAction("fireball", ACTION_DEFAULT), nullptr);
return NextAction::array(0, new NextAction("frostbolt", 5.4f),
new NextAction("ice lance", 5.3f), // cast during movement
new NextAction("fire blast", 5.2f), // cast during movement if ice lance is not learned
new NextAction("shoot", 5.1f),
new NextAction("fireball", 5.0f), nullptr);
}
// ===== Trigger Initialization ===
void FrostMageStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
{
GenericMageStrategy::InitTriggers(triggers);
triggers.push_back(new TriggerNode("icy veins", NextAction::array(0, new NextAction("icy veins", 50.0f), nullptr)));
// No logic currently for cold snap usage.. possibly use right after icy veins drops off?
// triggers.push_back(new TriggerNode("cold snap", NextAction::array(0, new NextAction("cold snap", 50.0f),
// nullptr)));
triggers.push_back(new TriggerNode(
"no pet", NextAction::array(0, new NextAction("summon water elemental", ACTION_HIGH), nullptr)));
triggers.push_back(
new TriggerNode("has pet", NextAction::array(0, new NextAction("toggle pet spell", ACTION_HIGH + 1), nullptr)));
triggers.push_back(
new TriggerNode("medium health", NextAction::array(0, new NextAction("ice barrier", ACTION_NORMAL), nullptr)));
triggers.push_back(
new TriggerNode("being attacked", NextAction::array(0, new NextAction("ice barrier", ACTION_HIGH + 1), nullptr)));
triggers.push_back(new TriggerNode(
"brain freeze", NextAction::array(0, new NextAction("frostfire bolt", ACTION_NORMAL + 3), nullptr)));
// Combo cast the last charge of fingers of frost for double crits.
// Should only do this on the final charge of FoF.
triggers.push_back(new TriggerNode("fingers of frost single",
NextAction::array(0, new NextAction("frostbolt", ACTION_NORMAL + 2),
new NextAction("deep freeze", ACTION_NORMAL + 1), nullptr)));
// May not need this, frostbolt is the default action so probably don't need to specify.
// Maybe uncomment if you find the mage is prioritising auxillary spells while this buff is up, and wasting the
// proc. triggers.push_back(new TriggerNode("fingers of frost double", NextAction::array(0, new
// NextAction("frostbolt", ACTION_NORMAL), nullptr)));
// Pet/Defensive triggers
triggers.push_back(new TriggerNode("no pet", NextAction::array(0, new NextAction("summon water elemental", 30.0f), nullptr)));
triggers.push_back(new TriggerNode("has pet", NextAction::array(0, new NextAction("toggle pet spell", 29.5f), nullptr)));
triggers.push_back(new TriggerNode("medium health", NextAction::array(0, new NextAction("ice barrier", 29.0f), nullptr)));
triggers.push_back(new TriggerNode("being attacked", NextAction::array(0, new NextAction("ice barrier", 29.0f), nullptr)));
// Same 2-spell combo for various freeze procs
triggers.push_back(new TriggerNode("frost nova on target",
NextAction::array(0, new NextAction("frostbolt", ACTION_NORMAL + 2),
new NextAction("deep freeze", ACTION_NORMAL + 1), nullptr)));
triggers.push_back(new TriggerNode("frostbite on target",
NextAction::array(0, new NextAction("frostbolt", ACTION_NORMAL + 2),
new NextAction("deep freeze", ACTION_NORMAL + 1), nullptr)));
}
void FrostMageAoeStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
{
triggers.push_back(
new TriggerNode("medium aoe", NextAction::array(0, new NextAction("blizzard", ACTION_HIGH), nullptr)));
triggers.push_back(
new TriggerNode("light aoe", NextAction::array(0, new NextAction("cone of cold", ACTION_HIGH + 1), nullptr)));
// Proc/Freeze triggers
triggers.push_back(new TriggerNode("brain freeze", NextAction::array(0, new NextAction("frostfire bolt", 19.5f), nullptr)));
triggers.push_back(new TriggerNode("fingers of frost", NextAction::array(0,
new NextAction("deep freeze", 19.0f),
new NextAction("frostbolt", 18.0f), nullptr)));
triggers.push_back(new TriggerNode("frostbite on target", NextAction::array(0,
new NextAction("deep freeze", 19.0f),
new NextAction("frostbolt", 18.0f), nullptr)));
triggers.push_back(new TriggerNode("frost nova on target", NextAction::array(0,
new NextAction("deep freeze", 19.0f),
new NextAction("frostbolt", 18.0f), nullptr)));
}

View File

@@ -20,13 +20,4 @@ public:
NextAction** getDefaultActions() override;
};
class FrostMageAoeStrategy : public CombatStrategy
{
public:
FrostMageAoeStrategy(PlayerbotAI* botAI) : CombatStrategy(botAI) {}
void InitTriggers(std::vector<TriggerNode*>& triggers) override;
std::string const getName() override { return "frost aoe"; }
};
#endif

View File

@@ -4,7 +4,7 @@
*/
#include "GenericMageNonCombatStrategy.h"
#include "AiFactory.h"
#include "Playerbots.h"
class GenericMageNonCombatStrategyActionNodeFactory : public NamedObjectFactory<ActionNode>
@@ -52,35 +52,23 @@ void GenericMageNonCombatStrategy::InitTriggers(std::vector<TriggerNode*>& trigg
{
NonCombatStrategy::InitTriggers(triggers);
triggers.push_back(
new TriggerNode("arcane intellect", NextAction::array(0, new NextAction("arcane intellect", 21.0f), nullptr)));
triggers.push_back(
new TriggerNode("no focus magic", NextAction::array(0, new NextAction("focus magic on party", 19.0f), nullptr)));
// triggers.push_back(new TriggerNode("no drink", NextAction::array(0, new NextAction("conjure water", 16.0f),
// nullptr))); triggers.push_back(new TriggerNode("no food", NextAction::array(0, new NextAction("conjure
// food", 15.0f), nullptr)));
triggers.push_back(new TriggerNode("arcane intellect", NextAction::array(0, new NextAction("arcane intellect", 21.0f), nullptr)));
triggers.push_back(new TriggerNode("no focus magic", NextAction::array(0, new NextAction("focus magic on party", 19.0f), nullptr)));
triggers.push_back(new TriggerNode("often", NextAction::array(0, new NextAction("apply oil", 1.0f), nullptr)));
triggers.push_back(new TriggerNode("no mana gem", NextAction::array(0, new NextAction("conjure mana gem", 20.0f), nullptr)));
}
void MageBuffManaStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
{
triggers.push_back(
new TriggerNode("mage armor", NextAction::array(0, new NextAction("mage armor", 19.0f), nullptr)));
triggers.push_back(new TriggerNode("mage armor", NextAction::array(0, new NextAction("mage armor", 19.0f), nullptr)));
}
void MageBuffDpsStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
{
triggers.push_back(
new TriggerNode("mage armor", NextAction::array(0, new NextAction("molten armor", 19.0f), nullptr)));
triggers.push_back(new TriggerNode("mage armor", NextAction::array(0, new NextAction("molten armor", 19.0f), nullptr)));
}
void MageBuffStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
{
triggers.push_back(
new TriggerNode("arcane intellect on party",
NextAction::array(0, new NextAction("arcane intellect on party", 20.0f), nullptr)));
// triggers.push_back(new TriggerNode("give water", NextAction::array(0, new NextAction("give water", 14.0f),
// nullptr))); triggers.push_back(new TriggerNode("give food", NextAction::array(0, new NextAction("give
// food", 13.0f), nullptr)));
triggers.push_back(new TriggerNode("arcane intellect on party", NextAction::array(0, new NextAction("arcane intellect on party", 20.0f), nullptr)));
}

View File

@@ -4,7 +4,7 @@
*/
#include "GenericMageStrategy.h"
#include "AiFactory.h"
#include "Playerbots.h"
#include "RangedCombatStrategy.h"
@@ -160,49 +160,119 @@ void GenericMageStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
{
RangedCombatStrategy::InitTriggers(triggers);
// triggers.push_back(new TriggerNode("enemy out of spell", NextAction::array(0, new NextAction("reach spell",
// ACTION_MOVE + 9), nullptr)));
triggers.push_back(
new TriggerNode("enemy is close", NextAction::array(0, new NextAction("frost nova", 50.0f), nullptr)));
triggers.push_back(
new TriggerNode("counterspell on enemy healer",
NextAction::array(0, new NextAction("counterspell on enemy healer", 40.0f), nullptr)));
triggers.push_back(
new TriggerNode("critical health", NextAction::array(0, new NextAction("ice block", 80.0f), nullptr)));
triggers.push_back(
new TriggerNode("spellsteal", NextAction::array(0, new NextAction("spellsteal", 40.0f), nullptr)));
triggers.push_back(
new TriggerNode("medium threat", NextAction::array(0, new NextAction("invisibility", 60.0f), nullptr)));
triggers.push_back(
new TriggerNode("low mana", NextAction::array(0, new NextAction("evocation", ACTION_EMERGENCY + 5), nullptr)));
triggers.push_back(
new TriggerNode("fire ward", NextAction::array(0, new NextAction("fire ward", ACTION_EMERGENCY), nullptr)));
triggers.push_back(
new TriggerNode("frost ward", NextAction::array(0, new NextAction("frost ward", ACTION_EMERGENCY), nullptr)));
triggers.push_back(new TriggerNode("enemy too close for spell",
NextAction::array(0, new NextAction("blink back", ACTION_MOVE + 5), nullptr)));
// Threat Triggers
triggers.push_back(new TriggerNode("high threat", NextAction::array(0, new NextAction("mirror image", 60.0f), nullptr)));
triggers.push_back(new TriggerNode("medium threat", NextAction::array(0, new NextAction("invisibility", 30.0f), nullptr)));
// Defensive Triggers
triggers.push_back(new TriggerNode("critical health", NextAction::array(0, new NextAction("ice block", 90.0f), nullptr)));
triggers.push_back(new TriggerNode("low health", NextAction::array(0, new NextAction("mana shield", 85.0f), nullptr)));
triggers.push_back(new TriggerNode("fire ward", NextAction::array(0, new NextAction("fire ward", 90.0f), nullptr)));
triggers.push_back(new TriggerNode("frost ward", NextAction::array(0, new NextAction("frost ward", 90.0f), nullptr)));
triggers.push_back(new TriggerNode("enemy is close and no firestarter strategy", NextAction::array(0, new NextAction("frost nova", 50.0f), nullptr)));
triggers.push_back(new TriggerNode("enemy too close for spell and no firestarter strategy", NextAction::array(0, new NextAction("blink back", 35.0f), nullptr)));
// Mana Threshold Triggers
Player* bot = botAI->GetBot();
if (bot->HasSpell(42985)) // Mana Sapphire
triggers.push_back(new TriggerNode("high mana", NextAction::array(0, new NextAction("use mana sapphire", 90.0f), nullptr)));
else if (bot->HasSpell(27101)) // Mana Emerald
triggers.push_back(new TriggerNode("high mana", NextAction::array(0, new NextAction("use mana emerald", 90.0f), nullptr)));
else if (bot->HasSpell(10054)) // Mana Ruby
triggers.push_back(new TriggerNode("high mana", NextAction::array(0, new NextAction("use mana ruby", 90.0f), nullptr)));
else if (bot->HasSpell(10053)) // Mana Citrine
triggers.push_back(new TriggerNode("high mana", NextAction::array(0, new NextAction("use mana citrine", 90.0f), nullptr)));
else if (bot->HasSpell(3552)) // Mana Jade
triggers.push_back(new TriggerNode("high mana", NextAction::array(0, new NextAction("use mana jade", 90.0f), nullptr)));
else if (bot->HasSpell(759)) // Mana Agate
triggers.push_back(new TriggerNode("high mana", NextAction::array(0, new NextAction("use mana agate", 90.0f), nullptr)));
triggers.push_back(new TriggerNode("medium mana", NextAction::array(0, new NextAction("mana potion", 90.0f), nullptr)));
triggers.push_back(new TriggerNode("low mana", NextAction::array(0, new NextAction("evocation", 90.0f), nullptr)));
// Counterspell / Spellsteal Triggers
triggers.push_back(new TriggerNode("spellsteal", NextAction::array(0, new NextAction("spellsteal", 40.0f), nullptr)));
triggers.push_back(new TriggerNode("counterspell on enemy healer", NextAction::array(0, new NextAction("counterspell on enemy healer", 40.0f), nullptr)));
}
void MageCureStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
{
triggers.push_back(
new TriggerNode("remove curse", NextAction::array(0, new NextAction("remove curse", 41.0f), nullptr)));
triggers.push_back(new TriggerNode("remove curse on party",
NextAction::array(0, new NextAction("remove curse on party", 40.0f), nullptr)));
triggers.push_back(new TriggerNode("remove curse", NextAction::array(0, new NextAction("remove curse", 41.0f), nullptr)));
triggers.push_back(new TriggerNode("remove curse on party", NextAction::array(0, new NextAction("remove curse on party", 40.0f), nullptr)));
}
void MageBoostStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
{
triggers.push_back(new TriggerNode("icy veins", NextAction::array(0, new NextAction("icy veins", 50.0f), nullptr)));
triggers.push_back(
new TriggerNode("presence of mind", NextAction::array(0, new NextAction("presence of mind", 42.0f), nullptr)));
// triggers.push_back(new TriggerNode("arcane power", NextAction::array(0, new NextAction("arcane power", 41.0f), nullptr)));
triggers.push_back(
new TriggerNode("mirror image", NextAction::array(0, new NextAction("mirror image", 41.0f), nullptr)));
Player* bot = botAI->GetBot();
int tab = AiFactory::GetPlayerSpecTab(bot);
if (tab == 0) // Arcane
{
triggers.push_back(new TriggerNode("arcane power", NextAction::array(0, new NextAction("arcane power", 29.0f), nullptr)));
triggers.push_back(new TriggerNode("icy veins", NextAction::array(0, new NextAction("icy veins", 28.5f), nullptr)));
triggers.push_back(new TriggerNode("mirror image", NextAction::array(0, new NextAction("mirror image", 28.0f), nullptr)));
}
else if (tab == 1)
{
if (bot->HasSpell(44614) /*Frostfire Bolt*/ && bot->HasAura(15047) /*Ice Shards*/)
{ // Frostfire
triggers.push_back(new TriggerNode("combustion", NextAction::array(0, new NextAction("combustion", 18.0f), nullptr)));
triggers.push_back(new TriggerNode("icy veins", NextAction::array(0, new NextAction("icy veins", 17.5f), nullptr)));
triggers.push_back(new TriggerNode("mirror image", NextAction::array(0, new NextAction("mirror image", 17.0f), nullptr)));
}
else
{ // Fire
triggers.push_back(new TriggerNode("combustion", NextAction::array(0, new NextAction("combustion", 18.0f), nullptr)));
triggers.push_back(new TriggerNode("mirror image", NextAction::array(0, new NextAction("mirror image", 17.5f), nullptr)));
}
}
else if (tab == 2) // Frost
{
triggers.push_back(new TriggerNode("cold snap", NextAction::array(0, new NextAction("cold snap", 28.0f), nullptr)));
triggers.push_back(new TriggerNode("icy veins", NextAction::array(0, new NextAction("icy veins", 27.5f), nullptr)));
triggers.push_back(new TriggerNode("mirror image", NextAction::array(0, new NextAction("mirror image", 26.0f), nullptr)));
}
}
void MageCcStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
{
triggers.push_back(new TriggerNode("polymorph", NextAction::array(0, new NextAction("polymorph", 30.0f), nullptr)));
}
void MageAoeStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
{
triggers.push_back(new TriggerNode("blizzard channel check", NextAction::array(0, new NextAction("cancel channel", 26.0f), nullptr)));
Player* bot = botAI->GetBot();
int tab = AiFactory::GetPlayerSpecTab(bot);
if (tab == 0) // Arcane
{
triggers.push_back(new TriggerNode("flamestrike active and medium aoe", NextAction::array(0, new NextAction("blizzard", 24.0f), nullptr)));
triggers.push_back(new TriggerNode("medium aoe", NextAction::array(0,
new NextAction("flamestrike", 23.0f),
new NextAction("blizzard", 22.0f), nullptr)));
triggers.push_back(new TriggerNode("light aoe", NextAction::array(0, new NextAction("arcane explosion", 21.0f), nullptr)));
}
else if (tab == 1) // Fire and Frostfire
{
triggers.push_back(
new TriggerNode("medium aoe", NextAction::array(0,
new NextAction("dragon's breath", 39.0f),
new NextAction("blast wave", 38.0f),
new NextAction("flamestrike", 23.0f),
new NextAction("blizzard", 22.0f), nullptr)));
triggers.push_back(new TriggerNode("flamestrike active and medium aoe", NextAction::array(0, new NextAction("blizzard", 24.0f), nullptr)));
triggers.push_back(new TriggerNode("firestarter", NextAction::array(0, new NextAction("flamestrike", 40.0f), nullptr)));
triggers.push_back(new TriggerNode("living bomb on attackers", NextAction::array(0, new NextAction("living bomb on attackers", 21.0f), nullptr)));
}
else if (tab == 2) // Frost
{
triggers.push_back(new TriggerNode("flamestrike active and medium aoe", NextAction::array(0, new NextAction("blizzard", 24.0f), nullptr)));
triggers.push_back(new TriggerNode("medium aoe", NextAction::array(0,
new NextAction("flamestrike", 23.0f),
new NextAction("blizzard", 22.0f), nullptr)));
triggers.push_back(new TriggerNode("light aoe", NextAction::array(0, new NextAction("cone of cold", 21.0f), nullptr)));
}
}

View File

@@ -51,4 +51,13 @@ public:
std::string const getName() override { return "cc"; }
};
class MageAoeStrategy : public CombatStrategy
{
public:
MageAoeStrategy(PlayerbotAI* botAI) : CombatStrategy(botAI) {}
void InitTriggers(std::vector<TriggerNode*>& triggers) override;
std::string const getName() override { return "aoe"; }
};
#endif

View File

@@ -5,7 +5,7 @@
#include "MageActions.h"
#include <cmath>
#include "UseItemAction.h"
#include "PlayerbotAIConfig.h"
#include "Playerbots.h"
#include "ServerFacade.h"
@@ -13,6 +13,42 @@
Value<Unit*>* CastPolymorphAction::GetTargetValue() { return context->GetValue<Unit*>("cc target", getName()); }
bool UseManaSapphireAction::isUseful()
{
Player* bot = botAI->GetBot();
return AI_VALUE2(bool, "combat", "self target") && bot->GetItemCount(33312, false) > 0; // Mana Sapphire
}
bool UseManaEmeraldAction::isUseful()
{
Player* bot = botAI->GetBot();
return AI_VALUE2(bool, "combat", "self target") && bot->GetItemCount(22044, false) > 0; // Mana Emerald
}
bool UseManaRubyAction::isUseful()
{
Player* bot = botAI->GetBot();
return AI_VALUE2(bool, "combat", "self target") && bot->GetItemCount(8008, false) > 0; // Mana Ruby
}
bool UseManaCitrineAction::isUseful()
{
Player* bot = botAI->GetBot();
return AI_VALUE2(bool, "combat", "self target") && bot->GetItemCount(8007, false) > 0; // Mana Citrine
}
bool UseManaJadeAction::isUseful()
{
Player* bot = botAI->GetBot();
return AI_VALUE2(bool, "combat", "self target") && bot->GetItemCount(5513, false) > 0; // Mana Jade
}
bool UseManaAgateAction::isUseful()
{
Player* bot = botAI->GetBot();
return AI_VALUE2(bool, "combat", "self target") && bot->GetItemCount(5514, false) > 0; // Mana Agate
}
bool CastFrostNovaAction::isUseful()
{
Unit* target = AI_VALUE(Unit*, "current target");
@@ -105,4 +141,14 @@ bool CastBlinkBackAction::Execute(Event event)
// can cast spell check passed in isUseful()
bot->SetOrientation(bot->GetAngle(target) + M_PI);
return CastSpellAction::Execute(event);
}
}
bool CancelChannelAction::Execute(Event event)
{
if (bot->GetCurrentSpell(CURRENT_CHANNELED_SPELL))
{
bot->InterruptSpell(CURRENT_CHANNELED_SPELL);
return true;
}
return false;
}

View File

@@ -8,11 +8,280 @@
#include "GenericSpellActions.h"
#include "SharedDefines.h"
#include "UseItemAction.h"
class PlayerbotAI;
BUFF_ACTION(CastFireWardAction, "fire ward");
BUFF_ACTION(CastFrostWardAction, "frost ward");
// Buff and Out of Combat Actions
class CastMoltenArmorAction : public CastBuffSpellAction
{
public:
CastMoltenArmorAction(PlayerbotAI* botAI) : CastBuffSpellAction(botAI, "molten armor") {}
};
class CastMageArmorAction : public CastBuffSpellAction
{
public:
CastMageArmorAction(PlayerbotAI* botAI) : CastBuffSpellAction(botAI, "mage armor") {}
};
class CastIceArmorAction : public CastBuffSpellAction
{
public:
CastIceArmorAction(PlayerbotAI* botAI) : CastBuffSpellAction(botAI, "ice armor") {}
};
class CastFrostArmorAction : public CastBuffSpellAction
{
public:
CastFrostArmorAction(PlayerbotAI* botAI) : CastBuffSpellAction(botAI, "frost armor") {}
};
class CastArcaneIntellectAction : public CastBuffSpellAction
{
public:
CastArcaneIntellectAction(PlayerbotAI* botAI) : CastBuffSpellAction(botAI, "arcane intellect") {}
};
class CastArcaneIntellectOnPartyAction : public BuffOnPartyAction
{
public:
CastArcaneIntellectOnPartyAction(PlayerbotAI* botAI) : BuffOnPartyAction(botAI, "arcane intellect") {}
};
class CastFocusMagicOnPartyAction : public CastSpellAction
{
public:
CastFocusMagicOnPartyAction(PlayerbotAI* botAI) : CastSpellAction(botAI, "focus magic") {}
Unit* GetTarget() override;
};
class CastSummonWaterElementalAction : public CastBuffSpellAction
{
public:
CastSummonWaterElementalAction(PlayerbotAI* botAI) : CastBuffSpellAction(botAI, "summon water elemental") {}
};
// Boost Actions
class CastCombustionAction : public CastBuffSpellAction
{
public:
CastCombustionAction(PlayerbotAI* botAI) : CastBuffSpellAction(botAI, "combustion") {}
};
class CastArcanePowerAction : public CastBuffSpellAction
{
public:
CastArcanePowerAction(PlayerbotAI* botAI) : CastBuffSpellAction(botAI, "arcane power") {}
};
class CastPresenceOfMindAction : public CastBuffSpellAction
{
public:
CastPresenceOfMindAction(PlayerbotAI* botAI) : CastBuffSpellAction(botAI, "presence of mind") {}
};
class CastIcyVeinsAction : public CastBuffSpellAction
{
public:
CastIcyVeinsAction(PlayerbotAI* botAI) : CastBuffSpellAction(botAI, "icy veins") {}
};
class CastColdSnapAction : public CastBuffSpellAction
{
public:
CastColdSnapAction(PlayerbotAI* botAI) : CastBuffSpellAction(botAI, "cold snap") {}
};
// Defensive Actions
class CastFireWardAction : public CastBuffSpellAction
{
public:
CastFireWardAction(PlayerbotAI* botAI) : CastBuffSpellAction(botAI, "fire ward") {}
};
class CastFrostWardAction : public CastBuffSpellAction
{
public:
CastFrostWardAction(PlayerbotAI* botAI) : CastBuffSpellAction(botAI, "frost ward") {}
};
class CastIceBarrierAction : public CastBuffSpellAction
{
public:
CastIceBarrierAction(PlayerbotAI* botAI) : CastBuffSpellAction(botAI, "ice barrier") {}
};
class CastInvisibilityAction : public CastBuffSpellAction
{
public:
CastInvisibilityAction(PlayerbotAI* botAI) : CastBuffSpellAction(botAI, "invisibility") {}
};
class CastIceBlockAction : public CastBuffSpellAction
{
public:
CastIceBlockAction(PlayerbotAI* botAI) : CastBuffSpellAction(botAI, "ice block") {}
};
class CastMirrorImageAction : public CastBuffSpellAction
{
public:
CastMirrorImageAction(PlayerbotAI* botAI) : CastBuffSpellAction(botAI, "mirror image") {}
};
class CastBlinkBackAction : public CastSpellAction
{
public:
CastBlinkBackAction(PlayerbotAI* botAI) : CastSpellAction(botAI, "blink") {}
bool Execute(Event event) override;
};
class CastManaShieldAction : public CastBuffSpellAction
{
public:
CastManaShieldAction(PlayerbotAI* botAI) : CastBuffSpellAction(botAI, "mana shield") {}
};
// Utility Actions
class CastEvocationAction : public CastSpellAction
{
public:
CastEvocationAction(PlayerbotAI* botAI) : CastSpellAction(botAI, "evocation") {}
std::string const GetTargetName() override { return "self target"; }
};
class CastConjureManaGemAction : public CastBuffSpellAction
{
public:
CastConjureManaGemAction(PlayerbotAI* botAI) : CastBuffSpellAction(botAI, "conjure mana gem") {}
};
class CastConjureFoodAction : public CastBuffSpellAction
{
public:
CastConjureFoodAction(PlayerbotAI* botAI) : CastBuffSpellAction(botAI, "conjure food") {}
};
class CastConjureWaterAction : public CastBuffSpellAction
{
public:
CastConjureWaterAction(PlayerbotAI* botAI) : CastBuffSpellAction(botAI, "conjure water") {}
};
class UseManaSapphireAction : public UseItemAction
{
public:
UseManaSapphireAction(PlayerbotAI* botAI) : UseItemAction(botAI, "mana sapphire") {}
bool isUseful() override;
};
class UseManaEmeraldAction : public UseItemAction
{
public:
UseManaEmeraldAction(PlayerbotAI* botAI) : UseItemAction(botAI, "mana emerald") {}
bool isUseful() override;
};
class UseManaRubyAction : public UseItemAction
{
public:
UseManaRubyAction(PlayerbotAI* botAI) : UseItemAction(botAI, "mana ruby") {}
bool isUseful() override;
};
class UseManaCitrineAction : public UseItemAction
{
public:
UseManaCitrineAction(PlayerbotAI* botAI) : UseItemAction(botAI, "mana citrine") {}
bool isUseful() override;
};
class UseManaJadeAction : public UseItemAction
{
public:
UseManaJadeAction(PlayerbotAI* botAI) : UseItemAction(botAI, "mana jade") {}
bool isUseful() override;
};
class UseManaAgateAction : public UseItemAction
{
public:
UseManaAgateAction(PlayerbotAI* botAI) : UseItemAction(botAI, "mana agate") {}
bool isUseful() override;
};
// CC, Interrupt, and Dispel Actions
class CastPolymorphAction : public CastBuffSpellAction
{
public:
CastPolymorphAction(PlayerbotAI* botAI) : CastBuffSpellAction(botAI, "polymorph") {}
Value<Unit*>* GetTargetValue() override;
};
class CastSpellstealAction : public CastSpellAction
{
public:
CastSpellstealAction(PlayerbotAI* botAI) : CastSpellAction(botAI, "spellsteal") {}
};
class CastCounterspellAction : public CastSpellAction
{
public:
CastCounterspellAction(PlayerbotAI* botAI) : CastSpellAction(botAI, "counterspell") {}
};
class CastCounterspellOnEnemyHealerAction : public CastSpellOnEnemyHealerAction
{
public:
CastCounterspellOnEnemyHealerAction(PlayerbotAI* botAI) : CastSpellOnEnemyHealerAction(botAI, "counterspell") {}
};
class CastFrostNovaAction : public CastSpellAction
{
public:
CastFrostNovaAction(PlayerbotAI* botAI) : CastSpellAction(botAI, "frost nova") {}
bool isUseful() override;
};
class CastDeepFreezeAction : public CastSpellAction
{
public:
CastDeepFreezeAction(PlayerbotAI* botAI) : CastSpellAction(botAI, "deep freeze") {}
bool isPossible() override { return true; }
};
class CastRemoveCurseAction : public CastCureSpellAction
{
public:
CastRemoveCurseAction(PlayerbotAI* botAI) : CastCureSpellAction(botAI, "remove curse") {}
};
class CastRemoveLesserCurseAction : public CastCureSpellAction
{
public:
CastRemoveLesserCurseAction(PlayerbotAI* botAI) : CastCureSpellAction(botAI, "remove lesser curse") {}
};
class CastRemoveCurseOnPartyAction : public CurePartyMemberAction
{
public:
CastRemoveCurseOnPartyAction(PlayerbotAI* botAI) : CurePartyMemberAction(botAI, "remove curse", DISPEL_CURSE) {}
};
class CastRemoveLesserCurseOnPartyAction : public CurePartyMemberAction
{
public:
CastRemoveLesserCurseOnPartyAction(PlayerbotAI* botAI)
: CurePartyMemberAction(botAI, "remove lesser curse", DISPEL_CURSE)
{
}
};
// Damage and Debuff Actions
class CastFireballAction : public CastSpellAction
{
@@ -57,18 +326,26 @@ public:
CastPyroblastAction(PlayerbotAI* botAI) : CastSpellAction(botAI, "pyroblast") {}
};
class CastFlamestrikeAction : public CastDebuffSpellAction
class CastLivingBombAction : public CastDebuffSpellAction
{
public:
CastFlamestrikeAction(PlayerbotAI* botAI) : CastDebuffSpellAction(botAI, "flamestrike", true, 0.0f) {}
ActionThreatType getThreatType() override { return ActionThreatType::Aoe; }
CastLivingBombAction(PlayerbotAI* botAI) : CastDebuffSpellAction(botAI, "living bomb", true) {}
bool isUseful() override
{
// Bypass TTL check
return CastAuraSpellAction::isUseful();
}
};
class CastFrostNovaAction : public CastSpellAction
class CastLivingBombOnAttackersAction : public CastDebuffSpellOnAttackerAction
{
public:
CastFrostNovaAction(PlayerbotAI* botAI) : CastSpellAction(botAI, "frost nova") {}
bool isUseful() override;
CastLivingBombOnAttackersAction(PlayerbotAI* botAI) : CastDebuffSpellOnAttackerAction(botAI, "living bomb", true) {}
bool isUseful() override
{
// Bypass TTL check
return CastAuraSpellAction::isUseful();
}
};
class CastFrostboltAction : public CastSpellAction
@@ -89,12 +366,6 @@ public:
CastIceLanceAction(PlayerbotAI* botAI) : CastSpellAction(botAI, "ice lance") {}
};
class CastDeepFreezeAction : public CastSpellAction
{
public:
CastDeepFreezeAction(PlayerbotAI* botAI) : CastSpellAction(botAI, "deep freeze") {}
};
class CastBlizzardAction : public CastSpellAction
{
public:
@@ -110,143 +381,11 @@ public:
bool isUseful() override;
};
class CastArcaneIntellectAction : public CastBuffSpellAction
class CastFlamestrikeAction : public CastDebuffSpellAction
{
public:
CastArcaneIntellectAction(PlayerbotAI* botAI) : CastBuffSpellAction(botAI, "arcane intellect") {}
};
class CastArcaneIntellectOnPartyAction : public BuffOnPartyAction
{
public:
CastArcaneIntellectOnPartyAction(PlayerbotAI* botAI) : BuffOnPartyAction(botAI, "arcane intellect") {}
};
class CastRemoveCurseAction : public CastCureSpellAction
{
public:
CastRemoveCurseAction(PlayerbotAI* botAI) : CastCureSpellAction(botAI, "remove curse") {}
};
class CastRemoveLesserCurseAction : public CastCureSpellAction
{
public:
CastRemoveLesserCurseAction(PlayerbotAI* botAI) : CastCureSpellAction(botAI, "remove lesser curse") {}
};
class CastIcyVeinsAction : public CastBuffSpellAction
{
public:
CastIcyVeinsAction(PlayerbotAI* botAI) : CastBuffSpellAction(botAI, "icy veins") {}
};
class CastColdSnapAction : public CastBuffSpellAction
{
public:
CastColdSnapAction(PlayerbotAI* botAI) : CastBuffSpellAction(botAI, "cold snap") {}
};
class CastIceBarrierAction : public CastBuffSpellAction
{
public:
CastIceBarrierAction(PlayerbotAI* botAI) : CastBuffSpellAction(botAI, "ice barrier") {}
};
class CastSummonWaterElementalAction : public CastBuffSpellAction
{
public:
CastSummonWaterElementalAction(PlayerbotAI* botAI) : CastBuffSpellAction(botAI, "summon water elemental") {}
};
class CastCombustionAction : public CastBuffSpellAction
{
public:
CastCombustionAction(PlayerbotAI* botAI) : CastBuffSpellAction(botAI, "combustion") {}
};
BEGIN_SPELL_ACTION(CastCounterspellAction, "counterspell")
END_SPELL_ACTION()
class CastRemoveCurseOnPartyAction : public CurePartyMemberAction
{
public:
CastRemoveCurseOnPartyAction(PlayerbotAI* botAI) : CurePartyMemberAction(botAI, "remove curse", DISPEL_CURSE) {}
};
class CastRemoveLesserCurseOnPartyAction : public CurePartyMemberAction
{
public:
CastRemoveLesserCurseOnPartyAction(PlayerbotAI* botAI)
: CurePartyMemberAction(botAI, "remove lesser curse", DISPEL_CURSE)
{
}
};
class CastConjureFoodAction : public CastBuffSpellAction
{
public:
CastConjureFoodAction(PlayerbotAI* botAI) : CastBuffSpellAction(botAI, "conjure food") {}
};
class CastConjureWaterAction : public CastBuffSpellAction
{
public:
CastConjureWaterAction(PlayerbotAI* botAI) : CastBuffSpellAction(botAI, "conjure water") {}
};
class CastIceBlockAction : public CastBuffSpellAction
{
public:
CastIceBlockAction(PlayerbotAI* botAI) : CastBuffSpellAction(botAI, "ice block") {}
};
class CastMoltenArmorAction : public CastBuffSpellAction
{
public:
CastMoltenArmorAction(PlayerbotAI* botAI) : CastBuffSpellAction(botAI, "molten armor") {}
};
class CastMageArmorAction : public CastBuffSpellAction
{
public:
CastMageArmorAction(PlayerbotAI* botAI) : CastBuffSpellAction(botAI, "mage armor") {}
};
class CastIceArmorAction : public CastBuffSpellAction
{
public:
CastIceArmorAction(PlayerbotAI* botAI) : CastBuffSpellAction(botAI, "ice armor") {}
};
class CastFrostArmorAction : public CastBuffSpellAction
{
public:
CastFrostArmorAction(PlayerbotAI* botAI) : CastBuffSpellAction(botAI, "frost armor") {}
};
class CastPolymorphAction : public CastBuffSpellAction
{
public:
CastPolymorphAction(PlayerbotAI* botAI) : CastBuffSpellAction(botAI, "polymorph") {}
Value<Unit*>* GetTargetValue() override;
};
class CastSpellstealAction : public CastSpellAction
{
public:
CastSpellstealAction(PlayerbotAI* botAI) : CastSpellAction(botAI, "spellsteal") {}
};
class CastLivingBombAction : public CastDebuffSpellAction
{
public:
CastLivingBombAction(PlayerbotAI* botAI) : CastDebuffSpellAction(botAI, "living bomb", true) {}
};
class CastLivingBombOnAttackersAction : public CastDebuffSpellOnAttackerAction
{
public:
CastLivingBombOnAttackersAction(PlayerbotAI* botAI) : CastDebuffSpellOnAttackerAction(botAI, "living bomb", true) {}
CastFlamestrikeAction(PlayerbotAI* botAI) : CastDebuffSpellAction(botAI, "flamestrike", true, 0.0f) {}
ActionThreatType getThreatType() override { return ActionThreatType::Aoe; }
};
class CastDragonsBreathAction : public CastSpellAction
@@ -265,55 +404,12 @@ public:
bool isUseful() override;
};
class CastInvisibilityAction : public CastBuffSpellAction
class CancelChannelAction : public Action
{
public:
CastInvisibilityAction(PlayerbotAI* botAI) : CastBuffSpellAction(botAI, "invisibility") {}
};
CancelChannelAction(PlayerbotAI* botAI) : Action(botAI, "cancel channel") {}
class CastEvocationAction : public CastSpellAction
{
public:
CastEvocationAction(PlayerbotAI* botAI) : CastSpellAction(botAI, "evocation") {}
std::string const GetTargetName() override { return "self target"; }
};
class CastCounterspellOnEnemyHealerAction : public CastSpellOnEnemyHealerAction
{
public:
CastCounterspellOnEnemyHealerAction(PlayerbotAI* botAI) : CastSpellOnEnemyHealerAction(botAI, "counterspell") {}
};
class CastArcanePowerAction : public CastBuffSpellAction
{
public:
CastArcanePowerAction(PlayerbotAI* botAI) : CastBuffSpellAction(botAI, "arcane power") {}
};
class CastPresenceOfMindAction : public CastBuffSpellAction
{
public:
CastPresenceOfMindAction(PlayerbotAI* botAI) : CastBuffSpellAction(botAI, "presence of mind") {}
};
class CastMirrorImageAction : public CastBuffSpellAction
{
public:
CastMirrorImageAction(PlayerbotAI* botAI) : CastBuffSpellAction(botAI, "mirror image") {}
};
class CastFocusMagicOnPartyAction : public CastSpellAction
{
public:
CastFocusMagicOnPartyAction(PlayerbotAI* botAI) : CastSpellAction(botAI, "focus magic") {}
Unit* GetTarget() override;
};
class CastBlinkBackAction : public CastSpellAction
{
public:
CastBlinkBackAction(PlayerbotAI* botAI) : CastSpellAction(botAI, "blink") {}
bool Execute(Event event) override;
};
#endif

View File

@@ -4,7 +4,6 @@
*/
#include "MageAiObjectContext.h"
#include "ArcaneMageStrategy.h"
#include "FireMageStrategy.h"
#include "FrostFireMageStrategy.h"
@@ -23,27 +22,23 @@ public:
{
creators["nc"] = &MageStrategyFactoryInternal::nc;
creators["pull"] = &MageStrategyFactoryInternal::pull;
creators["fire aoe"] = &MageStrategyFactoryInternal::fire_aoe;
creators["frostfire aoe"] = &MageStrategyFactoryInternal::frostfire_aoe;
creators["frost aoe"] = &MageStrategyFactoryInternal::frost_aoe;
creators["arcane aoe"] = &MageStrategyFactoryInternal::arcane_aoe;
creators["aoe"] = &MageStrategyFactoryInternal::aoe;
creators["cure"] = &MageStrategyFactoryInternal::cure;
creators["buff"] = &MageStrategyFactoryInternal::buff;
creators["boost"] = &MageStrategyFactoryInternal::boost;
creators["cc"] = &MageStrategyFactoryInternal::cc;
creators["firestarter"] = &MageStrategyFactoryInternal::firestarter;
}
private:
static Strategy* nc(PlayerbotAI* botAI) { return new GenericMageNonCombatStrategy(botAI); }
static Strategy* pull(PlayerbotAI* botAI) { return new PullStrategy(botAI, "shoot"); }
static Strategy* fire_aoe(PlayerbotAI* botAI) { return new FireMageAoeStrategy(botAI); }
static Strategy* frostfire_aoe(PlayerbotAI* botAI) { return new FrostFireMageAoeStrategy(botAI); }
static Strategy* frost_aoe(PlayerbotAI* botAI) { return new FrostMageAoeStrategy(botAI); }
static Strategy* arcane_aoe(PlayerbotAI* botAI) { return new ArcaneMageAoeStrategy(botAI); }
static Strategy* aoe(PlayerbotAI* botAI) { return new MageAoeStrategy(botAI); }
static Strategy* cure(PlayerbotAI* botAI) { return new MageCureStrategy(botAI); }
static Strategy* buff(PlayerbotAI* botAI) { return new MageBuffStrategy(botAI); }
static Strategy* boost(PlayerbotAI* botAI) { return new MageBoostStrategy(botAI); }
static Strategy* cc(PlayerbotAI* botAI) { return new MageCcStrategy(botAI); }
static Strategy* firestarter(PlayerbotAI* botAI) { return new FirestarterStrategy(botAI); }
};
class MageCombatStrategyFactoryInternal : public NamedObjectContext<Strategy>
@@ -86,8 +81,7 @@ public:
creators["fireball"] = &MageTriggerFactoryInternal::fireball;
creators["pyroblast"] = &MageTriggerFactoryInternal::pyroblast;
creators["combustion"] = &MageTriggerFactoryInternal::combustion;
creators["fingers of frost single"] = &MageTriggerFactoryInternal::fingers_of_frost_single;
creators["fingers of frost double"] = &MageTriggerFactoryInternal::fingers_of_frost_double;
creators["fingers of frost"] = &MageTriggerFactoryInternal::fingers_of_frost;
creators["brain freeze"] = &MageTriggerFactoryInternal::brain_freeze;
creators["icy veins"] = &MageTriggerFactoryInternal::icy_veins;
creators["cold snap"] = &MageTriggerFactoryInternal::cold_snap;
@@ -102,6 +96,7 @@ public:
creators["spellsteal"] = &MageTriggerFactoryInternal::spellsteal;
creators["hot streak"] = &MageTriggerFactoryInternal::hot_streak;
creators["living bomb"] = &MageTriggerFactoryInternal::living_bomb;
creators["living bomb on attackers"] = &MageTriggerFactoryInternal::living_bomb_on_attackers;
creators["missile barrage"] = &MageTriggerFactoryInternal::missile_barrage;
creators["arcane blast"] = &MageTriggerFactoryInternal::arcane_blast;
creators["counterspell on enemy healer"] = &MageTriggerFactoryInternal::counterspell_enemy_healer;
@@ -115,6 +110,20 @@ public:
creators["frostbite on target"] = &MageTriggerFactoryInternal::frostbite_on_target;
creators["no focus magic"] = &MageTriggerFactoryInternal::no_focus_magic;
creators["frostfire bolt"] = &MageTriggerFactoryInternal::frostfire_bolt;
creators["firestarter"] = &MageTriggerFactoryInternal::firestarter;
creators["improved scorch"] = &MageTriggerFactoryInternal::improved_scorch;
creators["flamestrike nearby"] = &MageTriggerFactoryInternal::flamestrike_nearby;
creators["flamestrike active and medium aoe"] = &MageTriggerFactoryInternal::flamestrike_blizzard;
creators["arcane blast 4 stacks and missile barrage"] = &MageTriggerFactoryInternal::arcane_blast_4_stacks_and_missile_barrage;
creators["icy veins on cd"] = &MageTriggerFactoryInternal::icy_veins_on_cd;
creators["deep freeze on cd"] = &MageTriggerFactoryInternal::deep_freeze_on_cd;
creators["no mana gem"] = &MageTriggerFactoryInternal::NoManaGem;
creators["blizzard channel check"] = &MageTriggerFactoryInternal::blizzard_channel_check;
creators["blast wave off cd"] = &MageTriggerFactoryInternal::blast_wave_off_cd;
creators["blast wave off cd and medium aoe"] = &MageTriggerFactoryInternal::blast_wave_off_cd_and_medium_aoe;
creators["no firestarter strategy"] = &MageTriggerFactoryInternal::no_firestarter_strategy;
creators["enemy is close and no firestarter strategy"] = &MageTriggerFactoryInternal::enemy_is_close_and_no_firestarter_strategy;
creators["enemy too close for spell and no firestarter strategy"] = &MageTriggerFactoryInternal::enemy_too_close_for_spell_and_no_firestarter_strategy;
}
private:
@@ -126,8 +135,7 @@ private:
static Trigger* fireball(PlayerbotAI* botAI) { return new FireballTrigger(botAI); }
static Trigger* pyroblast(PlayerbotAI* botAI) { return new PyroblastTrigger(botAI); }
static Trigger* combustion(PlayerbotAI* botAI) { return new CombustionTrigger(botAI); }
static Trigger* fingers_of_frost_single(PlayerbotAI* botAI) { return new FingersOfFrostSingleTrigger(botAI); }
static Trigger* fingers_of_frost_double(PlayerbotAI* botAI) { return new FingersOfFrostDoubleTrigger(botAI); }
static Trigger* fingers_of_frost(PlayerbotAI* botAI) { return new FingersOfFrostTrigger(botAI); }
static Trigger* brain_freeze(PlayerbotAI* botAI) { return new BrainFreezeTrigger(botAI); }
static Trigger* icy_veins(PlayerbotAI* botAI) { return new IcyVeinsTrigger(botAI); }
static Trigger* cold_snap(PlayerbotAI* botAI) { return new ColdSnapTrigger(botAI); }
@@ -141,6 +149,7 @@ private:
static Trigger* polymorph(PlayerbotAI* botAI) { return new PolymorphTrigger(botAI); }
static Trigger* spellsteal(PlayerbotAI* botAI) { return new SpellstealTrigger(botAI); }
static Trigger* living_bomb(PlayerbotAI* botAI) { return new LivingBombTrigger(botAI); }
static Trigger* living_bomb_on_attackers(PlayerbotAI* botAI) { return new LivingBombOnAttackersTrigger(botAI); }
static Trigger* missile_barrage(PlayerbotAI* botAI) { return new MissileBarrageTrigger(botAI); }
static Trigger* arcane_blast(PlayerbotAI* botAI) { return new ArcaneBlastTrigger(botAI); }
static Trigger* counterspell_enemy_healer(PlayerbotAI* botAI) { return new CounterspellEnemyHealerTrigger(botAI); }
@@ -150,6 +159,20 @@ private:
static Trigger* frostbite_on_target(PlayerbotAI* botAI) { return new FrostbiteOnTargetTrigger(botAI); }
static Trigger* no_focus_magic(PlayerbotAI* botAI) { return new NoFocusMagicTrigger(botAI); }
static Trigger* frostfire_bolt(PlayerbotAI* botAI) { return new FrostfireBoltTrigger(botAI); }
static Trigger* improved_scorch(PlayerbotAI* botAI) { return new ImprovedScorchTrigger(botAI); }
static Trigger* firestarter(PlayerbotAI* botAI) { return new FirestarterTrigger(botAI); }
static Trigger* flamestrike_nearby(PlayerbotAI* botAI) { return new FlamestrikeNearbyTrigger(botAI); }
static Trigger* flamestrike_blizzard(PlayerbotAI* botAI) { return new FlamestrikeBlizzardTrigger(botAI); }
static Trigger* arcane_blast_4_stacks_and_missile_barrage(PlayerbotAI* botAI) { return new ArcaneBlast4StacksAndMissileBarrageTrigger(botAI); }
static Trigger* icy_veins_on_cd(PlayerbotAI* botAI) { return new IcyVeinsCooldownTrigger(botAI); }
static Trigger* deep_freeze_on_cd(PlayerbotAI* botAI) { return new DeepFreezeCooldownTrigger(botAI); }
static Trigger* NoManaGem(PlayerbotAI* botAI) { return new NoManaGemTrigger(botAI); }
static Trigger* blizzard_channel_check(PlayerbotAI* botAI) { return new BlizzardChannelCheckTrigger(botAI); }
static Trigger* blast_wave_off_cd(PlayerbotAI* botAI) { return new BlastWaveOffCdTrigger(botAI); }
static Trigger* blast_wave_off_cd_and_medium_aoe(PlayerbotAI* botAI) { return new BlastWaveOffCdTriggerAndMediumAoeTrigger(botAI); }
static Trigger* no_firestarter_strategy(PlayerbotAI* botAI) { return new NoFirestarterStrategyTrigger(botAI); }
static Trigger* enemy_is_close_and_no_firestarter_strategy(PlayerbotAI* botAI) { return new EnemyIsCloseAndNoFirestarterStrategyTrigger(botAI); }
static Trigger* enemy_too_close_for_spell_and_no_firestarter_strategy(PlayerbotAI* botAI) { return new EnemyTooCloseForSpellAndNoFirestarterStrategyTrigger(botAI); }
};
class MageAiObjectContextInternal : public NamedObjectContext<Action>
@@ -170,6 +193,7 @@ public:
creators["arcane intellect on party"] = &MageAiObjectContextInternal::arcane_intellect_on_party;
creators["conjure water"] = &MageAiObjectContextInternal::conjure_water;
creators["conjure food"] = &MageAiObjectContextInternal::conjure_food;
creators["conjure mana gem"] = &MageAiObjectContextInternal::conjure_mana_gem;
creators["molten armor"] = &MageAiObjectContextInternal::molten_armor;
creators["mage armor"] = &MageAiObjectContextInternal::mage_armor;
creators["ice armor"] = &MageAiObjectContextInternal::ice_armor;
@@ -207,6 +231,14 @@ public:
creators["mirror image"] = &MageAiObjectContextInternal::mirror_image;
creators["focus magic on party"] = &MageAiObjectContextInternal::focus_magic_on_party;
creators["blink back"] = &MageAiObjectContextInternal::blink_back;
creators["use mana sapphire"] = &MageAiObjectContextInternal::use_mana_sapphire;
creators["use mana emerald"] = &MageAiObjectContextInternal::use_mana_emerald;
creators["use mana ruby"] = &MageAiObjectContextInternal::use_mana_ruby;
creators["use mana citrine"] = &MageAiObjectContextInternal::use_mana_citrine;
creators["use mana jade"] = &MageAiObjectContextInternal::use_mana_jade;
creators["use mana agate"] = &MageAiObjectContextInternal::use_mana_agate;
creators["cancel channel"] = &MageAiObjectContextInternal::cancel_channel;
creators["mana shield"] = &MageAiObjectContextInternal::mana_shield;
}
private:
@@ -228,6 +260,7 @@ private:
static Action* arcane_intellect_on_party(PlayerbotAI* botAI) { return new CastArcaneIntellectOnPartyAction(botAI); }
static Action* conjure_water(PlayerbotAI* botAI) { return new CastConjureWaterAction(botAI); }
static Action* conjure_food(PlayerbotAI* botAI) { return new CastConjureFoodAction(botAI); }
static Action* conjure_mana_gem(PlayerbotAI* botAI) { return new CastConjureManaGemAction(botAI); }
static Action* molten_armor(PlayerbotAI* botAI) { return new CastMoltenArmorAction(botAI); }
static Action* mage_armor(PlayerbotAI* botAI) { return new CastMageArmorAction(botAI); }
static Action* ice_armor(PlayerbotAI* botAI) { return new CastIceArmorAction(botAI); }
@@ -241,10 +274,7 @@ private:
static Action* remove_curse(PlayerbotAI* botAI) { return new CastRemoveCurseAction(botAI); }
static Action* remove_curse_on_party(PlayerbotAI* botAI) { return new CastRemoveCurseOnPartyAction(botAI); }
static Action* remove_lesser_curse(PlayerbotAI* botAI) { return new CastRemoveLesserCurseAction(botAI); }
static Action* remove_lesser_curse_on_party(PlayerbotAI* botAI)
{
return new CastRemoveLesserCurseOnPartyAction(botAI);
}
static Action* remove_lesser_curse_on_party(PlayerbotAI* botAI) { return new CastRemoveLesserCurseOnPartyAction(botAI); }
static Action* icy_veins(PlayerbotAI* botAI) { return new CastIcyVeinsAction(botAI); }
static Action* cold_snap(PlayerbotAI* botAI) { return new CastColdSnapAction(botAI); }
static Action* ice_barrier(PlayerbotAI* botAI) { return new CastIceBarrierAction(botAI); }
@@ -259,13 +289,18 @@ private:
static Action* blast_wave(PlayerbotAI* botAI) { return new CastBlastWaveAction(botAI); }
static Action* invisibility(PlayerbotAI* botAI) { return new CastInvisibilityAction(botAI); }
static Action* evocation(PlayerbotAI* botAI) { return new CastEvocationAction(botAI); }
static Action* counterspell_on_enemy_healer(PlayerbotAI* botAI)
{
return new CastCounterspellOnEnemyHealerAction(botAI);
}
static Action* counterspell_on_enemy_healer(PlayerbotAI* botAI) { return new CastCounterspellOnEnemyHealerAction(botAI); }
static Action* mirror_image(PlayerbotAI* botAI) { return new CastMirrorImageAction(botAI); }
static Action* focus_magic_on_party(PlayerbotAI* botAI) { return new CastFocusMagicOnPartyAction(botAI); }
static Action* blink_back(PlayerbotAI* botAI) { return new CastBlinkBackAction(botAI); }
static Action* use_mana_sapphire(PlayerbotAI* botAI) { return new UseManaSapphireAction(botAI); }
static Action* use_mana_emerald(PlayerbotAI* botAI) { return new UseManaEmeraldAction(botAI); }
static Action* use_mana_ruby(PlayerbotAI* botAI) { return new UseManaRubyAction(botAI); }
static Action* use_mana_citrine(PlayerbotAI* botAI) { return new UseManaCitrineAction(botAI); }
static Action* use_mana_jade(PlayerbotAI* botAI) { return new UseManaJadeAction(botAI); }
static Action* use_mana_agate(PlayerbotAI* botAI) { return new UseManaAgateAction(botAI); }
static Action* cancel_channel(PlayerbotAI* botAI) { return new CancelChannelAction(botAI); }
static Action* mana_shield(PlayerbotAI* botAI) { return new CastManaShieldAction(botAI); }
};
SharedNamedObjectContextList<Strategy> MageAiObjectContext::sharedStrategyContexts;
@@ -309,4 +344,4 @@ void MageAiObjectContext::BuildSharedTriggerContexts(SharedNamedObjectContextLis
void MageAiObjectContext::BuildSharedValueContexts(SharedNamedObjectContextList<UntypedValue>& valueContexts)
{
AiObjectContext::BuildSharedValueContexts(valueContexts);
}
}

View File

@@ -4,9 +4,33 @@
*/
#include "MageTriggers.h"
#include "MageActions.h"
#include "Playerbots.h"
#include "Player.h"
#include "Spell.h"
#include "DynamicObject.h"
#include "Value.h"
#include "SpellAuraEffects.h"
#include "ServerFacade.h"
bool NoManaGemTrigger::IsActive()
{
static const std::vector<uint32> gemIds = {
33312, // Mana Sapphire
22044, // Mana Emerald
8008, // Mana Ruby
8007, // Mana Citrine
5513, // Mana Jade
5514 // Mana Agate
};
Player* bot = botAI->GetBot();
for (uint32 gemId : gemIds)
{
if (bot->GetItemCount(gemId, false) > 0) // false = only in bags
return false;
}
return true;
}
bool ArcaneIntellectOnPartyTrigger::IsActive()
{
@@ -25,25 +49,6 @@ bool MageArmorTrigger::IsActive()
!botAI->HasAura("molten armor", target) && !botAI->HasAura("mage armor", target);
}
bool FingersOfFrostSingleTrigger::IsActive()
{
// Fingers of Frost "stack" count is always 1.
// The value is instead stored in the charges.
Aura* aura = botAI->GetAura("fingers of frost", bot, false, true, -1);
return (aura && aura->GetCharges() == 1);
}
bool ArcaneBlastStackTrigger::IsActive()
{
Aura* aura = botAI->GetAura(getName(), GetTarget(), false, true, 3);
if (!aura)
return false;
if (aura->GetStackAmount() >= 4)
return true;
bool hasMissileBarrage = botAI->HasAura(44401, bot);
return hasMissileBarrage;
}
bool FrostNovaOnTargetTrigger::IsActive()
{
Unit* target = GetTarget();
@@ -84,3 +89,95 @@ bool NoFocusMagicTrigger::IsActive()
}
return true;
}
bool DeepFreezeCooldownTrigger::IsActive()
{
Player* bot = botAI->GetBot();
static const uint32 DEEP_FREEZE_SPELL_ID = 44572;
// If the bot does NOT have Deep Freeze, treat as "on cooldown"
if (!bot->HasSpell(DEEP_FREEZE_SPELL_ID))
return true;
// Otherwise, use the default cooldown logic
return SpellCooldownTrigger::IsActive();
}
const std::set<uint32> FlamestrikeNearbyTrigger::FLAMESTRIKE_SPELL_IDS = {2120, 2121, 8422, 8423, 10215,
10216, 27086, 42925, 42926};
bool FlamestrikeNearbyTrigger::IsActive()
{
Player* bot = botAI->GetBot();
for (uint32 spellId : FLAMESTRIKE_SPELL_IDS)
{
Aura* aura = bot->GetAura(spellId, bot->GetGUID());
if (!aura)
continue;
DynamicObject* dynObj = aura->GetDynobjOwner();
if (!dynObj)
continue;
float dist = bot->GetDistance2d(dynObj->GetPositionX(), dynObj->GetPositionY());
if (dist <= radius)
return true;
}
return false;
}
bool ImprovedScorchTrigger::IsActive()
{
Unit* target = GetTarget();
if (!target || !target->IsAlive() || !target->IsInWorld())
return false;
// List of all spell IDs for Improved Scorch, Winter's Chill, and Shadow Mastery
static const uint32 ImprovedScorchExclusiveDebuffs[] = {// Shadow Mastery
17794, 17797, 17798, 17799, 17800,
// Winter's Chill
12579,
// Improved Scorch
22959};
for (uint32 spellId : ImprovedScorchExclusiveDebuffs)
{
if (target->HasAura(spellId))
return false;
}
// Use default DebuffTrigger logic for the rest (only trigger if debuff is missing or expiring)
return DebuffTrigger::IsActive();
}
const std::set<uint32> BlizzardChannelCheckTrigger::BLIZZARD_SPELL_IDS = {
10, // Blizzard Rank 1
6141, // Blizzard Rank 2
8427, // Blizzard Rank 3
10185, // Blizzard Rank 4
10186, // Blizzard Rank 5
10187, // Blizzard Rank 6
27085, // Blizzard Rank 7
42938, // Blizzard Rank 8
42939 // Blizzard Rank 9
};
bool BlizzardChannelCheckTrigger::IsActive()
{
Player* bot = botAI->GetBot();
// Check if the bot is channeling a spell
if (Spell* spell = bot->GetCurrentSpell(CURRENT_CHANNELED_SPELL))
{
// Only trigger if the spell being channeled is Blizzard
if (BLIZZARD_SPELL_IDS.count(spell->m_spellInfo->Id))
{
uint8 attackerCount = AI_VALUE(uint8, "attacker count");
return attackerCount < minEnemies;
}
}
// Not channeling Blizzard
return false;
}

View File

@@ -9,11 +9,15 @@
#include "CureTriggers.h"
#include "GenericTriggers.h"
#include "SharedDefines.h"
#include "Trigger.h"
#include "Playerbots.h"
#include "PlayerbotAI.h"
#include <set>
#include <unordered_set>
class PlayerbotAI;
DEFLECT_TRIGGER(FireWardTrigger, "fire ward");
DEFLECT_TRIGGER(FrostWardTrigger, "frost ward");
// Buff and Out of Combat Triggers
class ArcaneIntellectOnPartyTrigger : public BuffOnPartyTrigger
{
@@ -37,30 +41,53 @@ public:
bool IsActive() override;
};
class LivingBombTrigger : public DebuffTrigger
class NoFocusMagicTrigger : public Trigger
{
public:
LivingBombTrigger(PlayerbotAI* botAI) : DebuffTrigger(botAI, "living bomb", 1, true) {}
NoFocusMagicTrigger(PlayerbotAI* botAI) : Trigger(botAI, "no focus magic") {}
bool IsActive() override;
};
class FireballTrigger : public DebuffTrigger
class IceBarrierTrigger : public BuffTrigger
{
public:
FireballTrigger(PlayerbotAI* botAI) : DebuffTrigger(botAI, "fireball", 1, true) {}
IceBarrierTrigger(PlayerbotAI* botAI) : BuffTrigger(botAI, "ice barrier") {}
};
class PyroblastTrigger : public DebuffTrigger
class NoManaGemTrigger : public Trigger
{
public:
PyroblastTrigger(PlayerbotAI* botAI) : DebuffTrigger(botAI, "pyroblast", 1, true) {}
NoManaGemTrigger(PlayerbotAI* botAI) : Trigger(botAI, "no mana gem") {}
bool IsActive() override;
};
class FireWardTrigger : public DeflectSpellTrigger
{
public:
FireWardTrigger(PlayerbotAI* botAI) : DeflectSpellTrigger(botAI, "fire ward") {}
};
class FrostWardTrigger : public DeflectSpellTrigger
{
public:
FrostWardTrigger(PlayerbotAI* botAI) : DeflectSpellTrigger(botAI, "frost ward") {}
};
// Proc and Boost Triggers
class HotStreakTrigger : public HasAuraTrigger
{
public:
HotStreakTrigger(PlayerbotAI* botAI) : HasAuraTrigger(botAI, "hot streak") {}
};
class FirestarterTrigger : public HasAuraTrigger
{
public:
FirestarterTrigger(PlayerbotAI* botAI) : HasAuraTrigger(botAI, "firestarter") {}
};
class MissileBarrageTrigger : public HasAuraTrigger
{
public:
@@ -73,30 +100,19 @@ public:
ArcaneBlastTrigger(PlayerbotAI* botAI) : BuffTrigger(botAI, "arcane blast") {}
};
class FingersOfFrostSingleTrigger : public HasAuraStackTrigger
class ArcaneBlastStackTrigger : public HasAuraStackTrigger
{
public:
FingersOfFrostSingleTrigger(PlayerbotAI* ai) : HasAuraStackTrigger(ai, "fingers of frost", 1, 1) {}
bool IsActive() override;
ArcaneBlastStackTrigger(PlayerbotAI* botAI) : HasAuraStackTrigger(botAI, "arcane blast", 4, 1) {}
};
class FingersOfFrostDoubleTrigger : public HasAuraStackTrigger
class ArcaneBlast4StacksAndMissileBarrageTrigger : public TwoTriggers
{
public:
FingersOfFrostDoubleTrigger(PlayerbotAI* ai) : HasAuraStackTrigger(ai, "fingers of frost", 2, 1) {}
// bool IsActive() override;
};
class BrainFreezeTrigger : public HasAuraTrigger
{
public:
BrainFreezeTrigger(PlayerbotAI* botAI) : HasAuraTrigger(botAI, "fireball!") {}
};
class CounterspellInterruptSpellTrigger : public InterruptSpellTrigger
{
public:
CounterspellInterruptSpellTrigger(PlayerbotAI* botAI) : InterruptSpellTrigger(botAI, "counterspell") {}
ArcaneBlast4StacksAndMissileBarrageTrigger(PlayerbotAI* ai)
: TwoTriggers(ai, "arcane blast stack", "missile barrage")
{
}
};
class CombustionTrigger : public BoostTrigger
@@ -105,23 +121,50 @@ public:
CombustionTrigger(PlayerbotAI* botAI) : BoostTrigger(botAI, "combustion") {}
};
class IcyVeinsCooldownTrigger : public SpellCooldownTrigger
{
public:
IcyVeinsCooldownTrigger(PlayerbotAI* botAI) : SpellCooldownTrigger(botAI, "icy veins") {}
};
class DeepFreezeCooldownTrigger : public SpellCooldownTrigger
{
public:
DeepFreezeCooldownTrigger(PlayerbotAI* botAI) : SpellCooldownTrigger(botAI, "deep freeze") {}
bool IsActive() override;
};
class ColdSnapTrigger : public TwoTriggers
{
public:
ColdSnapTrigger(PlayerbotAI* ai) : TwoTriggers(ai, "icy veins on cd", "deep freeze on cd") {}
};
class MirrorImageTrigger : public BoostTrigger
{
public:
MirrorImageTrigger(PlayerbotAI* botAI) : BoostTrigger(botAI, "mirror image") {}
};
class IcyVeinsTrigger : public BoostTrigger
{
public:
IcyVeinsTrigger(PlayerbotAI* botAI) : BoostTrigger(botAI, "icy veins") {}
};
class ColdSnapTrigger : public BoostTrigger
class ArcanePowerTrigger : public BoostTrigger
{
public:
ColdSnapTrigger(PlayerbotAI* botAI) : BoostTrigger(botAI, "cold snap") {}
ArcanePowerTrigger(PlayerbotAI* botAI) : BoostTrigger(botAI, "arcane power") {}
};
class PresenceOfMindTrigger : public BoostTrigger
{
public:
PresenceOfMindTrigger(PlayerbotAI* botAI) : BoostTrigger(botAI, "presence of mind") {}
};
class IceBarrierTrigger : public BuffTrigger
{
public:
IceBarrierTrigger(PlayerbotAI* botAI) : BuffTrigger(botAI, "ice barrier") {}
};
// CC, Interrupt, and Dispel Triggers
class PolymorphTrigger : public HasCcTargetTrigger
{
@@ -155,29 +198,63 @@ public:
CounterspellEnemyHealerTrigger(PlayerbotAI* botAI) : InterruptEnemyHealerTrigger(botAI, "counterspell") {}
};
class ArcanePowerTrigger : public BuffTrigger
class CounterspellInterruptSpellTrigger : public InterruptSpellTrigger
{
public:
ArcanePowerTrigger(PlayerbotAI* botAI) : BuffTrigger(botAI, "arcane power") {}
CounterspellInterruptSpellTrigger(PlayerbotAI* botAI) : InterruptSpellTrigger(botAI, "counterspell") {}
};
class PresenceOfMindTrigger : public BuffTrigger
// Damage and Debuff Triggers
class LivingBombTrigger : public DebuffTrigger
{
public:
PresenceOfMindTrigger(PlayerbotAI* botAI) : BuffTrigger(botAI, "presence of mind") {}
LivingBombTrigger(PlayerbotAI* botAI) : DebuffTrigger(botAI, "living bomb", 1, true) {}
bool IsActive() override { return BuffTrigger::IsActive(); }
};
class ArcaneBlastStackTrigger : public HasAuraStackTrigger
class LivingBombOnAttackersTrigger : public DebuffOnAttackerTrigger
{
public:
ArcaneBlastStackTrigger(PlayerbotAI* botAI) : HasAuraStackTrigger(botAI, "arcane blast", 3, 1) {}
LivingBombOnAttackersTrigger(PlayerbotAI* ai) : DebuffOnAttackerTrigger(ai, "living bomb", true) {}
bool IsActive() override { return BuffTrigger::IsActive(); }
};
class FireballTrigger : public DebuffTrigger
{
public:
FireballTrigger(PlayerbotAI* botAI) : DebuffTrigger(botAI, "fireball", 1, true) {}
};
class ImprovedScorchTrigger : public DebuffTrigger
{
public:
ImprovedScorchTrigger(PlayerbotAI* botAI) : DebuffTrigger(botAI, "improved scorch", 1, true, 0.5f) {}
bool IsActive() override;
};
class MirrorImageTrigger : public BoostTrigger
class PyroblastTrigger : public DebuffTrigger
{
public:
MirrorImageTrigger(PlayerbotAI* botAI) : BoostTrigger(botAI, "mirror image") {}
PyroblastTrigger(PlayerbotAI* botAI) : DebuffTrigger(botAI, "pyroblast", 1, true) {}
};
class FrostfireBoltTrigger : public DebuffTrigger
{
public:
FrostfireBoltTrigger(PlayerbotAI* botAI) : DebuffTrigger(botAI, "frostfire bolt", 1, true) {}
};
class FingersOfFrostTrigger : public HasAuraTrigger
{
public:
FingersOfFrostTrigger(PlayerbotAI* botAI) : HasAuraTrigger(botAI, "fingers of frost") {}
};
class BrainFreezeTrigger : public HasAuraTrigger
{
public:
BrainFreezeTrigger(PlayerbotAI* botAI) : HasAuraTrigger(botAI, "fireball!") {}
};
class FrostNovaOnTargetTrigger : public DebuffTrigger
@@ -194,17 +271,74 @@ public:
bool IsActive() override;
};
class NoFocusMagicTrigger : public Trigger
class FlamestrikeNearbyTrigger : public Trigger
{
public:
NoFocusMagicTrigger(PlayerbotAI* botAI) : Trigger(botAI, "no focus magic") {}
FlamestrikeNearbyTrigger(PlayerbotAI* botAI, float radius = 30.0f)
: Trigger(botAI, "flamestrike nearby"), radius(radius)
{
}
bool IsActive() override;
protected:
float radius;
static const std::set<uint32> FLAMESTRIKE_SPELL_IDS;
};
class FrostfireBoltTrigger : public DebuffTrigger
class FlamestrikeBlizzardTrigger : public TwoTriggers
{
public:
FrostfireBoltTrigger(PlayerbotAI* botAI) : DebuffTrigger(botAI, "frostfire bolt", 1, true) {}
FlamestrikeBlizzardTrigger(PlayerbotAI* ai) : TwoTriggers(ai, "flamestrike nearby", "medium aoe") {}
};
class BlizzardChannelCheckTrigger : public Trigger
{
public:
BlizzardChannelCheckTrigger(PlayerbotAI* botAI, uint32 minEnemies = 2)
: Trigger(botAI, "blizzard channel check"), minEnemies(minEnemies) {}
bool IsActive() override;
protected:
uint32 minEnemies;
static const std::set<uint32> BLIZZARD_SPELL_IDS;
};
class BlastWaveOffCdTrigger : public SpellNoCooldownTrigger
{
public:
BlastWaveOffCdTrigger(PlayerbotAI* botAI) : SpellNoCooldownTrigger(botAI, "blast wave") {}
};
class BlastWaveOffCdTriggerAndMediumAoeTrigger : public TwoTriggers
{
public:
BlastWaveOffCdTriggerAndMediumAoeTrigger(PlayerbotAI* ai) : TwoTriggers(ai, "blast wave off cd", "medium aoe") {}
};
class NoFirestarterStrategyTrigger : public Trigger
{
public:
NoFirestarterStrategyTrigger(PlayerbotAI* botAI) : Trigger(botAI, "no firestarter strategy") {}
bool IsActive() override
{
return !botAI->HasStrategy("firestarter", BOT_STATE_COMBAT);
}
};
class EnemyIsCloseAndNoFirestarterStrategyTrigger : public TwoTriggers
{
public:
EnemyIsCloseAndNoFirestarterStrategyTrigger(PlayerbotAI* botAI)
: TwoTriggers(botAI, "enemy is close", "no firestarter strategy") {}
};
class EnemyTooCloseForSpellAndNoFirestarterStrategyTrigger : public TwoTriggers
{
public:
EnemyTooCloseForSpellAndNoFirestarterStrategyTrigger(PlayerbotAI* botAI)
: TwoTriggers(botAI, "enemy too close for spell", "no firestarter strategy") {}
};
#endif