Merge pull request #880 from liyunfan1223/improve_dps

Improve DPS rotation
This commit is contained in:
Yunfan Li
2025-01-20 15:50:45 +08:00
committed by GitHub
39 changed files with 383 additions and 113 deletions

View File

@@ -2928,7 +2928,10 @@ bool PlayerbotAI::CanCastSpell(uint32 spellid, Unit* target, bool checkHasSpell,
}
Unit* oldSel = bot->GetSelectedUnit();
Spell* spell = new Spell(bot, spellInfo, TRIGGERED_NONE);
// TRIGGERED_IGNORE_POWER_AND_REAGENT_COST flag for not calling CheckPower in check
// which avoids buff charge to be ineffectively reduced (e.g. dk freezing fog for howling blast)
/// @TODO: Fix all calls to ApplySpellMod
Spell* spell = new Spell(bot, spellInfo, TRIGGERED_IGNORE_POWER_AND_REAGENT_COST);
spell->m_targets.SetUnitTarget(target);
spell->m_CastItem = castItem;
@@ -2938,7 +2941,6 @@ bool PlayerbotAI::CanCastSpell(uint32 spellid, Unit* target, bool checkHasSpell,
;
}
spell->m_targets.SetItemTarget(itemTarget);
SpellCastResult result = spell->CheckCast(true);
delete spell;

View File

@@ -511,6 +511,7 @@ bool PlayerbotAIConfig::Initialize()
autoTrainSpells = sConfigMgr->GetOption<std::string>("AiPlayerbot.AutoTrainSpells", "yes");
autoPickTalents = sConfigMgr->GetOption<bool>("AiPlayerbot.AutoPickTalents", true);
autoUpgradeEquip = sConfigMgr->GetOption<bool>("AiPlayerbot.AutoUpgradeEquip", false);
hunterWolfPet = sConfigMgr->GetOption<int32>("AiPlayerbot.HunterWolfPet", 0);
autoLearnTrainerSpells = sConfigMgr->GetOption<bool>("AiPlayerbot.AutoLearnTrainerSpells", true);
autoLearnQuestSpells = sConfigMgr->GetOption<bool>("AiPlayerbot.AutoLearnQuestSpells", false);
autoTeleportForLevel = sConfigMgr->GetOption<bool>("AiPlayerbot.AutoTeleportForLevel", false);

View File

@@ -289,6 +289,7 @@ public:
std::string autoTrainSpells;
bool autoPickTalents;
bool autoUpgradeEquip;
int32 hunterWolfPet;
bool autoLearnTrainerSpells;
bool autoDoQuests;
bool enableNewRpgStrategy;

View File

@@ -484,10 +484,11 @@ void PlayerbotHolder::OnBotLogin(Player* const bot)
if (!groupValid)
{
WorldPacket p;
std::string const member = bot->GetName();
p << uint32(PARTY_OP_LEAVE) << member << uint32(0);
bot->GetSession()->HandleGroupDisbandOpcode(p);
bot->RemoveFromGroup();
// WorldPacket p;
// std::string const member = bot->GetName();
// p << uint32(PARTY_OP_LEAVE) << member << uint32(0);
// bot->GetSession()->HandleGroupDisbandOpcode(p);
}
}

View File

