Warrior strategy update (#838)

* Enraged regen at critial health

* Enraged regen action context

* Enraged regen on critical health trigger

* Enraged regen on critical health trigger

* Added logic for Arms to use Retaliation

* Added logic for Arms to use Retaliation

* Used correct class enums for !players

* Retaliation on medium health

* Removed temp line

* Added check for attacker->GetVictim() != bot

* Adjusted triggers for emergency actions

* Added Shattering Throw logic

* Added Shattering Throw logic

* Added Shattering Throw logic

* Added Shattering Throw logic

* Added Shattering Throw logic

* Added Shattering Throw logic

* Fixed ActionNode for Shattering Throw

* Added debug logging

* More debug logs

* Better debug logs

* Adjusted range on action

* Adjusted priorities

* More logging

* Update WarriorActions.cpp

* Update WarriorActions.h

* Changed trigger name for differentiation

* Updated to new shattering throw trigger name

* Update WarriorTriggers.h with new ST name

* Update ArmsWarriorStrategy.cpp

* Changed priority

* Shattering Throw and Retaliation stance reqs

Battlestance needed for Shattering Throw and Retaliation

* Created isUseful for Shattering Throw

* Created isUseful for Shattering Throw

* GetTarget instead of GetTargetValue

* Changed to GetTarget instead of GetTargetValue

* Commented out Execute function

* Commented out Execute function

* isPossible was failing, created basic isPossible

IsImmuneToSpell was returning true for Shattering Throw

2 DAYS! :(

* isPossible was failing, created basic isPossible

* Added some more isPossible checks

* Update WarriorActions.cpp

* Missing )

* Missing !

* Removed logging

* Removed logging

* Clean up

* Cleanup

* Corrected logic for Rogue's Expose Armor trigger

Logic was checking the Rogue, not the Rogue's target, for Sunder Armor before casting Expose Armor.
This commit is contained in:
avirar
2025-01-04 02:06:47 +11:00
committed by GitHub
parent c59c0cc802
commit 739a0df44c
10 changed files with 279 additions and 7 deletions

View File

@@ -105,7 +105,8 @@ bool SprintTrigger::IsActive()
bool ExposeArmorTrigger::IsActive()
{
return DebuffTrigger::IsActive() && !botAI->HasAura("sunder armor", bot, false, false, -1, true) &&
Unit* target = AI_VALUE(Unit*, "current target"); // Get the bot's current target
return DebuffTrigger::IsActive() && !botAI->HasAura("sunder armor", target, false, false, -1, true) &&
AI_VALUE2(uint8, "combo", "current target") <= 3;
}

View File

@@ -17,6 +17,9 @@ public:
creators["piercing howl"] = &piercing_howl;
creators["mocking blow"] = &mocking_blow;
creators["heroic strike"] = &heroic_strike;
creators["enraged regeneration"] = &enraged_regeneration;
creators["retaliation"] = &retaliation;
creators["shattering throw"] = &shattering_throw;
}
private:
@@ -25,6 +28,30 @@ private:
ACTION_NODE_A(piercing_howl, "piercing howl", "mocking blow");
ACTION_NODE_A(mocking_blow, "mocking blow", "hamstring");
ACTION_NODE_A(heroic_strike, "heroic strike", "melee");
static ActionNode* enraged_regeneration(PlayerbotAI* botAI)
{
return new ActionNode("enraged regeneration",
/*P*/ nullptr,
/*A*/ nullptr,
/*C*/ nullptr);
}
static ActionNode* retaliation(PlayerbotAI* botAI)
{
return new ActionNode("retaliation",
/*P*/ nullptr,
/*A*/ nullptr,
/*C*/ nullptr);
}
static ActionNode* shattering_throw(PlayerbotAI* botAI)
{
return new ActionNode("shattering throw",
/*P*/ nullptr,
/*A*/ nullptr,
/*C*/ nullptr);
}
};
ArmsWarriorStrategy::ArmsWarriorStrategy(PlayerbotAI* botAI) : GenericWarriorStrategy(botAI)
@@ -93,8 +120,17 @@ void ArmsWarriorStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
triggers.push_back(
new TriggerNode("death wish", NextAction::array(0, new NextAction("death wish", ACTION_HIGH + 2), nullptr)));
triggers.push_back(new TriggerNode(
"critical health", NextAction::array(0, new NextAction("intimidating shout", ACTION_EMERGENCY), nullptr)));
triggers.push_back(new TriggerNode("critical health",
NextAction::array(0, new NextAction("intimidating shout", ACTION_EMERGENCY), nullptr)));
triggers.push_back(new TriggerNode("medium health",
NextAction::array(0, new NextAction("enraged regeneration", ACTION_EMERGENCY), nullptr)));
triggers.push_back(new TriggerNode("almost full health",
NextAction::array(0, new NextAction("retaliation", ACTION_EMERGENCY + 1), nullptr)));
triggers.push_back(new TriggerNode("shattering throw trigger",
NextAction::array(0, new NextAction("shattering throw", ACTION_INTERRUPT + 1), nullptr)));
// triggers.push_back(new TriggerNode("medium aoe",
// NextAction::array(0, new NextAction("thunder clap", ACTION_HIGH + 2), nullptr)));

