mirror of
https://github.com/mod-playerbots/mod-playerbots
synced 2025-11-29 15:58:20 +08:00
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:
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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,9 +120,18 @@ 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)));
|
||||
/*
|
||||
|
||||
@@ -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)));
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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:
|
||||
|
||||
Reference in New Issue
Block a user