@@ -36,6 +36,7 @@
#include "SharedDefines.h"
#include "SpellAuraDefines.h"
#include "StatsWeightCalculator.h"
#include "World.h"
#define PLAYER_SKILL_INDEX(x) (PLAYER_SKILL_INFO_1_1 + ((x)*3))
@@ -786,6 +787,13 @@ void PlayerbotFactory::InitPet()
if (itr->second.minlevel > bot->GetLevel())
continue;
bool onlyWolf = sPlayerbotAIConfig->hunterWolfPet == 2 ||
(sPlayerbotAIConfig->hunterWolfPet == 1 &&
bot->GetLevel() >= sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL));
// Wolf only (for higher dps)
if (onlyWolf && itr->second.family != CREATURE_FAMILY_WOLF)
continue;
ids.push_back(itr->first);
}
@@ -2807,11 +2815,11 @@ void PlayerbotFactory::InitAmmo()
uint32 entry = sRandomItemMgr->GetAmmo(level, subClass);
uint32 count = bot->GetItemCount(entry);
uint32 maxCount = 6000;
uint32 maxCount = bot->getClass() == CLASS_HUNTER ? 6000 : 1000;
if (count < maxCount / 2)
if (count < maxCount)
{
if (Item* newItem = StoreNewItemInInventorySlot(bot, entry, maxCount / 2))
if (Item* newItem = StoreNewItemInInventorySlot(bot, entry, maxCount - count))
{
newItem->AddToUpdateQueueOf(bot);
}

View File

@@ -84,9 +84,7 @@ bool LeaveGroupAction::Leave(Player* player)
bool shouldStay = randomBot && bot->GetGroup() && player == bot;
if (!shouldStay)
{
WorldPacket p;
p << uint32(PARTY_OP_LEAVE) << bot->GetName() << uint32(0);
bot->GetSession()->HandleGroupDisbandOpcode(p);
bot->RemoveFromGroup();
}
if (randomBot)

View File

@@ -74,14 +74,14 @@ public:
creators["plague strike"] = &DeathKnightTriggerFactoryInternal::plague_strike;
creators["plague strike on attacker"] = &DeathKnightTriggerFactoryInternal::plague_strike_on_attacker;
creators["icy touch"] = &DeathKnightTriggerFactoryInternal::icy_touch;
creators["icy touch 8s"] = &DeathKnightTriggerFactoryInternal::icy_touch_8s;
creators["dd cd and icy touch 8s"] = &DeathKnightTriggerFactoryInternal::dd_cd_and_icy_touch_8s;
creators["icy touch 3s"] = &DeathKnightTriggerFactoryInternal::icy_touch_3s;
creators["dd cd and icy touch 3s"] = &DeathKnightTriggerFactoryInternal::dd_cd_and_icy_touch_3s;
creators["death coil"] = &DeathKnightTriggerFactoryInternal::death_coil;
creators["icy touch on attacker"] = &DeathKnightTriggerFactoryInternal::icy_touch_on_attacker;
creators["improved icy talons"] = &DeathKnightTriggerFactoryInternal::improved_icy_talons;
creators["plague strike"] = &DeathKnightTriggerFactoryInternal::plague_strike;
creators["plague strike 8s"] = &DeathKnightTriggerFactoryInternal::plague_strike_8s;
creators["dd cd and plague strike 8s"] = &DeathKnightTriggerFactoryInternal::dd_cd_and_plague_strike_8s;
creators["plague strike 3s"] = &DeathKnightTriggerFactoryInternal::plague_strike_3s;
creators["dd cd and plague strike 3s"] = &DeathKnightTriggerFactoryInternal::dd_cd_and_plague_strike_3s;
creators["horn of winter"] = &DeathKnightTriggerFactoryInternal::horn_of_winter;
creators["mind freeze"] = &DeathKnightTriggerFactoryInternal::mind_freeze;
creators["mind freeze on enemy healer"] = &DeathKnightTriggerFactoryInternal::mind_freeze_on_enemy_healer;
@@ -94,6 +94,7 @@ public:
creators["high blood rune"] = &DeathKnightTriggerFactoryInternal::high_blood_rune;
creators["high frost rune"] = &DeathKnightTriggerFactoryInternal::high_frost_rune;
creators["high unholy rune"] = &DeathKnightTriggerFactoryInternal::high_unholy_rune;
creators["no rune"] = &DeathKnightTriggerFactoryInternal::no_rune;
creators["freezing fog"] = &DeathKnightTriggerFactoryInternal::freezing_fog;
creators["no desolation"] = &DeathKnightTriggerFactoryInternal::no_desolation;
creators["dd cd and no desolation"] = &DeathKnightTriggerFactoryInternal::dd_cd_and_no_desolation;
@@ -106,15 +107,15 @@ private:
static Trigger* pestilence_glyph(PlayerbotAI* botAI) { return new PestilenceGlyphTrigger(botAI); }
static Trigger* blood_strike(PlayerbotAI* botAI) { return new BloodStrikeTrigger(botAI); }
static Trigger* plague_strike(PlayerbotAI* botAI) { return new PlagueStrikeDebuffTrigger(botAI); }
static Trigger* plague_strike_8s(PlayerbotAI* botAI) { return new PlagueStrike8sDebuffTrigger(botAI); }
static Trigger* dd_cd_and_plague_strike_8s(PlayerbotAI* botAI) { return new TwoTriggers(botAI, "death and decay cooldown", "plague strike 8s"); }
static Trigger* plague_strike_3s(PlayerbotAI* botAI) { return new PlagueStrike3sDebuffTrigger(botAI); }
static Trigger* dd_cd_and_plague_strike_3s(PlayerbotAI* botAI) { return new TwoTriggers(botAI, "death and decay cooldown", "plague strike 3s"); }
static Trigger* plague_strike_on_attacker(PlayerbotAI* botAI)
{
return new PlagueStrikeDebuffOnAttackerTrigger(botAI);
}
static Trigger* icy_touch(PlayerbotAI* botAI) { return new IcyTouchDebuffTrigger(botAI); }
static Trigger* icy_touch_8s(PlayerbotAI* botAI) { return new IcyTouch8sDebuffTrigger(botAI); }
static Trigger* dd_cd_and_icy_touch_8s(PlayerbotAI* botAI) { return new TwoTriggers(botAI, "death and decay cooldown", "icy touch 8s"); }
static Trigger* icy_touch_3s(PlayerbotAI* botAI) { return new IcyTouch3sDebuffTrigger(botAI); }
static Trigger* dd_cd_and_icy_touch_3s(PlayerbotAI* botAI) { return new TwoTriggers(botAI, "death and decay cooldown", "icy touch 3s"); }
static Trigger* death_coil(PlayerbotAI* botAI) { return new DeathCoilTrigger(botAI); }
static Trigger* icy_touch_on_attacker(PlayerbotAI* botAI) { return new IcyTouchDebuffOnAttackerTrigger(botAI); }
static Trigger* improved_icy_talons(PlayerbotAI* botAI) { return new ImprovedIcyTalonsTrigger(botAI); }
@@ -136,6 +137,7 @@ private:
static Trigger* high_blood_rune(PlayerbotAI* botAI) { return new HighBloodRuneTrigger(botAI); }
static Trigger* high_frost_rune(PlayerbotAI* botAI) { return new HighFrostRuneTrigger(botAI); }
static Trigger* high_unholy_rune(PlayerbotAI* botAI) { return new HighUnholyRuneTrigger(botAI); }
static Trigger* no_rune(PlayerbotAI* botAI) { return new NoRuneTrigger(botAI); }
static Trigger* freezing_fog(PlayerbotAI* botAI) { return new FreezingFogTrigger(botAI); }
static Trigger* no_desolation(PlayerbotAI* botAI) { return new DesolationTrigger(botAI); }
static Trigger* dd_cd_and_no_desolation(PlayerbotAI* botAI) { return new TwoTriggers(botAI, "death and decay cooldown", "no desolation"); }

View File

@@ -37,20 +37,32 @@ bool PestilenceGlyphTrigger::IsActive()
return false;
}
// Based on runeSlotTypes
bool HighBloodRuneTrigger::IsActive()
{
return !bot->GetRuneCooldown(0) && !bot->GetRuneCooldown(1);
return bot->GetRuneCooldown(0) <= 2000 && bot->GetRuneCooldown(1) <= 2000;
}
bool HighFrostRuneTrigger::IsActive()
{
return !bot->GetRuneCooldown(2) && !bot->GetRuneCooldown(3);
return bot->GetRuneCooldown(4) <= 2000 && bot->GetRuneCooldown(5) <= 2000;
}
bool HighUnholyRuneTrigger::IsActive()
{
return !bot->GetRuneCooldown(4) && !bot->GetRuneCooldown(5);
return bot->GetRuneCooldown(2) <= 2000 && bot->GetRuneCooldown(3) <= 2000;
}
bool NoRuneTrigger::IsActive()
{
for (uint32 i = 0; i < MAX_RUNES; ++i)
{
if (!bot->GetRuneCooldown(i))
return false;
}
return true;
}
bool DesolationTrigger::IsActive()
{
return bot->HasAura(66817) && BuffTrigger::IsActive();
@@ -62,5 +74,5 @@ bool DeathAndDecayCooldownTrigger::IsActive()
if (!spellId)
return true;
return bot->GetSpellCooldownDelay(spellId) >= 3000;
return bot->GetSpellCooldownDelay(spellId) >= 2000;
}