View File

@@ -18,6 +18,7 @@ public:
creators["piercing howl"] = &piercing_howl;
// creators["bloodthirst"] = &bloodthirst;
creators["pummel"] = &pummel;
creators["enraged regeneration"] = &enraged_regeneration;
}
private:
@@ -27,6 +28,14 @@ private:
// ACTION_NODE_A(death_wish, "death wish", "berserker rage");
// ACTION_NODE_A(bloodthirst, "bloodthirst", "melee");
ACTION_NODE_A(pummel, "pummel", "intercept");
static ActionNode* enraged_regeneration(PlayerbotAI* botAI)
{
return new ActionNode("enraged regeneration",
/*P*/ nullptr,
/*A*/ nullptr,
/*C*/ nullptr);
}
};
FuryWarriorStrategy::FuryWarriorStrategy(PlayerbotAI* botAI) : GenericWarriorStrategy(botAI)
@@ -85,4 +94,6 @@ void FuryWarriorStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
new TriggerNode("death wish", NextAction::array(0, new NextAction("death wish", ACTION_HIGH), nullptr)));
triggers.push_back(
new TriggerNode("recklessness", NextAction::array(0, new NextAction("recklessness", ACTION_HIGH), nullptr)));
triggers.push_back(new TriggerNode("critical health",
NextAction::array(0, new NextAction("enraged regeneration", ACTION_EMERGENCY), nullptr)));
}

View File

@@ -20,6 +20,8 @@ public:
creators["charge"] = &charge;
creators["mocking blow"] = &mocking_blow;
creators["overpower"] = &overpower;
creators["retaliation"] = &retaliation;
creators["shattering throw"] = &shattering_throw;
// temp
creators["mortal strike"] = &mortal_strike;
@@ -57,6 +59,8 @@ private:
ACTION_NODE_P(intervene, "intervene", "defensive stance");
// temp
ACTION_NODE_P(mortal_strike, "mortal strike", "battle stance");
ACTION_NODE_P(retaliation, "retaliation", "battle stance");
ACTION_NODE_P(shattering_throw, "shattering throw", "battle stance");
};
class GenericWarriorStrategy : public CombatStrategy

View File

