Compare commits

..

No commits in common. "24e69229e3c9e000477fd72cdcde474cd84cea3d" and "aea58414b0919055afba7d1719cd2b3edb94ee97" have entirely different histories.

5 changed files with 79 additions and 133 deletions

View File

@ -29,7 +29,6 @@ bool CastFireNovaAction::isUseful() {
Unit* target = AI_VALUE(Unit*, "current target"); Unit* target = AI_VALUE(Unit*, "current target");
if (!target) if (!target)
return false; return false;
Creature* fireTotem = bot->GetMap()->GetCreature(bot->m_SummonSlot[1]); Creature* fireTotem = bot->GetMap()->GetCreature(bot->m_SummonSlot[1]);
if (!fireTotem) if (!fireTotem)
return false; return false;
@ -46,10 +45,7 @@ bool CastCleansingTotemAction::isUseful()
} }
// Will only cast Stoneclaw Totem if low on health and not in a group // Will only cast Stoneclaw Totem if low on health and not in a group
bool CastStoneclawTotemAction::isUseful() bool CastStoneclawTotemAction::isUseful() { return !botAI->GetBot()->GetGroup(); }
{
return !bot->GetGroup();
}
// Will only cast Lava Burst if Flame Shock is on the target // Will only cast Lava Burst if Flame Shock is on the target
bool CastLavaBurstAction::isUseful() bool CastLavaBurstAction::isUseful()
@ -58,10 +54,10 @@ bool CastLavaBurstAction::isUseful()
if (!target) if (!target)
return false; return false;
static const uint32 FLAME_SHOCK_SPELL_IDS[] = {8050, 8052, 8053, 10447, 10448, 29228, 25457, 49232, 49233}; static const uint32 FLAME_SHOCK_IDS[] = {8050, 8052, 8053, 10447, 10448, 29228, 25457, 49232, 49233};
ObjectGuid botGuid = bot->GetGUID(); ObjectGuid botGuid = botAI->GetBot()->GetGUID();
for (uint32 spellId : FLAME_SHOCK_SPELL_IDS) for (uint32 spellId : FLAME_SHOCK_IDS)
{ {
if (target->HasAura(spellId, botGuid)) if (target->HasAura(spellId, botGuid))
return true; return true;
@ -73,6 +69,7 @@ bool CastLavaBurstAction::isUseful()
// There is no existing code for guardians casting spells in the AC/Playerbots repo. // There is no existing code for guardians casting spells in the AC/Playerbots repo.
bool CastSpiritWalkAction::Execute(Event event) bool CastSpiritWalkAction::Execute(Event event)
{ {
Player* bot = botAI->GetBot();
constexpr uint32 SPIRIT_WOLF = 29264; constexpr uint32 SPIRIT_WOLF = 29264;
constexpr uint32 SPIRIT_WALK_SPELL = 58875; constexpr uint32 SPIRIT_WALK_SPELL = 58875;
@ -93,30 +90,28 @@ bool CastSpiritWalkAction::Execute(Event event)
// Set Strategy Assigned Totems (Actions) - First, it checks // Set Strategy Assigned Totems (Actions) - First, it checks
// the highest-rank spell the bot knows for each totem type, // the highest-rank spell the bot knows for each totem type,
// then adds it to the Call of the Elements bar. // then adds it to the Call of the Elements bar.
bool SetTotemAction::Execute(Event event) bool SetTotemAction::Execute(Event event)
{ {
const size_t spellIdsCount = sizeof(totemSpellIds) / sizeof(uint32);
if (spellIdsCount == 0)
return false; // early return
uint32 totemSpell = 0; uint32 totemSpell = 0;
for (size_t i = 0; i < totemSpellIdsCount; ++i)
// Iterate backwards to prioritize the highest-rank totem spell the bot knows
for (size_t i = spellIdsCount; i-- > 0;)
{ {
if (bot->HasSpell(totemSpellIds[i])) const uint32 spellId = totemSpellIds[i];
if (bot->HasSpell(spellId))
{ {
totemSpell = totemSpellIds[i]; totemSpell = spellId;
break; break;
} }
} }
if (!totemSpell) if (totemSpell == 0)
{
return false; return false;
}
if (const ActionButton* button = bot->GetActionButton(actionButtonId))
{
if (button->GetType() == ACTION_BUTTON_SPELL && button->GetAction() == totemSpell)
{
return false;
}
}
bot->addActionButton(actionButtonId, totemSpell, ACTION_BUTTON_SPELL); bot->addActionButton(actionButtonId, totemSpell, ACTION_BUTTON_SPELL);
return true; return true;

View File

@ -531,18 +531,12 @@ public:
class SetTotemAction : public Action class SetTotemAction : public Action
{ {
public: public:
// Template constructor: infers N (size of the id array) at compile time SetTotemAction(PlayerbotAI* botAI, std::string const totemName, const uint32 totemSpellIds[], int actionButtonId)
template <size_t N> : Action(botAI, "set " + totemName), totemSpellIds(totemSpellIds), actionButtonId(actionButtonId)
SetTotemAction(PlayerbotAI* botAI, std::string const& totemName, const uint32 (&ids)[N], int actionButtonId) {
: Action(botAI, "set " + totemName) }
, totemSpellIds(ids)
, totemSpellIdsCount(N)
, actionButtonId(actionButtonId)
{}
bool Execute(Event event) override; bool Execute(Event event) override;
uint32 const* totemSpellIds; uint32 const* totemSpellIds;
size_t totemSpellIdsCount;
int actionButtonId; int actionButtonId;
}; };

View File

@ -19,7 +19,6 @@ bool MainHandWeaponNoImbueTrigger::IsActive()
Item* const itemForSpell = bot->GetItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_MAINHAND); Item* const itemForSpell = bot->GetItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_MAINHAND);
if (!itemForSpell || itemForSpell->GetEnchantmentId(TEMP_ENCHANTMENT_SLOT)) if (!itemForSpell || itemForSpell->GetEnchantmentId(TEMP_ENCHANTMENT_SLOT))
return false; return false;
return true; return true;
} }
@ -28,20 +27,18 @@ bool OffHandWeaponNoImbueTrigger::IsActive()
Item* const itemForSpell = bot->GetItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_OFFHAND); Item* const itemForSpell = bot->GetItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_OFFHAND);
if (!itemForSpell) if (!itemForSpell)
return false; return false;
uint32 invType = itemForSpell->GetTemplate()->InventoryType; uint32 invType = itemForSpell->GetTemplate()->InventoryType;
bool allowedType = (invType == INVTYPE_WEAPON) || (invType == INVTYPE_WEAPONOFFHAND); bool allowedType = (invType == INVTYPE_WEAPON) || (invType == INVTYPE_WEAPONOFFHAND);
if (itemForSpell->GetEnchantmentId(TEMP_ENCHANTMENT_SLOT) || !allowedType) if (itemForSpell->GetEnchantmentId(TEMP_ENCHANTMENT_SLOT) ||
!allowedType)
return false; return false;
return true; return true;
} }
bool ShockTrigger::IsActive() bool ShockTrigger::IsActive()
{ {
return SpellTrigger::IsActive() && return SpellTrigger::IsActive() && !botAI->HasAura("flame shock", GetTarget(), false, true) &&
!botAI->HasAura("flame shock", GetTarget(), false, true) && !botAI->HasAura("frost shock", GetTarget(), false, true);
!botAI->HasAura("frost shock", GetTarget(), false, true);
} }
// Checks if the target's health is above 25%/1500 hp. Returns false if either are true. // Checks if the target's health is above 25%/1500 hp. Returns false if either are true.
@ -64,20 +61,13 @@ bool EarthShockExecuteTrigger::IsActive()
bool TotemTrigger::IsActive() bool TotemTrigger::IsActive()
{ {
return AI_VALUE(uint8, "attacker count") >= attackerCount && return AI_VALUE(uint8, "attacker count") >= attackerCount && !AI_VALUE2(bool, "has totem", name) &&
!AI_VALUE2(bool, "has totem", name) && !botAI->HasAura(name, bot);
!botAI->HasAura(name, bot);
} }
bool WaterWalkingTrigger::IsActive() bool WaterWalkingTrigger::IsActive() { return BuffTrigger::IsActive() && AI_VALUE2(bool, "swimming", "self target"); }
{
return BuffTrigger::IsActive() && AI_VALUE2(bool, "swimming", "self target");
}
bool WaterBreathingTrigger::IsActive() bool WaterBreathingTrigger::IsActive() { return BuffTrigger::IsActive() && AI_VALUE2(bool, "swimming", "self target"); }
{
return BuffTrigger::IsActive() && AI_VALUE2(bool, "swimming", "self target");
}
bool WaterWalkingOnPartyTrigger::IsActive() bool WaterWalkingOnPartyTrigger::IsActive()
{ {
@ -102,13 +92,14 @@ bool ElementalMasteryTrigger::IsActive()
// code exists in the AC/Playerbots repo for checking if a guardian's spell is on cooldown. // code exists in the AC/Playerbots repo for checking if a guardian's spell is on cooldown.
bool SpiritWalkTrigger::IsActive() bool SpiritWalkTrigger::IsActive()
{ {
constexpr uint32 SPIRIT_WOLF = 29264u; Player* bot = botAI->GetBot();
constexpr uint32 SPIRIT_WALK_SPELL_ID = 58875u; constexpr uint32 SPIRIT_WOLF = 29264;
constexpr int COOLDOWN_IN_SECONDS = 32; constexpr uint32 SPIRIT_WALK = 58875;
constexpr int COOLDOWN_SECONDS = 32;
time_t now = time(nullptr); time_t now = time(nullptr);
if ((now - lastSpiritWalkTime) < COOLDOWN_IN_SECONDS) if ((now - lastSpiritWalkTime) < COOLDOWN_SECONDS)
return false; return false;
for (Unit* unit : bot->m_Controlled) for (Unit* unit : bot->m_Controlled)
@ -116,14 +107,13 @@ bool SpiritWalkTrigger::IsActive()
Creature* wolf = dynamic_cast<Creature*>(unit); Creature* wolf = dynamic_cast<Creature*>(unit);
if (wolf && wolf->GetEntry() == SPIRIT_WOLF && wolf->IsAlive()) if (wolf && wolf->GetEntry() == SPIRIT_WOLF && wolf->IsAlive())
{ {
if (!bot->HasAura(SPIRIT_WALK_SPELL_ID)) if (!bot->HasAura(SPIRIT_WALK))
{ {
lastSpiritWalkTime = now; lastSpiritWalkTime = now;
return true; return true;
} }
} }
} }
return false; return false;
} }
@ -137,10 +127,8 @@ bool CallOfTheElementsTrigger::IsActive()
} }
int emptyCount = 0; int emptyCount = 0;
static const uint8 slots[] = { static const uint8 slots[] = {SUMMON_SLOT_TOTEM_EARTH, SUMMON_SLOT_TOTEM_FIRE, SUMMON_SLOT_TOTEM_WATER,
SUMMON_SLOT_TOTEM_EARTH, SUMMON_SLOT_TOTEM_FIRE, SUMMON_SLOT_TOTEM_AIR};
SUMMON_SLOT_TOTEM_WATER, SUMMON_SLOT_TOTEM_AIR
};
for (uint8 slot : slots) for (uint8 slot : slots)
{ {
@ -185,11 +173,13 @@ bool CallOfTheElementsTrigger::IsActive()
// 5. Finally, if any totem summon slot is not empty, the trigger will fire. // 5. Finally, if any totem summon slot is not empty, the trigger will fire.
bool TotemicRecallTrigger::IsActive() bool TotemicRecallTrigger::IsActive()
{ {
Player* bot = botAI->GetBot();
if (!bot->HasSpell(SPELL_TOTEMIC_RECALL)) if (!bot->HasSpell(SPELL_TOTEMIC_RECALL))
return false; return false;
Map* map = bot->GetMap(); Map* map = bot->GetMap();
if (map && map->IsDungeon()) if (map->IsDungeon())
{ {
InstanceScript* instance = ((InstanceMap*)map)->GetInstanceScript(); InstanceScript* instance = ((InstanceMap*)map)->GetInstanceScript();
if (instance) if (instance)
@ -210,10 +200,8 @@ bool TotemicRecallTrigger::IsActive()
Player* member = ref->GetSource(); Player* member = ref->GetSource();
if (!member) if (!member)
continue; continue;
if (member->IsInCombat()) if (member->IsInCombat())
return false; return false;
Pet* pet = member->GetPet(); Pet* pet = member->GetPet();
if (pet && pet->IsInCombat()) if (pet && pet->IsInCombat())
return false; return false;
@ -226,9 +214,7 @@ bool TotemicRecallTrigger::IsActive()
Creature* totem = bot->GetMap()->GetCreature(guid); Creature* totem = bot->GetMap()->GetCreature(guid);
uint32 currentSpell = 0; uint32 currentSpell = 0;
if (totem) if (totem)
{
currentSpell = totem->GetUInt32Value(UNIT_CREATED_BY_SPELL); currentSpell = totem->GetUInt32Value(UNIT_CREATED_BY_SPELL);
}
for (size_t i = 0; i < MANA_TIDE_TOTEM_COUNT; ++i) for (size_t i = 0; i < MANA_TIDE_TOTEM_COUNT; ++i)
{ {
@ -243,9 +229,7 @@ bool TotemicRecallTrigger::IsActive()
Creature* totem = bot->GetMap()->GetCreature(guid); Creature* totem = bot->GetMap()->GetCreature(guid);
uint32 currentSpell = 0; uint32 currentSpell = 0;
if (totem) if (totem)
{
currentSpell = totem->GetUInt32Value(UNIT_CREATED_BY_SPELL); currentSpell = totem->GetUInt32Value(UNIT_CREATED_BY_SPELL);
}
for (size_t i = 0; i < FIRE_ELEMENTAL_TOTEM_COUNT; ++i) for (size_t i = 0; i < FIRE_ELEMENTAL_TOTEM_COUNT; ++i)
{ {
@ -261,8 +245,8 @@ bool TotemicRecallTrigger::IsActive()
} }
// Find the active totem strategy for this slot, and return the highest-rank spellId the bot knows for it // Find the active totem strategy for this slot, and return the highest-rank spellId the bot knows for it
static uint32 GetRequiredTotemSpellId(PlayerbotAI* ai, const char* strategies[], static uint32 GetRequiredTotemSpellId(PlayerbotAI* ai, const char* strategies[], const uint32* spellList[],
const uint32* spellList[], const size_t spellCounts[], size_t numStrategies) const size_t spellCounts[], size_t numStrategies)
{ {
Player* bot = ai->GetBot(); Player* bot = ai->GetBot();
for (size_t i = 0; i < numStrategies; ++i) for (size_t i = 0; i < numStrategies; ++i)
@ -273,13 +257,10 @@ static uint32 GetRequiredTotemSpellId(PlayerbotAI* ai, const char* strategies[],
for (size_t j = 0; j < spellCounts[i]; ++j) for (size_t j = 0; j < spellCounts[i]; ++j)
{ {
if (bot->HasSpell(spellList[i][j])) if (bot->HasSpell(spellList[i][j]))
{
return spellList[i][j]; return spellList[i][j];
}
} }
} }
} }
return 0; // No relevant strategy active, or bot doesn't know any rank return 0; // No relevant strategy active, or bot doesn't know any rank
} }
@ -289,11 +270,9 @@ static uint32 GetSummonedTotemSpellId(Player* bot, uint8 slot)
ObjectGuid guid = bot->m_SummonSlot[slot]; ObjectGuid guid = bot->m_SummonSlot[slot];
if (guid.IsEmpty()) if (guid.IsEmpty())
return 0; return 0;
Creature* totem = bot->GetMap()->GetCreature(guid); Creature* totem = bot->GetMap()->GetCreature(guid);
if (!totem) if (!totem)
return 0; return 0;
return totem->GetUInt32Value(UNIT_CREATED_BY_SPELL); return totem->GetUInt32Value(UNIT_CREATED_BY_SPELL);
} }
@ -309,10 +288,8 @@ bool NoEarthTotemTrigger::IsActive()
if (!guid.IsEmpty()) if (!guid.IsEmpty())
{ {
totem = bot->GetMap()->GetCreature(guid); totem = bot->GetMap()->GetCreature(guid);
if (totem) if (totem)
{
currentSpell = totem->GetUInt32Value(UNIT_CREATED_BY_SPELL); currentSpell = totem->GetUInt32Value(UNIT_CREATED_BY_SPELL);
}
} }
// Define supported earth totem strategies for this slot: // Define supported earth totem strategies for this slot:
@ -320,18 +297,16 @@ bool NoEarthTotemTrigger::IsActive()
static const uint32* spells[] = {STRENGTH_OF_EARTH_TOTEM, STONESKIN_TOTEM, TREMOR_TOTEM, EARTHBIND_TOTEM}; static const uint32* spells[] = {STRENGTH_OF_EARTH_TOTEM, STONESKIN_TOTEM, TREMOR_TOTEM, EARTHBIND_TOTEM};
static const size_t counts[] = {STRENGTH_OF_EARTH_TOTEM_COUNT, STONESKIN_TOTEM_COUNT, TREMOR_TOTEM_COUNT, static const size_t counts[] = {STRENGTH_OF_EARTH_TOTEM_COUNT, STONESKIN_TOTEM_COUNT, TREMOR_TOTEM_COUNT,
EARTHBIND_TOTEM_COUNT}; EARTHBIND_TOTEM_COUNT};
uint32 requiredSpell = GetRequiredTotemSpellId(botAI, names, spells, counts, 4); uint32 requiredSpell = GetRequiredTotemSpellId(botAI, names, spells, counts, 4);
// EXCEPTION: If Stoneclaw Totem is out and in range, consider the slot "occupied" (do not fire the trigger) // EXCEPTION: If Stoneclaw Totem is out and in range, consider the slot "occupied" (do not fire the trigger)
for (size_t i = 0; i < STONECLAW_TOTEM_COUNT; ++i) for (size_t i = 0; i < STONECLAW_TOTEM_COUNT; ++i)
{
if (currentSpell == STONECLAW_TOTEM[i] && totem && totem->GetDistance(bot) <= 30.0f) if (currentSpell == STONECLAW_TOTEM[i] && totem && totem->GetDistance(bot) <= 30.0f)
return false; return false;
}
// If no relevant strategy, only care if the slot is empty or totem is too far away // If no relevant strategy, only care if the slot is empty or totem is too far away
if (!requiredSpell) if (!requiredSpell)
return guid.IsEmpty() || !totem || totem->GetDistance(bot) > 30.0f; return guid.IsEmpty() || !totem || totem->GetDistance(bot) > 30.0f;
// Fire if slot is empty or wrong totem or totem is too far away // Fire if slot is empty or wrong totem or totem is too far away
@ -350,10 +325,8 @@ bool NoFireTotemTrigger::IsActive()
if (!guid.IsEmpty()) if (!guid.IsEmpty())
{ {
totem = bot->GetMap()->GetCreature(guid); totem = bot->GetMap()->GetCreature(guid);
if (totem) if (totem)
{
currentSpell = totem->GetUInt32Value(UNIT_CREATED_BY_SPELL); currentSpell = totem->GetUInt32Value(UNIT_CREATED_BY_SPELL);
}
} }
// Define supported fire totem strategies for this slot: // Define supported fire totem strategies for this slot:
@ -366,11 +339,9 @@ bool NoFireTotemTrigger::IsActive()
uint32 requiredSpell = GetRequiredTotemSpellId(botAI, names, spells, counts, 5); uint32 requiredSpell = GetRequiredTotemSpellId(botAI, names, spells, counts, 5);
// EXCEPTION: If Fire Elemental is out and in range, consider the slot "occupied" (do not fire the trigger) // EXCEPTION: If Fire Elemental is out and in range, consider the slot "occupied" (do not fire the trigger)
for (size_t i = 0; i < FIRE_ELEMENTAL_TOTEM_COUNT; ++i) for (size_t i = 0; i < FIRE_ELEMENTAL_TOTEM_COUNT; ++i)
{
if (currentSpell == FIRE_ELEMENTAL_TOTEM[i] && totem && totem->GetDistance(bot) <= 30.0f) if (currentSpell == FIRE_ELEMENTAL_TOTEM[i] && totem && totem->GetDistance(bot) <= 30.0f)
return false; return false;
}
// If no relevant strategy, only care if the slot is empty or totem is too far away // If no relevant strategy, only care if the slot is empty or totem is too far away
if (!requiredSpell) if (!requiredSpell)
@ -393,9 +364,7 @@ bool NoWaterTotemTrigger::IsActive()
{ {
totem = bot->GetMap()->GetCreature(guid); totem = bot->GetMap()->GetCreature(guid);
if (totem) if (totem)
{
currentSpell = totem->GetUInt32Value(UNIT_CREATED_BY_SPELL); currentSpell = totem->GetUInt32Value(UNIT_CREATED_BY_SPELL);
}
} }
// Define supported water totem strategies for this slot: // Define supported water totem strategies for this slot:
@ -407,17 +376,13 @@ bool NoWaterTotemTrigger::IsActive()
uint32 requiredSpell = GetRequiredTotemSpellId(botAI, names, spells, counts, 4); uint32 requiredSpell = GetRequiredTotemSpellId(botAI, names, spells, counts, 4);
// EXCEPTION: If Mana Tide is out and in range, consider the slot "occupied" (do not fire the trigger) // EXCEPTION: If Mana Tide is out and in range, consider the slot "occupied" (do not fire the trigger)
for (size_t i = 0; i < MANA_TIDE_TOTEM_COUNT; ++i) for (size_t i = 0; i < MANA_TIDE_TOTEM_COUNT; ++i)
{
if (currentSpell == MANA_TIDE_TOTEM[i] && totem && totem->GetDistance(bot) <= 30.0f) if (currentSpell == MANA_TIDE_TOTEM[i] && totem && totem->GetDistance(bot) <= 30.0f)
return false; return false;
}
// If no relevant strategy, only care if the slot is empty or totem is too far away // If no relevant strategy, only care if the slot is empty or totem is too far away
if (!requiredSpell) if (!requiredSpell)
{
return guid.IsEmpty() || !totem || totem->GetDistance(bot) > 30.0f; return guid.IsEmpty() || !totem || totem->GetDistance(bot) > 30.0f;
}
// Fire if slot is empty or wrong totem or totem is too far away // Fire if slot is empty or wrong totem or totem is too far away
return !currentSpell || currentSpell != requiredSpell || !totem || totem->GetDistance(bot) > 30.0f; return !currentSpell || currentSpell != requiredSpell || !totem || totem->GetDistance(bot) > 30.0f;
@ -436,9 +401,7 @@ bool NoAirTotemTrigger::IsActive()
{ {
totem = bot->GetMap()->GetCreature(guid); totem = bot->GetMap()->GetCreature(guid);
if (totem) if (totem)
{
currentSpell = totem->GetUInt32Value(UNIT_CREATED_BY_SPELL); currentSpell = totem->GetUInt32Value(UNIT_CREATED_BY_SPELL);
}
} }
// Define supported air totem strategies for this slot: // Define supported air totem strategies for this slot:
@ -451,9 +414,7 @@ bool NoAirTotemTrigger::IsActive()
// If no relevant strategy, only care if the slot is empty or totem is too far away // If no relevant strategy, only care if the slot is empty or totem is too far away
if (!requiredSpell) if (!requiredSpell)
{
return guid.IsEmpty() || !totem || totem->GetDistance(bot) > 30.0f; return guid.IsEmpty() || !totem || totem->GetDistance(bot) > 30.0f;
}
// Fire if slot is empty or wrong totem or totem is too far away // Fire if slot is empty or wrong totem or totem is too far away
return !currentSpell || currentSpell != requiredSpell || !totem || totem->GetDistance(bot) > 30.0f; return !currentSpell || currentSpell != requiredSpell || !totem || totem->GetDistance(bot) > 30.0f;
@ -461,26 +422,30 @@ bool NoAirTotemTrigger::IsActive()
bool SetTotemTrigger::IsActive() bool SetTotemTrigger::IsActive()
{ {
uint32 highestKnownSpell = 0; if (!bot->HasSpell(SPELL_CALL_OF_THE_ELEMENTS))
for (size_t i = 0; i < totemSpellIdsCount; ++i) return false;
{
const uint32 spellId = totemSpellIds[i];
if (bot->HasSpell(spellId))
{
highestKnownSpell = spellId;
break;
}
}
if (!highestKnownSpell) if (!bot->HasSpell(requiredSpellId))
return false; return false;
ActionButton const* button = bot->GetActionButton(actionButtonId); ActionButton const* button = bot->GetActionButton(actionButtonId);
if (!button || button->GetType() != ACTION_BUTTON_SPELL || button->GetAction() == 0) if (!button || button->GetType() != ACTION_BUTTON_SPELL || button->GetAction() == 0)
return true; return true;
if (button->GetAction() != highestKnownSpell) const size_t totemSpellIdsCount = sizeof(totemSpellIds) / sizeof(uint32);
return true; if (totemSpellIdsCount == 0)
{
return false;
}
return false; for (int i = (int)totemSpellIdsCount - 1; i >= 0; --i)
{
const uint32 spellId = totemSpellIds[i];
if (bot->HasSpell(spellId))
{
return button->GetAction() != spellId;
}
}
return false;
} }

View File

@ -359,22 +359,17 @@ public:
class SetTotemTrigger : public Trigger class SetTotemTrigger : public Trigger
{ {
public: public:
// Template constructor: infers N (size of the id array) at compile time SetTotemTrigger(PlayerbotAI* ai, std::string const spellName, const uint32 requiredSpellId,
template <size_t N> const uint32 totemSpellIds[], int actionButtonId)
SetTotemTrigger(PlayerbotAI* ai, std::string const& spellName, uint32 requiredSpellId, : Trigger(ai, "set " + spellName),
const uint32 (&ids)[N], int actionButtonId) requiredSpellId(requiredSpellId),
: Trigger(ai, "set " + spellName) totemSpellIds(totemSpellIds),
, requiredSpellId(requiredSpellId) actionButtonId(actionButtonId) {}
, totemSpellIds(ids)
, totemSpellIdsCount(N)
, actionButtonId(actionButtonId)
{}
bool IsActive() override; bool IsActive() override;
private: private:
uint32 requiredSpellId; uint32 requiredSpellId;
uint32 const* totemSpellIds; uint32 const* totemSpellIds;
size_t totemSpellIdsCount;
int actionButtonId; int actionButtonId;
}; };

View File

@ -4,6 +4,7 @@
*/ */
#include "StatsValues.h" #include "StatsValues.h"
#include "Playerbots.h" #include "Playerbots.h"
#include "ServerFacade.h" #include "ServerFacade.h"
@ -119,10 +120,6 @@ bool HasManaValue::Calculate()
if (!target) if (!target)
return false; return false;
constexpr uint32 PRIEST_SPIRIT_OF_REDEMPTION_SPELL_ID = 20711u;
if (target->HasAura(PRIEST_SPIRIT_OF_REDEMPTION_SPELL_ID))
return false;
return target->GetPower(POWER_MANA); return target->GetPower(POWER_MANA);
} }