View File

@@ -20,10 +20,10 @@ public:
PlagueStrikeDebuffTrigger(PlayerbotAI* botAI) : DebuffTrigger(botAI, "blood plague", 1, true, .0f) {}
};
class PlagueStrike8sDebuffTrigger : public DebuffTrigger
class PlagueStrike3sDebuffTrigger : public DebuffTrigger
{
public:
PlagueStrike8sDebuffTrigger(PlayerbotAI* botAI) : DebuffTrigger(botAI, "blood plague", 1, true, .0f, 3000) {}
PlagueStrike3sDebuffTrigger(PlayerbotAI* botAI) : DebuffTrigger(botAI, "blood plague", 1, true, .0f, 3000) {}
};
// DEBUFF_CHECKISOWNER_TRIGGER(IcyTouchDebuffTrigger, "frost fever");
@@ -33,10 +33,10 @@ public:
IcyTouchDebuffTrigger(PlayerbotAI* botAI) : DebuffTrigger(botAI, "frost fever", 1, true, .0f) {}
};
class IcyTouch8sDebuffTrigger : public DebuffTrigger
class IcyTouch3sDebuffTrigger : public DebuffTrigger
{
public:
IcyTouch8sDebuffTrigger(PlayerbotAI* botAI) : DebuffTrigger(botAI, "frost fever", 1, true, .0f, 3000) {}
IcyTouch3sDebuffTrigger(PlayerbotAI* botAI) : DebuffTrigger(botAI, "frost fever", 1, true, .0f, 3000) {}
};
BUFF_TRIGGER(UnbreakableArmorTrigger, "unbreakable armor");
@@ -165,6 +165,13 @@ public:
bool IsActive() override;
};
class NoRuneTrigger : public Trigger
{
public:
NoRuneTrigger(PlayerbotAI* botAI) : Trigger(botAI, "no rune") {}
bool IsActive() override;
};
class FreezingFogTrigger : public HasAuraTrigger
{
public:

View File

@@ -80,7 +80,7 @@ NextAction** UnholyDKStrategy::getDefaultActions()
return NextAction::array(
0, new NextAction("death and decay", ACTION_HIGH + 5),
new NextAction("summon gargoyle", ACTION_DEFAULT + 0.4f),
new NextAction("empower rune weapon", ACTION_DEFAULT + 0.3f),
// new NextAction("empower rune weapon", ACTION_DEFAULT + 0.3f),
new NextAction("horn of winter", ACTION_DEFAULT + 0.2f),
new NextAction("death coil", ACTION_DEFAULT + 0.1f),
new NextAction("melee", ACTION_DEFAULT), nullptr);
@@ -93,8 +93,8 @@ void UnholyDKStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
"death and decay cooldown", NextAction::array(0,
new NextAction("ghoul frenzy", ACTION_DEFAULT + 0.9f),
new NextAction("scourge strike", ACTION_DEFAULT + 0.8f),
new NextAction("blood boil", ACTION_DEFAULT + 0.7f),
new NextAction("icy touch", ACTION_DEFAULT + 0.6f),
new NextAction("icy touch", ACTION_DEFAULT + 0.7f),
new NextAction("blood strike", ACTION_DEFAULT + 0.6f),
new NextAction("plague strike", ACTION_DEFAULT + 0.5f),
nullptr)));
@@ -111,24 +111,26 @@ void UnholyDKStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
new NextAction("icy touch", ACTION_NORMAL + 3), nullptr)));
triggers.push_back(new TriggerNode(
"high unholy rune", NextAction::array(0,
new NextAction("plague strike", ACTION_NORMAL + 2), nullptr)));
"high blood rune", NextAction::array(0, new NextAction("blood strike", ACTION_NORMAL + 2), nullptr)));
triggers.push_back(new TriggerNode(
"high blood rune", NextAction::array(0, new NextAction("blood strike", ACTION_NORMAL + 1), nullptr)));
"high unholy rune", NextAction::array(0,
new NextAction("plague strike", ACTION_NORMAL + 1), nullptr)));
triggers.push_back(
new TriggerNode("dd cd and plague strike 8s", NextAction::array(0, new NextAction("plague strike", ACTION_HIGH + 2), nullptr)));
new TriggerNode("dd cd and plague strike 3s", NextAction::array(0, new NextAction("plague strike", ACTION_HIGH + 1), nullptr)));
triggers.push_back(
new TriggerNode("dd cd and icy touch 8s", NextAction::array(0, new NextAction("icy touch", ACTION_HIGH + 1), nullptr)));
new TriggerNode("dd cd and icy touch 3s", NextAction::array(0, new NextAction("icy touch", ACTION_HIGH + 2), nullptr)));
triggers.push_back(
new TriggerNode("no rune", NextAction::array(0, new NextAction("empower rune weapon", ACTION_HIGH + 1), nullptr)));
// triggers.push_back(new TriggerNode("often", NextAction::array(0, new NextAction(, ACTION_NORMAL + 2), nullptr)));
triggers.push_back(new TriggerNode(
"army of the dead", NextAction::array(0, new NextAction("army of the dead", ACTION_HIGH + 6), nullptr)));
triggers.push_back(
new TriggerNode("bone shield", NextAction::array(0, new NextAction("bone shield", ACTION_HIGH + 1), nullptr)));
new TriggerNode("bone shield", NextAction::array(0, new NextAction("bone shield", ACTION_HIGH + 3), nullptr)));
}
void UnholyDKAoeStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)

View File

@@ -41,8 +41,11 @@ DpsHunterStrategy::DpsHunterStrategy(PlayerbotAI* botAI) : GenericHunterStrategy
NextAction** DpsHunterStrategy::getDefaultActions()
{
return NextAction::array(
0, new NextAction("kill shot", ACTION_DEFAULT + 0.8f), new NextAction("chimera shot", ACTION_DEFAULT + 0.7f),
new NextAction("explosive shot", ACTION_DEFAULT + 0.6f), new NextAction("aimed shot", ACTION_DEFAULT + 0.5f),
0,
new NextAction("explosive shot", ACTION_HIGH + 1.0f),
new NextAction("kill shot", ACTION_DEFAULT + 0.8f),
new NextAction("chimera shot", ACTION_DEFAULT + 0.6f),
new NextAction("aimed shot", ACTION_DEFAULT + 0.5f),
new NextAction("silencing shot", ACTION_DEFAULT + 0.4f),
new NextAction("kill command", ACTION_DEFAULT + 0.3f),
// new NextAction("arcane shot", ACTION_DEFAULT + 0.2f),
@@ -55,7 +58,7 @@ void DpsHunterStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
GenericHunterStrategy::InitTriggers(triggers);
triggers.push_back(
new TriggerNode("black arrow", NextAction::array(0, new NextAction("black arrow", 15.0f), nullptr)));
new TriggerNode("black arrow", NextAction::array(0, new NextAction("black arrow", 19.0f), nullptr)));
triggers.push_back(
new TriggerNode("low mana", NextAction::array(0, new NextAction("viper sting", 23.0f), nullptr)));
triggers.push_back(

View File

@@ -49,6 +49,8 @@ void GenericHunterNonCombatStrategy::InitTriggers(std::vector<TriggerNode*>& tri
triggers.push_back(new TriggerNode("often", NextAction::array(0, new NextAction("apply oil", 1.0f), nullptr)));
triggers.push_back(
new TriggerNode("low ammo", NextAction::array(0, new NextAction("say::low ammo", ACTION_NORMAL), nullptr)));
triggers.push_back(
new TriggerNode("no track", NextAction::array(0, new NextAction("track humanoids", ACTION_NORMAL), nullptr)));
// triggers.push_back(new TriggerNode("no ammo", NextAction::array(0, new NextAction("switch to melee",
// ACTION_NORMAL + 1), new NextAction("say::no ammo", ACTION_NORMAL), nullptr))); triggers.push_back(new
// TriggerNode("has ammo", NextAction::array(0, new NextAction("switch to ranged", ACTION_NORMAL), nullptr)));

View File

@@ -75,6 +75,7 @@ public:
creators["aspect of the wild"] = &HunterTriggerFactoryInternal::aspect_of_the_wild;
creators["aspect of the viper"] = &HunterTriggerFactoryInternal::aspect_of_the_viper;
creators["trueshot aura"] = &HunterTriggerFactoryInternal::trueshot_aura;
creators["no track"] = &HunterTriggerFactoryInternal::no_track;
creators["serpent sting on attacker"] = &HunterTriggerFactoryInternal::serpent_sting_on_attacker;
creators["pet not happy"] = &HunterTriggerFactoryInternal::pet_not_happy;
creators["concussive shot on snare target"] = &HunterTriggerFactoryInternal::concussive_shot_on_snare_target;
@@ -99,6 +100,7 @@ private:
static Trigger* pet_not_happy(PlayerbotAI* botAI) { return new HunterPetNotHappy(botAI); }
static Trigger* serpent_sting_on_attacker(PlayerbotAI* botAI) { return new SerpentStingOnAttackerTrigger(botAI); }
static Trigger* trueshot_aura(PlayerbotAI* botAI) { return new TrueshotAuraTrigger(botAI); }
static Trigger* no_track(PlayerbotAI* botAI) { return new NoTrackTrigger(botAI); }
static Trigger* aspect_of_the_viper(PlayerbotAI* botAI) { return new HunterAspectOfTheViperTrigger(botAI); }
static Trigger* black_arrow(PlayerbotAI* botAI) { return new BlackArrowTrigger(botAI); }
static Trigger* NoStings(PlayerbotAI* botAI) { return new HunterNoStingsActiveTrigger(botAI); }
@@ -159,6 +161,7 @@ public:
creators["aspect of the pack"] = &HunterAiObjectContextInternal::aspect_of_the_pack;
creators["aspect of the cheetah"] = &HunterAiObjectContextInternal::aspect_of_the_cheetah;
creators["trueshot aura"] = &HunterAiObjectContextInternal::trueshot_aura;
creators["track humanoids"] = &HunterAiObjectContextInternal::track_humanoids;
creators["feign death"] = &HunterAiObjectContextInternal::feign_death;
creators["wing clip"] = &HunterAiObjectContextInternal::wing_clip;
creators["raptor strike"] = &HunterAiObjectContextInternal::raptor_strike;
@@ -182,6 +185,7 @@ private:
static Action* feed_pet(PlayerbotAI* botAI) { return new FeedPetAction(botAI); }
static Action* feign_death(PlayerbotAI* botAI) { return new CastFeignDeathAction(botAI); }
static Action* trueshot_aura(PlayerbotAI* botAI) { return new CastTrueshotAuraAction(botAI); }
static Action* track_humanoids(PlayerbotAI* botAI) { return new CastBuffSpellAction(botAI, "track humanoids"); }
static Action* auto_shot(PlayerbotAI* botAI) { return new CastAutoShotAction(botAI); }
static Action* aimed_shot(PlayerbotAI* botAI) { return new CastAimedShotAction(botAI); }
static Action* chimera_shot(PlayerbotAI* botAI) { return new CastChimeraShotAction(botAI); }

View File

@@ -56,7 +56,7 @@ bool HunterPetNotHappy::IsActive()
bool HunterAspectOfTheViperTrigger::IsActive()
{
return SpellTrigger::IsActive() && !botAI->HasAura(spell, GetTarget()) &&
AI_VALUE2(uint8, "mana", "self target") < sPlayerbotAIConfig->lowMana;
AI_VALUE2(uint8, "mana", "self target") < (sPlayerbotAIConfig->lowMana / 2);
;
}
@@ -88,3 +88,36 @@ bool SwitchToMeleeTrigger::IsActive()
(target->GetVictim() == bot &&
sServerFacade->IsDistanceLessOrEqualThan(AI_VALUE2(float, "distance", "current target"), 8.0f));
}
bool NoTrackTrigger::IsActive()
{
std::vector<std::string> track_list = {
"track beasts",
"track demons",
"track dragonkin",
"track elementals",
"track giants",
"track hidden",
"track humanoids"
};
for (auto &track: track_list)
{
if (botAI->HasAura(track, bot))
return false;
}
return true;
}
bool SerpentStingOnAttackerTrigger::IsActive()
{
if (!DebuffOnAttackerTrigger::IsActive())
return false;
Unit* target = GetTarget();
if (!target)
{
return false;
}
return !botAI->HasAura("scorpid sting", target, false, true) &&
!botAI->HasAura("viper sting", target, false, true);
}

View File

@@ -101,10 +101,18 @@ public:
TrueshotAuraTrigger(PlayerbotAI* botAI) : BuffTrigger(botAI, "trueshot aura") {}
};
class NoTrackTrigger : public BuffTrigger
{
public:
NoTrackTrigger(PlayerbotAI* botAI) : BuffTrigger(botAI, "no track") {}
bool IsActive() override;
};
class SerpentStingOnAttackerTrigger : public DebuffOnAttackerTrigger
{
public:
SerpentStingOnAttackerTrigger(PlayerbotAI* botAI) : DebuffOnAttackerTrigger(botAI, "serpent sting", true) {}
bool IsActive() override;
};
BEGIN_TRIGGER(HunterPetNotHappy, Trigger)

View File

@@ -31,7 +31,7 @@ private:
{
return new ActionNode("arcane barrage",
/*P*/ nullptr,
/*A*/ NextAction::array(0, new NextAction("arcane missiles"), nullptr),
/*A*/ nullptr,
/*C*/ nullptr);
}
@@ -59,8 +59,10 @@ ArcaneMageStrategy::ArcaneMageStrategy(PlayerbotAI* botAI) : GenericMageStrategy
NextAction** ArcaneMageStrategy::getDefaultActions()
{
return NextAction::array(0, new NextAction("arcane blast", ACTION_DEFAULT + 0.1f),
new NextAction("shoot", ACTION_DEFAULT), NULL);
return NextAction::array(0, new NextAction("arcane blast", ACTION_DEFAULT + 0.3f),
// new NextAction("arcane barrage", ACTION_DEFAULT + 0.2f), // cast during movement
new NextAction("fire blast", ACTION_DEFAULT + 0.1f), // cast during movement
new NextAction("shoot", ACTION_DEFAULT), nullptr);
}
void ArcaneMageStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)