@@ -23,6 +23,7 @@ public:
creators["taunt"] = &taunt;
creators["taunt spell"] = &taunt;
creators["vigilance"] = &vigilance;
creators["enraged regeneration"] = &enraged_regeneration;
}
private:
@@ -50,6 +51,14 @@ private:
/*A*/ nullptr,
/*C*/ nullptr);
}
static ActionNode* enraged_regeneration(PlayerbotAI* botAI)
{
return new ActionNode("enraged regeneration",
/*P*/ nullptr,
/*A*/ nullptr,
/*C*/ nullptr);
}
};
TankWarriorStrategy::TankWarriorStrategy(PlayerbotAI* botAI) : GenericWarriorStrategy(botAI)
@@ -105,8 +114,10 @@ void TankWarriorStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
NextAction::array(0, new NextAction("heroic throw on snare target", ACTION_INTERRUPT), nullptr)));
triggers.push_back(new TriggerNode(
"low health", NextAction::array(0, new NextAction("shield wall", ACTION_MEDIUM_HEAL), nullptr)));
triggers.push_back(new TriggerNode(
"critical health", NextAction::array(0, new NextAction("last stand", ACTION_EMERGENCY + 3), nullptr)));
triggers.push_back(new TriggerNode("critical health",
NextAction::array(0, new NextAction("last stand", ACTION_EMERGENCY + 3),
new NextAction("enraged regeneration", ACTION_EMERGENCY + 2),
nullptr)));
// triggers.push_back(new TriggerNode("medium aoe", NextAction::array(0, new NextAction("battle shout taunt",
// ACTION_HIGH + 1), nullptr)));
triggers.push_back(new TriggerNode(

View File

@@ -101,3 +101,144 @@ bool CastVigilanceAction::Execute(Event event)
return botAI->CastSpell("vigilance", target);
}
bool CastRetaliationAction::isUseful()
{
// Spell cooldown check
if (!bot->HasSpell(20230))
{
return false;
}
// Spell cooldown check
if (bot->HasSpellCooldown(20230))
{
return false;
}
uint8 meleeAttackers = 0;
GuidVector attackers = AI_VALUE(GuidVector, "attackers");
for (ObjectGuid const& guid : attackers)
{
Unit* attacker = botAI->GetUnit(guid);
if (!attacker || !attacker->IsAlive() || attacker->GetVictim() != bot)
continue;
// Check if the attacker is melee-based using unit_class
if (attacker->GetTypeId() == TYPEID_UNIT)
{
Creature* creature = attacker->ToCreature();
if (creature && (creature->IsClass(CLASS_WARRIOR)
|| creature->IsClass(CLASS_ROGUE)
|| creature->IsClass(CLASS_PALADIN)))
{
++meleeAttackers;
}
}
else if (attacker->GetTypeId() == TYPEID_PLAYER)
{
Player* playerAttacker = attacker->ToPlayer();
if (playerAttacker && botAI->IsMelee(playerAttacker)) // Reuse existing Player melee check
{
++meleeAttackers;
}
}
// Early exit if we already have enough melee attackers
if (meleeAttackers >= 2)
break;
}
// Only cast Retaliation if there are at least 2 melee attackers and the buff is not active
return meleeAttackers >= 2 && !botAI->HasAura("retaliation", bot);
}
Unit* CastShatteringThrowAction::GetTarget()
{
GuidVector enemies = AI_VALUE(GuidVector, "possible targets");
for (ObjectGuid const& guid : enemies)
{
Unit* enemy = botAI->GetUnit(guid);
if (!enemy || !enemy->IsAlive() || enemy->IsFriendlyTo(bot))
continue;
if (bot->IsWithinDistInMap(enemy, 25.0f) &&
(enemy->HasAura(642) || // Divine Shield
enemy->HasAura(45438) || // Ice Block
enemy->HasAura(41450))) // Blessing of Protection
{
return enemy;
}
}
return nullptr; // No valid target
}
bool CastShatteringThrowAction::isUseful()
{
// Spell cooldown check
if (!bot->HasSpell(64382))
{
return false;
}
// Spell cooldown check
if (bot->HasSpellCooldown(64382))
{
return false;
}
GuidVector enemies = AI_VALUE(GuidVector, "possible targets");
for (ObjectGuid const& guid : enemies)
{
Unit* enemy = botAI->GetUnit(guid);
if (!enemy || !enemy->IsAlive() || enemy->IsFriendlyTo(bot))
continue;
// Check if the enemy is within 25 yards and has the specific auras
if (bot->IsWithinDistInMap(enemy, 25.0f) &&
(enemy->HasAura(642) || // Divine Shield
enemy->HasAura(45438) || // Ice Block
enemy->HasAura(41450))) // Blessing of Protection
{
return true;
}
}
return false; // No valid targets within range
}
bool CastShatteringThrowAction::isPossible()
{
Unit* target = GetTarget();
if (!target)
return false;
// Range check: Shattering Throw is 30 yards
if (!bot->IsWithinDistInMap(target, 30.0f))
{
return false;
}
// Check line of sight
if (!bot->IsWithinLOSInMap(target))
{
return false;
}
// If the minimal checks above pass, simply return true.
return true;
}
bool CastShatteringThrowAction::Execute(Event event)
{
Unit* target = GetTarget();
if (!target)
return false;
return botAI->CastSpell("shattering throw", target);
}

View File

@@ -60,11 +60,11 @@ SNARE_ACTION(CastThunderClapSnareAction, "thunder clap");
SNARE_ACTION(CastHamstringAction, "hamstring");
MELEE_ACTION(CastOverpowerAction, "overpower");
MELEE_ACTION(CastMockingBlowAction, "mocking blow");
BUFF_ACTION(CastRetaliationAction, "retaliation");
// BUFF_ACTION(CastRetaliationAction, "retaliation");
// arms 3.3.5
SPELL_ACTION(CastHeroicThrowAction, "heroic throw");
SNARE_ACTION(CastHeroicThrowSnareAction, "heroic throw");
DEBUFF_ACTION(CastShatteringThrowAction, "shattering throw");
// DEBUFF_ACTION(CastShatteringThrowAction, "shattering throw");
// arms talents
MELEE_ACTION(CastMortalStrikeAction, "mortal strike");
@@ -144,4 +144,23 @@ public:
bool Execute(Event event) override;
};
class CastRetaliationAction : public CastBuffSpellAction
{
public:
CastRetaliationAction(PlayerbotAI* botAI) : CastBuffSpellAction(botAI, "retaliation") {}
bool isUseful() override;
};
class CastShatteringThrowAction : public CastSpellAction
{
public:
CastShatteringThrowAction(PlayerbotAI* botAI) : CastSpellAction(botAI, "shattering throw") {}
Unit* GetTarget() override;
bool isUseful() override;
bool isPossible() override;
bool Execute(Event event) override;
};
#endif

