Files
mod-playerbots/src/strategy/warrior/WarriorActions.cpp
avirar 739a0df44c 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.
2025-01-03 16:06:47 +01:00

245 lines
6.6 KiB
C++

/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it
* and/or modify it under version 2 of the License, or (at your option), any later version.
*/
#include "WarriorActions.h"
#include "Playerbots.h"
bool CastSunderArmorAction::isUseful()
{
Aura* aura = botAI->GetAura("sunder armor", GetTarget(), false, true);
return !aura || aura->GetStackAmount() < 5 || aura->GetDuration() <= 6000;
}
Value<Unit*>* CastVigilanceAction::GetTargetValue()
{
Group* group = bot->GetGroup();
if (!group)
{
return new ManualSetValue<Unit*>(botAI, nullptr);
}
Player* currentVigilanceTarget = nullptr;
Player* mainTank = nullptr;
Player* assistTank1 = nullptr;
Player* assistTank2 = nullptr;
Player* highestGearScorePlayer = nullptr;
uint32 highestGearScore = 0;
// Iterate once through the group to gather all necessary information
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
{
Player* member = ref->GetSource();
if (!member || member == bot || !member->IsAlive())
continue;
// Check if member has Vigilance applied by the bot
if (!currentVigilanceTarget && botAI->HasAura("vigilance", member, false, true))
{
currentVigilanceTarget = member;
}
// Identify Main Tank
if (!mainTank && botAI->IsMainTank(member))
{
mainTank = member;
}
// Identify Assist Tanks
if (assistTank1 == nullptr && botAI->IsAssistTankOfIndex(member, 0))
{
assistTank1 = member;
}
else if (assistTank2 == nullptr && botAI->IsAssistTankOfIndex(member, 1))
{
assistTank2 = member;
}
// Determine Highest Gear Score
uint32 gearScore = botAI->GetEquipGearScore(member, false, false);
if (gearScore > highestGearScore)
{
highestGearScore = gearScore;
highestGearScorePlayer = member;
}
}
// Determine the highest-priority target
Player* highestPriorityTarget = mainTank ? mainTank :
(assistTank1 ? assistTank1 :
(assistTank2 ? assistTank2 : highestGearScorePlayer));
// If no valid target, return nullptr
if (!highestPriorityTarget)
{
return new ManualSetValue<Unit*>(botAI, nullptr);
}
// If the current target is already the highest-priority target, do nothing
if (currentVigilanceTarget == highestPriorityTarget)
{
return new ManualSetValue<Unit*>(botAI, nullptr);
}
// Assign the new target
Unit* targetUnit = highestPriorityTarget->ToUnit();
if (targetUnit)
{
return new ManualSetValue<Unit*>(botAI, targetUnit);
}
return new ManualSetValue<Unit*>(botAI, nullptr);
}
bool CastVigilanceAction::Execute(Event event)
{
Unit* target = GetTarget();
if (!target || target == bot)
return false;
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);
}