View File

@@ -9,7 +9,8 @@
NextAction** FireMageStrategy::getDefaultActions()
{
return NextAction::array(0, new NextAction("fireball", ACTION_DEFAULT + 0.1f),
return NextAction::array(0, new NextAction("fireball", ACTION_DEFAULT + 0.2f),
new NextAction("fire blast", ACTION_DEFAULT + 0.1f), // cast during movement
new NextAction("shoot", ACTION_DEFAULT), NULL);
}
@@ -23,6 +24,8 @@ void FireMageStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
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)));
}
@@ -30,7 +33,10 @@ void FireMageStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
void FireMageAoeStrategy::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)));
new TriggerNode("medium aoe", NextAction::array(0,
new NextAction("dragon's breath", 24.0f),
new NextAction("flamestrike", 23.0f),
new NextAction("blast wave", 22.0f),
new NextAction("living bomb on attackers", 21.0f),
new NextAction("blizzard", 20.0f), nullptr)));
}

View File

@@ -77,7 +77,7 @@ void FrostMageStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
triggers.push_back(
new TriggerNode("has pet", NextAction::array(0, new NextAction("toggle pet spell", ACTION_HIGH + 1), nullptr)));
triggers.push_back(
new TriggerNode("ice barrier", NextAction::array(0, new NextAction("ice barrier", ACTION_NORMAL), nullptr)));
new TriggerNode("medium health", NextAction::array(0, new NextAction("ice barrier", ACTION_NORMAL), nullptr)));
triggers.push_back(new TriggerNode(
"brain freeze", NextAction::array(0, new NextAction("frostfire bolt", ACTION_NORMAL + 3), nullptr)));

View File

@@ -54,6 +54,9 @@ void GenericMageNonCombatStrategy::InitTriggers(std::vector<TriggerNode*>& trigg
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)));

View File

@@ -59,7 +59,7 @@ private:
{
return new ActionNode("fire blast",
/*P*/ nullptr,
/*A*/ NextAction::array(0, new NextAction("scorch"), nullptr),
/*A*/ nullptr,
/*C*/ nullptr);
}
@@ -115,16 +115,16 @@ private:
{
return new ActionNode("dragon's breath",
/*P*/ nullptr,
/*A*/ NextAction::array(0, new NextAction("blast wave"), nullptr),
/*C*/ NextAction::array(0, new NextAction("flamestrike", 71.0f), nullptr));
/*A*/ nullptr,
/*C*/ nullptr);
}
static ActionNode* blast_wave([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode("blast wave",
/*P*/ nullptr,
/*A*/ NextAction::array(0, new NextAction("frost nova"), nullptr),
/*C*/ NextAction::array(0, new NextAction("flamestrike", 71.0f), nullptr));
/*A*/ nullptr,
/*C*/ nullptr);
}
static ActionNode* remove_curse([[maybe_unused]] PlayerbotAI* botAI)
@@ -194,8 +194,7 @@ 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("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)));
}

View File