View File

@@ -101,6 +101,7 @@ public:
// creators["slam"] = &WarriorTriggerFactoryInternal::slam;
creators["vigilance"] = &WarriorTriggerFactoryInternal::vigilance;
creators["shattering throw trigger"] = &WarriorTriggerFactoryInternal::shattering_throw_trigger;
}
private:
@@ -172,6 +173,7 @@ private:
// static Trigger* slam(PlayerbotAI* ai) { return new SlamTrigger(ai); }
static Trigger* vigilance(PlayerbotAI* botAI) { return new VigilanceTrigger(botAI); }
static Trigger* shattering_throw_trigger(PlayerbotAI* botAI) { return new ShatteringThrowTrigger(botAI); }
};
class WarriorAiObjectContextInternal : public NamedObjectContext<Action>
@@ -242,6 +244,7 @@ public:
creators["heroic throw on snare target"] = &WarriorAiObjectContextInternal::heroic_throw_on_snare_target;
creators["shattering throw"] = &WarriorAiObjectContextInternal::shattering_throw;
creators["vigilance"] = &WarriorAiObjectContextInternal::vigilance;
creators["enraged regeneration"] = &WarriorAiObjectContextInternal::enraged_regeneration;
}
private:
@@ -312,6 +315,7 @@ private:
static Action* heroic_throw(PlayerbotAI* botAI) { return new CastHeroicThrowAction(botAI); }
static Action* bladestorm(PlayerbotAI* botAI) { return new CastBladestormAction(botAI); }
static Action* vigilance(PlayerbotAI* botAI) { return new CastVigilanceAction(botAI); }
static Action* enraged_regeneration(PlayerbotAI* botAI) { return new CastEnragedRegenerationAction(botAI); }
};
WarriorAiObjectContext::WarriorAiObjectContext(PlayerbotAI* botAI) : AiObjectContext(botAI)

View File

@@ -84,3 +84,39 @@ bool VigilanceTrigger::IsActive()
return false; // No need to reassign Vigilance
}
bool ShatteringThrowTrigger::IsActive()
{
// Spell cooldown check
if (!bot->HasSpell(64382))
{
return false;
}
// Spell cooldown check
if (bot->HasSpellCooldown(64382))
{
return false;
}
GuidVector enemies = AI_VALUE(GuidVector, "possible targets");
for (ObjectGuid const& guid : enemies)
{
Unit* enemy = botAI->GetUnit(guid);
if (!enemy || !enemy->IsAlive() || enemy->IsFriendlyTo(bot))
continue;
// Check if the enemy is within 25 yards and has the specific auras
if (bot->IsWithinDistInMap(enemy, 25.0f) &&
(enemy->HasAura(642) || // Divine Shield
enemy->HasAura(45438) || // Ice Block
enemy->HasAura(41450))) // Blessing of Protection
{
return true;
}
}
return false; // No valid targets within range
}

View File

@@ -72,6 +72,15 @@ public:
bool IsActive() override;
};
class ShatteringThrowTrigger : public Trigger
{
public:
ShatteringThrowTrigger(PlayerbotAI* botAI) : Trigger(botAI, "shattering throw trigger") {}
bool IsActive() override;
};
// class SlamTrigger : public HasAuraTrigger
// {
// public: