Warlock overhaul (#1397)

This is a complete overhaul of the warlock class, making 3 new strategies (affliction, demonology, and destruction), as well as finishing the warlock tank strategy (shadow ward and searing pain). It also includes a soulstone fix, where the bots can change who they soulstone based on the non-combat strategies you set for them. It also includes a self-resurrect action and trigger that allows the bots to resurrect using a soulstone or reincarnation. Many other skills were added to finish out the warlock skillset.
This commit is contained in:
ThePenguinMan96
2025-06-27 11:00:38 -07:00
committed by GitHub
parent cf8253579e
commit 00cc2468f1
29 changed files with 1762 additions and 573 deletions

View File

@@ -5,23 +5,204 @@
#include "WarlockActions.h"
#include <string>
#include <vector>
#include "Event.h"
#include "Item.h"
#include "ObjectGuid.h"
#include "Player.h"
#include "PlayerbotAI.h"
#include "Playerbots.h"
#include "ServerFacade.h"
#include "Unit.h"
bool CastDrainSoulAction::isUseful() { return AI_VALUE2(uint32, "item count", "soul shard") < 10; }
Value<Unit*>* CastBanishAction::GetTargetValue() { return context->GetValue<Unit*>("cc target", "banish"); }
bool CastBanishAction::Execute(Event event) { return botAI->CastSpell("banish", GetTarget()); }
Value<Unit*>* CastFearOnCcAction::GetTargetValue() { return context->GetValue<Unit*>("cc target", "fear"); }
bool CastFearOnCcAction::Execute(Event event) { return botAI->CastSpell("fear", GetTarget()); }
bool CastFearOnCcAction::isPossible() { return true; }
bool CastFearOnCcAction::isUseful() { return true; }
// Checks if the bot has less than 32 soul shards, and if so, allows casting Drain Soul
bool CastDrainSoulAction::isUseful() { return AI_VALUE2(uint32, "item count", "soul shard") < 32; }
// Checks if the bot's health is above a certain threshold, and if so, allows casting Life Tap
bool CastLifeTapAction::isUseful() { return AI_VALUE2(uint8, "health", "self target") > sPlayerbotAIConfig->lowHealth; }
Unit* UseSoulstoneAction::GetTarget() { return botAI->GetMaster(); }
// Checks if the target marked with the moon icon can be banished
bool CastBanishOnCcAction::isPossible()
{
Unit* target = GetTarget();
if (!target)
return false;
// Only possible on elementals or demons
uint32 creatureType = target->GetCreatureType();
if (creatureType != CREATURE_TYPE_DEMON && creatureType != CREATURE_TYPE_ELEMENTAL)
return false;
// Use base class to check spell available, range, etc
return CastCrowdControlSpellAction::isPossible();
}
// Checks if the target marked with the moon icon can be feared
bool CastFearOnCcAction::isPossible()
{
Unit* target = GetTarget();
if (!target)
return false;
// Fear cannot be cast on mechanical or undead creatures
uint32 creatureType = target->GetCreatureType();
if (creatureType == CREATURE_TYPE_MECHANICAL || creatureType == CREATURE_TYPE_UNDEAD)
return false;
// Use base class to check spell available, range, etc
return CastCrowdControlSpellAction::isPossible();
}
// Checks if the enemies are close enough to use Shadowflame
bool CastShadowflameAction::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, 7.0f); // 7 yard cone
return facingTarget && targetClose;
}
// Checks if the bot knows Seed of Corruption, and prevents the use of Rain of Fire if it does
bool CastRainOfFireAction::isUseful()
{
Unit* target = GetTarget();
if (!target)
return false;
if (bot->HasSpell(27243) || bot->HasSpell(47835) || bot->HasSpell(47836)) // Seed of Corruption spell IDs
return false;
return true;
}
// Checks if the enemies are close enough to use Hellfire
bool CastHellfireAction::isUseful()
{
Unit* target = AI_VALUE(Unit*, "current target");
if (!target)
return false;
return bot->IsWithinCombatRange(target, 5.0f); // 5 yard AoE radius
}
// Checks if the "meta melee aoe" strategy is active, OR if the bot is in melee range of the target
bool CastImmolationAuraAction::isUseful()
{
if (botAI->HasStrategy("meta melee", BOT_STATE_COMBAT))
return true;
Unit* target = AI_VALUE(Unit*, "current target");
if (!target)
return false;
if (!bot->HasAura(47241)) // 47241 is Metamorphosis spell ID (WotLK)
return false;
return bot->IsWithinCombatRange(target, 5.0f); // 5 yard AoE radius
}
// Checks if the "warlock tank" strategy is active, and if so, prevents the use of Soulshatter
bool CastSoulshatterAction::isUseful()
{
if (botAI->HasStrategy("tank", BOT_STATE_COMBAT))
return false;
return true;
}
// Checks if the target has a soulstone aura
static bool HasSoulstoneAura(Unit* unit)
{
static const std::vector<uint32> soulstoneAuraIds = {20707, 20762, 20763, 20764, 20765, 27239, 47883};
for (uint32 spellId : soulstoneAuraIds)
if (unit->HasAura(spellId))
return true;
return false;
}
// Use the soulstone item on the bot itself with nc strategy "ss self"
bool UseSoulstoneSelfAction::Execute(Event event)
{
std::vector<Item*> items = AI_VALUE2(std::vector<Item*>, "inventory items", "soulstone");
if (items.empty())
return false;
if (HasSoulstoneAura(bot))
return false;
bot->SetSelection(bot->GetGUID());
return UseItem(items[0], ObjectGuid::Empty, nullptr, bot);
}
// Use the soulstone item on the bot's master with nc strategy "ss master"
bool UseSoulstoneMasterAction::Execute(Event event)
{
std::vector<Item*> items = AI_VALUE2(std::vector<Item*>, "inventory items", "soulstone");
if (items.empty())
return false;
Player* master = botAI->GetMaster();
if (!master || HasSoulstoneAura(master))
return false;
bot->SetSelection(master->GetGUID());
return UseItem(items[0], ObjectGuid::Empty, nullptr, master);
}
// Use the soulstone item on a tank in the group with nc strategy "ss tank"
bool UseSoulstoneTankAction::Execute(Event event)
{
std::vector<Item*> items = AI_VALUE2(std::vector<Item*>, "inventory items", "soulstone");
if (items.empty())
return false;
Player* tank = nullptr;
Group* group = bot->GetGroup();
if (group)
{
for (GroupReference* gref = group->GetFirstMember(); gref; gref = gref->next())
{
Player* member = gref->GetSource();
if (member && member->IsAlive() && botAI->IsTank(member) && !HasSoulstoneAura(member))
{
tank = member;
break;
}
}
}
if (!tank)
return false;
bot->SetSelection(tank->GetGUID());
return UseItem(items[0], ObjectGuid::Empty, nullptr, tank);
}
// Use the soulstone item on a healer in the group with nc strategy "ss healer"
bool UseSoulstoneHealerAction::Execute(Event event)
{
std::vector<Item*> items = AI_VALUE2(std::vector<Item*>, "inventory items", "soulstone");
if (items.empty())
return false;
Player* healer = nullptr;
Group* group = bot->GetGroup();
if (group)
{
for (GroupReference* gref = group->GetFirstMember(); gref; gref = gref->next())
{
Player* member = gref->GetSource();
if (member && member->IsAlive() && botAI->IsHeal(member) && !HasSoulstoneAura(member))
{
healer = member;
break;
}
}
}
if (!healer)
return false;
bot->SetSelection(healer->GetGUID());
return UseItem(items[0], ObjectGuid::Empty, nullptr, healer);
}