@@ -5,13 +5,18 @@
#include "MageActions.h"
#include "PlayerbotAIConfig.h"
#include "Playerbots.h"
#include "ServerFacade.h"
#include "SharedDefines.h"
Value<Unit*>* CastPolymorphAction::GetTargetValue() { return context->GetValue<Unit*>("cc target", getName()); }
bool CastFrostNovaAction::isUseful()
{
Unit* target = AI_VALUE(Unit*, "current target");
if (target && target->ToCreature() && target->ToCreature()->HasMechanicTemplateImmunity(1 << (MECHANIC_FREEZE - 1)))
return false;
return sServerFacade->IsDistanceLessOrEqualThan(AI_VALUE2(float, "distance", GetTargetName()), 10.f);
}
@@ -21,3 +26,65 @@ bool CastConeOfColdAction::isUseful()
bool targetClose = sServerFacade->IsDistanceLessOrEqualThan(AI_VALUE2(float, "distance", GetTargetName()), 10.f);
return facingTarget && targetClose;
}
bool CastDragonsBreathAction::isUseful()
{
Unit* target = AI_VALUE(Unit*, "current target");
if (!target)
return false;
bool facingTarget = AI_VALUE2(bool, "facing", "current target");
bool targetClose = bot->IsWithinCombatRange(target, 10.0f);
return facingTarget && targetClose;
}
bool CastBlastWaveAction::isUseful()
{
Unit* target = AI_VALUE(Unit*, "current target");
if (!target)
return false;
bool targetClose = bot->IsWithinCombatRange(target, 10.0f);
return targetClose;
}
Unit* CastFocusMagicOnPartyAction::GetTarget()
{
Group* group = bot->GetGroup();
if (!group)
return nullptr;
Unit* casterDps = nullptr;
Unit* healer = nullptr;
Unit* target = nullptr;
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
{
Player* member = ref->GetSource();
if (!member || member == bot || !member->IsAlive())
continue;
if (member->GetMap() != bot->GetMap() || bot->GetDistance(member) > sPlayerbotAIConfig->spellDistance)
continue;
if (member->HasAura(54646))
continue;
if (member->getClass() == CLASS_MAGE)
return member;
if (!casterDps && botAI->IsCaster(member) && botAI->IsDps(member))
casterDps = member;
if (!healer && botAI->IsHeal(member))
healer = member;
if (!target)
target = member;
}
if (casterDps)
return casterDps;
if (healer)
return healer;
return target;
}

View File

@@ -57,10 +57,10 @@ public:
CastPyroblastAction(PlayerbotAI* botAI) : CastSpellAction(botAI, "pyroblast") {}
};
class CastFlamestrikeAction : public CastSpellAction
class CastFlamestrikeAction : public CastDebuffSpellAction
{
public:
CastFlamestrikeAction(PlayerbotAI* botAI) : CastSpellAction(botAI, "flamestrike") {}
CastFlamestrikeAction(PlayerbotAI* botAI) : CastDebuffSpellAction(botAI, "flamestrike", true, 0.0f) {}
ActionThreatType getThreatType() override { return ActionThreatType::Aoe; }
};
@@ -243,11 +243,18 @@ public:
CastLivingBombAction(PlayerbotAI* botAI) : CastDebuffSpellAction(botAI, "living bomb", true) {}
};
class CastLivingBombOnAttackersAction : public CastDebuffSpellOnAttackerAction
{
public:
CastLivingBombOnAttackersAction(PlayerbotAI* botAI) : CastDebuffSpellOnAttackerAction(botAI, "living bomb", true) {}
};
class CastDragonsBreathAction : public CastSpellAction
{
public:
CastDragonsBreathAction(PlayerbotAI* botAI) : CastSpellAction(botAI, "dragon's breath") {}
ActionThreatType getThreatType() override { return ActionThreatType::Aoe; }
bool isUseful() override;
};
class CastBlastWaveAction : public CastSpellAction
@@ -255,6 +262,7 @@ class CastBlastWaveAction : public CastSpellAction
public:
CastBlastWaveAction(PlayerbotAI* botAI) : CastSpellAction(botAI, "blast wave") {}
ActionThreatType getThreatType() override { return ActionThreatType::Aoe; }
bool isUseful() override;
};
class CastInvisibilityAction : public CastBuffSpellAction
@@ -294,4 +302,11 @@ public:
CastMirrorImageAction(PlayerbotAI* botAI) : CastBuffSpellAction(botAI, "mirror image") {}
};
class CastFocusMagicOnPartyAction : public CastSpellAction
{
public:
CastFocusMagicOnPartyAction(PlayerbotAI* botAI) : CastSpellAction(botAI, "focus magic") {}
Unit* GetTarget() override;
};
#endif

View File

@@ -108,6 +108,7 @@ public:
creators["mirror image"] = &MageTriggerFactoryInternal::mirror_image;
creators["frost nova on target"] = &MageTriggerFactoryInternal::frost_nova_on_target;
creators["frostbite on target"] = &MageTriggerFactoryInternal::frostbite_on_target;
creators["no focus magic"] = &MageTriggerFactoryInternal::no_focus_magic;
}
private:
@@ -141,6 +142,7 @@ private:
static Trigger* mirror_image(PlayerbotAI* botAI) { return new MirrorImageTrigger(botAI); }
static Trigger* frost_nova_on_target(PlayerbotAI* botAI) { return new FrostNovaOnTargetTrigger(botAI); }
static Trigger* frostbite_on_target(PlayerbotAI* botAI) { return new FrostbiteOnTargetTrigger(botAI); }
static Trigger* no_focus_magic(PlayerbotAI* botAI) { return new NoFocusMagicTrigger(botAI); }
};
class MageAiObjectContextInternal : public NamedObjectContext<Action>
@@ -184,6 +186,7 @@ public:
creators["polymorph"] = &MageAiObjectContextInternal::polymorph;
creators["spellsteal"] = &MageAiObjectContextInternal::spellsteal;
creators["living bomb"] = &MageAiObjectContextInternal::living_bomb;
creators["living bomb on attackers"] = &MageAiObjectContextInternal::living_bomb_on_attackers;
creators["dragon's breath"] = &MageAiObjectContextInternal::dragons_breath;
creators["blast wave"] = &MageAiObjectContextInternal::blast_wave;
creators["invisibility"] = &MageAiObjectContextInternal::invisibility;
@@ -195,6 +198,7 @@ public:
creators["fire ward"] = &MageAiObjectContextInternal::fire_ward;
creators["frost ward"] = &MageAiObjectContextInternal::frost_ward;
creators["mirror image"] = &MageAiObjectContextInternal::mirror_image;
creators["focus magic on party"] = &MageAiObjectContextInternal::focus_magic_on_party;
}
private:
@@ -242,6 +246,7 @@ private:
static Action* polymorph(PlayerbotAI* botAI) { return new CastPolymorphAction(botAI); }
static Action* spellsteal(PlayerbotAI* botAI) { return new CastSpellstealAction(botAI); }
static Action* living_bomb(PlayerbotAI* botAI) { return new CastLivingBombAction(botAI); }
static Action* living_bomb_on_attackers(PlayerbotAI* botAI) { return new CastLivingBombOnAttackersAction(botAI); }
static Action* dragons_breath(PlayerbotAI* botAI) { return new CastDragonsBreathAction(botAI); }
static Action* blast_wave(PlayerbotAI* botAI) { return new CastBlastWaveAction(botAI); }
static Action* invisibility(PlayerbotAI* botAI) { return new CastInvisibilityAction(botAI); }
@@ -251,6 +256,7 @@ private:
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); }
};
MageAiObjectContext::MageAiObjectContext(PlayerbotAI* botAI) : AiObjectContext(botAI)

View File

@@ -33,6 +33,17 @@ bool FingersOfFrostSingleTrigger::IsActive()
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();
@@ -52,3 +63,24 @@ bool FrostbiteOnTargetTrigger::IsActive()
}
return botAI->HasAura(spell, target);
}
bool NoFocusMagicTrigger::IsActive()
{
if (!bot->HasSpell(54646))
return false;
Group* group = bot->GetGroup();
if (!group)
return false;
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
{
Player* member = ref->GetSource();
if (!member || member == bot || !member->IsAlive())
continue;
if (member->HasAura(54646, bot->GetGUID()))
return false;
}
return true;
}

View File

@@ -171,6 +171,7 @@ class ArcaneBlastStackTrigger : public HasAuraStackTrigger
{
public:
ArcaneBlastStackTrigger(PlayerbotAI* botAI) : HasAuraStackTrigger(botAI, "arcane blast", 3, 1) {}
bool IsActive() override;
};
class MirrorImageTrigger : public BoostTrigger
@@ -193,4 +194,11 @@ public:
bool IsActive() override;
};
class NoFocusMagicTrigger : public Trigger
{
public:
NoFocusMagicTrigger(PlayerbotAI* botAI) : Trigger(botAI, "no focus magic") {}
bool IsActive() override;
};
#endif

View File

@@ -15,10 +15,10 @@ ShadowPriestStrategy::ShadowPriestStrategy(PlayerbotAI* botAI) : GenericPriestSt
NextAction** ShadowPriestStrategy::getDefaultActions()
{
return NextAction::array(0, new NextAction("mind blast", ACTION_DEFAULT + 0.2f),
// new NextAction("shadow word: death", 12.0f),
new NextAction("mind flay", ACTION_DEFAULT + 0.1f),
new NextAction("shoot", ACTION_DEFAULT), NULL);
return NextAction::array(0, new NextAction("mind blast", ACTION_DEFAULT + 0.3f),
new NextAction("mind flay", ACTION_DEFAULT + 0.2f),
new NextAction("shadow word: death", ACTION_DEFAULT + 0.1f), // cast during movement
new NextAction("shoot", ACTION_DEFAULT), nullptr);
}
void ShadowPriestStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)

View File

@@ -41,7 +41,9 @@ CasterShamanStrategy::CasterShamanStrategy(PlayerbotAI* botAI) : GenericShamanSt
NextAction** CasterShamanStrategy::getDefaultActions()
{
return NextAction::array(0, new NextAction("lava burst", ACTION_DEFAULT + 0.2f),
new NextAction("lightning bolt", ACTION_DEFAULT), NULL);
new NextAction("lightning bolt", ACTION_DEFAULT + 0.1f),
// new NextAction("earth shock", ACTION_DEFAULT), // cast during movement
nullptr);
}
void CasterShamanStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)

View File

@@ -5,6 +5,7 @@
#include "ShamanTriggers.h"
#include "ItemTemplate.h"
#include "Playerbots.h"
/*
@@ -46,8 +47,12 @@ bool MainHandWeaponNoImbueTrigger::IsActive()
bool OffHandWeaponNoImbueTrigger::IsActive()
{
Item* const itemForSpell = bot->GetItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_OFFHAND);
if (!itemForSpell || itemForSpell->GetEnchantmentId(TEMP_ENCHANTMENT_SLOT) ||
itemForSpell->GetTemplate()->InventoryType != INVTYPE_WEAPON)
if (!itemForSpell)
return false;
uint32 invType = itemForSpell->GetTemplate()->InventoryType;
bool allowedType = (invType == INVTYPE_WEAPON) || (invType == INVTYPE_WEAPONOFFHAND);
if (itemForSpell->GetEnchantmentId(TEMP_ENCHANTMENT_SLOT) ||
!allowedType)
return false;
return true;
}

View File

@@ -302,7 +302,7 @@ public:
class FlameShockTrigger : public DebuffTrigger
{
public:
FlameShockTrigger(PlayerbotAI* ai) : DebuffTrigger(ai, "flame shock", 1, true) {}
FlameShockTrigger(PlayerbotAI* ai) : DebuffTrigger(ai, "flame shock", 1, true, 6.0f) {}
};
class WrathOfAirTotemTrigger : public TotemTrigger

View File

@@ -618,8 +618,8 @@ public:
{
}
std::string const GetTargetName() { return "self target"; }
virtual bool IsActive();
std::string const GetTargetName() override { return "self target"; }
bool IsActive() override;
private:
int stack;

View File

@@ -36,7 +36,7 @@ float EstimatedGroupDpsValue::Calculate()
if (member == bot) // calculated
continue;
if (!member || !member->IsInWorld())
if (!member || !member->IsInWorld() || !member->IsAlive())
continue;
if (member->GetMapId() != bot->GetMapId())

View File

@@ -49,8 +49,8 @@ DpsWarlockStrategy::DpsWarlockStrategy(PlayerbotAI* botAI) : GenericWarlockStrat
NextAction** DpsWarlockStrategy::getDefaultActions()
{
return NextAction::array(
0, new NextAction("haunt", ACTION_DEFAULT + 0.3f), new NextAction("demonic empowerment", ACTION_DEFAULT + 0.2f),
new NextAction("shadow bolt", ACTION_DEFAULT + 0.1f), new NextAction("shoot", ACTION_DEFAULT), nullptr);
0, new NextAction("haunt", ACTION_DEFAULT + 0.4f), new NextAction("demonic empowerment", ACTION_DEFAULT + 0.3f),
new NextAction("shadow bolt", ACTION_DEFAULT + 0.2f), new NextAction("shoot", ACTION_DEFAULT), nullptr);
}
void DpsWarlockStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
@@ -71,6 +71,12 @@ void DpsWarlockStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
triggers.push_back(new TriggerNode("decimation", NextAction::array(0, new NextAction("soul fire", 16.0f), NULL)));
// cast during movement
triggers.push_back(
new TriggerNode("high mana", NextAction::array(0, new NextAction("life tap", ACTION_DEFAULT + 0.1f), nullptr)));
triggers.push_back(new TriggerNode("life tap glyph buff", NextAction::array(0, new NextAction("life tap", 28.0f), NULL)));
triggers.push_back(
new TriggerNode("metamorphosis", NextAction::array(0, new NextAction("metamorphosis", 20.0f), NULL)));
}
@@ -78,9 +84,9 @@ void DpsWarlockStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
void DpsAoeWarlockStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
{
triggers.push_back(
new TriggerNode("medium aoe", NextAction::array(0, new NextAction("seed of corruption", 39.0f),
new NextAction("seed of corruption on attacker", 38.0f),
new NextAction("rain of fire", 37.0f), nullptr)));
new TriggerNode("medium aoe", NextAction::array(0, new NextAction("seed of corruption", 33.0f),
new NextAction("seed of corruption on attacker", 32.0f),
new NextAction("rain of fire", 31.0f), nullptr)));
triggers.push_back(new TriggerNode("corruption on attacker",
NextAction::array(0, new NextAction("corruption on attacker", 27.0f), nullptr)));
triggers.push_back(

View File

@@ -18,7 +18,7 @@ Value<Unit*>* CastFearOnCcAction::GetTargetValue() { return context->GetValue<Un
bool CastFearOnCcAction::Execute(Event event) { return botAI->CastSpell("fear", GetTarget()); }
bool CastFearOnCcAction::isPossible() { return botAI->CanCastSpell("fear", GetTarget()); }
bool CastFearOnCcAction::isPossible() { return true; }
bool CastFearOnCcAction::isUseful() { return true; }

View File

@@ -6,6 +6,7 @@
#include "WarlockAiObjectContext.h"
#include "DpsWarlockStrategy.h"
#include "GenericTriggers.h"
#include "GenericWarlockNonCombatStrategy.h"
#include "NamedObjectContext.h"
#include "Playerbots.h"
@@ -98,6 +99,7 @@ public:
creators["unstable affliction on attacker"] = &WarlockTriggerFactoryInternal::unstable_affliction_on_attacker;
creators["haunt"] = &WarlockTriggerFactoryInternal::haunt;
creators["decimation"] = &WarlockTriggerFactoryInternal::decimation;
creators["life tap glyph buff"] = &WarlockTriggerFactoryInternal::life_tap_glyph_buff;
creators["molten core"] = &WarlockTriggerFactoryInternal::molten_core;
creators["metamorphosis"] = &WarlockTriggerFactoryInternal::metamorphosis;
}
@@ -131,6 +133,7 @@ private:
}
static Trigger* haunt(PlayerbotAI* ai) { return new HauntTrigger(ai); }
static Trigger* decimation(PlayerbotAI* ai) { return new DecimationTrigger(ai); }
static Trigger* life_tap_glyph_buff(PlayerbotAI* ai) { return new LifeTapGlyphBuffTrigger(ai); }
static Trigger* molten_core(PlayerbotAI* ai) { return new MoltenCoreTrigger(ai); }
static Trigger* metamorphosis(PlayerbotAI* ai) { return new MetamorphosisTrigger(ai); }
};

View File

@@ -5,6 +5,7 @@
#include "WarlockTriggers.h"
#include "GenericTriggers.h"
#include "Playerbots.h"
bool DemonArmorTrigger::IsActive()
@@ -44,4 +45,13 @@ bool DecimationTrigger::IsActive()
{
Aura* aura = botAI->GetAura(getName(), GetTarget(), false, true);
return aura && aura->GetDuration() > 3000;
}
bool LifeTapGlyphBuffTrigger::IsActive()
{
// Check life tap glyph first
if (!botAI->HasAura(63320, bot))
return false;
return BuffTrigger::IsActive();
}

View File

@@ -163,6 +163,13 @@ public:
bool IsActive() override;
};
class LifeTapGlyphBuffTrigger : public BuffTrigger
{
public:
LifeTapGlyphBuffTrigger(PlayerbotAI* ai) : BuffTrigger(ai, "life tap") {}
bool IsActive() override;
};
class MoltenCoreTrigger : public HasAuraTrigger
{
public:

View File

@@ -13,12 +13,12 @@ bool CastSunderArmorAction::isUseful()
return !aura || aura->GetStackAmount() < 5 || aura->GetDuration() <= 6000;
}
Value<Unit*>* CastVigilanceAction::GetTargetValue()
Unit* CastVigilanceAction::GetTarget()
{
Group* group = bot->GetGroup();
if (!group)
{
return new ManualSetValue<Unit*>(botAI, nullptr);
return nullptr;
}
Player* currentVigilanceTarget = nullptr;
@@ -74,23 +74,23 @@ Value<Unit*>* CastVigilanceAction::GetTargetValue()
// If no valid target, return nullptr
if (!highestPriorityTarget)
{
return new ManualSetValue<Unit*>(botAI, nullptr);
return nullptr;
}
// If the current target is already the highest-priority target, do nothing
if (currentVigilanceTarget == highestPriorityTarget)
{
return new ManualSetValue<Unit*>(botAI, nullptr);
return nullptr;
}
// Assign the new target
Unit* targetUnit = highestPriorityTarget->ToUnit();
if (targetUnit)
{
return new ManualSetValue<Unit*>(botAI, targetUnit);
return targetUnit;
}
return new ManualSetValue<Unit*>(botAI, nullptr);
return nullptr;
}
bool CastVigilanceAction::Execute(Event event)

View File

@@ -140,7 +140,7 @@ class CastVigilanceAction : public BuffOnPartyAction
public:
CastVigilanceAction(PlayerbotAI* botAI) : BuffOnPartyAction(botAI, "vigilance") {}
Value<Unit*>* GetTargetValue() override;
Unit* GetTarget() override;
bool Execute(Event event) override;
};