mirror of
https://github.com/mod-playerbots/mod-playerbots.git
synced 2026-01-13 09:07:19 +00:00
Compare commits
46 Commits
directory_
...
revert-136
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3a756f5840 | ||
|
|
499a80db14 | ||
|
|
dfa87faf5e | ||
|
|
d15ec79252 | ||
|
|
cfc8e85706 | ||
|
|
fc6309c521 | ||
|
|
9986469042 | ||
|
|
db9bcb97ba | ||
|
|
3c05e47cb2 | ||
|
|
89556dafa1 | ||
|
|
b726f3dfcb | ||
|
|
45f98e52b4 | ||
|
|
f8660bc939 | ||
|
|
c24b7a7bb3 | ||
|
|
1195e67c97 | ||
|
|
faee49beaa | ||
|
|
d87d5a46c7 | ||
|
|
c34617e133 | ||
|
|
a4ff66f12a | ||
|
|
e68c5a76c6 | ||
|
|
5910866362 | ||
|
|
c5b185455c | ||
|
|
e1a8bd66c5 | ||
|
|
f7bd9ae5c1 | ||
|
|
b5426152fb | ||
|
|
f3ee83d471 | ||
|
|
b2534691b8 | ||
|
|
36adb62f2a | ||
|
|
fd99b373c2 | ||
|
|
e2b5ab766d | ||
|
|
9641092078 | ||
|
|
aaad67f7b9 | ||
|
|
7901f5da19 | ||
|
|
4f26a8a09b | ||
|
|
f17ec36cde | ||
|
|
3881940f33 | ||
|
|
dc703cc897 | ||
|
|
97f582b9b1 | ||
|
|
95c572bf48 | ||
|
|
43b0852438 | ||
|
|
9ca326c8cb | ||
|
|
f365b79e96 | ||
|
|
e104c5f8be | ||
|
|
0c6f656236 | ||
|
|
4603dbaf35 | ||
|
|
e48c3351d3 |
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
||||
DROP TABLE IF EXISTS `playerbot_account_keys`;
|
||||
DROP TABLE IF EXISTS `playerbots_account_keys`;
|
||||
|
||||
CREATE TABLE `playerbot_account_keys` (
|
||||
CREATE TABLE `playerbots_account_keys` (
|
||||
`account_id` INT PRIMARY KEY,
|
||||
`security_key` VARCHAR(255) NOT NULL,
|
||||
`created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||
@@ -1,6 +1,6 @@
|
||||
DROP TABLE IF EXISTS `playerbot_account_links`;
|
||||
DROP TABLE IF EXISTS `playerbots_account_links`;
|
||||
|
||||
CREATE TABLE `playerbot_account_links` (
|
||||
CREATE TABLE `playerbots_account_links` (
|
||||
`id` INT AUTO_INCREMENT PRIMARY KEY,
|
||||
`account_id` INT NOT NULL,
|
||||
`linked_account_id` INT NOT NULL,
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
15
data/sql/playerbots/updates/db_playerbots/2025_05_09_00.sql
Normal file
15
data/sql/playerbots/updates/db_playerbots/2025_05_09_00.sql
Normal file
@@ -0,0 +1,15 @@
|
||||
DROP TABLE IF EXISTS `playerbot_account_keys`;
|
||||
CREATE TABLE IF NOT EXISTS `playerbots_account_keys` (
|
||||
`account_id` INT PRIMARY KEY,
|
||||
`security_key` VARCHAR(255) NOT NULL,
|
||||
`created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||
) ENGINE=INNODB DEFAULT CHARSET=latin1;
|
||||
|
||||
DROP TABLE IF EXISTS `playerbot_account_links`;
|
||||
CREATE TABLE IF NOT EXISTS `playerbots_account_links` (
|
||||
`id` INT AUTO_INCREMENT PRIMARY KEY,
|
||||
`account_id` INT NOT NULL,
|
||||
`linked_account_id` INT NOT NULL,
|
||||
`created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
UNIQUE KEY `account_link` (`account_id`, `linked_account_id`)
|
||||
) ENGINE=INNODB DEFAULT CHARSET=latin1;
|
||||
@@ -531,7 +531,7 @@ void AiFactory::AddDefaultNonCombatStrategies(Player* player, PlayerbotAI* const
|
||||
nonCombatEngine->addStrategiesNoInit("bthreat", "tank assist", "barmor", nullptr);
|
||||
if (player->GetLevel() >= 20)
|
||||
{
|
||||
nonCombatEngine->addStrategy("bstats", false);
|
||||
nonCombatEngine->addStrategy("bhealth", false);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -305,6 +305,49 @@ ItemIds ChatHelper::parseItems(std::string const text)
|
||||
return itemIds;
|
||||
}
|
||||
|
||||
ItemWithRandomProperty ChatHelper::parseItemWithRandomProperty(std::string const text)
|
||||
{
|
||||
ItemWithRandomProperty res;
|
||||
|
||||
size_t itemStart = text.find("Hitem:");
|
||||
if (itemStart == std::string::npos)
|
||||
return res;
|
||||
|
||||
itemStart += 6;
|
||||
if (itemStart >= text.length())
|
||||
return res;
|
||||
|
||||
size_t colonPos = text.find(':', itemStart);
|
||||
if (colonPos == std::string::npos)
|
||||
return res;
|
||||
|
||||
std::string itemIdStr = text.substr(itemStart, colonPos - itemStart);
|
||||
res.itemId = atoi(itemIdStr.c_str());
|
||||
|
||||
std::vector<std::string> params;
|
||||
size_t currentPos = colonPos + 1;
|
||||
|
||||
while (currentPos < text.length()) {
|
||||
size_t nextColon = text.find(':', currentPos);
|
||||
if (nextColon == std::string::npos) {
|
||||
size_t hTag = text.find("|h", currentPos);
|
||||
if (hTag != std::string::npos) {
|
||||
params.push_back(text.substr(currentPos, hTag - currentPos));
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
params.push_back(text.substr(currentPos, nextColon - currentPos));
|
||||
currentPos = nextColon + 1;
|
||||
}
|
||||
|
||||
if (params.size() >= 6) {
|
||||
res.randomPropertyId = atoi(params[5].c_str());
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
std::string const ChatHelper::FormatQuest(Quest const* quest)
|
||||
{
|
||||
if (!quest)
|
||||
@@ -382,7 +425,7 @@ std::string const ChatHelper::FormatSpell(SpellInfo const* spellInfo)
|
||||
std::string const ChatHelper::FormatItem(ItemTemplate const* proto, uint32 count, uint32 total)
|
||||
{
|
||||
char color[32];
|
||||
sprintf(color, "%x", ItemQualityColors[proto->Quality]);
|
||||
snprintf(color, sizeof(color), "%x", ItemQualityColors[proto->Quality]);
|
||||
|
||||
std::string itemName;
|
||||
const ItemLocale* locale = sObjectMgr->GetItemLocale(proto->ItemId);
|
||||
@@ -409,7 +452,7 @@ std::string const ChatHelper::FormatItem(ItemTemplate const* proto, uint32 count
|
||||
std::string const ChatHelper::FormatQItem(uint32 itemId)
|
||||
{
|
||||
char color[32];
|
||||
sprintf(color, "%x", ItemQualityColors[0]);
|
||||
snprintf(color, sizeof(color), "%x", ItemQualityColors[0]);
|
||||
|
||||
std::ostringstream out;
|
||||
out << "|c" << color << "|Hitem:" << itemId << ":0:0:0:0:0:0:0"
|
||||
|
||||
@@ -25,6 +25,11 @@ struct ItemTemplate;
|
||||
typedef std::set<uint32> ItemIds;
|
||||
typedef std::set<uint32> SpellIds;
|
||||
|
||||
struct ItemWithRandomProperty {
|
||||
uint32 itemId{0};
|
||||
int32 randomPropertyId{0};
|
||||
};
|
||||
|
||||
class ChatHelper : public PlayerbotAIAware
|
||||
{
|
||||
public:
|
||||
@@ -33,6 +38,7 @@ public:
|
||||
static std::string const formatMoney(uint32 copper);
|
||||
static uint32 parseMoney(std::string const text);
|
||||
static ItemIds parseItems(std::string const text);
|
||||
static ItemWithRandomProperty parseItemWithRandomProperty(std::string const text);
|
||||
uint32 parseSpell(std::string const text);
|
||||
static std::string parseValue(const std::string& type, const std::string& text);
|
||||
|
||||
|
||||
@@ -85,7 +85,7 @@ void LootObject::Refresh(Player* bot, ObjectGuid lootGUID)
|
||||
bool hasAnyQuestItems = false;
|
||||
|
||||
GameObjectQuestItemList const* items = sObjectMgr->GetGameObjectQuestItemList(go->GetEntry());
|
||||
for (int i = 0; i < MAX_GAMEOBJECT_QUEST_ITEMS; i++)
|
||||
for (size_t i = 0; i < MAX_GAMEOBJECT_QUEST_ITEMS; i++)
|
||||
{
|
||||
if (!items || i >= items->size())
|
||||
break;
|
||||
@@ -297,6 +297,11 @@ bool LootObject::IsLootPossible(Player* bot)
|
||||
return false;
|
||||
}
|
||||
|
||||
// Prevent bot from running to chests that are unlootable (e.g. Gunship Armory before completing the event)
|
||||
GameObject* go = botAI->GetGameObject(guid);
|
||||
if (go && go->HasFlag(GAMEOBJECT_FLAGS, GO_FLAG_INTERACT_COND | GO_FLAG_NOT_SELECTABLE))
|
||||
return false;
|
||||
|
||||
if (skillId == SKILL_NONE)
|
||||
return true;
|
||||
|
||||
|
||||
@@ -29,6 +29,7 @@ public:
|
||||
LootObject() : skillId(0), reqSkillValue(0), reqItem(0) {}
|
||||
LootObject(Player* bot, ObjectGuid guid);
|
||||
LootObject(LootObject const& other);
|
||||
LootObject& operator=(LootObject const& other) = default;
|
||||
|
||||
bool IsEmpty() { return !guid; }
|
||||
bool IsLootPossible(Player* bot);
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
#include "CreatureData.h"
|
||||
#include "EmoteAction.h"
|
||||
#include "Engine.h"
|
||||
#include "EventProcessor.h"
|
||||
#include "ExternalEventHelper.h"
|
||||
#include "GameObjectData.h"
|
||||
#include "GameTime.h"
|
||||
@@ -156,7 +157,7 @@ PlayerbotAI::PlayerbotAI(Player* bot)
|
||||
masterIncomingPacketHandlers.AddHandler(CMSG_GAMEOBJ_USE, "use game object");
|
||||
masterIncomingPacketHandlers.AddHandler(CMSG_AREATRIGGER, "area trigger");
|
||||
// masterIncomingPacketHandlers.AddHandler(CMSG_GAMEOBJ_USE, "use game object");
|
||||
masterIncomingPacketHandlers.AddHandler(CMSG_LOOT_ROLL, "loot roll");
|
||||
// masterIncomingPacketHandlers.AddHandler(CMSG_LOOT_ROLL, "loot roll");
|
||||
masterIncomingPacketHandlers.AddHandler(CMSG_GOSSIP_HELLO, "gossip hello");
|
||||
masterIncomingPacketHandlers.AddHandler(CMSG_QUESTGIVER_HELLO, "gossip hello");
|
||||
masterIncomingPacketHandlers.AddHandler(CMSG_ACTIVATETAXI, "activate taxi");
|
||||
@@ -796,6 +797,7 @@ bool PlayerbotAI::IsAllowedCommand(std::string const text)
|
||||
unsecuredCommands.insert("sendmail");
|
||||
unsecuredCommands.insert("invite");
|
||||
unsecuredCommands.insert("leave");
|
||||
unsecuredCommands.insert("lfg");
|
||||
unsecuredCommands.insert("rpg status");
|
||||
}
|
||||
|
||||
@@ -1423,12 +1425,21 @@ void PlayerbotAI::DoNextAction(bool min)
|
||||
master = newMaster;
|
||||
botAI->SetMaster(newMaster);
|
||||
botAI->ResetStrategies();
|
||||
botAI->ChangeStrategy("+follow", BOT_STATE_NON_COMBAT);
|
||||
|
||||
if (!bot->InBattleground())
|
||||
{
|
||||
botAI->ChangeStrategy("+follow", BOT_STATE_NON_COMBAT);
|
||||
|
||||
if (botAI->GetMaster() == botAI->GetGroupMaster())
|
||||
botAI->TellMaster("Hello, I follow you!");
|
||||
if (botAI->GetMaster() == botAI->GetGroupMaster())
|
||||
botAI->TellMaster("Hello, I follow you!");
|
||||
else
|
||||
botAI->TellMaster(!urand(0, 2) ? "Hello!" : "Hi!");
|
||||
}
|
||||
else
|
||||
botAI->TellMaster(!urand(0, 2) ? "Hello!" : "Hi!");
|
||||
{
|
||||
// we're in a battleground, stay with the pack and focus on objective
|
||||
botAI->ChangeStrategy("-follow", BOT_STATE_NON_COMBAT);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6270,3 +6281,23 @@ SpellFamilyNames PlayerbotAI::Class2SpellFamilyName(uint8 cls)
|
||||
}
|
||||
return SPELLFAMILY_GENERIC;
|
||||
}
|
||||
|
||||
void PlayerbotAI::AddTimedEvent(std::function<void()> callback, uint32 delayMs)
|
||||
{
|
||||
class LambdaEvent final : public BasicEvent
|
||||
{
|
||||
std::function<void()> _cb;
|
||||
public:
|
||||
explicit LambdaEvent(std::function<void()> cb) : _cb(std::move(cb)) {}
|
||||
bool Execute(uint64 /*execTime*/, uint32 /*diff*/) override
|
||||
{
|
||||
_cb();
|
||||
return true; // remove after execution
|
||||
}
|
||||
};
|
||||
|
||||
// Every Player already owns an EventMap called m_Events
|
||||
bot->m_Events.AddEvent(
|
||||
new LambdaEvent(std::move(callback)),
|
||||
bot->m_Events.CalculateTime(delayMs));
|
||||
}
|
||||
@@ -590,6 +590,9 @@ public:
|
||||
NewRpgStatistic rpgStatistic;
|
||||
std::unordered_set<uint32> lowPriorityQuest;
|
||||
|
||||
// Schedules a callback to run once after <delayMs> milliseconds.
|
||||
void AddTimedEvent(std::function<void()> callback, uint32 delayMs);
|
||||
|
||||
private:
|
||||
static void _fillGearScoreData(Player* player, Item* item, std::vector<uint32>* gearScore, uint32& twoHandScore,
|
||||
bool mixed = false);
|
||||
|
||||
@@ -82,6 +82,8 @@ bool PlayerbotAIConfig::Initialize()
|
||||
sitDelay = sConfigMgr->GetOption<int32>("AiPlayerbot.SitDelay", 30000);
|
||||
returnDelay = sConfigMgr->GetOption<int32>("AiPlayerbot.ReturnDelay", 7000);
|
||||
lootDelay = sConfigMgr->GetOption<int32>("AiPlayerbot.LootDelay", 1000);
|
||||
disabledWithoutRealPlayerLoginDelay = sConfigMgr->GetOption<int32>("AiPlayerbot.DisabledWithoutRealPlayerLoginDelay", 30);
|
||||
disabledWithoutRealPlayerLogoutDelay = sConfigMgr->GetOption<int32>("AiPlayerbot.DisabledWithoutRealPlayerLogoutDelay", 300);
|
||||
|
||||
farDistance = sConfigMgr->GetOption<float>("AiPlayerbot.FarDistance", 20.0f);
|
||||
sightDistance = sConfigMgr->GetOption<float>("AiPlayerbot.SightDistance", 75.0f);
|
||||
@@ -129,6 +131,7 @@ bool PlayerbotAIConfig::Initialize()
|
||||
allowAccountBots = sConfigMgr->GetOption<bool>("AiPlayerbot.AllowAccountBots", true);
|
||||
allowGuildBots = sConfigMgr->GetOption<bool>("AiPlayerbot.AllowGuildBots", true);
|
||||
allowTrustedAccountBots = sConfigMgr->GetOption<bool>("AiPlayerbot.AllowTrustedAccountBots", true);
|
||||
disabledWithoutRealPlayer = sConfigMgr->GetOption<bool>("AiPlayerbot.DisabledWithoutRealPlayer", false);
|
||||
randomBotGuildNearby = sConfigMgr->GetOption<bool>("AiPlayerbot.RandomBotGuildNearby", false);
|
||||
randomBotInvitePlayer = sConfigMgr->GetOption<bool>("AiPlayerbot.RandomBotInvitePlayer", false);
|
||||
inviteChat = sConfigMgr->GetOption<bool>("AiPlayerbot.InviteChat", false);
|
||||
@@ -153,7 +156,9 @@ bool PlayerbotAIConfig::Initialize()
|
||||
LoadList<std::vector<uint32>>(
|
||||
sConfigMgr->GetOption<std::string>("AiPlayerbot.RandomBotQuestIds", "7848,3802,5505,6502,7761"),
|
||||
randomBotQuestIds);
|
||||
|
||||
|
||||
LoadSet<std::set<uint32>>(sConfigMgr->GetOption<std::string>("AiPlayerbot.DisallowedGameObjects", "176213,17155"),
|
||||
disallowedGameObjects);
|
||||
botAutologin = sConfigMgr->GetOption<bool>("AiPlayerbot.BotAutologin", false);
|
||||
randomBotAutologin = sConfigMgr->GetOption<bool>("AiPlayerbot.RandomBotAutologin", true);
|
||||
minRandomBots = sConfigMgr->GetOption<int32>("AiPlayerbot.MinRandomBots", 50);
|
||||
@@ -307,11 +312,48 @@ bool PlayerbotAIConfig::Initialize()
|
||||
summonAtInnkeepersEnabled = sConfigMgr->GetOption<bool>("AiPlayerbot.SummonAtInnkeepersEnabled", true);
|
||||
randomBotMinLevel = sConfigMgr->GetOption<int32>("AiPlayerbot.RandomBotMinLevel", 1);
|
||||
randomBotMaxLevel = sConfigMgr->GetOption<int32>("AiPlayerbot.RandomBotMaxLevel", 80);
|
||||
if (randomBotMaxLevel > sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL))
|
||||
randomBotMaxLevel = sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL);
|
||||
randomBotLoginAtStartup = sConfigMgr->GetOption<bool>("AiPlayerbot.RandomBotLoginAtStartup", true);
|
||||
randomBotTeleLowerLevel = sConfigMgr->GetOption<int32>("AiPlayerbot.RandomBotTeleLowerLevel", 3);
|
||||
randomBotTeleHigherLevel = sConfigMgr->GetOption<int32>("AiPlayerbot.RandomBotTeleHigherLevel", 1);
|
||||
randomBotTeleLowerLevel = sConfigMgr->GetOption<int32>("AiPlayerbot.RandomBotTeleLowerLevel", 1);
|
||||
randomBotTeleHigherLevel = sConfigMgr->GetOption<int32>("AiPlayerbot.RandomBotTeleHigherLevel", 3);
|
||||
openGoSpell = sConfigMgr->GetOption<int32>("AiPlayerbot.OpenGoSpell", 6477);
|
||||
|
||||
// Zones for NewRpgStrategy teleportation brackets
|
||||
std::vector<uint32> zoneIds = {
|
||||
// Classic WoW - Low-level zones
|
||||
1, 12, 14, 85, 141, 215, 3430, 3524,
|
||||
// Classic WoW - Mid-level zones
|
||||
17, 38, 40, 130, 148, 3433, 3525,
|
||||
// Classic WoW - High-level zones
|
||||
10, 11, 44, 267, 331, 400, 406,
|
||||
// Classic WoW - Higher-level zones
|
||||
3, 8, 15, 16, 33, 45, 47, 51, 357, 405, 440,
|
||||
// Classic WoW - Top-level zones
|
||||
4, 28, 46, 139, 361, 490, 618, 1377,
|
||||
// The Burning Crusade - Zones
|
||||
3483, 3518, 3519, 3520, 3521, 3522, 3523, 4080,
|
||||
// Wrath of the Lich King - Zones
|
||||
65, 66, 67, 210, 394, 495, 2817, 3537, 3711, 4197
|
||||
};
|
||||
|
||||
for (uint32 zoneId : zoneIds)
|
||||
{
|
||||
std::string setting = "AiPlayerbot.ZoneBracket." + std::to_string(zoneId);
|
||||
std::string value = sConfigMgr->GetOption<std::string>(setting, "");
|
||||
|
||||
if (!value.empty())
|
||||
{
|
||||
size_t commaPos = value.find(',');
|
||||
if (commaPos != std::string::npos)
|
||||
{
|
||||
uint32 minLevel = atoi(value.substr(0, commaPos).c_str());
|
||||
uint32 maxLevel = atoi(value.substr(commaPos + 1).c_str());
|
||||
zoneBrackets[zoneId] = std::make_pair(minLevel, maxLevel);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
randomChangeMultiplier = sConfigMgr->GetOption<float>("AiPlayerbot.RandomChangeMultiplier", 1.0);
|
||||
|
||||
randomBotCombatStrategies = sConfigMgr->GetOption<std::string>("AiPlayerbot.RandomBotCombatStrategies", "-threat");
|
||||
@@ -331,6 +373,12 @@ bool PlayerbotAIConfig::Initialize()
|
||||
useFlyMountAtMinLevel = sConfigMgr->GetOption<int32>("AiPlayerbot.UseFlyMountAtMinLevel", 60);
|
||||
useFastFlyMountAtMinLevel = sConfigMgr->GetOption<int32>("AiPlayerbot.UseFastFlyMountAtMinLevel", 70);
|
||||
|
||||
// stagger bot flightpath takeoff
|
||||
delayMin = sConfigMgr->GetOption<uint32>("AiPlayerbot.BotTaxiDelayMinMs", 350u);
|
||||
delayMax = sConfigMgr->GetOption<uint32>("AiPlayerbot.BotTaxiDelayMaxMs", 5000u);
|
||||
gapMs = sConfigMgr->GetOption<uint32>("AiPlayerbot.BotTaxiGapMs", 200u);
|
||||
gapJitterMs = sConfigMgr->GetOption<uint32>("AiPlayerbot.BotTaxiGapJitterMs", 100u);
|
||||
|
||||
LOG_INFO("server.loading", "Loading TalentSpecs...");
|
||||
|
||||
for (uint32 cls = 1; cls < MAX_CLASSES; ++cls)
|
||||
@@ -466,6 +514,7 @@ bool PlayerbotAIConfig::Initialize()
|
||||
equipmentPersistence = sConfigMgr->GetOption<bool>("AiPlayerbot.EquipmentPersistence", false);
|
||||
equipmentPersistenceLevel = sConfigMgr->GetOption<int32>("AiPlayerbot.EquipmentPersistenceLevel", 80);
|
||||
groupInvitationPermission = sConfigMgr->GetOption<int32>("AiPlayerbot.GroupInvitationPermission", 1);
|
||||
keepAltsInGroup = sConfigMgr->GetOption<bool>("AiPlayerbot.KeepAltsInGroup", false);
|
||||
allowSummonInCombat = sConfigMgr->GetOption<bool>("AiPlayerbot.AllowSummonInCombat", true);
|
||||
allowSummonWhenMasterIsDead = sConfigMgr->GetOption<bool>("AiPlayerbot.AllowSummonWhenMasterIsDead", true);
|
||||
allowSummonWhenBotIsDead = sConfigMgr->GetOption<bool>("AiPlayerbot.AllowSummonWhenBotIsDead", true);
|
||||
@@ -483,7 +532,7 @@ bool PlayerbotAIConfig::Initialize()
|
||||
autoGearQualityLimit = sConfigMgr->GetOption<int32>("AiPlayerbot.AutoGearQualityLimit", 3);
|
||||
autoGearScoreLimit = sConfigMgr->GetOption<int32>("AiPlayerbot.AutoGearScoreLimit", 0);
|
||||
|
||||
playerbotsXPrate = sConfigMgr->GetOption<float>("AiPlayerbot.PlayerbotsXPRate", 1.0);
|
||||
randomBotXPRate = sConfigMgr->GetOption<float>("AiPlayerbot.RandomBotXPRate", 1.0);
|
||||
randomBotAllianceRatio = sConfigMgr->GetOption<int32>("AiPlayerbot.RandomBotAllianceRatio", 50);
|
||||
randomBotHordeRatio = sConfigMgr->GetOption<int32>("AiPlayerbot.RandomBotHordeRatio", 50);
|
||||
disableDeathKnightLogin = sConfigMgr->GetOption<bool>("AiPlayerbot.DisableDeathKnightLogin", 0);
|
||||
|
||||
@@ -55,6 +55,7 @@ public:
|
||||
bool IsInPvpProhibitedArea(uint32 id);
|
||||
|
||||
bool enabled;
|
||||
bool disabledWithoutRealPlayer;
|
||||
bool allowAccountBots, allowGuildBots, allowTrustedAccountBots;
|
||||
bool randomBotGuildNearby, randomBotInvitePlayer, inviteChat;
|
||||
uint32 globalCoolDown, reactDelay, maxWaitForMove, disableMoveSplinePath, maxMovementSearchTime, expireActionTime,
|
||||
@@ -71,6 +72,7 @@ public:
|
||||
float maxAoeAvoidRadius;
|
||||
std::set<uint32> aoeAvoidSpellWhitelist;
|
||||
bool tellWhenAvoidAoe;
|
||||
std::set<uint32> disallowedGameObjects;
|
||||
|
||||
uint32 openGoSpell;
|
||||
bool randomBotAutologin;
|
||||
@@ -99,6 +101,7 @@ public:
|
||||
uint32 minRandomBotPvpTime, maxRandomBotPvpTime;
|
||||
uint32 randomBotsPerInterval;
|
||||
uint32 minRandomBotsPriceChangeInterval, maxRandomBotsPriceChangeInterval;
|
||||
uint32 disabledWithoutRealPlayerLoginDelay, disabledWithoutRealPlayerLogoutDelay;
|
||||
bool randomBotJoinLfg;
|
||||
|
||||
// chat
|
||||
@@ -195,6 +198,7 @@ public:
|
||||
|
||||
bool randomBotLoginAtStartup;
|
||||
uint32 randomBotTeleLowerLevel, randomBotTeleHigherLevel;
|
||||
std::map<uint32, std::pair<uint32, uint32>> zoneBrackets;
|
||||
bool logInGroupOnly, logValuesPerTick;
|
||||
bool fleeingEnabled;
|
||||
bool summonAtInnkeepersEnabled;
|
||||
@@ -275,7 +279,7 @@ public:
|
||||
bool randomBotShowCloak;
|
||||
bool randomBotFixedLevel;
|
||||
bool disableRandomLevels;
|
||||
float playerbotsXPrate;
|
||||
float randomBotXPRate;
|
||||
uint32 randomBotAllianceRatio;
|
||||
uint32 randomBotHordeRatio;
|
||||
bool disableDeathKnightLogin;
|
||||
@@ -330,6 +334,8 @@ public:
|
||||
bool equipmentPersistence;
|
||||
int32 equipmentPersistenceLevel;
|
||||
int32 groupInvitationPermission;
|
||||
bool keepAltsInGroup = false;
|
||||
bool KeepAltsInGroup() const { return keepAltsInGroup; }
|
||||
bool allowSummonInCombat;
|
||||
bool allowSummonWhenMasterIsDead;
|
||||
bool allowSummonWhenBotIsDead;
|
||||
@@ -348,6 +354,12 @@ public:
|
||||
uint32 useFlyMountAtMinLevel;
|
||||
uint32 useFastFlyMountAtMinLevel;
|
||||
|
||||
// stagger flightpath takeoff
|
||||
uint32 delayMin;
|
||||
uint32 delayMax;
|
||||
uint32 gapMs;
|
||||
uint32 gapJitterMs;
|
||||
|
||||
std::string const GetTimestampStr();
|
||||
bool hasLog(std::string const fileName)
|
||||
{
|
||||
|
||||
@@ -157,7 +157,7 @@ void PlayerbotHolder::AddPlayerBot(ObjectGuid playerGuid, uint32 masterAccountId
|
||||
bool PlayerbotHolder::IsAccountLinked(uint32 accountId, uint32 linkedAccountId)
|
||||
{
|
||||
QueryResult result = PlayerbotsDatabase.Query(
|
||||
"SELECT 1 FROM playerbot_account_links WHERE account_id = {} AND linked_account_id = {}", accountId, linkedAccountId);
|
||||
"SELECT 1 FROM playerbots_account_links WHERE account_id = {} AND linked_account_id = {}", accountId, linkedAccountId);
|
||||
return result != nullptr;
|
||||
}
|
||||
|
||||
@@ -473,11 +473,11 @@ void PlayerbotHolder::OnBotLogin(Player* const bot)
|
||||
}
|
||||
|
||||
Player* master = botAI->GetMaster();
|
||||
if (!master)
|
||||
if (master)
|
||||
{
|
||||
// Log a warning to indicate that the master is null
|
||||
LOG_DEBUG("mod-playerbots", "Master is null for bot with GUID: {}", bot->GetGUID().GetRawValue());
|
||||
return;
|
||||
ObjectGuid masterGuid = master->GetGUID();
|
||||
if (master->GetGroup() && !master->GetGroup()->IsLeader(masterGuid))
|
||||
master->GetGroup()->ChangeLeader(masterGuid);
|
||||
}
|
||||
|
||||
Group* group = bot->GetGroup();
|
||||
@@ -496,7 +496,10 @@ void PlayerbotHolder::OnBotLogin(Player* const bot)
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
|
||||
// Don't disband alt groups when master goes away
|
||||
// Controlled by config
|
||||
if (sPlayerbotAIConfig->KeepAltsInGroup())
|
||||
{
|
||||
uint32 account = sCharacterCache->GetCharacterAccountIdByGuid(member);
|
||||
if (!sPlayerbotAIConfig->IsInRandomAccountList(account))
|
||||
@@ -1734,7 +1737,7 @@ void PlayerbotMgr::HandleSetSecurityKeyCommand(Player* player, const std::string
|
||||
|
||||
// Store the hashed key in the database
|
||||
PlayerbotsDatabase.Execute(
|
||||
"REPLACE INTO playerbot_account_keys (account_id, security_key) VALUES ({}, '{}')",
|
||||
"REPLACE INTO playerbots_account_keys (account_id, security_key) VALUES ({}, '{}')",
|
||||
accountId, hashedKey.str());
|
||||
|
||||
ChatHandler(player->GetSession()).PSendSysMessage("Security key set successfully.");
|
||||
@@ -1752,7 +1755,7 @@ void PlayerbotMgr::HandleLinkAccountCommand(Player* player, const std::string& a
|
||||
Field* fields = result->Fetch();
|
||||
uint32 linkedAccountId = fields[0].Get<uint32>();
|
||||
|
||||
result = PlayerbotsDatabase.Query("SELECT security_key FROM playerbot_account_keys WHERE account_id = {}", linkedAccountId);
|
||||
result = PlayerbotsDatabase.Query("SELECT security_key FROM playerbots_account_keys WHERE account_id = {}", linkedAccountId);
|
||||
if (!result)
|
||||
{
|
||||
ChatHandler(player->GetSession()).PSendSysMessage("Invalid security key.");
|
||||
@@ -1778,10 +1781,10 @@ void PlayerbotMgr::HandleLinkAccountCommand(Player* player, const std::string& a
|
||||
|
||||
uint32 accountId = player->GetSession()->GetAccountId();
|
||||
PlayerbotsDatabase.Execute(
|
||||
"INSERT IGNORE INTO playerbot_account_links (account_id, linked_account_id) VALUES ({}, {})",
|
||||
"INSERT IGNORE INTO playerbots_account_links (account_id, linked_account_id) VALUES ({}, {})",
|
||||
accountId, linkedAccountId);
|
||||
PlayerbotsDatabase.Execute(
|
||||
"INSERT IGNORE INTO playerbot_account_links (account_id, linked_account_id) VALUES ({}, {})",
|
||||
"INSERT IGNORE INTO playerbots_account_links (account_id, linked_account_id) VALUES ({}, {})",
|
||||
linkedAccountId, accountId);
|
||||
|
||||
ChatHandler(player->GetSession()).PSendSysMessage("Account linked successfully.");
|
||||
@@ -1790,7 +1793,7 @@ void PlayerbotMgr::HandleLinkAccountCommand(Player* player, const std::string& a
|
||||
void PlayerbotMgr::HandleViewLinkedAccountsCommand(Player* player)
|
||||
{
|
||||
uint32 accountId = player->GetSession()->GetAccountId();
|
||||
QueryResult result = PlayerbotsDatabase.Query("SELECT linked_account_id FROM playerbot_account_links WHERE account_id = {}", accountId);
|
||||
QueryResult result = PlayerbotsDatabase.Query("SELECT linked_account_id FROM playerbots_account_links WHERE account_id = {}", accountId);
|
||||
|
||||
if (!result)
|
||||
{
|
||||
@@ -1831,7 +1834,7 @@ void PlayerbotMgr::HandleUnlinkAccountCommand(Player* player, const std::string&
|
||||
uint32 linkedAccountId = fields[0].Get<uint32>();
|
||||
uint32 accountId = player->GetSession()->GetAccountId();
|
||||
|
||||
PlayerbotsDatabase.Execute("DELETE FROM playerbot_account_links WHERE (account_id = {} AND linked_account_id = {}) OR (account_id = {} AND linked_account_id = {})",
|
||||
PlayerbotsDatabase.Execute("DELETE FROM playerbots_account_links WHERE (account_id = {} AND linked_account_id = {}) OR (account_id = {} AND linked_account_id = {})",
|
||||
accountId, linkedAccountId, linkedAccountId, accountId);
|
||||
|
||||
ChatHandler(player->GetSession()).PSendSysMessage("Account unlinked successfully.");
|
||||
|
||||
@@ -219,9 +219,12 @@ public:
|
||||
if (!player->GetSession()->IsBot())
|
||||
return;
|
||||
|
||||
if (sPlayerbotAIConfig->playerbotsXPrate != 1.0)
|
||||
if (!sRandomPlayerbotMgr->IsRandomBot(player))
|
||||
return;
|
||||
|
||||
if (sPlayerbotAIConfig->randomBotXPRate != 1.0)
|
||||
{
|
||||
amount = static_cast<uint32>(std::round(static_cast<float>(amount) * sPlayerbotAIConfig->playerbotsXPrate));
|
||||
amount = static_cast<uint32>(std::round(static_cast<float>(amount) * sPlayerbotAIConfig->randomBotXPRate));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -49,6 +49,7 @@
|
||||
#include "UpdateTime.h"
|
||||
#include "World.h"
|
||||
#include "RandomPlayerbotFactory.h"
|
||||
#include <WorldSessionMgr.h>
|
||||
|
||||
struct GuidClassRaceInfo {
|
||||
ObjectGuid::LowType guid;
|
||||
@@ -355,7 +356,43 @@ void RandomPlayerbotMgr::UpdateAIInternal(uint32 elapsed, bool /*minimal*/)
|
||||
PERF_MON_TOTAL,
|
||||
onlineBotCount < maxAllowedBotCount ? "RandomPlayerbotMgr::Login" : "RandomPlayerbotMgr::UpdateAIInternal");
|
||||
|
||||
if (availableBotCount < maxAllowedBotCount)
|
||||
bool realPlayerIsLogged = false;
|
||||
if (sPlayerbotAIConfig->disabledWithoutRealPlayer)
|
||||
{
|
||||
if (sWorldSessionMgr->GetActiveAndQueuedSessionCount() > 0)
|
||||
{
|
||||
RealPlayerLastTimeSeen = time(nullptr);
|
||||
realPlayerIsLogged = true;
|
||||
|
||||
if (DelayLoginBotsTimer == 0)
|
||||
{
|
||||
DelayLoginBotsTimer = time(nullptr) + sPlayerbotAIConfig->disabledWithoutRealPlayerLoginDelay;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (DelayLoginBotsTimer)
|
||||
{
|
||||
DelayLoginBotsTimer = 0;
|
||||
}
|
||||
|
||||
if (RealPlayerLastTimeSeen != 0 && onlineBotCount > 0 &&
|
||||
time(nullptr) > RealPlayerLastTimeSeen + sPlayerbotAIConfig->disabledWithoutRealPlayerLogoutDelay)
|
||||
{
|
||||
LogoutAllBots();
|
||||
LOG_INFO("playerbots",
|
||||
"Logout all bots due no real player session.");
|
||||
}
|
||||
}
|
||||
|
||||
if (availableBotCount < maxAllowedBotCount &&
|
||||
(sPlayerbotAIConfig->disabledWithoutRealPlayer == false ||
|
||||
(realPlayerIsLogged && DelayLoginBotsTimer != 0 && time(nullptr) >= DelayLoginBotsTimer)))
|
||||
{
|
||||
AddRandomBots();
|
||||
}
|
||||
}
|
||||
else if (availableBotCount < maxAllowedBotCount)
|
||||
{
|
||||
AddRandomBots();
|
||||
}
|
||||
@@ -391,7 +428,11 @@ void RandomPlayerbotMgr::UpdateAIInternal(uint32 elapsed, bool /*minimal*/)
|
||||
}
|
||||
}
|
||||
uint32 updateBots = sPlayerbotAIConfig->randomBotsPerInterval * onlineBotFocus / 100;
|
||||
uint32 maxNewBots = onlineBotCount < maxAllowedBotCount ? maxAllowedBotCount - onlineBotCount : 0;
|
||||
uint32 maxNewBots = onlineBotCount < maxAllowedBotCount &&
|
||||
(sPlayerbotAIConfig->disabledWithoutRealPlayer == false ||
|
||||
(realPlayerIsLogged && DelayLoginBotsTimer != 0 && time(nullptr) >= DelayLoginBotsTimer))
|
||||
? maxAllowedBotCount - onlineBotCount
|
||||
: 0;
|
||||
uint32 loginBots = std::min(sPlayerbotAIConfig->randomBotsPerInterval - updateBots, maxNewBots);
|
||||
|
||||
if (!availableBots.empty())
|
||||
@@ -432,6 +473,8 @@ void RandomPlayerbotMgr::UpdateAIInternal(uint32 elapsed, bool /*minimal*/)
|
||||
if (!loginBots)
|
||||
break;
|
||||
}
|
||||
|
||||
DelayLoginBotsTimer = 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1563,7 +1606,12 @@ void RandomPlayerbotMgr::PrepareZone2LevelBracket()
|
||||
zone2LevelBracket[2817] = {77, 80}; // Crystalsong Forest
|
||||
zone2LevelBracket[3537] = {68, 75}; // Borean Tundra
|
||||
zone2LevelBracket[3711] = {75, 80}; // Sholazar Basin
|
||||
zone2LevelBracket[4197] = {79, 80}; // Wintergrasp
|
||||
zone2LevelBracket[4197] = {79, 80}; // Wintergrasp
|
||||
|
||||
// Override with values from config
|
||||
for (auto const& [zoneId, bracketPair] : sPlayerbotAIConfig->zoneBrackets) {
|
||||
zone2LevelBracket[zoneId] = {bracketPair.first, bracketPair.second};
|
||||
}
|
||||
}
|
||||
|
||||
void RandomPlayerbotMgr::PrepareTeleportCache()
|
||||
@@ -1627,8 +1675,8 @@ void RandomPlayerbotMgr::PrepareTeleportCache()
|
||||
uint32 level = (min_level + max_level + 1) / 2;
|
||||
WorldLocation loc(mapId, x, y, z, 0);
|
||||
collected_locs++;
|
||||
for (int32 l = (int32)level - (int32)sPlayerbotAIConfig->randomBotTeleHigherLevel;
|
||||
l <= (int32)level + (int32)sPlayerbotAIConfig->randomBotTeleLowerLevel; l++)
|
||||
for (int32 l = (int32)level - (int32)sPlayerbotAIConfig->randomBotTeleLowerLevel;
|
||||
l <= (int32)level + (int32)sPlayerbotAIConfig->randomBotTeleHigherLevel; l++)
|
||||
{
|
||||
if (l < 1 || l > maxLevel)
|
||||
{
|
||||
|
||||
@@ -206,6 +206,8 @@ private:
|
||||
time_t BgCheckTimer;
|
||||
time_t LfgCheckTimer;
|
||||
time_t PlayersCheckTimer;
|
||||
time_t RealPlayerLastTimeSeen = 0;
|
||||
time_t DelayLoginBotsTimer;
|
||||
time_t printStatsTimer;
|
||||
uint32 AddRandomBots();
|
||||
bool ProcessBot(uint32 bot);
|
||||
|
||||
@@ -813,6 +813,10 @@ void PlayerbotFactory::InitPetTalents()
|
||||
void PlayerbotFactory::InitPet()
|
||||
{
|
||||
Pet* pet = bot->GetPet();
|
||||
|
||||
if (!pet && bot->GetPetStable() && bot->GetPetStable()->CurrentPet)
|
||||
return;
|
||||
|
||||
if (!pet)
|
||||
{
|
||||
if (bot->getClass() != CLASS_HUNTER || bot->GetLevel() < 10)
|
||||
@@ -861,7 +865,8 @@ void PlayerbotFactory::InitPet()
|
||||
uint32 pet_number = sObjectMgr->GeneratePetNumber();
|
||||
if (bot->GetPetStable() && bot->GetPetStable()->CurrentPet)
|
||||
{
|
||||
bot->GetPetStable()->CurrentPet.value();
|
||||
auto petGuid = bot->GetPetStable()->CurrentPet.value(); // To correct the build warnin in VS
|
||||
// bot->GetPetStable()->CurrentPet.value();
|
||||
// bot->GetPetStable()->CurrentPet.reset();
|
||||
bot->RemovePet(nullptr, PET_SAVE_AS_CURRENT);
|
||||
bot->RemovePet(nullptr, PET_SAVE_NOT_IN_SLOT);
|
||||
@@ -1738,7 +1743,7 @@ void PlayerbotFactory::InitEquipment(bool incremental, bool second_chance)
|
||||
|
||||
if (incremental && oldItem)
|
||||
{
|
||||
float old_score = calculator.CalculateItem(oldItem->GetEntry());
|
||||
float old_score = calculator.CalculateItem(oldItem->GetEntry(), oldItem->GetItemRandomPropertyId());
|
||||
if (bestScoreForSlot < 1.2f * old_score)
|
||||
continue;
|
||||
}
|
||||
@@ -2200,33 +2205,15 @@ void PlayerbotFactory::InitSkills()
|
||||
//uint32 maxValue = level * 5; //not used, line marked for removal.
|
||||
bot->UpdateSkillsForLevel();
|
||||
|
||||
auto SafeLearn = [this](uint32 spellId)
|
||||
{
|
||||
if (!bot->HasSpell(spellId))
|
||||
bot->learnSpell(spellId, false, true); // Avoid duplicate attempts in DB
|
||||
};
|
||||
|
||||
// Define Riding skill according to level
|
||||
if (bot->GetLevel() >= 70)
|
||||
bot->SetSkill(SKILL_RIDING, 300, 300, 300);
|
||||
else if (bot->GetLevel() >= 60)
|
||||
bot->SetSkill(SKILL_RIDING, 225, 225, 225);
|
||||
else if (bot->GetLevel() >= 40)
|
||||
bot->SetSkill(SKILL_RIDING, 150, 150, 150);
|
||||
else if (bot->GetLevel() >= 20)
|
||||
bot->SetSkill(SKILL_RIDING, 75, 75, 75);
|
||||
else
|
||||
bot->SetSkill(SKILL_RIDING, 0, 0, 0);
|
||||
|
||||
// Safe learning of mount spells
|
||||
bot->SetSkill(SKILL_RIDING, 0, 0, 0);
|
||||
if (bot->GetLevel() >= sPlayerbotAIConfig->useGroundMountAtMinLevel)
|
||||
SafeLearn(33388); // Apprentice
|
||||
bot->learnSpell(33388);
|
||||
if (bot->GetLevel() >= sPlayerbotAIConfig->useFastGroundMountAtMinLevel)
|
||||
SafeLearn(33391); // Journeyman
|
||||
bot->learnSpell(33391);
|
||||
if (bot->GetLevel() >= sPlayerbotAIConfig->useFlyMountAtMinLevel)
|
||||
SafeLearn(34090); // Expert
|
||||
bot->learnSpell(34090);
|
||||
if (bot->GetLevel() >= sPlayerbotAIConfig->useFastFlyMountAtMinLevel)
|
||||
SafeLearn(34091); // Artisan
|
||||
bot->learnSpell(34091);
|
||||
|
||||
uint32 skillLevel = bot->GetLevel() < 40 ? 0 : 1;
|
||||
uint32 dualWieldLevel = bot->GetLevel() < 20 ? 0 : 1;
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include <cstdint>
|
||||
|
||||
#include "DBCStores.h"
|
||||
#include "ItemEnchantmentMgr.h"
|
||||
#include "ItemTemplate.h"
|
||||
#include "ObjectMgr.h"
|
||||
#include "PlayerbotAI.h"
|
||||
@@ -205,7 +206,7 @@ void StatsCollector::CollectSpellStats(uint32 spellId, float multiplier, int32 s
|
||||
}
|
||||
}
|
||||
|
||||
void StatsCollector::CollectEnchantStats(SpellItemEnchantmentEntry const* enchant)
|
||||
void StatsCollector::CollectEnchantStats(SpellItemEnchantmentEntry const* enchant, uint32 default_enchant_amount)
|
||||
{
|
||||
for (int s = 0; s < MAX_SPELL_ITEM_ENCHANTMENT_EFFECTS; ++s)
|
||||
{
|
||||
@@ -231,6 +232,10 @@ void StatsCollector::CollectEnchantStats(SpellItemEnchantmentEntry const* enchan
|
||||
}
|
||||
case ITEM_ENCHANTMENT_TYPE_STAT:
|
||||
{
|
||||
// for item random suffix
|
||||
if (!enchant_amount)
|
||||
enchant_amount = default_enchant_amount;
|
||||
|
||||
if (!enchant_amount)
|
||||
{
|
||||
break;
|
||||
@@ -250,7 +255,7 @@ bool StatsCollector::SpecialSpellFilter(uint32 spellId)
|
||||
// trinket
|
||||
switch (spellId)
|
||||
{
|
||||
case 60764: // Totem of Splintering
|
||||
case 60764: // Totem of Splintering
|
||||
if (type_ & (CollectorType::SPELL))
|
||||
return true;
|
||||
break;
|
||||
@@ -744,7 +749,7 @@ void StatsCollector::HandleApplyAura(const SpellEffectInfo& effectInfo, float mu
|
||||
|
||||
int32 StatsCollector::AverageValue(const SpellEffectInfo& effectInfo)
|
||||
{
|
||||
//float basePointsPerLevel = effectInfo.RealPointsPerLevel; //not used, line marked for removal.
|
||||
// float basePointsPerLevel = effectInfo.RealPointsPerLevel; //not used, line marked for removal.
|
||||
int32 basePoints = effectInfo.BasePoints;
|
||||
int32 randomPoints = int32(effectInfo.DieSides);
|
||||
|
||||
@@ -767,7 +772,7 @@ bool StatsCollector::CheckSpellValidation(uint32 spellFamilyName, flag96 spelFal
|
||||
{
|
||||
if (PlayerbotAI::Class2SpellFamilyName(cls_) != spellFamilyName)
|
||||
return false;
|
||||
|
||||
|
||||
bool isHealingSpell = PlayerbotAI::IsHealingSpell(spellFamilyName, spelFalimyFlags);
|
||||
// strict to healer
|
||||
if (strict && (type_ & CollectorType::SPELL_HEAL))
|
||||
|
||||
@@ -66,7 +66,7 @@ public:
|
||||
void Reset();
|
||||
void CollectItemStats(ItemTemplate const* proto);
|
||||
void CollectSpellStats(uint32 spellId, float multiplier = 1.0f, int32 spellCooldown = -1);
|
||||
void CollectEnchantStats(SpellItemEnchantmentEntry const* enchant);
|
||||
void CollectEnchantStats(SpellItemEnchantmentEntry const* enchant, uint32 default_enchant_amount = 0);
|
||||
bool CanBeTriggeredByType(SpellInfo const* spellInfo, uint32 procFlags, bool strict = true);
|
||||
bool CheckSpellValidation(uint32 spellFamilyName, flag96 spelFalimyFlags, bool strict = true);
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
|
||||
#include "AiFactory.h"
|
||||
#include "DBCStores.h"
|
||||
#include "ItemEnchantmentMgr.h"
|
||||
#include "ItemTemplate.h"
|
||||
#include "ObjectMgr.h"
|
||||
#include "PlayerbotAI.h"
|
||||
@@ -59,7 +60,7 @@ void StatsWeightCalculator::Reset()
|
||||
}
|
||||
}
|
||||
|
||||
float StatsWeightCalculator::CalculateItem(uint32 itemId)
|
||||
float StatsWeightCalculator::CalculateItem(uint32 itemId, int32 randomPropertyIds)
|
||||
{
|
||||
ItemTemplate const* proto = &sObjectMgr->GetItemTemplateStore()->at(itemId);
|
||||
|
||||
@@ -69,6 +70,9 @@ float StatsWeightCalculator::CalculateItem(uint32 itemId)
|
||||
Reset();
|
||||
|
||||
collector_->CollectItemStats(proto);
|
||||
|
||||
if (randomPropertyIds != 0)
|
||||
CalculateRandomProperty(randomPropertyIds, itemId);
|
||||
|
||||
if (enable_overflow_penalty_)
|
||||
ApplyOverflowPenalty(player_);
|
||||
@@ -116,6 +120,53 @@ float StatsWeightCalculator::CalculateEnchant(uint32 enchantId)
|
||||
return weight_;
|
||||
}
|
||||
|
||||
void StatsWeightCalculator::CalculateRandomProperty(int32 randomPropertyId, uint32 itemId)
|
||||
{
|
||||
if (randomPropertyId > 0)
|
||||
{
|
||||
ItemRandomPropertiesEntry const* item_rand = sItemRandomPropertiesStore.LookupEntry(randomPropertyId);
|
||||
if (!item_rand)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
for (uint32 i = PROP_ENCHANTMENT_SLOT_0; i < MAX_ENCHANTMENT_SLOT; ++i)
|
||||
{
|
||||
uint32 enchantId = item_rand->Enchantment[i - PROP_ENCHANTMENT_SLOT_0];
|
||||
SpellItemEnchantmentEntry const* enchant = sSpellItemEnchantmentStore.LookupEntry(enchantId);
|
||||
if (enchant)
|
||||
collector_->CollectEnchantStats(enchant);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ItemRandomSuffixEntry const* item_rand = sItemRandomSuffixStore.LookupEntry(-randomPropertyId);
|
||||
if (!item_rand)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
for (uint32 i = PROP_ENCHANTMENT_SLOT_0; i < MAX_ENCHANTMENT_SLOT; ++i)
|
||||
{
|
||||
uint32 enchantId = item_rand->Enchantment[i - PROP_ENCHANTMENT_SLOT_0];
|
||||
SpellItemEnchantmentEntry const* enchant = sSpellItemEnchantmentStore.LookupEntry(enchantId);
|
||||
uint32 enchant_amount = 0;
|
||||
|
||||
for (int k = 0; k < MAX_ITEM_ENCHANTMENT_EFFECTS; ++k)
|
||||
{
|
||||
if (item_rand->Enchantment[k] == enchantId)
|
||||
{
|
||||
enchant_amount = uint32((item_rand->AllocationPct[k] * GenerateEnchSuffixFactor(itemId)) / 10000);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (enchant)
|
||||
collector_->CollectEnchantStats(enchant, enchant_amount);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void StatsWeightCalculator::GenerateWeights(Player* player)
|
||||
{
|
||||
GenerateBasicWeights(player);
|
||||
@@ -293,8 +344,8 @@ void StatsWeightCalculator::GenerateBasicWeights(Player* player)
|
||||
stats_weights_[STATS_TYPE_CRIT] += 0.8f;
|
||||
stats_weights_[STATS_TYPE_HASTE] += 1.0f;
|
||||
}
|
||||
else if ((cls == CLASS_PALADIN && tab == PALADIN_TAB_HOLY) || // holy
|
||||
(cls == CLASS_SHAMAN && tab == SHAMAN_TAB_RESTORATION)) // heal
|
||||
else if ((cls == CLASS_PALADIN && tab == PALADIN_TAB_HOLY) || // holy
|
||||
(cls == CLASS_SHAMAN && tab == SHAMAN_TAB_RESTORATION)) // heal
|
||||
{
|
||||
stats_weights_[STATS_TYPE_INTELLECT] += 0.9f;
|
||||
stats_weights_[STATS_TYPE_SPIRIT] += 0.15f;
|
||||
@@ -303,7 +354,7 @@ void StatsWeightCalculator::GenerateBasicWeights(Player* player)
|
||||
stats_weights_[STATS_TYPE_CRIT] += 0.6f;
|
||||
stats_weights_[STATS_TYPE_HASTE] += 0.8f;
|
||||
}
|
||||
else if ((cls == CLASS_PRIEST && tab != PRIEST_TAB_SHADOW) || // discipline / holy
|
||||
else if ((cls == CLASS_PRIEST && tab != PRIEST_TAB_SHADOW) || // discipline / holy
|
||||
(cls == CLASS_DRUID && tab == DRUID_TAB_RESTORATION))
|
||||
{
|
||||
stats_weights_[STATS_TYPE_INTELLECT] += 0.8f;
|
||||
@@ -464,9 +515,9 @@ void StatsWeightCalculator::CalculateItemTypePenalty(ItemTemplate const* proto)
|
||||
// {
|
||||
// weight_ *= 1.0;
|
||||
// }
|
||||
// double hand
|
||||
if (proto->Class == ITEM_CLASS_WEAPON)
|
||||
{
|
||||
// double hand
|
||||
bool isDoubleHand = proto->Class == ITEM_CLASS_WEAPON &&
|
||||
!(ITEM_SUBCLASS_MASK_SINGLE_HAND & (1 << proto->SubClass)) &&
|
||||
!(ITEM_SUBCLASS_MASK_WEAPON_RANGED & (1 << proto->SubClass));
|
||||
@@ -474,29 +525,41 @@ void StatsWeightCalculator::CalculateItemTypePenalty(ItemTemplate const* proto)
|
||||
if (isDoubleHand)
|
||||
{
|
||||
weight_ *= 0.5;
|
||||
}
|
||||
// spec without double hand
|
||||
// enhancement, rogue, ice dk, unholy dk, shield tank, fury warrior without titan's grip but with duel wield
|
||||
if (isDoubleHand &&
|
||||
((cls == CLASS_SHAMAN && tab == SHAMAN_TAB_ENHANCEMENT && player_->CanDualWield()) ||
|
||||
(cls == CLASS_ROGUE) || (cls == CLASS_DEATH_KNIGHT && tab == DEATHKNIGHT_TAB_FROST) ||
|
||||
(cls == CLASS_WARRIOR && tab == WARRIOR_TAB_FURY && !player_->CanTitanGrip() && player_->CanDualWield()) ||
|
||||
(cls == CLASS_WARRIOR && tab == WARRIOR_TAB_PROTECTION) ||
|
||||
(cls == CLASS_PALADIN && tab == PALADIN_TAB_PROTECTION)))
|
||||
{
|
||||
weight_ *= 0.1;
|
||||
// spec without double hand
|
||||
// enhancement, rogue, ice dk, unholy dk, shield tank, fury warrior without titan's grip but with duel wield
|
||||
if (((cls == CLASS_SHAMAN && tab == SHAMAN_TAB_ENHANCEMENT && player_->CanDualWield()) ||
|
||||
(cls == CLASS_ROGUE) || (cls == CLASS_DEATH_KNIGHT && tab == DEATHKNIGHT_TAB_FROST) ||
|
||||
(cls == CLASS_WARRIOR && tab == WARRIOR_TAB_FURY && !player_->CanTitanGrip() && player_->CanDualWield()) ||
|
||||
(cls == CLASS_WARRIOR && tab == WARRIOR_TAB_PROTECTION) ||
|
||||
(cls == CLASS_PALADIN && tab == PALADIN_TAB_PROTECTION)))
|
||||
{
|
||||
weight_ *= 0.1;
|
||||
}
|
||||
|
||||
}
|
||||
// spec with double hand
|
||||
// fury without duel wield, arms, bear, retribution, blood dk
|
||||
if (!isDoubleHand &&
|
||||
((cls == CLASS_HUNTER && !player_->CanDualWield()) ||
|
||||
(cls == CLASS_WARRIOR && tab == WARRIOR_TAB_FURY && !player_->CanDualWield()) ||
|
||||
(cls == CLASS_WARRIOR && tab == WARRIOR_TAB_ARMS) || (cls == CLASS_DRUID && tab == DRUID_TAB_FERAL) ||
|
||||
(cls == CLASS_PALADIN && tab == PALADIN_TAB_RETRIBUTION) ||
|
||||
(cls == CLASS_DEATH_KNIGHT && tab == DEATHKNIGHT_TAB_BLOOD) ||
|
||||
(cls == CLASS_SHAMAN && tab == SHAMAN_TAB_ENHANCEMENT && !player_->CanDualWield())))
|
||||
if (!isDoubleHand)
|
||||
{
|
||||
weight_ *= 0.1;
|
||||
if ((cls == CLASS_HUNTER && !player_->CanDualWield()) ||
|
||||
(cls == CLASS_WARRIOR && tab == WARRIOR_TAB_FURY && !player_->CanDualWield()) ||
|
||||
(cls == CLASS_WARRIOR && tab == WARRIOR_TAB_ARMS) || (cls == CLASS_DRUID && tab == DRUID_TAB_FERAL) ||
|
||||
(cls == CLASS_PALADIN && tab == PALADIN_TAB_RETRIBUTION) ||
|
||||
(cls == CLASS_DEATH_KNIGHT && tab == DEATHKNIGHT_TAB_BLOOD) ||
|
||||
(cls == CLASS_SHAMAN && tab == SHAMAN_TAB_ENHANCEMENT && !player_->CanDualWield()))
|
||||
{
|
||||
weight_ *= 0.1;
|
||||
}
|
||||
// caster's main hand (cannot duel weapon but can equip two-hands stuff)
|
||||
if (cls == CLASS_MAGE ||
|
||||
cls == CLASS_PRIEST ||
|
||||
cls == CLASS_WARLOCK ||
|
||||
cls == CLASS_DRUID ||
|
||||
(cls == CLASS_SHAMAN && !player_->CanDualWield()))
|
||||
{
|
||||
weight_ *= 0.65;
|
||||
}
|
||||
|
||||
}
|
||||
// fury with titan's grip
|
||||
if ((!isDoubleHand || proto->SubClass == ITEM_SUBCLASS_WEAPON_POLEARM ||
|
||||
@@ -505,15 +568,18 @@ void StatsWeightCalculator::CalculateItemTypePenalty(ItemTemplate const* proto)
|
||||
{
|
||||
weight_ *= 0.1;
|
||||
}
|
||||
|
||||
if (cls == CLASS_HUNTER && proto->SubClass == ITEM_SUBCLASS_WEAPON_THROWN)
|
||||
{
|
||||
weight_ *= 0.1;
|
||||
}
|
||||
|
||||
if (cls == CLASS_ROGUE && (tab == ROGUE_TAB_ASSASSINATION || tab == ROGUE_TAB_SUBTLETY) &&
|
||||
proto->SubClass != ITEM_SUBCLASS_WEAPON_DAGGER)
|
||||
{
|
||||
weight_ *= 0.5;
|
||||
}
|
||||
|
||||
if (cls == CLASS_ROGUE && player_->HasAura(13964) &&
|
||||
(proto->SubClass == ITEM_SUBCLASS_WEAPON_SWORD || proto->SubClass == ITEM_SUBCLASS_WEAPON_AXE))
|
||||
{
|
||||
@@ -559,12 +625,13 @@ void StatsWeightCalculator::ApplyOverflowPenalty(Player* player)
|
||||
if (hitOverflowType_ & CollectorType::SPELL)
|
||||
{
|
||||
hit_current = player->GetTotalAuraModifier(SPELL_AURA_MOD_SPELL_HIT_CHANCE);
|
||||
hit_current += player->GetTotalAuraModifier(SPELL_AURA_MOD_INCREASES_SPELL_PCT_TO_HIT); // suppression (18176)
|
||||
hit_current +=
|
||||
player->GetTotalAuraModifier(SPELL_AURA_MOD_INCREASES_SPELL_PCT_TO_HIT); // suppression (18176)
|
||||
hit_current += player->GetRatingBonusValue(CR_HIT_SPELL);
|
||||
|
||||
if (cls == CLASS_PRIEST && tab == PRIEST_TAB_SHADOW && player->HasAura(15835)) // Shadow Focus
|
||||
if (cls == CLASS_PRIEST && tab == PRIEST_TAB_SHADOW && player->HasAura(15835)) // Shadow Focus
|
||||
hit_current += 3;
|
||||
if (cls == CLASS_MAGE && tab == MAGE_TAB_ARCANE && player->HasAura(12840)) // Arcane Focus
|
||||
if (cls == CLASS_MAGE && tab == MAGE_TAB_ARCANE && player->HasAura(12840)) // Arcane Focus
|
||||
hit_current += 3;
|
||||
|
||||
hit_overflow = SPELL_HIT_OVERFLOW;
|
||||
@@ -657,7 +724,7 @@ void StatsWeightCalculator::ApplyWeightFinetune(Player* player)
|
||||
{
|
||||
if (type_ & (CollectorType::MELEE | CollectorType::RANGED))
|
||||
{
|
||||
float armor_penetration_current/*, armor_penetration_overflow*/; //not used, line marked for removal.
|
||||
float armor_penetration_current /*, armor_penetration_overflow*/; // not used, line marked for removal.
|
||||
armor_penetration_current = player->GetRatingBonusValue(CR_ARMOR_PENETRATION);
|
||||
if (armor_penetration_current > 50)
|
||||
stats_weights_[STATS_TYPE_ARMOR_PENETRATION] *= 1.2f;
|
||||
|
||||
@@ -28,18 +28,19 @@ class StatsWeightCalculator
|
||||
public:
|
||||
StatsWeightCalculator(Player* player);
|
||||
void Reset();
|
||||
float CalculateItem(uint32 itemId);
|
||||
float CalculateItem(uint32 itemId, int32 randomPropertyId = 0);
|
||||
float CalculateEnchant(uint32 enchantId);
|
||||
|
||||
|
||||
void SetOverflowPenalty(bool apply) { enable_overflow_penalty_ = apply; }
|
||||
void SetItemSetBonus(bool apply) { enable_item_set_bonus_ = apply; }
|
||||
void SetQualityBlend(bool apply) { enable_quality_blend_ = apply; }
|
||||
|
||||
private:
|
||||
|
||||
private:
|
||||
void GenerateWeights(Player* player);
|
||||
void GenerateBasicWeights(Player* player);
|
||||
void GenerateAdditionalWeights(Player* player);
|
||||
|
||||
|
||||
void CalculateRandomProperty(int32 randomPropertyId, uint32 itemId);
|
||||
void CalculateItemSetMod(Player* player, ItemTemplate const* proto);
|
||||
void CalculateSocketBonus(Player* player, ItemTemplate const* proto);
|
||||
|
||||
|
||||
@@ -75,6 +75,7 @@ AiObjectContext::AiObjectContext(PlayerbotAI* botAI) : PlayerbotAIAware(botAI)
|
||||
actionContexts.Add(new WotlkDungeonUPActionContext());
|
||||
actionContexts.Add(new WotlkDungeonCoSActionContext());
|
||||
actionContexts.Add(new WotlkDungeonFoSActionContext());
|
||||
actionContexts.Add(new WotlkDungeonPoSActionContext());
|
||||
actionContexts.Add(new WotlkDungeonToCActionContext());
|
||||
|
||||
triggerContexts.Add(new TriggerContext());
|
||||
@@ -102,7 +103,8 @@ AiObjectContext::AiObjectContext(PlayerbotAI* botAI) : PlayerbotAIAware(botAI)
|
||||
triggerContexts.Add(new WotlkDungeonOccTriggerContext());
|
||||
triggerContexts.Add(new WotlkDungeonUPTriggerContext());
|
||||
triggerContexts.Add(new WotlkDungeonCoSTriggerContext());
|
||||
triggerContexts.Add(new WotlkDungeonFosTriggerContext());
|
||||
triggerContexts.Add(new WotlkDungeonFoSTriggerContext());
|
||||
triggerContexts.Add(new WotlkDungeonPoSTriggerContext());
|
||||
triggerContexts.Add(new WotlkDungeonToCTriggerContext());
|
||||
|
||||
valueContexts.Add(new ValueContext());
|
||||
|
||||
@@ -232,6 +232,20 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
class CollectItemsVisitor : public IterateItemsVisitor
|
||||
{
|
||||
public:
|
||||
CollectItemsVisitor() : IterateItemsVisitor() {}
|
||||
|
||||
std::vector<Item*> items;
|
||||
|
||||
bool Visit(Item* item) override
|
||||
{
|
||||
items.push_back(item);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
class ItemCountByQuality : public IterateItemsVisitor
|
||||
{
|
||||
public:
|
||||
|
||||
@@ -19,6 +19,7 @@ PassiveMultiplier::PassiveMultiplier(PlayerbotAI* botAI) : Multiplier(botAI, "pa
|
||||
allowedActions.push_back("nc");
|
||||
allowedActions.push_back("reset botAI");
|
||||
allowedActions.push_back("check mount state");
|
||||
allowedActions.push_back("lfg");
|
||||
}
|
||||
|
||||
if (allowedParts.empty())
|
||||
|
||||
@@ -13,7 +13,7 @@ class PlayerbotAI;
|
||||
class AcceptBgInvitationAction : public Action
|
||||
{
|
||||
public:
|
||||
AcceptBgInvitationAction(PlayerbotAI* botAI) : Action(botAI, "accept bg invitatio") {}
|
||||
AcceptBgInvitationAction(PlayerbotAI* botAI) : Action(botAI, "accept bg invitation") {}
|
||||
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
@@ -22,8 +22,7 @@ bool AcceptInvitationAction::Execute(Event event)
|
||||
std::string name;
|
||||
packet >> flag >> name;
|
||||
|
||||
// Player* inviter = ObjectAccessor::FindPlayer(grp->GetLeaderGUID());
|
||||
Player* inviter = ObjectAccessor::FindPlayerByName(name, true);
|
||||
Player* inviter = ObjectAccessor::FindPlayer(grp->GetLeaderGUID());
|
||||
if (!inviter)
|
||||
return false;
|
||||
|
||||
@@ -36,11 +35,17 @@ bool AcceptInvitationAction::Execute(Event event)
|
||||
return false;
|
||||
}
|
||||
|
||||
if (bot->isAFK())
|
||||
bot->ToggleAFK();
|
||||
|
||||
WorldPacket p;
|
||||
uint32 roles_mask = 0;
|
||||
p << roles_mask;
|
||||
bot->GetSession()->HandleGroupAcceptOpcode(p);
|
||||
|
||||
if (!bot->GetGroup() || !bot->GetGroup()->IsMember(inviter->GetGUID()))
|
||||
return false;
|
||||
|
||||
if (sRandomPlayerbotMgr->IsRandomBot(bot))
|
||||
botAI->SetMaster(inviter);
|
||||
// else
|
||||
|
||||
@@ -554,11 +554,9 @@ bool BGJoinAction::JoinQueue(uint32 type)
|
||||
}
|
||||
else
|
||||
{
|
||||
WorldPacket* arena_packet = new WorldPacket(CMSG_BATTLEMASTER_JOIN_ARENA, 20);
|
||||
*arena_packet << unit->GetGUID() << arenaslot << asGroup << uint8(isRated);
|
||||
/// FIX race condition
|
||||
// bot->GetSession()->HandleBattlemasterJoinArena(arena_packet);
|
||||
bot->GetSession()->QueuePacket(arena_packet);
|
||||
WorldPacket arena_packet(CMSG_BATTLEMASTER_JOIN_ARENA, 20);
|
||||
arena_packet << unit->GetGUID() << arenaslot << asGroup << uint8(isRated);
|
||||
bot->GetSession()->HandleBattlemasterJoinArena(arena_packet);
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
@@ -183,6 +183,7 @@ public:
|
||||
creators["bwl chat shortcut"] = &ChatActionContext::bwl_chat_shortcut;
|
||||
creators["tell estimated dps"] = &ChatActionContext::tell_estimated_dps;
|
||||
creators["join"] = &ChatActionContext::join;
|
||||
creators["lfg"] = &ChatActionContext::lfg;
|
||||
creators["calc"] = &ChatActionContext::calc;
|
||||
}
|
||||
|
||||
@@ -212,6 +213,7 @@ private:
|
||||
static Action* spirit_healer(PlayerbotAI* botAI) { return new SpiritHealerAction(botAI); }
|
||||
static Action* rti(PlayerbotAI* botAI) { return new RtiAction(botAI); }
|
||||
static Action* invite(PlayerbotAI* botAI) { return new InviteToGroupAction(botAI); }
|
||||
static Action* lfg(PlayerbotAI* botAI) { return new LfgAction(botAI); }
|
||||
static Action* spell(PlayerbotAI* botAI) { return new TellSpellAction(botAI); }
|
||||
static Action* cast_custom_spell(PlayerbotAI* botAI) { return new CastCustomSpellAction(botAI); }
|
||||
static Action* cast_custom_nc_spell(PlayerbotAI* botAI) { return new CastCustomNcSpellAction(botAI); }
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include "Event.h"
|
||||
#include "ItemCountValue.h"
|
||||
#include "ItemUsageValue.h"
|
||||
#include "ItemVisitors.h"
|
||||
#include "Playerbots.h"
|
||||
#include "StatsWeightCalculator.h"
|
||||
|
||||
@@ -311,19 +312,28 @@ bool EquipUpgradesAction::Execute(Event event)
|
||||
return false;
|
||||
}
|
||||
|
||||
ListItemsVisitor visitor;
|
||||
CollectItemsVisitor visitor;
|
||||
IterateItems(&visitor, ITERATE_ITEMS_IN_BAGS);
|
||||
|
||||
ItemIds items;
|
||||
for (std::map<uint32, uint32>::iterator i = visitor.items.begin(); i != visitor.items.end(); ++i)
|
||||
for (auto i = visitor.items.begin(); i != visitor.items.end(); ++i)
|
||||
{
|
||||
ItemUsage usage = AI_VALUE2(ItemUsage, "item usage", i->first);
|
||||
Item* item = *i;
|
||||
if (!item)
|
||||
break;
|
||||
int32 randomProperty = item->GetItemRandomPropertyId();
|
||||
uint32 itemId = item->GetTemplate()->ItemId;
|
||||
std::string itemUsageParam;
|
||||
if (randomProperty != 0) {
|
||||
itemUsageParam = std::to_string(itemId) + "," + std::to_string(randomProperty);
|
||||
} else {
|
||||
itemUsageParam = std::to_string(itemId);
|
||||
}
|
||||
ItemUsage usage = AI_VALUE2(ItemUsage, "item usage", itemUsageParam);
|
||||
|
||||
if (usage == ITEM_USAGE_EQUIP || usage == ITEM_USAGE_REPLACE || usage == ITEM_USAGE_BAD_EQUIP)
|
||||
{
|
||||
// LOG_INFO("playerbots", "Bot {} <{}> EquipUpgradesAction {} ({})", bot->GetGUID().ToString().c_str(),
|
||||
// bot->GetName().c_str(), i->first, usage == 1 ? "no item in slot" : usage == 2 ? "replace" : usage == 3 ?
|
||||
// "wrong item but empty slot" : "");
|
||||
items.insert(i->first);
|
||||
items.insert(itemId);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -333,21 +343,31 @@ bool EquipUpgradesAction::Execute(Event event)
|
||||
|
||||
bool EquipUpgradeAction::Execute(Event event)
|
||||
{
|
||||
ListItemsVisitor visitor;
|
||||
CollectItemsVisitor visitor;
|
||||
IterateItems(&visitor, ITERATE_ITEMS_IN_BAGS);
|
||||
|
||||
ItemIds items;
|
||||
for (std::map<uint32, uint32>::iterator i = visitor.items.begin(); i != visitor.items.end(); ++i)
|
||||
for (auto i = visitor.items.begin(); i != visitor.items.end(); ++i)
|
||||
{
|
||||
ItemUsage usage = AI_VALUE2(ItemUsage, "item usage", i->first);
|
||||
Item* item = *i;
|
||||
if (!item)
|
||||
break;
|
||||
int32 randomProperty = item->GetItemRandomPropertyId();
|
||||
uint32 itemId = item->GetTemplate()->ItemId;
|
||||
std::string itemUsageParam;
|
||||
if (randomProperty != 0) {
|
||||
itemUsageParam = std::to_string(itemId) + "," + std::to_string(randomProperty);
|
||||
} else {
|
||||
itemUsageParam = std::to_string(itemId);
|
||||
}
|
||||
ItemUsage usage = AI_VALUE2(ItemUsage, "item usage", itemUsageParam);
|
||||
|
||||
if (usage == ITEM_USAGE_EQUIP || usage == ITEM_USAGE_REPLACE || usage == ITEM_USAGE_BAD_EQUIP)
|
||||
{
|
||||
// LOG_INFO("playerbots", "Bot {} <{}> EquipUpgradeAction item {} ({})", bot->GetGUID().ToString().c_str(),
|
||||
// bot->GetName().c_str(), i->first, usage == 1 ? "no item in slot" : usage == 2 ? "replace" : usage == 3 ?
|
||||
// "wrong item but empty slot" : "");
|
||||
items.insert(i->first);
|
||||
items.insert(itemId);
|
||||
}
|
||||
}
|
||||
|
||||
EquipItems(items);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -100,6 +100,10 @@ bool FollowAction::isUseful()
|
||||
|
||||
bool FollowAction::CanDeadFollow(Unit* target)
|
||||
{
|
||||
// In battleground, wait for spirit healer
|
||||
if (bot->InBattleground() && !bot->IsAlive())
|
||||
return false;
|
||||
|
||||
// Move to corpse when dead and player is alive or not a ghost.
|
||||
if (!bot->IsAlive() && (target->IsAlive() || !target->HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_GHOST)))
|
||||
return false;
|
||||
|
||||
@@ -5,35 +5,27 @@
|
||||
|
||||
#include "InviteToGroupAction.h"
|
||||
|
||||
#include "BroadcastHelper.h"
|
||||
#include "Event.h"
|
||||
#include "GuildMgr.h"
|
||||
#include "Log.h"
|
||||
#include "Playerbots.h"
|
||||
#include "ServerFacade.h"
|
||||
#include "BroadcastHelper.h"
|
||||
|
||||
bool InviteToGroupAction::Execute(Event event)
|
||||
bool InviteToGroupAction::Invite(Player* inviter, Player* player)
|
||||
{
|
||||
Player* master = event.getOwner();
|
||||
if (!master)
|
||||
if (!player)
|
||||
return false;
|
||||
|
||||
return Invite(master);
|
||||
}
|
||||
|
||||
bool InviteToGroupAction::Invite(Player* player)
|
||||
{
|
||||
if (!player || !player->IsInWorld())
|
||||
return false;
|
||||
|
||||
if (bot == player)
|
||||
if (inviter == player)
|
||||
return false;
|
||||
|
||||
if (!GET_PLAYERBOT_AI(player) && !botAI->GetSecurity()->CheckLevelFor(PLAYERBOT_SECURITY_INVITE, true, player))
|
||||
return false;
|
||||
|
||||
if (Group* group = player->GetGroup())
|
||||
if (Group* group = inviter->GetGroup())
|
||||
{
|
||||
if(GET_PLAYERBOT_AI(player) && !GET_PLAYERBOT_AI(player)->IsRealPlayer())
|
||||
if (GET_PLAYERBOT_AI(player) && !GET_PLAYERBOT_AI(player)->IsRealPlayer())
|
||||
if (!group->isRaidGroup() && group->GetMembersCount() > 4)
|
||||
group->ConvertToRaid();
|
||||
}
|
||||
@@ -42,7 +34,7 @@ bool InviteToGroupAction::Invite(Player* player)
|
||||
uint32 roles_mask = 0;
|
||||
p << player->GetName();
|
||||
p << roles_mask;
|
||||
bot->GetSession()->HandleGroupInviteOpcode(p);
|
||||
inviter->GetSession()->HandleGroupInviteOpcode(p);
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -65,15 +57,23 @@ bool InviteNearbyToGroupAction::Execute(Event event)
|
||||
if (player->GetGroup())
|
||||
continue;
|
||||
|
||||
if (!sPlayerbotAIConfig->randomBotInvitePlayer && GET_PLAYERBOT_AI(player)->IsRealPlayer())
|
||||
continue;
|
||||
|
||||
Group* group = bot->GetGroup();
|
||||
|
||||
if (player->isDND())
|
||||
continue;
|
||||
|
||||
if (player->IsBeingTeleported())
|
||||
continue;
|
||||
|
||||
PlayerbotAI* botAI = GET_PLAYERBOT_AI(bot);
|
||||
|
||||
if (botAI)
|
||||
{
|
||||
if (botAI->GetGrouperType() == GrouperType::SOLO && !botAI->HasRealPlayerMaster()) // Do not invite solo players.
|
||||
if (botAI->GetGrouperType() == GrouperType::SOLO &&
|
||||
!botAI->HasRealPlayerMaster()) // Do not invite solo players.
|
||||
continue;
|
||||
|
||||
if (botAI->HasActivePlayerMaster()) // Do not invite alts of active players.
|
||||
@@ -86,27 +86,25 @@ bool InviteNearbyToGroupAction::Execute(Event event)
|
||||
if (sServerFacade->GetDistance2d(bot, player) > sPlayerbotAIConfig->sightDistance)
|
||||
continue;
|
||||
|
||||
Group* group = bot->GetGroup();
|
||||
Guild* guild = sGuildMgr->GetGuildById(bot->GetGuildId());
|
||||
if (!botAI->HasActivePlayerMaster())
|
||||
{
|
||||
if (guild && bot->GetGuildId() == player->GetGuildId())
|
||||
{
|
||||
BroadcastHelper::BroadcastGuildGroupOrRaidInvite(botAI, bot, player, group);
|
||||
}
|
||||
else
|
||||
{
|
||||
std::map<std::string, std::string> placeholders;
|
||||
placeholders["%player"] = player->GetName();
|
||||
// When inviting the 5th member of the group convert to raid for future invites.
|
||||
if (group && botAI->GetGrouperType() > GrouperType::LEADER_5 && !group->isRaidGroup() &&
|
||||
bot->GetGroup()->GetMembersCount() > 3)
|
||||
group->ConvertToRaid();
|
||||
|
||||
if (group && group->isRaidGroup())
|
||||
bot->Say(BOT_TEXT2("join_raid", placeholders), (bot->GetTeamId() == TEAM_ALLIANCE ? LANG_COMMON : LANG_ORCISH));
|
||||
else
|
||||
bot->Say(BOT_TEXT2("join_group", placeholders), (bot->GetTeamId() == TEAM_ALLIANCE ? LANG_COMMON : LANG_ORCISH));
|
||||
}
|
||||
if (sPlayerbotAIConfig->inviteChat && sRandomPlayerbotMgr->IsRandomBot(bot))
|
||||
{
|
||||
std::map<std::string, std::string> placeholders;
|
||||
placeholders["%player"] = player->GetName();
|
||||
|
||||
if (group && group->isRaidGroup())
|
||||
bot->Say(BOT_TEXT2("join_raid", placeholders),
|
||||
(bot->GetTeamId() == ALLIANCE ? LANG_COMMON : LANG_ORCISH));
|
||||
else
|
||||
bot->Say(BOT_TEXT2("join_group", placeholders),
|
||||
(bot->GetTeamId() == ALLIANCE ? LANG_COMMON : LANG_ORCISH));
|
||||
}
|
||||
|
||||
return Invite(player);
|
||||
return Invite(bot, player);
|
||||
}
|
||||
|
||||
return false;
|
||||
@@ -114,6 +112,9 @@ bool InviteNearbyToGroupAction::Execute(Event event)
|
||||
|
||||
bool InviteNearbyToGroupAction::isUseful()
|
||||
{
|
||||
if (!sPlayerbotAIConfig->randomBotGroupNearby)
|
||||
return false;
|
||||
|
||||
if (bot->InBattleground())
|
||||
return false;
|
||||
|
||||
@@ -121,18 +122,22 @@ bool InviteNearbyToGroupAction::isUseful()
|
||||
return false;
|
||||
|
||||
GrouperType grouperType = botAI->GetGrouperType();
|
||||
|
||||
if (grouperType == GrouperType::SOLO || grouperType == GrouperType::MEMBER)
|
||||
return false;
|
||||
|
||||
if (Group* group = bot->GetGroup())
|
||||
Group* group = bot->GetGroup();
|
||||
|
||||
if (group)
|
||||
{
|
||||
if (group->IsFull())
|
||||
if (group->isRaidGroup() && group->IsFull())
|
||||
return false;
|
||||
|
||||
if (botAI->GetGroupMaster() != bot)
|
||||
return false;
|
||||
|
||||
uint32 memberCount = group->GetMembersCount();
|
||||
|
||||
if (memberCount >= uint8(grouperType))
|
||||
return false;
|
||||
}
|
||||
@@ -173,62 +178,273 @@ bool InviteGuildToGroupAction::Execute(Event event)
|
||||
if (player->isDND())
|
||||
continue;
|
||||
|
||||
if (!sPlayerbotAIConfig->randomBotInvitePlayer && GET_PLAYERBOT_AI(player)->IsRealPlayer())
|
||||
continue;
|
||||
|
||||
if (player->IsBeingTeleported())
|
||||
continue;
|
||||
|
||||
if (player->GetMapId() != bot->GetMapId() && player->GetLevel() < 30)
|
||||
continue;
|
||||
|
||||
if (WorldPosition(player).distance(bot) > 1000 && player->GetLevel() < 15)
|
||||
continue;
|
||||
|
||||
PlayerbotAI* playerAi = GET_PLAYERBOT_AI(player);
|
||||
|
||||
if (playerAi)
|
||||
{
|
||||
if (playerAi->GetGrouperType() == GrouperType::SOLO &&
|
||||
!playerAi->HasRealPlayerMaster()) // Do not invite solo players.
|
||||
!playerAi->HasRealPlayerMaster()) // Do not invite solo players.
|
||||
continue;
|
||||
|
||||
if (playerAi->HasActivePlayerMaster()) // Do not invite alts of active players.
|
||||
if (playerAi->HasActivePlayerMaster()) // Do not invite alts of active players.
|
||||
continue;
|
||||
|
||||
if (player->GetLevel() > bot->GetLevel() + 5) // Invite higher levels that need money so they can grind money and help out.
|
||||
if (player->GetLevel() >
|
||||
bot->GetLevel() + 5) // Invite higher levels that need money so they can grind money and help out.
|
||||
{
|
||||
if (!PAI_VALUE(bool,"should get money"))
|
||||
if (!PAI_VALUE(bool, "should get money"))
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!sPlayerbotAIConfig->randomBotGroupNearby)
|
||||
return false;
|
||||
}
|
||||
|
||||
if (bot->GetLevel() > player->GetLevel() + 5) // Do not invite members that too low level or risk dragging them to deadly places.
|
||||
if (bot->GetLevel() >
|
||||
player->GetLevel() + 5) // Do not invite members that too low level or risk dragging them to deadly places.
|
||||
continue;
|
||||
|
||||
if (!playerAi && sServerFacade->GetDistance2d(bot, player) > sPlayerbotAIConfig->sightDistance)
|
||||
continue;
|
||||
|
||||
Group* group = bot->GetGroup();
|
||||
BroadcastHelper::BroadcastGuildGroupOrRaidInvite(botAI, bot, player, group);
|
||||
return Invite(player);
|
||||
// When inviting the 5th member of the group convert to raid for future invites.
|
||||
if (group && botAI->GetGrouperType() > GrouperType::LEADER_5 && !group->isRaidGroup() &&
|
||||
bot->GetGroup()->GetMembersCount() > 3)
|
||||
{
|
||||
group->ConvertToRaid();
|
||||
}
|
||||
|
||||
if (sPlayerbotAIConfig->inviteChat &&
|
||||
(sRandomPlayerbotMgr->IsRandomBot(bot) || !botAI->HasActivePlayerMaster()))
|
||||
{
|
||||
BroadcastHelper::BroadcastGuildGroupOrRaidInvite(botAI, bot, player, group);
|
||||
}
|
||||
|
||||
return Invite(bot, player);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool InviteGuildToGroupAction::isUseful()
|
||||
{
|
||||
return bot->GetGuildId() && InviteNearbyToGroupAction::isUseful();
|
||||
};
|
||||
|
||||
bool JoinGroupAction::Execute(Event event)
|
||||
{
|
||||
if (bot->InBattleground())
|
||||
return false;
|
||||
|
||||
if (bot->InBattlegroundQueue())
|
||||
return false;
|
||||
|
||||
Player* master = event.getOwner();
|
||||
|
||||
Group* group = master->GetGroup();
|
||||
|
||||
if (group && (group->IsFull() || bot->GetGroup() == group))
|
||||
if (group)
|
||||
{
|
||||
if (group->IsFull())
|
||||
return false;
|
||||
|
||||
if (bot->GetGroup() == group)
|
||||
return false;
|
||||
}
|
||||
|
||||
if (bot->GetGroup())
|
||||
{
|
||||
if (botAI->HasRealPlayerMaster())
|
||||
return false;
|
||||
|
||||
if (!botAI->DoSpecificAction("leave", event, true))
|
||||
return false;
|
||||
}
|
||||
|
||||
return Invite(master, bot);
|
||||
}
|
||||
|
||||
bool LfgAction::Execute(Event event)
|
||||
{
|
||||
Player* requester = event.getOwner() ? event.getOwner() : GetMaster();
|
||||
|
||||
if (bot->InBattleground())
|
||||
return false;
|
||||
|
||||
if (bot->InBattlegroundQueue())
|
||||
return false;
|
||||
|
||||
if (!botAI->IsSafe(requester))
|
||||
return false;
|
||||
|
||||
if (requester->GetLevel() == DEFAULT_MAX_LEVEL && bot->GetLevel() != DEFAULT_MAX_LEVEL)
|
||||
return false;
|
||||
|
||||
if (requester->GetLevel() > bot->GetLevel() + 4 || bot->GetLevel() > requester->GetLevel() + 4)
|
||||
return false;
|
||||
|
||||
std::string param = event.getParam();
|
||||
|
||||
if (!param.empty() && param != "40" && param != "25" && param != "20" && param != "10" && param != "5")
|
||||
return false;
|
||||
|
||||
Group* group = requester->GetGroup();
|
||||
|
||||
std::unordered_map<Classes, std::unordered_map<BotRoles, uint32>> allowedClassNr;
|
||||
std::unordered_map<BotRoles, uint32> allowedRoles;
|
||||
|
||||
allowedRoles[BOT_ROLE_TANK] = 1;
|
||||
allowedRoles[BOT_ROLE_HEALER] = 1;
|
||||
allowedRoles[BOT_ROLE_DPS] = 3;
|
||||
|
||||
BotRoles role = botAI->IsTank(requester, false)
|
||||
? BOT_ROLE_TANK
|
||||
: (botAI->IsHeal(requester, false) ? BOT_ROLE_HEALER : BOT_ROLE_DPS);
|
||||
Classes cls = (Classes)requester->getClass();
|
||||
|
||||
if (group)
|
||||
{
|
||||
if (param.empty() && group->isRaidGroup())
|
||||
// Default to WotLK Raiding. Max size 25
|
||||
param = "25";
|
||||
// Select optimal group layout.
|
||||
if (param == "40")
|
||||
{
|
||||
allowedRoles[BOT_ROLE_TANK] = 4;
|
||||
allowedRoles[BOT_ROLE_HEALER] = 16;
|
||||
allowedRoles[BOT_ROLE_DPS] = 20;
|
||||
/*
|
||||
allowedClassNr[CLASS_PALADIN][BOT_ROLE_TANK] = 0;
|
||||
allowedClassNr[CLASS_DRUID][BOT_ROLE_TANK] = 1;
|
||||
|
||||
allowedClassNr[CLASS_DRUID][BOT_ROLE_HEALER] = 3;
|
||||
allowedClassNr[CLASS_PALADIN][BOT_ROLE_HEALER] = 4;
|
||||
allowedClassNr[CLASS_SHAMAN][BOT_ROLE_HEALER] = 4;
|
||||
allowedClassNr[CLASS_PRIEST][BOT_ROLE_HEALER] = 11;
|
||||
|
||||
allowedClassNr[CLASS_WARRIOR][BOT_ROLE_DPS] = 8;
|
||||
allowedClassNr[CLASS_PALADIN][BOT_ROLE_DPS] = 4;
|
||||
allowedClassNr[CLASS_HUNTER][BOT_ROLE_DPS] = 4;
|
||||
allowedClassNr[CLASS_ROGUE][BOT_ROLE_DPS] = 6;
|
||||
allowedClassNr[CLASS_PRIEST][BOT_ROLE_DPS] = 1;
|
||||
allowedClassNr[CLASS_SHAMAN][BOT_ROLE_DPS] = 4;
|
||||
allowedClassNr[CLASS_MAGE][BOT_ROLE_DPS] = 15;
|
||||
allowedClassNr[CLASS_WARLOCK][BOT_ROLE_DPS] = 4;
|
||||
allowedClassNr[CLASS_DRUID][BOT_ROLE_DPS] = 1;
|
||||
*/
|
||||
}
|
||||
else if (param == "25")
|
||||
{
|
||||
allowedRoles[BOT_ROLE_TANK] = 3;
|
||||
allowedRoles[BOT_ROLE_HEALER] = 7;
|
||||
allowedRoles[BOT_ROLE_DPS] = 15;
|
||||
}
|
||||
else if (param == "20")
|
||||
{
|
||||
allowedRoles[BOT_ROLE_TANK] = 2;
|
||||
allowedRoles[BOT_ROLE_HEALER] = 5;
|
||||
allowedRoles[BOT_ROLE_DPS] = 13;
|
||||
}
|
||||
else if (param == "10")
|
||||
{
|
||||
allowedRoles[BOT_ROLE_TANK] = 2;
|
||||
allowedRoles[BOT_ROLE_HEALER] = 3;
|
||||
allowedRoles[BOT_ROLE_DPS] = 5;
|
||||
}
|
||||
|
||||
if (group->IsFull())
|
||||
{
|
||||
if (param.empty() || param == "5" || group->isRaidGroup())
|
||||
return false; // Group or raid is full so stop trying.
|
||||
else
|
||||
group->ConvertToRaid(); // We want a raid but are in a group so convert and continue.
|
||||
}
|
||||
|
||||
Group::MemberSlotList const& groupSlot = group->GetMemberSlots();
|
||||
for (Group::member_citerator itr = groupSlot.begin(); itr != groupSlot.end(); itr++)
|
||||
{
|
||||
// Only add group member targets that are alive and near the player
|
||||
Player* player = ObjectAccessor::FindPlayer(itr->guid);
|
||||
|
||||
if (!botAI->IsSafe(player))
|
||||
return false;
|
||||
|
||||
role = botAI->IsTank(player, false) ? BOT_ROLE_TANK
|
||||
: (botAI->IsHeal(player, false) ? BOT_ROLE_HEALER : BOT_ROLE_DPS);
|
||||
cls = (Classes)player->getClass();
|
||||
|
||||
if (allowedRoles[role] > 0)
|
||||
allowedRoles[role]--;
|
||||
|
||||
if (allowedClassNr[cls].find(role) != allowedClassNr[cls].end() && allowedClassNr[cls][role] > 0)
|
||||
allowedClassNr[cls][role]--;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (allowedRoles[role] > 0)
|
||||
allowedRoles[role]--;
|
||||
|
||||
if (allowedClassNr[cls].find(role) != allowedClassNr[cls].end() && allowedClassNr[cls][role] > 0)
|
||||
allowedClassNr[cls][role]--;
|
||||
}
|
||||
|
||||
role = botAI->IsTank(bot, false) ? BOT_ROLE_TANK : (botAI->IsHeal(bot, false) ? BOT_ROLE_HEALER : BOT_ROLE_DPS);
|
||||
cls = (Classes)bot->getClass();
|
||||
|
||||
if (allowedRoles[role] == 0)
|
||||
return false;
|
||||
|
||||
if (allowedClassNr[cls].find(role) != allowedClassNr[cls].end() && allowedClassNr[cls][role] == 0)
|
||||
return false;
|
||||
|
||||
if (bot->GetGroup())
|
||||
if (!botAI->DoSpecificAction("leave", event, true))
|
||||
{
|
||||
if (botAI->HasRealPlayerMaster())
|
||||
return false;
|
||||
|
||||
return Invite(bot);
|
||||
if (!botAI->DoSpecificAction("leave", event, true))
|
||||
return false;
|
||||
}
|
||||
|
||||
bool invite = Invite(requester, bot);
|
||||
|
||||
if (invite)
|
||||
{
|
||||
Event acceptEvent("accept invitation", requester ? requester->GetGUID() : ObjectGuid::Empty);
|
||||
if (!botAI->DoSpecificAction("accept invitation", acceptEvent, true))
|
||||
return false;
|
||||
|
||||
std::map<std::string, std::string> placeholders;
|
||||
placeholders["%role"] = (role & BOT_ROLE_TANK ? "tank" : (role & BOT_ROLE_HEALER ? "healer" : "dps"));
|
||||
placeholders["%spotsleft"] = std::to_string(allowedRoles[role] - 1);
|
||||
|
||||
std::ostringstream out;
|
||||
if (allowedRoles[role] > 1)
|
||||
{
|
||||
out << "Joining as " << placeholders["%role"] << ", " << placeholders["%spotsleft"] << " "
|
||||
<< placeholders["%role"] << " spots left.";
|
||||
botAI->TellMasterNoFacing(out.str());
|
||||
|
||||
//botAI->DoSpecificAction("autogear");
|
||||
//botAI->DoSpecificAction("maintenance");
|
||||
}
|
||||
else
|
||||
{
|
||||
out << "Joining as " << placeholders["%role"] << ".";
|
||||
botAI->TellMasterNoFacing(out.str());
|
||||
|
||||
//botAI->DoSpecificAction("autogear");
|
||||
//botAI->DoSpecificAction("maintenance");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -16,9 +16,13 @@ class InviteToGroupAction : public Action
|
||||
public:
|
||||
InviteToGroupAction(PlayerbotAI* botAI, std::string const name = "invite") : Action(botAI, name) {}
|
||||
|
||||
bool Execute(Event event) override;
|
||||
bool Execute(Event event) override
|
||||
{
|
||||
Player* master = event.getOwner();
|
||||
return Invite(bot, master);
|
||||
}
|
||||
|
||||
virtual bool Invite(Player* player);
|
||||
virtual bool Invite(Player* inviter, Player* player);
|
||||
};
|
||||
|
||||
class JoinGroupAction : public InviteToGroupAction
|
||||
@@ -26,17 +30,12 @@ class JoinGroupAction : public InviteToGroupAction
|
||||
public:
|
||||
JoinGroupAction(PlayerbotAI* ai, std::string name = "join") : InviteToGroupAction(ai, name) {}
|
||||
bool Execute(Event event) override;
|
||||
bool isUseful() override { return !bot->IsBeingTeleported(); }
|
||||
};
|
||||
|
||||
class InviteNearbyToGroupAction : public InviteToGroupAction
|
||||
{
|
||||
public:
|
||||
InviteNearbyToGroupAction(PlayerbotAI* botAI, std::string const name = "invite nearby")
|
||||
: InviteToGroupAction(botAI, name)
|
||||
{
|
||||
}
|
||||
|
||||
InviteNearbyToGroupAction(PlayerbotAI* botAI, std::string const name = "invite nearby") : InviteToGroupAction(botAI, name) {}
|
||||
bool Execute(Event event) override;
|
||||
bool isUseful() override;
|
||||
};
|
||||
@@ -64,10 +63,17 @@ public:
|
||||
}
|
||||
|
||||
bool Execute(Event event) override;
|
||||
bool isUseful() override;
|
||||
bool isUseful() { return bot->GetGuildId() && InviteNearbyToGroupAction::isUseful(); };
|
||||
|
||||
private:
|
||||
std::vector<Player*> getGuildMembers();
|
||||
};
|
||||
|
||||
class LfgAction : public InviteToGroupAction
|
||||
{
|
||||
public:
|
||||
LfgAction(PlayerbotAI* botAI, std::string name = "lfg") : InviteToGroupAction(botAI, name) {}
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -18,8 +18,8 @@
|
||||
#include "GuildMgr.h"
|
||||
#include "BroadcastHelper.h"
|
||||
|
||||
bool LootAction::Execute(Event event)
|
||||
{
|
||||
bool LootAction::Execute(Event /*event*/)
|
||||
{
|
||||
if (!AI_VALUE(bool, "has available loot"))
|
||||
return false;
|
||||
|
||||
@@ -35,8 +35,17 @@ bool LootAction::Execute(Event event)
|
||||
// bot->GetSession()->HandleLootReleaseOpcode(packet);
|
||||
}
|
||||
|
||||
context->GetValue<LootObject>("loot target")->Set(lootObject);
|
||||
return true;
|
||||
// Provide a system to check if the game object id is disallowed in the user configurable list or not.
|
||||
// Check if the game object id is disallowed in the user configurable list or not.
|
||||
if (sPlayerbotAIConfig->disallowedGameObjects.find(lootObject.guid.GetEntry()) != sPlayerbotAIConfig->disallowedGameObjects.end())
|
||||
{
|
||||
return false; // Game object ID is disallowed, so do not proceed
|
||||
}
|
||||
else
|
||||
{
|
||||
context->GetValue<LootObject>("loot target")->Set(lootObject);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
bool LootAction::isUseful()
|
||||
@@ -61,7 +70,7 @@ enum ProfessionSpells
|
||||
TAILORING = 3908
|
||||
};
|
||||
|
||||
bool OpenLootAction::Execute(Event event)
|
||||
bool OpenLootAction::Execute(Event /*event*/)
|
||||
{
|
||||
LootObject lootObject = AI_VALUE(LootObject, "loot target");
|
||||
bool result = DoLoot(lootObject);
|
||||
@@ -70,7 +79,6 @@ bool OpenLootAction::Execute(Event event)
|
||||
AI_VALUE(LootObjectStack*, "available loot")->Remove(lootObject.guid);
|
||||
context->GetValue<LootObject>("loot target")->Set(LootObject());
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -131,6 +139,14 @@ bool OpenLootAction::DoLoot(LootObject& lootObject)
|
||||
if (go && (go->GetGoState() != GO_STATE_READY))
|
||||
return false;
|
||||
|
||||
// This prevents dungeon chests like Tribunal Chest (Halls of Stone) from being ninja'd by the bots
|
||||
if (go && go->HasFlag(GAMEOBJECT_FLAGS, GO_FLAG_INTERACT_COND))
|
||||
return false;
|
||||
|
||||
// This prevents raid chests like Gunship Armory (ICC) from being ninja'd by the bots
|
||||
if (go && go->HasFlag(GAMEOBJECT_FLAGS, GO_FLAG_NOT_SELECTABLE))
|
||||
return false;
|
||||
|
||||
if (lootObject.skillId == SKILL_MINING)
|
||||
return botAI->HasSkill(SKILL_MINING) ? botAI->CastSpell(MINING, bot) : false;
|
||||
|
||||
@@ -140,7 +156,7 @@ bool OpenLootAction::DoLoot(LootObject& lootObject)
|
||||
uint32 spellId = GetOpeningSpell(lootObject);
|
||||
if (!spellId)
|
||||
return false;
|
||||
|
||||
|
||||
return botAI->CastSpell(spellId, bot);
|
||||
}
|
||||
|
||||
@@ -189,7 +205,7 @@ uint32 OpenLootAction::GetOpeningSpell(LootObject& lootObject, GameObject* go)
|
||||
return sPlayerbotAIConfig->openGoSpell;
|
||||
}
|
||||
|
||||
bool OpenLootAction::CanOpenLock(LootObject& lootObject, SpellInfo const* spellInfo, GameObject* go)
|
||||
bool OpenLootAction::CanOpenLock(LootObject& /*lootObject*/, SpellInfo const* spellInfo, GameObject* go)
|
||||
{
|
||||
for (uint8 effIndex = 0; effIndex <= EFFECT_2; effIndex++)
|
||||
{
|
||||
@@ -205,8 +221,6 @@ bool OpenLootAction::CanOpenLock(LootObject& lootObject, SpellInfo const* spellI
|
||||
if (!lockInfo)
|
||||
return false;
|
||||
|
||||
bool reqKey = false; // some locks not have reqs
|
||||
|
||||
for (uint8 j = 0; j < 8; ++j)
|
||||
{
|
||||
switch (lockInfo->Type[j])
|
||||
@@ -369,7 +383,6 @@ bool StoreLootAction::Execute(Event event)
|
||||
uint32 itemcount;
|
||||
uint8 lootslot_type;
|
||||
uint8 itemindex;
|
||||
bool grab = false;
|
||||
|
||||
p >> itemindex;
|
||||
p >> itemid;
|
||||
@@ -506,7 +519,7 @@ bool StoreLootAction::IsLootAllowed(uint32 itemid, PlayerbotAI* botAI)
|
||||
return canLoot;
|
||||
}
|
||||
|
||||
bool ReleaseLootAction::Execute(Event event)
|
||||
bool ReleaseLootAction::Execute(Event /*event*/)
|
||||
{
|
||||
GuidVector gos = context->GetValue<GuidVector>("nearest game objects")->Get();
|
||||
for (ObjectGuid const guid : gos)
|
||||
|
||||
@@ -9,13 +9,12 @@
|
||||
#include "Group.h"
|
||||
#include "ItemUsageValue.h"
|
||||
#include "LootAction.h"
|
||||
#include "ObjectMgr.h"
|
||||
#include "PlayerbotAIConfig.h"
|
||||
#include "Playerbots.h"
|
||||
|
||||
bool LootRollAction::Execute(Event event)
|
||||
{
|
||||
Player* bot = QueryItemUsageAction::botAI->GetBot();
|
||||
|
||||
Group* group = bot->GetGroup();
|
||||
if (!group)
|
||||
return false;
|
||||
@@ -30,12 +29,24 @@ bool LootRollAction::Execute(Event event)
|
||||
ObjectGuid guid = roll->itemGUID;
|
||||
uint32 slot = roll->itemSlot;
|
||||
uint32 itemId = roll->itemid;
|
||||
int32 randomProperty = 0;
|
||||
if (roll->itemRandomPropId)
|
||||
randomProperty = roll->itemRandomPropId;
|
||||
else if (roll->itemRandomSuffix)
|
||||
randomProperty = -((int)roll->itemRandomSuffix);
|
||||
|
||||
RollVote vote = PASS;
|
||||
ItemTemplate const* proto = sObjectMgr->GetItemTemplate(itemId);
|
||||
if (!proto)
|
||||
continue;
|
||||
ItemUsage usage = AI_VALUE2(ItemUsage, "item usage", itemId);
|
||||
|
||||
std::string itemUsageParam;
|
||||
if (randomProperty != 0) {
|
||||
itemUsageParam = std::to_string(itemId) + "," + std::to_string(randomProperty);
|
||||
} else {
|
||||
itemUsageParam = std::to_string(itemId);
|
||||
}
|
||||
ItemUsage usage = AI_VALUE2(ItemUsage, "item usage", itemUsageParam);
|
||||
|
||||
// Armor Tokens are classed as MISC JUNK (Class 15, Subclass 0), luckily no other items I found have class bits and epic quality.
|
||||
if (proto->Class == ITEM_CLASS_MISC && proto->SubClass == ITEM_SUBCLASS_JUNK && proto->Quality == ITEM_QUALITY_EPIC)
|
||||
@@ -78,7 +89,14 @@ bool LootRollAction::Execute(Event event)
|
||||
{
|
||||
if (vote == NEED)
|
||||
{
|
||||
vote = GREED;
|
||||
if (RollUniqueCheck(proto, bot))
|
||||
{
|
||||
vote = PASS;
|
||||
}
|
||||
else
|
||||
{
|
||||
vote = GREED;
|
||||
}
|
||||
}
|
||||
else if (vote == GREED)
|
||||
{
|
||||
@@ -95,61 +113,11 @@ bool LootRollAction::Execute(Event event)
|
||||
group->CountRollVote(bot->GetGUID(), guid, vote);
|
||||
break;
|
||||
}
|
||||
// One item at a time
|
||||
return true;
|
||||
}
|
||||
|
||||
// WorldPacket p(event.getPacket()); //WorldPacket packet for CMSG_LOOT_ROLL, (8+4+1)
|
||||
// p.rpos(0); //reset packet pointer
|
||||
// p >> guid; //guid of the item rolled
|
||||
// p >> slot; //number of players invited to roll
|
||||
// p >> rollType; //need,greed or pass on roll
|
||||
|
||||
// std::vector<Roll*> rolls = group->GetRolls();
|
||||
// bot->Say("guid:" + std::to_string(guid.GetCounter()) +
|
||||
// "item entry:" + std::to_string(guid.GetEntry()), LANG_UNIVERSAL);
|
||||
// for (std::vector<Roll*>::iterator i = rolls.begin(); i != rolls.end(); ++i)
|
||||
// {
|
||||
// if ((*i)->isValid() && (*i)->itemGUID == guid && (*i)->itemSlot == slot)
|
||||
// {
|
||||
// uint32 itemId = (*i)->itemid;
|
||||
// bot->Say("item entry2:" + std::to_string(itemId), LANG_UNIVERSAL);
|
||||
// ItemTemplate const *proto = sObjectMgr->GetItemTemplate(itemId);
|
||||
// if (!proto)
|
||||
// continue;
|
||||
|
||||
// switch (proto->Class)
|
||||
// {
|
||||
// case ITEM_CLASS_WEAPON:
|
||||
// case ITEM_CLASS_ARMOR:
|
||||
// if (!QueryItemUsage(proto).empty())
|
||||
// vote = NEED;
|
||||
// else if (bot->HasSkill(SKILL_ENCHANTING))
|
||||
// vote = DISENCHANT;
|
||||
// break;
|
||||
// default:
|
||||
// if (StoreLootAction::IsLootAllowed(itemId, botAI))
|
||||
// vote = NEED;
|
||||
// break;
|
||||
// }
|
||||
// break;
|
||||
// }
|
||||
// }
|
||||
|
||||
// if (ItemTemplate const* proto = sObjectMgr->GetItemTemplate(guid.GetEntry()))
|
||||
// {
|
||||
// switch (proto->Class)
|
||||
// {
|
||||
// case ITEM_CLASS_WEAPON:
|
||||
// case ITEM_CLASS_ARMOR:
|
||||
// if (!QueryItemUsage(proto).empty())
|
||||
// vote = NEED;
|
||||
// break;
|
||||
// default:
|
||||
// if (StoreLootAction::IsLootAllowed(guid.GetEntry(), botAI))
|
||||
// vote = NEED;
|
||||
// break;
|
||||
// }
|
||||
// }
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -235,3 +203,24 @@ bool CanBotUseToken(ItemTemplate const* proto, Player* bot)
|
||||
|
||||
return false; // Bot's class cannot use this token
|
||||
}
|
||||
|
||||
bool RollUniqueCheck(ItemTemplate const* proto, Player* bot)
|
||||
{
|
||||
// Count the total number of the item (equipped + in bags)
|
||||
uint32 totalItemCount = bot->GetItemCount(proto->ItemId, true);
|
||||
|
||||
// Count the number of the item in bags only
|
||||
uint32 bagItemCount = bot->GetItemCount(proto->ItemId, false);
|
||||
|
||||
// Determine if the unique item is already equipped
|
||||
bool isEquipped = (totalItemCount > bagItemCount);
|
||||
if (isEquipped && proto->HasFlag(ITEM_FLAG_UNIQUE_EQUIPPABLE))
|
||||
{
|
||||
return true; // Unique Item is already equipped
|
||||
}
|
||||
else if (proto->HasFlag(ITEM_FLAG_UNIQUE_EQUIPPABLE) && (bagItemCount > 1))
|
||||
{
|
||||
return true; // Unique item already in bag, don't roll for it
|
||||
}
|
||||
return false; // Item is not equipped or in bags, roll for it
|
||||
}
|
||||
|
||||
@@ -26,6 +26,7 @@ protected:
|
||||
};
|
||||
|
||||
bool CanBotUseToken(ItemTemplate const* proto, Player* bot);
|
||||
bool RollUniqueCheck(ItemTemplate const* proto, Player* bot);
|
||||
|
||||
class MasterLootRollAction : public LootRollAction
|
||||
{
|
||||
|
||||
@@ -183,7 +183,7 @@ void OutfitAction::Update(std::string const name)
|
||||
{
|
||||
ListItemsVisitor visitor;
|
||||
IterateItems(&visitor, ITERATE_ITEMS_IN_EQUIP);
|
||||
|
||||
|
||||
ItemIds items;
|
||||
for (std::map<uint32, uint32>::iterator i = visitor.items.begin(); i != visitor.items.end(); ++i)
|
||||
items.insert(i->first);
|
||||
|
||||
@@ -19,8 +19,14 @@ bool ReleaseSpiritAction::Execute(Event event)
|
||||
{
|
||||
if (bot->IsAlive())
|
||||
{
|
||||
botAI->TellMasterNoFacing("I am not dead, will wait here");
|
||||
botAI->ChangeStrategy("-follow,+stay", BOT_STATE_NON_COMBAT);
|
||||
if (!bot->InBattleground())
|
||||
{
|
||||
botAI->TellMasterNoFacing("I am not dead, will wait here");
|
||||
// -follow in bg is overwriten each tick with +follow
|
||||
// +stay in bg causes stuttering effect as bot is cycled between +stay and +follow each tick
|
||||
botAI->ChangeStrategy("-follow,+stay", BOT_STATE_NON_COMBAT);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -37,6 +43,7 @@ bool ReleaseSpiritAction::Execute(Event event)
|
||||
botAI->TellMasterNoFacing(message);
|
||||
|
||||
IncrementDeathCount();
|
||||
bot->DurabilityRepairAll(false, 1.0f, false);
|
||||
LogRelease("released");
|
||||
|
||||
WorldPacket releasePacket(CMSG_REPOP_REQUEST);
|
||||
@@ -73,6 +80,7 @@ void ReleaseSpiritAction::LogRelease(const std::string& releaseMsg, bool isAutoR
|
||||
bool AutoReleaseSpiritAction::Execute(Event event)
|
||||
{
|
||||
IncrementDeathCount();
|
||||
bot->DurabilityRepairAll(false, 1.0f, false);
|
||||
LogRelease("auto released", true);
|
||||
|
||||
WorldPacket packet(CMSG_REPOP_REQUEST);
|
||||
|
||||
@@ -8,6 +8,8 @@
|
||||
#include "Event.h"
|
||||
#include "LastMovementValue.h"
|
||||
#include "Playerbots.h"
|
||||
#include "PlayerbotAIConfig.h"
|
||||
#include "Config.h"
|
||||
|
||||
bool TaxiAction::Execute(Event event)
|
||||
{
|
||||
@@ -48,6 +50,36 @@ bool TaxiAction::Execute(Event event)
|
||||
}
|
||||
}
|
||||
|
||||
// stagger bot takeoff
|
||||
uint32 delayMin = sConfigMgr->GetOption<uint32>("AiPlayerbot.BotTaxiDelayMinMs", 350u, false);
|
||||
uint32 delayMax = sConfigMgr->GetOption<uint32>("AiPlayerbot.BotTaxiDelayMaxMs", 5000u, false);
|
||||
uint32 gapMs = sConfigMgr->GetOption<uint32>("AiPlayerbot.BotTaxiGapMs", 200u, false);
|
||||
uint32 gapJitterMs = sConfigMgr->GetOption<uint32>("AiPlayerbot.BotTaxiGapJitterMs", 100u, false);
|
||||
|
||||
// Only for follower bots
|
||||
if (botAI->HasRealPlayerMaster())
|
||||
{
|
||||
uint32 index = botAI->GetGroupSlotIndex(bot);
|
||||
uint32 delay = delayMin + index * gapMs + urand(0, gapJitterMs);
|
||||
|
||||
delay = std::min(delay, delayMax);
|
||||
|
||||
// Store the npc’s GUID so we can re-acquire the pointer later
|
||||
ObjectGuid npcGuid = npc->GetGUID();
|
||||
|
||||
// schedule the take-off
|
||||
botAI->AddTimedEvent(
|
||||
[bot = bot, &movement, npcGuid]() -> void
|
||||
{
|
||||
if (Creature* npcPtr = ObjectAccessor::GetCreature(*bot, npcGuid))
|
||||
if (!movement.taxiNodes.empty())
|
||||
bot->ActivateTaxiPathTo(movement.taxiNodes, npcPtr);
|
||||
},
|
||||
delay);
|
||||
botAI->SetNextCheckDelay(delay + 50);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (param == "?")
|
||||
{
|
||||
botAI->TellMasterNoFacing("=== Taxi ===");
|
||||
|
||||
@@ -140,17 +140,16 @@ bool TellEstimatedDpsAction::Execute(Event event)
|
||||
bool TellCalculateItemAction::Execute(Event event)
|
||||
{
|
||||
std::string const text = event.getParam();
|
||||
ItemIds ids = chat->parseItems(text);
|
||||
ItemWithRandomProperty item = chat->parseItemWithRandomProperty(text);
|
||||
StatsWeightCalculator calculator(bot);
|
||||
for (const uint32 &id : ids)
|
||||
{
|
||||
const ItemTemplate* proto = sObjectMgr->GetItemTemplate(id);
|
||||
if (!proto)
|
||||
continue;
|
||||
float score = calculator.CalculateItem(id);
|
||||
std::ostringstream out;
|
||||
out << "Calculated score of " << chat->FormatItem(proto) << " : " << score;
|
||||
botAI->TellMasterNoFacing(out.str());
|
||||
}
|
||||
|
||||
const ItemTemplate* proto = sObjectMgr->GetItemTemplate(item.itemId);
|
||||
if (!proto)
|
||||
return false;
|
||||
float score = calculator.CalculateItem(item.itemId, item.randomPropertyId);
|
||||
|
||||
std::ostringstream out;
|
||||
out << "Calculated score of " << chat->FormatItem(proto) << " : " << score;
|
||||
botAI->TellMasterNoFacing(out.str());
|
||||
return true;
|
||||
}
|
||||
@@ -15,6 +15,16 @@ bool TradeAction::Execute(Event event)
|
||||
{
|
||||
std::string const text = event.getParam();
|
||||
|
||||
// Table with prefixes to be excluded from analysis
|
||||
static const std::vector<std::string> excludedPrefixes = {"RPLL_H_"};
|
||||
|
||||
// If text starts with any excluded prefix, don't process it further.
|
||||
for (const auto& prefix : excludedPrefixes)
|
||||
{
|
||||
if (text.find(prefix) == 0)
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!bot->GetTrader())
|
||||
{
|
||||
GuidVector guids = chat->parseGameobjects(text);
|
||||
|
||||
@@ -46,7 +46,20 @@ void UnequipAction::UnequipItem(FindItemVisitor* visitor)
|
||||
IterateItems(visitor, ITERATE_ALL_ITEMS);
|
||||
std::vector<Item*> items = visitor->GetResult();
|
||||
if (!items.empty())
|
||||
UnequipItem(*items.begin());
|
||||
{
|
||||
// Prefer equipped item
|
||||
auto equipped = std::find_if(items.begin(), items.end(),
|
||||
[](Item* item)
|
||||
{
|
||||
uint8 bag = item->GetBagSlot();
|
||||
return bag == INVENTORY_SLOT_BAG_0 && item->GetSlot() < EQUIPMENT_SLOT_END;
|
||||
});
|
||||
|
||||
if (equipped != items.end())
|
||||
UnequipItem(*equipped);
|
||||
else
|
||||
UnequipItem(*items.begin());
|
||||
}
|
||||
}
|
||||
|
||||
void UnequipAction::UnequipItem(Item* item)
|
||||
|
||||
@@ -40,15 +40,15 @@ bool XpGainAction::Execute(Event event)
|
||||
BroadcastHelper::BroadcastKill(botAI, bot, creature);
|
||||
}
|
||||
|
||||
// playerbotsXPrate is now implemented in OnPlayerGiveXP script
|
||||
// if (!sRandomPlayerbotMgr->IsRandomBot(bot) || sPlayerbotAIConfig->playerbotsXPrate == 1)
|
||||
// randomBotXPRate is now implemented in OnPlayerGiveXP script
|
||||
// if (!sRandomPlayerbotMgr->IsRandomBot(bot) || sPlayerbotAIConfig->randomBotXPRate == 1)
|
||||
// return true;
|
||||
|
||||
// Unit* victim = nullptr;
|
||||
// if (guid)
|
||||
// victim = botAI->GetUnit(guid);
|
||||
|
||||
// xpgain = xpgain * (sPlayerbotAIConfig->playerbotsXPrate - 1);
|
||||
// xpgain = xpgain * (sPlayerbotAIConfig->randomBotXPRate - 1);
|
||||
// GiveXP(xpgain, victim);
|
||||
|
||||
return true;
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include "BearTankDruidStrategy.h"
|
||||
#include "CasterDruidStrategy.h"
|
||||
#include "CatDpsDruidStrategy.h"
|
||||
#include "OffhealDruidCatStrategy.h"
|
||||
#include "DruidActions.h"
|
||||
#include "DruidBearActions.h"
|
||||
#include "DruidCatActions.h"
|
||||
@@ -61,6 +62,7 @@ public:
|
||||
creators["caster"] = &DruidDruidStrategyFactoryInternal::caster;
|
||||
creators["dps"] = &DruidDruidStrategyFactoryInternal::cat;
|
||||
creators["heal"] = &DruidDruidStrategyFactoryInternal::heal;
|
||||
creators["offheal"] = &DruidDruidStrategyFactoryInternal::offheal;
|
||||
}
|
||||
|
||||
private:
|
||||
@@ -68,6 +70,7 @@ private:
|
||||
static Strategy* cat(PlayerbotAI* botAI) { return new CatDpsDruidStrategy(botAI); }
|
||||
static Strategy* caster(PlayerbotAI* botAI) { return new CasterDruidStrategy(botAI); }
|
||||
static Strategy* heal(PlayerbotAI* botAI) { return new HealDruidStrategy(botAI); }
|
||||
static Strategy* offheal(PlayerbotAI* botAI) { return new OffhealDruidCatStrategy(botAI); }
|
||||
};
|
||||
|
||||
class DruidTriggerFactoryInternal : public NamedObjectContext<Trigger>
|
||||
|
||||
179
src/strategy/druid/OffhealDruidCatStrategy.cpp
Normal file
179
src/strategy/druid/OffhealDruidCatStrategy.cpp
Normal file
@@ -0,0 +1,179 @@
|
||||
/*
|
||||
* 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 "OffhealDruidCatStrategy.h"
|
||||
|
||||
#include "Playerbots.h"
|
||||
#include "Strategy.h"
|
||||
|
||||
class OffhealDruidCatStrategyActionNodeFactory : public NamedObjectFactory<ActionNode>
|
||||
{
|
||||
public:
|
||||
OffhealDruidCatStrategyActionNodeFactory()
|
||||
{
|
||||
creators["cat form"] = &cat_form;
|
||||
creators["mangle (cat)"] = &mangle_cat;
|
||||
creators["shred"] = &shred;
|
||||
creators["rake"] = &rake;
|
||||
creators["rip"] = &rip;
|
||||
creators["ferocious bite"] = &ferocious_bite;
|
||||
creators["savage roar"] = &savage_roar;
|
||||
creators["faerie fire (feral)"] = &faerie_fire_feral;
|
||||
creators["healing touch on party"] = &healing_touch_on_party;
|
||||
creators["regrowth on party"] = ®rowth_on_party;
|
||||
creators["rejuvenation on party"] = &rejuvenation_on_party;
|
||||
}
|
||||
|
||||
private:
|
||||
static ActionNode* cat_form([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode("cat form",
|
||||
/*P*/ nullptr,
|
||||
/*A*/ nullptr,
|
||||
/*C*/ nullptr);
|
||||
}
|
||||
|
||||
static ActionNode* mangle_cat([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode("mangle (cat)",
|
||||
/*P*/ nullptr,
|
||||
/*A*/ nullptr,
|
||||
/*C*/ nullptr);
|
||||
}
|
||||
|
||||
static ActionNode* shred([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode("shred",
|
||||
/*P*/ nullptr,
|
||||
/*A*/ NextAction::array(0, new NextAction("claw"), nullptr),
|
||||
/*C*/ nullptr);
|
||||
}
|
||||
|
||||
static ActionNode* rake([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode("rake",
|
||||
/*P*/ nullptr,
|
||||
/*A*/ nullptr,
|
||||
/*C*/ nullptr);
|
||||
}
|
||||
|
||||
static ActionNode* rip([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode("rip",
|
||||
/*P*/ nullptr,
|
||||
/*A*/ nullptr,
|
||||
/*C*/ nullptr);
|
||||
}
|
||||
|
||||
static ActionNode* ferocious_bite([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode("ferocious bite",
|
||||
/*P*/ nullptr,
|
||||
/*A*/ NextAction::array(0, new NextAction("rip"), nullptr),
|
||||
/*C*/ nullptr);
|
||||
}
|
||||
|
||||
static ActionNode* savage_roar([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode("savage roar",
|
||||
/*P*/ nullptr,
|
||||
/*A*/ nullptr,
|
||||
/*C*/ nullptr);
|
||||
}
|
||||
|
||||
static ActionNode* faerie_fire_feral([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode("faerie fire (feral)",
|
||||
/*P*/ nullptr,
|
||||
/*A*/ nullptr,
|
||||
/*C*/ nullptr);
|
||||
}
|
||||
|
||||
static ActionNode* healing_touch_on_party([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode("healing touch on party",
|
||||
/*P*/ NextAction::array(0, new NextAction("caster form"), nullptr),
|
||||
/*A*/ nullptr,
|
||||
/*C*/ NextAction::array(0, new NextAction("cat form"), nullptr));
|
||||
}
|
||||
|
||||
static ActionNode* regrowth_on_party([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode("regrowth on party",
|
||||
/*P*/ NextAction::array(0, new NextAction("caster form"), nullptr),
|
||||
/*A*/ nullptr,
|
||||
/*C*/ NextAction::array(0, new NextAction("cat form"), nullptr));
|
||||
}
|
||||
|
||||
static ActionNode* rejuvenation_on_party([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode("rejuvenation on party",
|
||||
/*P*/ NextAction::array(0, new NextAction("caster form"), nullptr),
|
||||
/*A*/ nullptr,
|
||||
/*C*/ NextAction::array(0, new NextAction("cat form"), nullptr));
|
||||
}
|
||||
};
|
||||
|
||||
OffhealDruidCatStrategy::OffhealDruidCatStrategy(PlayerbotAI* botAI) : FeralDruidStrategy(botAI)
|
||||
{
|
||||
actionNodeFactories.Add(new OffhealDruidCatStrategyActionNodeFactory());
|
||||
}
|
||||
|
||||
NextAction** OffhealDruidCatStrategy::getDefaultActions()
|
||||
{
|
||||
return NextAction::array(0, new NextAction("mangle (cat)", ACTION_DEFAULT + 0.5f),
|
||||
new NextAction("shred", ACTION_DEFAULT + 0.4f),
|
||||
new NextAction("rake", ACTION_DEFAULT + 0.3f), new NextAction("melee", ACTION_DEFAULT),
|
||||
new NextAction("cat form", ACTION_DEFAULT - 0.1f), nullptr);
|
||||
}
|
||||
|
||||
void OffhealDruidCatStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
||||
{
|
||||
FeralDruidStrategy::InitTriggers(triggers);
|
||||
|
||||
triggers.push_back(
|
||||
new TriggerNode("cat form", NextAction::array(0, new NextAction("cat form", ACTION_HIGH + 8), nullptr)));
|
||||
triggers.push_back(
|
||||
new TriggerNode("savage roar", NextAction::array(0, new NextAction("savage roar", ACTION_HIGH + 7), nullptr)));
|
||||
triggers.push_back(new TriggerNode("combo points available",
|
||||
NextAction::array(0, new NextAction("rip", ACTION_HIGH + 6), nullptr)));
|
||||
triggers.push_back(new TriggerNode(
|
||||
"ferocious bite time", NextAction::array(0, new NextAction("ferocious bite", ACTION_HIGH + 5), nullptr)));
|
||||
triggers.push_back(
|
||||
new TriggerNode("target with combo points almost dead",
|
||||
NextAction::array(0, new NextAction("ferocious bite", ACTION_HIGH + 4), nullptr)));
|
||||
triggers.push_back(new TriggerNode("mangle (cat)",
|
||||
NextAction::array(0, new NextAction("mangle (cat)", ACTION_HIGH + 3), nullptr)));
|
||||
triggers.push_back(new TriggerNode("rake", NextAction::array(0, new NextAction("rake", ACTION_HIGH + 2), nullptr)));
|
||||
triggers.push_back(new TriggerNode("almost full energy available",
|
||||
NextAction::array(0, new NextAction("shred", ACTION_DEFAULT + 0.4f), nullptr)));
|
||||
triggers.push_back(new TriggerNode("combo points not full",
|
||||
NextAction::array(0, new NextAction("shred", ACTION_DEFAULT + 0.4f), nullptr)));
|
||||
triggers.push_back(new TriggerNode(
|
||||
"faerie fire (feral)", NextAction::array(0, new NextAction("faerie fire (feral)", ACTION_NORMAL), nullptr)));
|
||||
triggers.push_back(new TriggerNode("enemy out of melee",
|
||||
NextAction::array(0, new NextAction("feral charge - cat", ACTION_HIGH + 9),
|
||||
new NextAction("dash", ACTION_HIGH + 8), nullptr)));
|
||||
triggers.push_back(
|
||||
new TriggerNode("medium aoe", NextAction::array(0, new NextAction("swipe (cat)", ACTION_HIGH + 3), nullptr)));
|
||||
triggers.push_back(new TriggerNode(
|
||||
"low energy", NextAction::array(0, new NextAction("tiger's fury", ACTION_NORMAL + 1), nullptr)));
|
||||
|
||||
triggers.push_back(new TriggerNode(
|
||||
"party member critical health",
|
||||
NextAction::array(0, new NextAction("regrowth on party", ACTION_CRITICAL_HEAL + 6),
|
||||
new NextAction("healing touch on party", ACTION_CRITICAL_HEAL + 5), nullptr)));
|
||||
triggers.push_back(new TriggerNode(
|
||||
"party member low health",
|
||||
NextAction::array(0, new NextAction("healing touch on party", ACTION_MEDIUM_HEAL + 5), nullptr)));
|
||||
triggers.push_back(
|
||||
new TriggerNode("party member medium health",
|
||||
NextAction::array(0, new NextAction("rejuvenation on party", ACTION_LIGHT_HEAL + 8), nullptr)));
|
||||
triggers.push_back(new TriggerNode(
|
||||
"party member to heal out of spell range",
|
||||
NextAction::array(0, new NextAction("reach party member to heal", ACTION_EMERGENCY + 3), nullptr)));
|
||||
triggers.push_back(
|
||||
new TriggerNode("low mana", NextAction::array(0, new NextAction("innervate", ACTION_HIGH + 4), nullptr)));
|
||||
}
|
||||
27
src/strategy/druid/OffhealDruidCatStrategy.h
Normal file
27
src/strategy/druid/OffhealDruidCatStrategy.h
Normal file
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef _PLAYERBOT_OFFHEALDRUIDCATSTRATEGY_H
|
||||
#define _PLAYERBOT_OFFHEALDRUIDCATSTRATEGY_H
|
||||
|
||||
#include "FeralDruidStrategy.h"
|
||||
|
||||
class PlayerbotAI;
|
||||
|
||||
class OffhealDruidCatStrategy : public FeralDruidStrategy
|
||||
{
|
||||
public:
|
||||
OffhealDruidCatStrategy(PlayerbotAI* botAI);
|
||||
|
||||
void InitTriggers(std::vector<TriggerNode*>& triggers) override;
|
||||
std::string const getName() override { return "offheal"; }
|
||||
NextAction** getDefaultActions() override;
|
||||
uint32 GetType() const override
|
||||
{
|
||||
return STRATEGY_TYPE_COMBAT | STRATEGY_TYPE_DPS | STRATEGY_TYPE_HEAL | STRATEGY_TYPE_MELEE;
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -15,6 +15,7 @@
|
||||
#include "wotlk/utgardepinnacle/UtgardePinnacleStrategy.h"
|
||||
#include "wotlk/cullingofstratholme/CullingOfStratholmeStrategy.h"
|
||||
#include "wotlk/forgeofsouls/ForgeOfSoulsStrategy.h"
|
||||
#include "wotlk/pitofsaron/PitOfSaronStrategy.h"
|
||||
#include "wotlk/trialofthechampion/TrialOfTheChampionStrategy.h"
|
||||
|
||||
/*
|
||||
@@ -79,10 +80,11 @@ class DungeonStrategyContext : public NamedObjectContext<Strategy>
|
||||
static Strategy* wotlk_up(PlayerbotAI* botAI) { return new WotlkDungeonUPStrategy(botAI); }
|
||||
static Strategy* wotlk_cos(PlayerbotAI* botAI) { return new WotlkDungeonCoSStrategy(botAI); }
|
||||
static Strategy* wotlk_fos(PlayerbotAI* botAI) { return new WotlkDungeonFoSStrategy(botAI); }
|
||||
static Strategy* wotlk_pos(PlayerbotAI* botAI) { return new WotlkDungeonPoSStrategy(botAI); }
|
||||
static Strategy* wotlk_toc(PlayerbotAI* botAI) { return new WotlkDungeonToCStrategy(botAI); }
|
||||
// NYI from here down
|
||||
static Strategy* wotlk_hor(PlayerbotAI* botAI) { return new WotlkDungeonUKStrategy(botAI); }
|
||||
static Strategy* wotlk_pos(PlayerbotAI* botAI) { return new WotlkDungeonUKStrategy(botAI); }
|
||||
|
||||
|
||||
};
|
||||
|
||||
|
||||
@@ -14,8 +14,8 @@
|
||||
#include "utgardepinnacle/UtgardePinnacleActionContext.h"
|
||||
#include "cullingofstratholme/CullingOfStratholmeActionContext.h"
|
||||
#include "forgeofsouls/ForgeOfSoulsActionContext.h"
|
||||
#include "pitofsaron/PitOfSaronActionContext.h"
|
||||
#include "trialofthechampion/TrialOfTheChampionActionContext.h"
|
||||
// #include "hallsofreflection/HallsOfReflectionActionContext.h"
|
||||
// #include "pitofsaron/PitOfSaronActionContext.h"
|
||||
|
||||
#endif
|
||||
|
||||
@@ -14,8 +14,9 @@
|
||||
#include "utgardepinnacle/UtgardePinnacleTriggerContext.h"
|
||||
#include "cullingofstratholme/CullingOfStratholmeTriggerContext.h"
|
||||
#include "forgeofsouls/ForgeOfSoulsTriggerContext.h"
|
||||
#include "pitofsaron/PitOfSaronTriggerContext.h"
|
||||
#include "trialofthechampion/TrialOfTheChampionTriggerContext.h"
|
||||
// #include "hallsofreflection/HallsOfReflectionTriggerContext.h"
|
||||
// #include "pitofsaron/PitOfSaronTriggerContext.h"
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
@@ -13,11 +13,13 @@ class WotlkDungeonFoSActionContext : public NamedObjectContext<Action>
|
||||
creators["move from bronjahm"] = &WotlkDungeonFoSActionContext::move_from_bronjahm;
|
||||
creators["attack corrupted soul fragment"] = &WotlkDungeonFoSActionContext::attack_corrupted_soul_fragment;
|
||||
creators["bronjahm group position"] = &WotlkDungeonFoSActionContext::bronjahm_group_position;
|
||||
creators["devourer of souls"] = &WotlkDungeonFoSActionContext::devourer_of_souls;
|
||||
}
|
||||
private:
|
||||
static Action* move_from_bronjahm(PlayerbotAI* ai) { return new MoveFromBronjahmAction(ai); }
|
||||
static Action* attack_corrupted_soul_fragment(PlayerbotAI* ai) { return new AttackCorruptedSoulFragmentAction(ai); }
|
||||
static Action* bronjahm_group_position(PlayerbotAI* ai) { return new BronjahmGroupPositionAction(ai); }
|
||||
static Action* devourer_of_souls(PlayerbotAI* ai) { return new DevourerOfSoulsAction(ai); }
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include "Playerbots.h"
|
||||
#include "ForgeOfSoulsActions.h"
|
||||
#include "ForgeOfSoulsStrategy.h"
|
||||
#include "SharedDefines.h"
|
||||
|
||||
bool MoveFromBronjahmAction::Execute(Event event)
|
||||
{
|
||||
@@ -8,36 +9,47 @@ bool MoveFromBronjahmAction::Execute(Event event)
|
||||
if (!boss)
|
||||
return false;
|
||||
|
||||
|
||||
float distance = bot->GetExactDist2d(boss->GetPosition());
|
||||
float targetDis = 20.0f;
|
||||
if (distance >= targetDis)
|
||||
return false;
|
||||
return MoveAway(boss, targetDis - distance);
|
||||
if (bot->GetExactDist2d(boss) < 10.0f)
|
||||
return FleePosition(boss->GetPosition(), 15.0f, 2000U);
|
||||
else
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
bool AttackCorruptedSoulFragmentAction::Execute(Event event)
|
||||
{
|
||||
Unit* fragment = nullptr;
|
||||
Unit* currentTarget = AI_VALUE(Unit*, "current target");
|
||||
GuidVector targets = AI_VALUE(GuidVector, "possible targets");
|
||||
|
||||
GuidVector targets = AI_VALUE(GuidVector, "possible targets no los");
|
||||
|
||||
for (auto &target : targets)
|
||||
// If no valid skull target, search for corrupted soul fragment
|
||||
Unit* empoweredPrince = nullptr;
|
||||
for (auto i = targets.begin(); i != targets.end(); ++i)
|
||||
{
|
||||
Unit* unit = botAI->GetUnit(target);
|
||||
if (unit && unit->GetEntry() == NPC_CORRUPTED_SOUL_FRAGMENT)
|
||||
Unit* unit = botAI->GetUnit(*i);
|
||||
if (!unit || !unit->IsAlive())
|
||||
continue;
|
||||
|
||||
if (unit->GetEntry() == NPC_CORRUPTED_SOUL_FRAGMENT)
|
||||
{
|
||||
fragment = unit;
|
||||
break;
|
||||
empoweredPrince = unit;
|
||||
|
||||
// Mark corrupted soul fragment with skull if in group and not already marked
|
||||
if (Group* group = bot->GetGroup())
|
||||
{
|
||||
ObjectGuid currentSkullGuid = group->GetTargetIcon(7);
|
||||
if (currentSkullGuid.IsEmpty() || currentSkullGuid != unit->GetGUID())
|
||||
{
|
||||
group->SetTargetIcon(7, bot->GetGUID(), unit->GetGUID()); // 7 = skull
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (fragment && AI_VALUE(Unit*, "current target") != fragment)
|
||||
return Attack(fragment);
|
||||
|
||||
return false;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -47,28 +59,73 @@ bool BronjahmGroupPositionAction::Execute(Event event)
|
||||
if (!boss)
|
||||
return false;
|
||||
|
||||
if (botAI->IsTank(bot))
|
||||
Aura* aura = botAI->GetAura("soulstorm", boss);
|
||||
bool hasAura = aura;
|
||||
|
||||
Unit* corruptedSoul = bot->FindNearestCreature(NPC_CORRUPTED_SOUL_FRAGMENT, 50.0f);
|
||||
bool activeSoulExists = corruptedSoul && corruptedSoul->IsAlive();
|
||||
|
||||
if (botAI->IsTank(bot) && botAI->HasAggro(boss))
|
||||
{
|
||||
bot->SetTarget(boss->GetGUID());
|
||||
if (AI_VALUE2(bool, "has aggro", "current target"))
|
||||
// If any corrupted soul exists, handle positioning carefully
|
||||
if (activeSoulExists)
|
||||
{
|
||||
GuidVector npcs = AI_VALUE(GuidVector, "nearest hostile npcs");
|
||||
for (auto& npc : npcs)
|
||||
{
|
||||
Unit* unit = botAI->GetUnit(npc);
|
||||
if (unit && unit->GetEntry() == NPC_CORRUPTED_SOUL_FRAGMENT)
|
||||
{
|
||||
// Soul exists - check positions
|
||||
float soulToBossDist = unit->GetExactDist2d(boss);
|
||||
float tankToBossDist = bot->GetExactDist2d(boss);
|
||||
float soulToTankDist = unit->GetExactDist2d(bot);
|
||||
|
||||
// Handle edge case: if soul is between tank and boss
|
||||
// This happens when: (tankToBossDist > soulToBossDist) AND (soulToTankDist < tankToBossDist)
|
||||
if (tankToBossDist > soulToBossDist && soulToTankDist < tankToBossDist)
|
||||
{
|
||||
// Calculate position 5 yards behind boss from the soul's perspective
|
||||
float angle = boss->GetAngle(unit); // Angle from boss to soul
|
||||
float oppositeAngle = Position::NormalizeOrientation(angle + M_PI); // Opposite direction
|
||||
|
||||
// Calculate position 5 yards behind boss
|
||||
float x = boss->GetPositionX() + 5.0f * cos(oppositeAngle);
|
||||
float y = boss->GetPositionY() + 5.0f * sin(oppositeAngle);
|
||||
float z = boss->GetPositionZ();
|
||||
|
||||
return MoveTo(bot->GetMapId(), x, y, z, false, false, false, true,
|
||||
MovementPriority::MOVEMENT_NORMAL);
|
||||
}
|
||||
|
||||
// If soul is near boss, flee from boss
|
||||
if (soulToBossDist < 10.0f)
|
||||
return FleePosition(unit->GetPosition(), 13.0f, 1000U);
|
||||
|
||||
// If soul exists but none of the above conditions, don't move to tank position yet
|
||||
bot->SetFacingToObject(boss);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// No souls exist, safe to move to tank position
|
||||
if (bot->GetExactDist2d(BRONJAHM_TANK_POSITION) > 5.0f)
|
||||
{
|
||||
return MoveTo(bot->GetMapId(), BRONJAHM_TANK_POSITION.GetPositionX(),
|
||||
BRONJAHM_TANK_POSITION.GetPositionY(), BRONJAHM_TANK_POSITION.GetPositionZ(), false,
|
||||
false, false, true, MovementPriority::MOVEMENT_NORMAL);
|
||||
else
|
||||
return Attack(boss);
|
||||
else
|
||||
{
|
||||
return Attack(boss);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
float maxMovement = 10.0f;
|
||||
|
||||
if (bot->GetExactDist2d(boss) > maxMovement)
|
||||
if (bot->GetExactDist2d(boss) > maxMovement && !activeSoulExists && (hasAura || boss->FindCurrentSpellBySpellId(SPELL_SOULSTORM_VISUAL) || boss->FindCurrentSpellBySpellId(SPELL_SOULSTORM_VISUAL2)))
|
||||
{
|
||||
if (bot->getClass() == CLASS_HUNTER)
|
||||
if (botAI->IsRanged(bot))
|
||||
{
|
||||
return Move(bot->GetAngle(boss), fmin(bot->GetExactDist2d(boss) - 6.5f, maxMovement));
|
||||
}
|
||||
@@ -80,8 +137,33 @@ bool BronjahmGroupPositionAction::Execute(Event event)
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool BronjahmGroupPositionAction::isUseful() { return true; }
|
||||
|
||||
bool DevourerOfSoulsAction::Execute(Event event)
|
||||
{
|
||||
Unit* boss = AI_VALUE2(Unit*, "find target", "devourer of souls");
|
||||
if (!boss)
|
||||
return false;
|
||||
|
||||
Aura* aura = botAI->GetAura("mirrored soul", boss);
|
||||
bool hasAura = aura;
|
||||
|
||||
if (!botAI->IsTank(bot) && !botAI->IsHeal(bot) && hasAura)
|
||||
{
|
||||
// Calculate the opposite direction
|
||||
float angle = bot->GetAngle(boss);
|
||||
float newAngle = Position::NormalizeOrientation(angle + M_PI); // Add 180 degrees (PI radians)
|
||||
|
||||
// Set the bot's orientation to face away from DoS = no attacks
|
||||
bot->SetFacingTo(newAngle);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -33,5 +33,19 @@ public:
|
||||
bool isUseful() override;
|
||||
};
|
||||
|
||||
class DevourerOfSoulsAction : public AttackAction
|
||||
{
|
||||
public:
|
||||
DevourerOfSoulsAction(PlayerbotAI * ai, float distance = 10.0f, float delta_angle = M_PI / 8)
|
||||
: AttackAction(ai, "devourer of souls")
|
||||
{
|
||||
this->distance = distance;
|
||||
this->delta_angle = delta_angle;
|
||||
}
|
||||
virtual bool Execute(Event event);
|
||||
|
||||
protected:
|
||||
float distance, delta_angle;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -15,8 +15,7 @@ float BronjahmMultiplier::GetValue(Action* action) {
|
||||
if (dynamic_cast<TankAssistAction*>(action))
|
||||
return 0.0f;
|
||||
|
||||
if (boss->FindCurrentSpellBySpellId(SPELL_CORRUPT_SOUL) &&
|
||||
bot->HasAura(SPELL_CORRUPT_SOUL))
|
||||
if (bot->HasAura(SPELL_CORRUPT_SOUL))
|
||||
{
|
||||
if (dynamic_cast<MovementAction*>(action) && !dynamic_cast<MoveFromBronjahmAction*>(action))
|
||||
{
|
||||
|
||||
@@ -1,18 +1,20 @@
|
||||
#include "ForgeOfSoulsStrategy.h"
|
||||
#include "ForgeOfSoulsMultipliers.h"
|
||||
|
||||
void WotlkDungeonFoSStrategy::InitTriggers(std::vector<TriggerNode*>& triggers) {
|
||||
triggers.push_back(
|
||||
new TriggerNode("move from bronjahm",
|
||||
NextAction::array(0, new NextAction("move from bronjahm", ACTION_MOVE + 5), nullptr)));
|
||||
triggers.push_back(new TriggerNode(
|
||||
"switch to soul fragment", NextAction::array(0, new NextAction("attack corrupted soul fragment", ACTION_RAID + 1), nullptr)));
|
||||
void WotlkDungeonFoSStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
||||
{
|
||||
triggers.push_back(new TriggerNode("move from bronjahm",
|
||||
NextAction::array(0, new NextAction("move from bronjahm", ACTION_MOVE + 5), nullptr)));
|
||||
triggers.push_back(new TriggerNode("switch to soul fragment",
|
||||
NextAction::array(0, new NextAction("attack corrupted soul fragment", ACTION_RAID + 2), nullptr)));
|
||||
triggers.push_back(new TriggerNode("bronjahm position",
|
||||
NextAction::array(0, new NextAction("bronjahm group position", ACTION_RAID + 1), nullptr)));
|
||||
triggers.push_back(new TriggerNode("devourer of souls",
|
||||
NextAction::array(0, new NextAction("devourer of souls", ACTION_RAID + 1), nullptr)));
|
||||
}
|
||||
|
||||
void WotlkDungeonFoSStrategy::InitMultipliers(std::vector<Multiplier*>& multipliers)
|
||||
{
|
||||
multipliers.push_back(new BronjahmMultiplier(botAI));
|
||||
multipliers.push_back(new AttackFragmentMultiplier(botAI));
|
||||
//multipliers.push_back(new AttackFragmentMultiplier(botAI));
|
||||
}
|
||||
|
||||
@@ -5,20 +5,22 @@
|
||||
#include "AiObjectContext.h"
|
||||
#include "ForgeOfSoulsTriggers.h"
|
||||
|
||||
class WotlkDungeonFosTriggerContext : public NamedObjectContext<Trigger>
|
||||
class WotlkDungeonFoSTriggerContext : public NamedObjectContext<Trigger>
|
||||
{
|
||||
public:
|
||||
WotlkDungeonFosTriggerContext()
|
||||
WotlkDungeonFoSTriggerContext()
|
||||
{
|
||||
creators["bronjahm position"] = &WotlkDungeonFosTriggerContext::bronjahm_position;
|
||||
creators["move from bronjahm"] = &WotlkDungeonFosTriggerContext::move_from_bronjahm;
|
||||
creators["switch to soul fragment"] = &WotlkDungeonFosTriggerContext::switch_to_soul_fragment;
|
||||
creators["bronjahm position"] = &WotlkDungeonFoSTriggerContext::bronjahm_position;
|
||||
creators["move from bronjahm"] = &WotlkDungeonFoSTriggerContext::move_from_bronjahm;
|
||||
creators["switch to soul fragment"] = &WotlkDungeonFoSTriggerContext::switch_to_soul_fragment;
|
||||
creators["devourer of souls"] = &WotlkDungeonFoSTriggerContext::devourer_of_souls;
|
||||
}
|
||||
|
||||
private:
|
||||
static Trigger* move_from_bronjahm(PlayerbotAI* ai) { return new MoveFromBronjahmTrigger(ai); }
|
||||
static Trigger* switch_to_soul_fragment(PlayerbotAI* ai) { return new SwitchToSoulFragment(ai); }
|
||||
static Trigger* bronjahm_position(PlayerbotAI* ai) { return new BronjahmPositionTrigger(ai); }
|
||||
static Trigger* devourer_of_souls(PlayerbotAI* ai) { return new DevourerOfSoulsTrigger(ai); }
|
||||
};
|
||||
|
||||
#endif // !_PLAYERBOT_WOTLKDUNGEONFOSTRIGGERCONTEXT_H
|
||||
|
||||
@@ -6,34 +6,50 @@
|
||||
bool MoveFromBronjahmTrigger::IsActive()
|
||||
{
|
||||
Unit* boss = AI_VALUE2(Unit*, "find target", "bronjahm");
|
||||
if (!boss)
|
||||
return false;
|
||||
|
||||
if (boss && boss->FindCurrentSpellBySpellId(SPELL_CORRUPT_SOUL) && bot->HasAura(SPELL_CORRUPT_SOUL))
|
||||
return true;
|
||||
if (!boss->FindCurrentSpellBySpellId(SPELL_CORRUPT_SOUL))
|
||||
return false;
|
||||
|
||||
return false;
|
||||
if (!bot->HasAura(SPELL_CORRUPT_SOUL))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SwitchToSoulFragment::IsActive()
|
||||
{
|
||||
Unit* fragment = nullptr;
|
||||
GuidVector targets = AI_VALUE(GuidVector, "possible targets no los");
|
||||
Unit* boss = AI_VALUE2(Unit*, "find target", "bronjahm");
|
||||
if (!boss)
|
||||
return false;
|
||||
|
||||
for (auto& target : targets)
|
||||
{
|
||||
Unit* unit = botAI->GetUnit(target);
|
||||
if (unit && unit->GetEntry() == NPC_CORRUPTED_SOUL_FRAGMENT)
|
||||
{
|
||||
if (botAI->IsDps(bot))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
Unit* corruptedSoul = bot->FindNearestCreature(NPC_CORRUPTED_SOUL_FRAGMENT, 50.0f);
|
||||
bool activeSoulExists = corruptedSoul && corruptedSoul->IsAlive();
|
||||
|
||||
return false;
|
||||
|
||||
if (!activeSoulExists)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BronjahmPositionTrigger::IsActive()
|
||||
{
|
||||
Unit* boss = AI_VALUE2(Unit*, "find target", "bronjahm");
|
||||
if (!boss)
|
||||
return false;
|
||||
|
||||
return bool(AI_VALUE2(Unit*, "find target", "bronjahm"));
|
||||
if (bot->HasAura(SPELL_CORRUPT_SOUL))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DevourerOfSoulsTrigger::IsActive()
|
||||
{
|
||||
Unit* boss = AI_VALUE2(Unit*, "find target", "devourer of souls");
|
||||
if (!boss)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -8,10 +8,16 @@
|
||||
|
||||
enum ForgeOfSoulsBronjahmIDs
|
||||
{
|
||||
// Boss1
|
||||
// Bronjahm
|
||||
NPC_CORRUPTED_SOUL_FRAGMENT = 36535,
|
||||
|
||||
SPELL_CORRUPT_SOUL = 68839
|
||||
SPELL_CORRUPT_SOUL = 68839,
|
||||
SPELL_SOULSTORM_VISUAL = 68870,
|
||||
SPELL_SOULSTORM_VISUAL2 = 68904,
|
||||
SPELL_SOULSTORM = 68872,
|
||||
|
||||
// Devourer of Souls
|
||||
SPELL_WAILING_SOULS = 68899,
|
||||
};
|
||||
|
||||
class MoveFromBronjahmTrigger : public Trigger
|
||||
@@ -37,4 +43,11 @@ public:
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class DevourerOfSoulsTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
DevourerOfSoulsTrigger(PlayerbotAI* ai) : Trigger(ai, "devourer of souls") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
#ifndef _PLAYERBOT_WOTLKDUNGEONPOSACTIONCONTEXT_H
|
||||
#define _PLAYERBOT_WOTLKDUNGEONPOSACTIONCONTEXT_H
|
||||
|
||||
#include "Action.h"
|
||||
#include "NamedObjectContext.h"
|
||||
#include "PitOfSaronActions.h"
|
||||
|
||||
class WotlkDungeonPoSActionContext : public NamedObjectContext<Action>
|
||||
{
|
||||
public:
|
||||
WotlkDungeonPoSActionContext()
|
||||
{
|
||||
creators["ick and krick"] = &WotlkDungeonPoSActionContext::ick_and_krick;
|
||||
creators["tyrannus"] = &WotlkDungeonPoSActionContext::tyrannus;
|
||||
}
|
||||
private:
|
||||
static Action* ick_and_krick(PlayerbotAI* ai) { return new IckAndKrickAction(ai); }
|
||||
static Action* tyrannus(PlayerbotAI* ai) { return new TyrannusAction(ai); }
|
||||
};
|
||||
|
||||
#endif
|
||||
315
src/strategy/dungeons/wotlk/pitofsaron/PitOfSaronActions.cpp
Normal file
315
src/strategy/dungeons/wotlk/pitofsaron/PitOfSaronActions.cpp
Normal file
@@ -0,0 +1,315 @@
|
||||
#include "Playerbots.h"
|
||||
#include "PitOfSaronActions.h"
|
||||
#include "PitOfSaronStrategy.h"
|
||||
#include "SharedDefines.h"
|
||||
|
||||
bool IckAndKrickAction::Execute(Event event)
|
||||
{
|
||||
Unit* boss = AI_VALUE2(Unit*, "find target", "Ick");
|
||||
if (!boss)
|
||||
return false;
|
||||
|
||||
std::vector<Unit*> orbs;
|
||||
bool orb = false;
|
||||
|
||||
// First gather all orbs
|
||||
GuidVector npcs1 = AI_VALUE(GuidVector, "nearest hostile npcs");
|
||||
for (auto& npc : npcs1)
|
||||
{
|
||||
Unit* unit = botAI->GetUnit(npc);
|
||||
if (unit && unit->IsAlive() && unit->HasAura(SPELL_EXPLODING_ORB_VISUAL))
|
||||
{
|
||||
orb = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool pursuit = bot->HasAura(SPELL_PURSUIT) || (!botAI->IsTank(bot) && boss->HasUnitState(UNIT_STATE_CASTING) && boss->FindCurrentSpellBySpellId(SPELL_PURSUIT));
|
||||
bool poisonNova = boss->HasUnitState(UNIT_STATE_CASTING) && (boss->FindCurrentSpellBySpellId(SPELL_POISON_NOVA_POS) || boss->FindCurrentSpellBySpellId(SPELL_POISON_NOVA_POS_HC));
|
||||
bool explosiveBarrage = orb || boss->HasUnitState(UNIT_STATE_CASTING) && (boss->FindCurrentSpellBySpellId(SPELL_EXPLOSIVE_BARRAGE_ICK) || boss->FindCurrentSpellBySpellId(SPELL_EXPLOSIVE_BARRAGE_KRICK));
|
||||
bool isTank = botAI->IsTank(bot);
|
||||
|
||||
if (pursuit && Pursuit(pursuit, boss))
|
||||
return true;
|
||||
|
||||
if (poisonNova && PoisonNova(poisonNova, boss))
|
||||
return true;
|
||||
|
||||
if (explosiveBarrage && ExplosiveBarrage(explosiveBarrage, boss))
|
||||
return true;
|
||||
|
||||
if (isTank && !pursuit && !poisonNova && !explosiveBarrage)
|
||||
{
|
||||
if (TankPosition(boss))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool IckAndKrickAction::TankPosition(Unit* boss)
|
||||
{
|
||||
if (botAI->HasAggro(boss))
|
||||
{
|
||||
float distance = bot->GetExactDist2d(ICKANDKRICK_TANK_POSITION.GetPositionX(), ICKANDKRICK_TANK_POSITION.GetPositionY());
|
||||
if (distance > 7.0f)
|
||||
{
|
||||
// Calculate direction vector
|
||||
float dirX = ICKANDKRICK_TANK_POSITION.GetPositionX() - bot->GetPositionX();
|
||||
float dirY = ICKANDKRICK_TANK_POSITION.GetPositionY() - bot->GetPositionY();
|
||||
float length = sqrt(dirX * dirX + dirY * dirY);
|
||||
dirX /= length;
|
||||
dirY /= length;
|
||||
|
||||
// Move in increments of 3.0f
|
||||
float moveX = bot->GetPositionX() + dirX * 3.0f;
|
||||
float moveY = bot->GetPositionY() + dirY * 3.0f;
|
||||
|
||||
return MoveTo(bot->GetMapId(), moveX, moveY, bot->GetPositionZ(), false, false, false, false, MovementPriority::MOVEMENT_COMBAT);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool IckAndKrickAction::Pursuit(bool pursuit, Unit* boss)
|
||||
{
|
||||
// Only execute this action when pursuit is active and for non-tank players
|
||||
if (!pursuit || botAI->IsTank(bot))
|
||||
return false;
|
||||
|
||||
// Get the tank position as a reference point
|
||||
Position tankPosition = ICKANDKRICK_TANK_POSITION;
|
||||
|
||||
// Calculate distance from boss
|
||||
float distanceToBoss = bot->GetExactDist2d(boss);
|
||||
|
||||
// If boss is close, move in a circular pattern
|
||||
if (distanceToBoss < 20.0f)
|
||||
{
|
||||
// Current bot position
|
||||
float x = bot->GetPositionX();
|
||||
float y = bot->GetPositionY();
|
||||
|
||||
// Calculate vector from tank to bot
|
||||
float dx = x - tankPosition.GetPositionX();
|
||||
float dy = y - tankPosition.GetPositionY();
|
||||
|
||||
// Normalize the vector
|
||||
float distance = std::sqrt(dx * dx + dy * dy);
|
||||
if (distance > 0)
|
||||
{
|
||||
dx /= distance;
|
||||
dy /= distance;
|
||||
}
|
||||
|
||||
// Rotate the vector by 90 degrees to create circular movement
|
||||
// (x,y) rotated 90 degrees becomes (-y,x)
|
||||
float rotatedX = -dy;
|
||||
float rotatedY = dx;
|
||||
|
||||
// Create a target position along the circular path
|
||||
// We want to maintain a specific radius from the tank
|
||||
float targetRadius = 20.0f; // 20 feet radius circle around tank
|
||||
|
||||
Position targetPos;
|
||||
targetPos.Relocate(tankPosition.GetPositionX() + rotatedX * targetRadius,
|
||||
tankPosition.GetPositionY() + rotatedY * targetRadius, bot->GetPositionZ());
|
||||
|
||||
// Move to the calculated circular position
|
||||
botAI->Reset();
|
||||
return MoveTo(bot->GetMapId(), targetPos.GetPositionX(), targetPos.GetPositionY(), bot->GetPositionZ(),
|
||||
false, false, false, false, MovementPriority::MOVEMENT_COMBAT);
|
||||
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool IckAndKrickAction::PoisonNova(bool poisonNova, Unit* boss)
|
||||
{
|
||||
if (bot->GetExactDist2d(boss) < 20.0f && poisonNova)
|
||||
return MoveAway(boss, 11.0f);
|
||||
else if (poisonNova)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
bool IckAndKrickAction::ExplosiveBarrage(bool explosiveBarrage, Unit* boss)
|
||||
{
|
||||
std::vector<Unit*> orbs;
|
||||
Unit* closestOrb = nullptr;
|
||||
float closestDistance = std::numeric_limits<float>::max();
|
||||
|
||||
// First gather all orbs
|
||||
GuidVector npcs1 = AI_VALUE(GuidVector, "nearest hostile npcs");
|
||||
for (auto& npc : npcs1)
|
||||
{
|
||||
Unit* unit = botAI->GetUnit(npc);
|
||||
if (unit && unit->IsAlive() && unit->HasAura(SPELL_EXPLODING_ORB_VISUAL))
|
||||
{
|
||||
orbs.push_back(unit);
|
||||
float dist = bot->GetDistance(unit);
|
||||
if (dist < closestDistance)
|
||||
{
|
||||
closestDistance = dist;
|
||||
closestOrb = unit;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!orbs.empty() && closestOrb && bot->GetExactDist2d(closestOrb) < 7.0f)
|
||||
{
|
||||
// Get player positions to avoid moving toward them
|
||||
std::vector<Position> playerPositions;
|
||||
Group* group = bot->GetGroup();
|
||||
if (group)
|
||||
{
|
||||
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
|
||||
{
|
||||
if (Player* member = ref->GetSource())
|
||||
{
|
||||
if (member != bot && member->IsAlive() && bot->GetMap() == member->GetMap() &&
|
||||
bot->GetExactDist2d(member) < 20.0f)
|
||||
{
|
||||
playerPositions.push_back(member->GetPosition());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Calculate several potential escape positions
|
||||
const int NUM_ANGLES = 16; // Check 16 different directions
|
||||
const float SAFE_DISTANCE = 7.0f;
|
||||
const float MAX_BOSS_DISTANCE = 30.0f; // Maximum allowed distance from boss
|
||||
const float ANGLE_INCREMENT = 2 * M_PI / NUM_ANGLES;
|
||||
|
||||
Position bestPos;
|
||||
bool foundValidPos = false;
|
||||
float bestScore = -1.0f;
|
||||
|
||||
for (int i = 0; i < NUM_ANGLES; i++)
|
||||
{
|
||||
float angle = i * ANGLE_INCREMENT;
|
||||
Position potentialPos;
|
||||
potentialPos.m_positionX = bot->GetPositionX() + SAFE_DISTANCE * cos(angle);
|
||||
potentialPos.m_positionY = bot->GetPositionY() + SAFE_DISTANCE * sin(angle);
|
||||
potentialPos.m_positionZ = bot->GetPositionZ();
|
||||
|
||||
// Check if position is valid (not in a wall)
|
||||
if (!bot->IsWithinLOS(potentialPos.m_positionX, potentialPos.m_positionY, potentialPos.m_positionZ))
|
||||
continue;
|
||||
|
||||
// Check if position is within maximum allowed distance from boss
|
||||
if (boss && sqrt(pow(potentialPos.m_positionX - boss->GetPositionX(), 2) +
|
||||
pow(potentialPos.m_positionY - boss->GetPositionY(), 2)) > MAX_BOSS_DISTANCE)
|
||||
continue;
|
||||
|
||||
// Score this position based on:
|
||||
// 1. Distance from all orbs (farther is better)
|
||||
// 2. Distance from other players (farther is better)
|
||||
// 3. Distance from boss (closer is better, but still safe)
|
||||
float score = 0.0f;
|
||||
|
||||
// Check distance from all orbs
|
||||
float minOrbDist = std::numeric_limits<float>::max();
|
||||
for (Unit* orb : orbs)
|
||||
{
|
||||
float orbDist = sqrt(pow(potentialPos.m_positionX - orb->GetPositionX(), 2) +
|
||||
pow(potentialPos.m_positionY - orb->GetPositionY(), 2));
|
||||
minOrbDist = std::min(minOrbDist, orbDist);
|
||||
}
|
||||
score += minOrbDist * 2.0f; // Weight orb distance more heavily
|
||||
|
||||
// Check distance from other players
|
||||
for (const Position& playerPos : playerPositions)
|
||||
{
|
||||
float playerDist = sqrt(pow(potentialPos.m_positionX - playerPos.m_positionX, 2) +
|
||||
pow(potentialPos.m_positionY - playerPos.m_positionY, 2));
|
||||
score += std::min(playerDist, 10.0f); // Cap player distance contribution
|
||||
}
|
||||
|
||||
// Factor in proximity to boss (closer is better, as long as we're safe from orbs)
|
||||
if (boss)
|
||||
{
|
||||
float bossDist = sqrt(pow(potentialPos.m_positionX - boss->GetPositionX(), 2) +
|
||||
pow(potentialPos.m_positionY - boss->GetPositionY(), 2));
|
||||
// Add points for being closer to boss (inverse relationship)
|
||||
// but only if we're safely away from orbs
|
||||
if (minOrbDist > SAFE_DISTANCE)
|
||||
{
|
||||
score += (MAX_BOSS_DISTANCE - bossDist) * 0.5f;
|
||||
}
|
||||
}
|
||||
|
||||
// If this is the best position so far, remember it
|
||||
if (score > bestScore)
|
||||
{
|
||||
bestScore = score;
|
||||
bestPos = potentialPos;
|
||||
foundValidPos = true;
|
||||
}
|
||||
}
|
||||
|
||||
// If we found a valid position, move there
|
||||
if (foundValidPos)
|
||||
{
|
||||
botAI->Reset();
|
||||
// Use MoveTo instead of FleePosition since we already calculated the exact position
|
||||
return MoveTo(bot->GetMapId(), bestPos.m_positionX, bestPos.m_positionY, bot->GetPositionZ(), false, false,
|
||||
false, true, MovementPriority::MOVEMENT_COMBAT);
|
||||
}
|
||||
else
|
||||
{
|
||||
return FleePosition(closestOrb->GetPosition(), 7.0f, 250U);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool TyrannusAction::Execute(Event event)
|
||||
{
|
||||
Unit* boss = AI_VALUE2(Unit*, "find target", "scourgelord tyrannus");
|
||||
if (!boss)
|
||||
return false;
|
||||
|
||||
bool rangedSpread = false;
|
||||
|
||||
if (botAI->IsRanged(bot) && boss->HealthBelowPct(99))
|
||||
rangedSpread = true;
|
||||
|
||||
if (rangedSpread && RangedSpread(rangedSpread))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool TyrannusAction::RangedSpread(bool rangedSpread)
|
||||
{
|
||||
float radius = 10.0f;
|
||||
float moveIncrement = 3.0f;
|
||||
|
||||
GuidVector members = AI_VALUE(GuidVector, "group members");
|
||||
if (botAI->IsRanged(bot) && rangedSpread)
|
||||
{
|
||||
// Ranged: spread from other members
|
||||
for (auto& member : members)
|
||||
{
|
||||
Unit* unit = botAI->GetUnit(member);
|
||||
if (!unit || !unit->IsAlive() || unit == bot)
|
||||
continue;
|
||||
|
||||
float dist = bot->GetExactDist2d(unit);
|
||||
if (dist < radius)
|
||||
{
|
||||
float moveDistance = std::min(moveIncrement, radius - dist + 1.0f);
|
||||
return FleePosition(unit->GetPosition(), moveDistance, 250U);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
32
src/strategy/dungeons/wotlk/pitofsaron/PitOfSaronActions.h
Normal file
32
src/strategy/dungeons/wotlk/pitofsaron/PitOfSaronActions.h
Normal file
@@ -0,0 +1,32 @@
|
||||
#ifndef _PLAYERBOT_WOTLKDUNGEONPOSACTIONS_H
|
||||
#define _PLAYERBOT_WOTLKDUNGEONPOSACTIONS_H
|
||||
|
||||
#include "Action.h"
|
||||
#include "AttackAction.h"
|
||||
#include "PlayerbotAI.h"
|
||||
#include "Playerbots.h"
|
||||
#include "PitOfSaronTriggers.h"
|
||||
|
||||
const Position ICKANDKRICK_TANK_POSITION = Position(816.8508f, 102.331505f, 509.1586f);
|
||||
|
||||
class IckAndKrickAction : public AttackAction
|
||||
{
|
||||
public:
|
||||
IckAndKrickAction(PlayerbotAI* ai) : AttackAction(ai, "ick and krick") {}
|
||||
bool Execute(Event event) override;
|
||||
|
||||
bool TankPosition(Unit* boss);
|
||||
bool Pursuit(bool pursuit, Unit* boss);
|
||||
bool PoisonNova(bool poisonNova, Unit* boss);
|
||||
bool ExplosiveBarrage(bool explosiveBarrage, Unit* boss);
|
||||
};
|
||||
|
||||
class TyrannusAction : public AttackAction
|
||||
{
|
||||
public:
|
||||
TyrannusAction(PlayerbotAI* ai) : AttackAction(ai, "tyrannus") {}
|
||||
bool Execute(Event event) override;
|
||||
|
||||
bool RangedSpread(bool rangedSpread);
|
||||
};
|
||||
#endif
|
||||
@@ -0,0 +1,39 @@
|
||||
#include "PitOfSaronMultipliers.h"
|
||||
#include "PitOfSaronActions.h"
|
||||
#include "GenericSpellActions.h"
|
||||
#include "ChooseTargetActions.h"
|
||||
#include "MovementActions.h"
|
||||
#include "PitOfSaronTriggers.h"
|
||||
|
||||
|
||||
|
||||
float IckAndKrickMultiplier::GetValue(Action* action)
|
||||
{
|
||||
Unit* boss = AI_VALUE2(Unit*, "find target", "ick");
|
||||
if (!boss)
|
||||
return 1.0f;
|
||||
|
||||
// Allow the IckAndKrickAction to run
|
||||
if (dynamic_cast<IckAndKrickAction*>(action))
|
||||
return 1.0f;
|
||||
|
||||
if (boss->HasUnitState(UNIT_STATE_CASTING) && (boss->FindCurrentSpellBySpellId(SPELL_POISON_NOVA_POS) || boss->FindCurrentSpellBySpellId(SPELL_POISON_NOVA_POS_HC)) && bot->GetExactDist2d(boss) < 20.0f)
|
||||
return 0.0f; // Cancel all other actions when we need to handle Poison Nova
|
||||
|
||||
if (bot->GetExactDist2d(boss) < 15.0f && bot->HasAura(SPELL_PURSUIT) && !botAI->IsTank(bot))
|
||||
return 0.0f; // Cancel all other actions when we need to handle Pursuit
|
||||
|
||||
if (!botAI->IsHeal(bot) && boss->HasUnitState(UNIT_STATE_CASTING) && (boss->FindCurrentSpellBySpellId(SPELL_EXPLOSIVE_BARRAGE_ICK) || boss->FindCurrentSpellBySpellId(SPELL_EXPLOSIVE_BARRAGE_KRICK)))
|
||||
return 0.0f; // Cancel all other actions when we need to handle Explosive Barrage
|
||||
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
float GarfrostMultiplier::GetValue(Action* action)
|
||||
{
|
||||
Unit* boss = AI_VALUE2(Unit*, "find target", "garfrost");
|
||||
if (!boss)
|
||||
return 1.0f;
|
||||
|
||||
return 1.0f;
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
#ifndef _PLAYERBOT_WOTLKDUNGEONPOSMULTIPLIERS_H
|
||||
#define _PLAYERBOT_WOTLKDUNGEONPOSMULTIPLIERS_H
|
||||
|
||||
#include "Multiplier.h"
|
||||
|
||||
class IckAndKrickMultiplier : public Multiplier
|
||||
{
|
||||
public:
|
||||
IckAndKrickMultiplier(PlayerbotAI* ai) : Multiplier(ai, "ick and krick") {}
|
||||
|
||||
public:
|
||||
virtual float GetValue(Action* action);
|
||||
};
|
||||
|
||||
class GarfrostMultiplier : public Multiplier
|
||||
{
|
||||
public:
|
||||
GarfrostMultiplier(PlayerbotAI* ai) : Multiplier(ai, "garfrost") { }
|
||||
|
||||
float GetValue(Action* action) override;
|
||||
};
|
||||
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,17 @@
|
||||
#include "PitOfSaronStrategy.h"
|
||||
#include "PitOfSaronMultipliers.h"
|
||||
|
||||
void WotlkDungeonPoSStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
||||
{
|
||||
triggers.push_back(new TriggerNode("ick and krick",
|
||||
NextAction::array(0, new NextAction("ick and krick", ACTION_RAID + 5), nullptr)));
|
||||
|
||||
triggers.push_back(new TriggerNode("tyrannus",
|
||||
NextAction::array(0, new NextAction("tyrannus", ACTION_RAID + 5), nullptr)));
|
||||
}
|
||||
|
||||
void WotlkDungeonPoSStrategy::InitMultipliers(std::vector<Multiplier*>& multipliers)
|
||||
{
|
||||
multipliers.push_back(new IckAndKrickMultiplier(botAI));
|
||||
//multipliers.push_back(new AttackFragmentMultiplier(botAI));
|
||||
}
|
||||
16
src/strategy/dungeons/wotlk/pitofsaron/PitOfSaronStrategy.h
Normal file
16
src/strategy/dungeons/wotlk/pitofsaron/PitOfSaronStrategy.h
Normal file
@@ -0,0 +1,16 @@
|
||||
#ifndef _PLAYERBOT_WOTLKDUNGEONPOSSTRATEGY_H
|
||||
#define _PLAYERBOT_WOTLKDUNGEONPOSSTRATEGY_H
|
||||
#include "Multiplier.h"
|
||||
#include "Strategy.h"
|
||||
|
||||
class WotlkDungeonPoSStrategy : public Strategy
|
||||
{
|
||||
public:
|
||||
WotlkDungeonPoSStrategy(PlayerbotAI* ai) : Strategy(ai) {}
|
||||
std::string const getName() override { return "pit of saron"; }
|
||||
void InitTriggers(std::vector<TriggerNode*> &triggers) override;
|
||||
void InitMultipliers(std::vector<Multiplier*> &multipliers) override;
|
||||
|
||||
};
|
||||
|
||||
#endif // !_PLAYERBOT_WOTLKDUNGEONFOSSTRATEGY_H
|
||||
@@ -0,0 +1,22 @@
|
||||
#ifndef _PLAYERBOT_WOTLKDUNGEONPOSTRIGGERCONTEXT_H
|
||||
#define _PLAYERBOT_WOTLKDUNGEONPOSTRIGGERCONTEXT_H
|
||||
|
||||
#include "NamedObjectContext.h"
|
||||
#include "AiObjectContext.h"
|
||||
#include "PitOfSaronTriggers.h"
|
||||
|
||||
class WotlkDungeonPoSTriggerContext : public NamedObjectContext<Trigger>
|
||||
{
|
||||
public:
|
||||
WotlkDungeonPoSTriggerContext()
|
||||
{
|
||||
creators["ick and krick"] = &WotlkDungeonPoSTriggerContext::ick_and_krick;
|
||||
creators["tyrannus"] = &WotlkDungeonPoSTriggerContext::tyrannus;
|
||||
}
|
||||
|
||||
private:
|
||||
static Trigger* ick_and_krick(PlayerbotAI* ai) { return new IckAndKrickTrigger(ai); }
|
||||
static Trigger* tyrannus(PlayerbotAI* ai) { return new TyrannusTrigger(ai); }
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,22 @@
|
||||
#include "Playerbots.h"
|
||||
#include "PitOfSaronTriggers.h"
|
||||
#include "AiObject.h"
|
||||
#include "AiObjectContext.h"
|
||||
|
||||
bool IckAndKrickTrigger::IsActive()
|
||||
{
|
||||
Unit* boss = AI_VALUE2(Unit*, "find target", "Ick");
|
||||
if (!boss)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TyrannusTrigger::IsActive()
|
||||
{
|
||||
Unit* boss = AI_VALUE2(Unit*, "find target", "scourgelord tyrannus");
|
||||
if (!boss)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
48
src/strategy/dungeons/wotlk/pitofsaron/PitOfSaronTriggers.h
Normal file
48
src/strategy/dungeons/wotlk/pitofsaron/PitOfSaronTriggers.h
Normal file
@@ -0,0 +1,48 @@
|
||||
#ifndef _PLAYERBOT_WOTLKDUNGEONPOSTRIGGERS_H
|
||||
#define _PLAYERBOT_WOTLKDUNGEONPOSTRIGGERS_H
|
||||
|
||||
#include "Trigger.h"
|
||||
#include "PlayerbotAIConfig.h"
|
||||
#include "GenericTriggers.h"
|
||||
#include "DungeonStrategyUtils.h"
|
||||
|
||||
enum PitOfSaronIDs
|
||||
{
|
||||
//NPCs
|
||||
NPC_GARFROST = 36494,
|
||||
NPC_KRICK = 36477,
|
||||
NPC_ICK = 36476,
|
||||
NPC_TYRANNUS = 36658,
|
||||
NPC_RIMEFANG = 36661,
|
||||
|
||||
//Spells
|
||||
SPELL_PURSUIT = 68987,
|
||||
SPELL_POISON_NOVA_POS = 68989,
|
||||
SPELL_POISON_NOVA_POS_HC = 70434,
|
||||
SPELL_EXPLOSIVE_BARRAGE_KRICK = 69012,
|
||||
SPELL_EXPLOSIVE_BARRAGE_ICK = 69263,
|
||||
SPELL_EXPLOSIVE_BARRAGE_SUMMON = 69015,
|
||||
SPELL_EXPLODING_ORB_VISUAL = 69017,
|
||||
SPELL_MARK_OF_RIMEFANG = 69275,
|
||||
RIMEFANG_SPELL_HOARFROST = 69246,
|
||||
RIMEFANG_SPELL_HOARFROST_HC = 69245,
|
||||
RIMEFANG_SPELL_HOARFROST_HC2 = 69645,
|
||||
};
|
||||
|
||||
class IckAndKrickTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
IckAndKrickTrigger(PlayerbotAI* ai) : Trigger(ai, "ick and krick") {}
|
||||
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class TyrannusTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
TyrannusTrigger(PlayerbotAI* ai) : Trigger(ai, "tyrannus") {}
|
||||
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -143,6 +143,7 @@ ChatCommandHandlerStrategy::ChatCommandHandlerStrategy(PlayerbotAI* botAI) : Pas
|
||||
supported.push_back("gb");
|
||||
supported.push_back("bank");
|
||||
supported.push_back("invite");
|
||||
supported.push_back("lfg");
|
||||
supported.push_back("spell");
|
||||
supported.push_back("rti");
|
||||
supported.push_back("position");
|
||||
|
||||
@@ -7,11 +7,10 @@
|
||||
|
||||
#include "Playerbots.h"
|
||||
|
||||
NextAction** DpsAssistStrategy::getDefaultActions()
|
||||
void DpsAssistStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
||||
{
|
||||
return NextAction::array(
|
||||
0, new NextAction("dps assist", 50.0f),
|
||||
nullptr);
|
||||
triggers.push_back(
|
||||
new TriggerNode("not dps target active", NextAction::array(0, new NextAction("dps assist", 50.0f), nullptr)));
|
||||
}
|
||||
|
||||
void DpsAoeStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
||||
|
||||
@@ -16,7 +16,7 @@ public:
|
||||
DpsAssistStrategy(PlayerbotAI* botAI) : NonCombatStrategy(botAI) {}
|
||||
|
||||
std::string const getName() override { return "dps assist"; }
|
||||
NextAction** getDefaultActions() override;
|
||||
void InitTriggers(std::vector<TriggerNode*>& triggers) override;
|
||||
};
|
||||
|
||||
class DpsAoeStrategy : public NonCombatStrategy
|
||||
|
||||
@@ -7,15 +7,8 @@
|
||||
|
||||
#include "Playerbots.h"
|
||||
|
||||
NextAction** TankAssistStrategy::getDefaultActions()
|
||||
void TankAssistStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
||||
{
|
||||
return NextAction::array(
|
||||
0, new NextAction("tank assist", 50.0f),
|
||||
nullptr);
|
||||
triggers.push_back(
|
||||
new TriggerNode("tank assist", NextAction::array(0, new NextAction("tank assist", 50.0f), nullptr)));
|
||||
}
|
||||
|
||||
// void TankAssistStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
||||
// {
|
||||
// triggers.push_back(
|
||||
// new TriggerNode("tank assist", NextAction::array(0, new NextAction("tank assist", 50.0f), nullptr)));
|
||||
// }
|
||||
|
||||
@@ -17,8 +17,7 @@ public:
|
||||
|
||||
std::string const getName() override { return "tank assist"; }
|
||||
uint32 GetType() const override { return STRATEGY_TYPE_TANK; }
|
||||
NextAction** getDefaultActions() override;
|
||||
// void InitTriggers(std::vector<TriggerNode*>& triggers) override;
|
||||
void InitTriggers(std::vector<TriggerNode*>& triggers) override;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -67,6 +67,9 @@ void WorldPacketHandlerStrategy::InitTriggers(std::vector<TriggerNode*>& trigger
|
||||
// quest ?
|
||||
//triggers.push_back(new TriggerNode("quest confirm", NextAction::array(0, new NextAction("quest confirm", relevance), nullptr)));
|
||||
triggers.push_back(new TriggerNode("questgiver quest details", NextAction::array(0, new NextAction("turn in query quest", relevance), nullptr)));
|
||||
|
||||
// loot roll
|
||||
triggers.push_back(new TriggerNode("very often", NextAction::array(0, new NextAction("loot roll", 10.0f), nullptr)));
|
||||
}
|
||||
|
||||
WorldPacketHandlerStrategy::WorldPacketHandlerStrategy(PlayerbotAI* botAI) : PassTroughStrategy(botAI)
|
||||
|
||||
@@ -39,6 +39,7 @@ public:
|
||||
creators["blessing of wisdom"] = &blessing_of_wisdom;
|
||||
creators["blessing of kings on party"] = &blessing_of_kings_on_party;
|
||||
creators["blessing of wisdom on party"] = &blessing_of_wisdom_on_party;
|
||||
creators["blessing of sanctuary on party"] = &blessing_of_sanctuary_on_party;
|
||||
creators["blessing of sanctuary"] = &blessing_of_sanctuary;
|
||||
creators["seal of command"] = &seal_of_command;
|
||||
creators["taunt spell"] = &hand_of_reckoning;
|
||||
@@ -83,6 +84,13 @@ private:
|
||||
/*A*/ nullptr,
|
||||
/*C*/ nullptr);
|
||||
}
|
||||
static ActionNode* blessing_of_sanctuary_on_party(PlayerbotAI* /* ai */)
|
||||
{
|
||||
return new ActionNode("blessing of sanctuary on party",
|
||||
/*P*/ nullptr,
|
||||
/*A*/ nullptr,
|
||||
/*C*/ nullptr);
|
||||
}
|
||||
static ActionNode* retribution_aura(PlayerbotAI* /* ai */)
|
||||
{
|
||||
return new ActionNode("retribution aura",
|
||||
|
||||
143
src/strategy/paladin/OffhealRetPaladinStrategy.cpp
Normal file
143
src/strategy/paladin/OffhealRetPaladinStrategy.cpp
Normal file
@@ -0,0 +1,143 @@
|
||||
/*
|
||||
* 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 "OffhealRetPaladinStrategy.h"
|
||||
|
||||
#include "Playerbots.h"
|
||||
#include "Strategy.h"
|
||||
|
||||
class OffhealRetPaladinStrategyActionNodeFactory : public NamedObjectFactory<ActionNode>
|
||||
{
|
||||
public:
|
||||
OffhealRetPaladinStrategyActionNodeFactory()
|
||||
{
|
||||
creators["retribution aura"] = &retribution_aura;
|
||||
creators["seal of corruption"] = &seal_of_corruption;
|
||||
creators["seal of vengeance"] = &seal_of_vengeance;
|
||||
creators["seal of command"] = &seal_of_command;
|
||||
creators["blessing of might"] = &blessing_of_might;
|
||||
creators["crusader strike"] = &crusader_strike;
|
||||
creators["divine plea"] = &divine_plea;
|
||||
}
|
||||
|
||||
private:
|
||||
static ActionNode* retribution_aura([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode("retribution aura",
|
||||
/*P*/ nullptr,
|
||||
/*A*/ NextAction::array(0, new NextAction("devotion aura"), nullptr),
|
||||
/*C*/ nullptr);
|
||||
}
|
||||
|
||||
static ActionNode* seal_of_corruption([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode("seal of corruption",
|
||||
/*P*/ nullptr,
|
||||
/*A*/ NextAction::array(0, new NextAction("seal of vengeance"), nullptr),
|
||||
/*C*/ nullptr);
|
||||
}
|
||||
|
||||
static ActionNode* seal_of_vengeance([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode("seal of vengeance",
|
||||
/*P*/ nullptr,
|
||||
/*A*/ NextAction::array(0, new NextAction("seal of command"), nullptr),
|
||||
/*C*/ nullptr);
|
||||
}
|
||||
|
||||
static ActionNode* seal_of_command([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode("seal of command",
|
||||
/*P*/ nullptr,
|
||||
/*A*/ NextAction::array(0, new NextAction("seal of righteousness"), nullptr),
|
||||
/*C*/ nullptr);
|
||||
}
|
||||
|
||||
static ActionNode* blessing_of_might([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode("blessing of might",
|
||||
/*P*/ nullptr,
|
||||
/*A*/ NextAction::array(0, new NextAction("blessing of kings"), nullptr),
|
||||
/*C*/ nullptr);
|
||||
}
|
||||
|
||||
static ActionNode* crusader_strike([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode("crusader strike",
|
||||
/*P*/ nullptr,
|
||||
/*A*/ nullptr,
|
||||
/*C*/ nullptr);
|
||||
}
|
||||
|
||||
static ActionNode* divine_plea([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode("divine plea",
|
||||
/*P*/ nullptr,
|
||||
/*A*/ nullptr,
|
||||
/*C*/ nullptr);
|
||||
}
|
||||
};
|
||||
|
||||
OffhealRetPaladinStrategy::OffhealRetPaladinStrategy(PlayerbotAI* botAI) : GenericPaladinStrategy(botAI)
|
||||
{
|
||||
actionNodeFactories.Add(new OffhealRetPaladinStrategyActionNodeFactory());
|
||||
}
|
||||
|
||||
NextAction** OffhealRetPaladinStrategy::getDefaultActions()
|
||||
{
|
||||
return NextAction::array(0, new NextAction("hammer of wrath", ACTION_DEFAULT + 0.6f),
|
||||
new NextAction("judgement of wisdom", ACTION_DEFAULT + 0.5f),
|
||||
new NextAction("crusader strike", ACTION_DEFAULT + 0.4f),
|
||||
new NextAction("divine storm", ACTION_DEFAULT + 0.3f),
|
||||
new NextAction("melee", ACTION_DEFAULT), nullptr);
|
||||
}
|
||||
|
||||
void OffhealRetPaladinStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
||||
{
|
||||
GenericPaladinStrategy::InitTriggers(triggers);
|
||||
|
||||
// Damage Triggers
|
||||
triggers.push_back(
|
||||
new TriggerNode("seal", NextAction::array(0, new NextAction("seal of corruption", ACTION_HIGH), nullptr)));
|
||||
triggers.push_back(
|
||||
new TriggerNode("low mana", NextAction::array(0, new NextAction("seal of wisdom", ACTION_HIGH + 5),
|
||||
new NextAction("divine plea", ACTION_HIGH + 4), nullptr)));
|
||||
triggers.push_back(
|
||||
new TriggerNode("art of war", NextAction::array(0, new NextAction("exorcism", ACTION_HIGH + 1), nullptr)));
|
||||
triggers.push_back(new TriggerNode(
|
||||
"avenging wrath", NextAction::array(0, new NextAction("avenging wrath", ACTION_HIGH + 2), nullptr)));
|
||||
triggers.push_back(
|
||||
new TriggerNode("medium aoe", NextAction::array(0, new NextAction("divine storm", ACTION_HIGH + 4),
|
||||
new NextAction("consecration", ACTION_HIGH + 3), nullptr)));
|
||||
triggers.push_back(new TriggerNode("enemy out of melee",
|
||||
NextAction::array(0, new NextAction("reach melee", ACTION_HIGH + 1), nullptr)));
|
||||
triggers.push_back(new TriggerNode(
|
||||
"retribution aura", NextAction::array(0, new NextAction("retribution aura", ACTION_NORMAL), nullptr)));
|
||||
triggers.push_back(new TriggerNode(
|
||||
"blessing of might", NextAction::array(0, new NextAction("blessing of might", ACTION_NORMAL + 1), nullptr)));
|
||||
triggers.push_back(new TriggerNode(
|
||||
"low health", NextAction::array(0, new NextAction("holy light", ACTION_CRITICAL_HEAL + 2), nullptr)));
|
||||
|
||||
// Healing Triggers
|
||||
triggers.push_back(
|
||||
new TriggerNode("party member critical health",
|
||||
NextAction::array(0, new NextAction("holy shock on party", ACTION_CRITICAL_HEAL + 6),
|
||||
new NextAction("holy light on party", ACTION_CRITICAL_HEAL + 4), nullptr)));
|
||||
triggers.push_back(
|
||||
new TriggerNode("party member low health",
|
||||
NextAction::array(0, new NextAction("holy light on party", ACTION_MEDIUM_HEAL + 5), nullptr)));
|
||||
triggers.push_back(new TriggerNode(
|
||||
"party member medium health",
|
||||
NextAction::array(0, new NextAction("flash of light on party", ACTION_LIGHT_HEAL + 8), nullptr)));
|
||||
triggers.push_back(new TriggerNode(
|
||||
"party member almost full health",
|
||||
NextAction::array(0, new NextAction("flash of light on party", ACTION_LIGHT_HEAL + 3), nullptr)));
|
||||
triggers.push_back(new TriggerNode(
|
||||
"party member to heal out of spell range",
|
||||
NextAction::array(0, new NextAction("reach party member to heal", ACTION_EMERGENCY + 3), nullptr)));
|
||||
triggers.push_back(new TriggerNode(
|
||||
"beacon of light on main tank",
|
||||
NextAction::array(0, new NextAction("beacon of light on main tank", ACTION_CRITICAL_HEAL + 7), nullptr)));
|
||||
}
|
||||
27
src/strategy/paladin/OffhealRetPaladinStrategy.h
Normal file
27
src/strategy/paladin/OffhealRetPaladinStrategy.h
Normal file
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef _PLAYERBOT_OFFHEALRETPALADINSTRATEGY_H
|
||||
#define _PLAYERBOT_OFFHEALRETPALADINSTRATEGY_H
|
||||
|
||||
#include "GenericPaladinStrategy.h"
|
||||
|
||||
class PlayerbotAI;
|
||||
|
||||
class OffhealRetPaladinStrategy : public GenericPaladinStrategy
|
||||
{
|
||||
public:
|
||||
OffhealRetPaladinStrategy(PlayerbotAI* botAI);
|
||||
|
||||
void InitTriggers(std::vector<TriggerNode*>& triggers) override;
|
||||
std::string const getName() override { return "offheal"; }
|
||||
NextAction** getDefaultActions() override;
|
||||
uint32 GetType() const override
|
||||
{
|
||||
return STRATEGY_TYPE_COMBAT | STRATEGY_TYPE_DPS | STRATEGY_TYPE_HEAL | STRATEGY_TYPE_MELEE;
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -12,6 +12,7 @@
|
||||
#include "PlayerbotFactory.h"
|
||||
#include "Playerbots.h"
|
||||
#include "SharedDefines.h"
|
||||
#include "../../../../../src/server/scripts/Spells/spell_generic.cpp"
|
||||
|
||||
inline std::string const GetActualBlessingOfMight(Unit* target)
|
||||
{
|
||||
@@ -19,6 +20,7 @@ inline std::string const GetActualBlessingOfMight(Unit* target)
|
||||
{
|
||||
return "blessing of might";
|
||||
}
|
||||
|
||||
int tab = AiFactory::GetPlayerSpecTab(target->ToPlayer());
|
||||
switch (target->getClass())
|
||||
{
|
||||
@@ -88,6 +90,23 @@ inline std::string const GetActualBlessingOfWisdom(Unit* target)
|
||||
return "blessing of wisdom";
|
||||
}
|
||||
|
||||
inline std::string const GetActualBlessingOfSanctuary(Unit* target, Player* bot)
|
||||
{
|
||||
Player* targetPlayer = target->ToPlayer();
|
||||
|
||||
if (!targetPlayer)
|
||||
{
|
||||
return "blessing of sanctuary";
|
||||
}
|
||||
|
||||
if (targetPlayer->HasTankSpec() && bot->HasSpell(SPELL_BLESSING_OF_SANCTUARY))
|
||||
{
|
||||
return "blessing of sanctuary";
|
||||
}
|
||||
|
||||
return "blessing of kings";
|
||||
}
|
||||
|
||||
Value<Unit*>* CastBlessingOnPartyAction::GetTargetValue()
|
||||
{
|
||||
return context->GetValue<Unit*>("party member without aura", name);
|
||||
@@ -139,6 +158,20 @@ bool CastBlessingOfWisdomOnPartyAction::Execute(Event event)
|
||||
return botAI->CastSpell(GetActualBlessingOfWisdom(target), target);
|
||||
}
|
||||
|
||||
Value<Unit*>* CastBlessingOfSanctuaryOnPartyAction::GetTargetValue()
|
||||
{
|
||||
return context->GetValue<Unit*>("party member without aura", "blessing of sanctuary,blessing of kings");
|
||||
}
|
||||
|
||||
bool CastBlessingOfSanctuaryOnPartyAction::Execute(Event event)
|
||||
{
|
||||
Unit* target = GetTarget();
|
||||
if (!target)
|
||||
return false;
|
||||
|
||||
return botAI->CastSpell(GetActualBlessingOfSanctuary(target, bot), target);
|
||||
}
|
||||
|
||||
bool CastSealSpellAction::isUseful() { return AI_VALUE2(bool, "combat", "self target"); }
|
||||
|
||||
Value<Unit*>* CastTurnUndeadAction::GetTargetValue() { return context->GetValue<Unit*>("cc target", getName()); }
|
||||
@@ -169,4 +202,4 @@ bool CastCancelDivineSacrificeAction::Execute(Event event)
|
||||
bool CastCancelDivineSacrificeAction::isUseful()
|
||||
{
|
||||
return botAI->HasAura("divine sacrifice", GetTarget(), false, true, -1, true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -149,14 +149,16 @@ public:
|
||||
CastBlessingOfSanctuaryAction(PlayerbotAI* botAI) : CastBuffSpellAction(botAI, "blessing of sanctuary") {}
|
||||
};
|
||||
|
||||
class CastBlessingOfSanctuaryOnPartyAction : public CastBlessingOnPartyAction
|
||||
class CastBlessingOfSanctuaryOnPartyAction : public BuffOnPartyAction
|
||||
{
|
||||
public:
|
||||
CastBlessingOfSanctuaryOnPartyAction(PlayerbotAI* botAI) : CastBlessingOnPartyAction(botAI, "blessing of sanctuary")
|
||||
CastBlessingOfSanctuaryOnPartyAction(PlayerbotAI* botAI) : BuffOnPartyAction(botAI, "blessing of sanctuary")
|
||||
{
|
||||
}
|
||||
|
||||
std::string const getName() override { return "blessing of sanctuary on party"; }
|
||||
Value<Unit*>* GetTargetValue() override;
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
class CastHolyLightAction : public CastHealingSpellAction
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include "DpsPaladinStrategy.h"
|
||||
#include "GenericPaladinNonCombatStrategy.h"
|
||||
#include "HealPaladinStrategy.h"
|
||||
#include "OffhealRetPaladinStrategy.h"
|
||||
#include "NamedObjectContext.h"
|
||||
#include "PaladinActions.h"
|
||||
#include "PaladinBuffStrategies.h"
|
||||
@@ -87,12 +88,14 @@ public:
|
||||
creators["tank"] = &PaladinCombatStrategyFactoryInternal::tank;
|
||||
creators["dps"] = &PaladinCombatStrategyFactoryInternal::dps;
|
||||
creators["heal"] = &PaladinCombatStrategyFactoryInternal::heal;
|
||||
creators["offheal"] = &PaladinCombatStrategyFactoryInternal::offheal;
|
||||
}
|
||||
|
||||
private:
|
||||
static Strategy* tank(PlayerbotAI* botAI) { return new TankPaladinStrategy(botAI); }
|
||||
static Strategy* dps(PlayerbotAI* botAI) { return new DpsPaladinStrategy(botAI); }
|
||||
static Strategy* heal(PlayerbotAI* botAI) { return new HealPaladinStrategy(botAI); }
|
||||
static Strategy* offheal(PlayerbotAI* botAI) { return new OffhealRetPaladinStrategy(botAI); }
|
||||
};
|
||||
|
||||
class PaladinTriggerFactoryInternal : public NamedObjectContext<Trigger>
|
||||
@@ -142,6 +145,7 @@ public:
|
||||
creators["blessing of kings on party"] = &PaladinTriggerFactoryInternal::blessing_of_kings_on_party;
|
||||
creators["blessing of wisdom on party"] = &PaladinTriggerFactoryInternal::blessing_of_wisdom_on_party;
|
||||
creators["blessing of might on party"] = &PaladinTriggerFactoryInternal::blessing_of_might_on_party;
|
||||
creators["blessing of sanctuary on party"] = &PaladinTriggerFactoryInternal::blessing_of_sanctuary_on_party;
|
||||
|
||||
creators["avenging wrath"] = &PaladinTriggerFactoryInternal::avenging_wrath;
|
||||
}
|
||||
@@ -203,11 +207,9 @@ private:
|
||||
static Trigger* sacred_shield_on_main_tank(PlayerbotAI* ai) { return new SacredShieldOnMainTankTrigger(ai); }
|
||||
|
||||
static Trigger* blessing_of_kings_on_party(PlayerbotAI* botAI) { return new BlessingOfKingsOnPartyTrigger(botAI); }
|
||||
static Trigger* blessing_of_wisdom_on_party(PlayerbotAI* botAI)
|
||||
{
|
||||
return new BlessingOfWisdomOnPartyTrigger(botAI);
|
||||
}
|
||||
static Trigger* blessing_of_wisdom_on_party(PlayerbotAI* botAI) { return new BlessingOfWisdomOnPartyTrigger(botAI); }
|
||||
static Trigger* blessing_of_might_on_party(PlayerbotAI* botAI) { return new BlessingOfMightOnPartyTrigger(botAI); }
|
||||
static Trigger* blessing_of_sanctuary_on_party(PlayerbotAI* botAI) { return new BlessingOfSanctuaryOnPartyTrigger(botAI); }
|
||||
|
||||
static Trigger* avenging_wrath(PlayerbotAI* botAI) { return new AvengingWrathTrigger(botAI); }
|
||||
};
|
||||
@@ -228,6 +230,7 @@ public:
|
||||
creators["blessing of kings on party"] = &PaladinAiObjectContextInternal::blessing_of_kings_on_party;
|
||||
creators["blessing of might on party"] = &PaladinAiObjectContextInternal::blessing_of_might_on_party;
|
||||
creators["blessing of wisdom on party"] = &PaladinAiObjectContextInternal::blessing_of_wisdom_on_party;
|
||||
creators["blessing of sanctuary on party"] = &PaladinAiObjectContextInternal::blessing_of_sanctuary_on_party;
|
||||
creators["redemption"] = &PaladinAiObjectContextInternal::redemption;
|
||||
creators["crusader strike"] = &PaladinAiObjectContextInternal::crusader_strike;
|
||||
creators["crusader aura"] = &PaladinAiObjectContextInternal::crusader_aura;
|
||||
@@ -314,18 +317,10 @@ private:
|
||||
static Action* blessing_of_wisdom(PlayerbotAI* botAI) { return new CastBlessingOfWisdomAction(botAI); }
|
||||
static Action* blessing_of_kings(PlayerbotAI* botAI) { return new CastBlessingOfKingsAction(botAI); }
|
||||
static Action* divine_storm(PlayerbotAI* botAI) { return new CastDivineStormAction(botAI); }
|
||||
static Action* blessing_of_kings_on_party(PlayerbotAI* botAI)
|
||||
{
|
||||
return new CastBlessingOfKingsOnPartyAction(botAI);
|
||||
}
|
||||
static Action* blessing_of_might_on_party(PlayerbotAI* botAI)
|
||||
{
|
||||
return new CastBlessingOfMightOnPartyAction(botAI);
|
||||
}
|
||||
static Action* blessing_of_wisdom_on_party(PlayerbotAI* botAI)
|
||||
{
|
||||
return new CastBlessingOfWisdomOnPartyAction(botAI);
|
||||
}
|
||||
static Action* blessing_of_kings_on_party(PlayerbotAI* botAI) { return new CastBlessingOfKingsOnPartyAction(botAI); }
|
||||
static Action* blessing_of_might_on_party(PlayerbotAI* botAI) { return new CastBlessingOfMightOnPartyAction(botAI); }
|
||||
static Action* blessing_of_wisdom_on_party(PlayerbotAI* botAI) { return new CastBlessingOfWisdomOnPartyAction(botAI); }
|
||||
static Action* blessing_of_sanctuary_on_party(PlayerbotAI* botAI) { return new CastBlessingOfSanctuaryOnPartyAction(botAI); }
|
||||
static Action* redemption(PlayerbotAI* botAI) { return new CastRedemptionAction(botAI); }
|
||||
static Action* crusader_strike(PlayerbotAI* botAI) { return new CastCrusaderStrikeAction(botAI); }
|
||||
static Action* crusader_aura(PlayerbotAI* botAI) { return new CastCrusaderAuraAction(botAI); }
|
||||
|
||||
@@ -19,8 +19,8 @@ void PaladinBuffManaStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
||||
void PaladinBuffHealthStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
||||
{
|
||||
triggers.push_back(
|
||||
new TriggerNode("blessing of kings on party",
|
||||
NextAction::array(0, new NextAction("blessing of kings on party", 11.0f), nullptr)));
|
||||
new TriggerNode("blessing of sanctuary on party",
|
||||
NextAction::array(0, new NextAction("blessing of sanctuary on party", 11.0f), nullptr)));
|
||||
// triggers.push_back(new TriggerNode("blessing", NextAction::array(0, new NextAction("blessing of kings",
|
||||
// ACTION_HIGH + 8), nullptr)));
|
||||
}
|
||||
|
||||
@@ -234,6 +234,15 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
class BlessingOfSanctuaryOnPartyTrigger : public BuffOnPartyTrigger
|
||||
{
|
||||
public:
|
||||
BlessingOfSanctuaryOnPartyTrigger(PlayerbotAI* botAI)
|
||||
: BuffOnPartyTrigger(botAI, "blessing of sanctuary,blessing of kings", 2 * 2000)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
class AvengingWrathTrigger : public BoostTrigger
|
||||
{
|
||||
public:
|
||||
|
||||
@@ -37,14 +37,28 @@ bool IccLmTankPositionAction::Execute(Event event)
|
||||
if (!boss)
|
||||
return false;
|
||||
|
||||
if (botAI->IsTank(bot) || botAI->IsMainTank(bot) || botAI->IsAssistTank(bot))
|
||||
if (botAI->IsTank(bot))
|
||||
{
|
||||
if (bot->GetExactDist2d(ICC_LM_TANK_POSITION) > 15.0f)
|
||||
return MoveTo(bot->GetMapId(), ICC_LM_TANK_POSITION.GetPositionX(),
|
||||
ICC_LM_TANK_POSITION.GetPositionY(), ICC_LM_TANK_POSITION.GetPositionZ(),
|
||||
false, false, false, false, MovementPriority::MOVEMENT_COMBAT);
|
||||
else
|
||||
return false;
|
||||
if ((botAI->HasAggro(boss) && botAI->IsMainTank(bot)) || botAI->IsAssistTank(bot))
|
||||
{
|
||||
float distance = bot->GetExactDist2d(ICC_LM_TANK_POSITION.GetPositionX(), ICC_LM_TANK_POSITION.GetPositionY());
|
||||
if (distance > 3.0f)
|
||||
{
|
||||
// Calculate direction vector
|
||||
float dirX = ICC_LM_TANK_POSITION.GetPositionX() - bot->GetPositionX();
|
||||
float dirY = ICC_LM_TANK_POSITION.GetPositionY() - bot->GetPositionY();
|
||||
float length = sqrt(dirX * dirX + dirY * dirY);
|
||||
dirX /= length;
|
||||
dirY /= length;
|
||||
|
||||
// Move in increments of 3.0f
|
||||
float moveX = bot->GetPositionX() + dirX * 3.0f;
|
||||
float moveY = bot->GetPositionY() + dirY * 3.0f;
|
||||
|
||||
return MoveTo(bot->GetMapId(), moveX, moveY, bot->GetPositionZ(), false, false, false, false,
|
||||
MovementPriority::MOVEMENT_COMBAT);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
@@ -214,10 +228,34 @@ bool IccAddsLadyDeathwhisperAction::Execute(Event event)
|
||||
|
||||
if (botAI->IsTank(bot) && boss->GetHealthPct() < 95.0f)
|
||||
{
|
||||
if (bot->GetExactDist2d(ICC_LDW_TANK_POSTION) > 20.0f)
|
||||
return MoveTo(bot->GetMapId(), ICC_LDW_TANK_POSTION.GetPositionX(),
|
||||
ICC_LDW_TANK_POSTION.GetPositionY(), ICC_LDW_TANK_POSTION.GetPositionZ(), false,
|
||||
false, false, false, MovementPriority::MOVEMENT_COMBAT);
|
||||
// Check if the bot is not the victim of a shade with entry 38222
|
||||
GuidVector npcs = AI_VALUE(GuidVector, "nearest hostile npcs");
|
||||
for (auto& npc : npcs)
|
||||
{
|
||||
Unit* unit = botAI->GetUnit(npc);
|
||||
if (unit && unit->GetEntry() == 38222 && unit->GetVictim() == bot)
|
||||
{
|
||||
return false; // Exit if the bot is the victim of the shade
|
||||
}
|
||||
}
|
||||
|
||||
float distance = bot->GetExactDist2d(ICC_LDW_TANK_POSTION.GetPositionX(), ICC_LDW_TANK_POSTION.GetPositionY());
|
||||
if (distance > 20.0f)
|
||||
{
|
||||
// Calculate direction vector
|
||||
float dirX = ICC_LDW_TANK_POSTION.GetPositionX() - bot->GetPositionX();
|
||||
float dirY = ICC_LDW_TANK_POSTION.GetPositionY() - bot->GetPositionY();
|
||||
float length = sqrt(dirX * dirX + dirY * dirY);
|
||||
dirX /= length;
|
||||
dirY /= length;
|
||||
|
||||
// Move in increments of 3.0f
|
||||
float moveX = bot->GetPositionX() + dirX * 3.0f;
|
||||
float moveY = bot->GetPositionY() + dirY * 3.0f;
|
||||
|
||||
return MoveTo(bot->GetMapId(), moveX, moveY, bot->GetPositionZ(), false, false, false, false,
|
||||
MovementPriority::MOVEMENT_COMBAT);
|
||||
}
|
||||
}
|
||||
|
||||
if (!botAI->IsTank(bot))
|
||||
@@ -1310,6 +1348,20 @@ bool IccPutricideVolatileOozeAction::Execute(Event event)
|
||||
|
||||
// Find the ooze
|
||||
Unit* ooze = AI_VALUE2(Unit*, "find target", "volatile ooze");
|
||||
if (!ooze)
|
||||
return false;
|
||||
|
||||
Unit* boss = AI_VALUE2(Unit*, "find target", "professor putricide");
|
||||
if (!boss)
|
||||
return false;
|
||||
|
||||
if (botAI->IsTank(bot) && bot->GetExactDist2d(ICC_PUTRICIDE_TANK_POSITION) > 20.0f && !boss->HealthBelowPct(36) && boss->GetVictim() == bot)
|
||||
{
|
||||
return MoveTo(bot->GetMapId(), ICC_PUTRICIDE_TANK_POSITION.GetPositionX(),
|
||||
ICC_PUTRICIDE_TANK_POSITION.GetPositionY(), ICC_PUTRICIDE_TANK_POSITION.GetPositionZ(), false, false,
|
||||
false, true, MovementPriority::MOVEMENT_COMBAT, true, false);
|
||||
}
|
||||
|
||||
bool botHasAura = botAI->HasAura("Volatile Ooze Adhesive", bot);
|
||||
bool botHasAura2 = botAI->HasAura("Gaseous Bloat", bot);
|
||||
bool botHasAura3 = botAI->HasAura("Unbound Plague", bot);
|
||||
@@ -1453,11 +1505,23 @@ bool IccPutricideGasCloudAction::Execute(Event event)
|
||||
|
||||
Unit* volatileOoze = AI_VALUE2(Unit*, "find target", "volatile ooze");
|
||||
|
||||
Unit* boss = AI_VALUE2(Unit*, "find target", "professor putricide");
|
||||
if (!boss)
|
||||
return false;
|
||||
|
||||
bool botHasAura = botAI->HasAura("Gaseous Bloat", bot);
|
||||
|
||||
if(!botHasAura && volatileOoze)
|
||||
return false;
|
||||
|
||||
if (botAI->IsTank(bot) && bot->GetExactDist2d(ICC_PUTRICIDE_TANK_POSITION) > 20.0f && !boss->HealthBelowPct(36) &&
|
||||
boss->GetVictim() == bot)
|
||||
{
|
||||
return MoveTo(bot->GetMapId(), ICC_PUTRICIDE_TANK_POSITION.GetPositionX(),
|
||||
ICC_PUTRICIDE_TANK_POSITION.GetPositionY(), ICC_PUTRICIDE_TANK_POSITION.GetPositionZ(), false,
|
||||
false, false, true, MovementPriority::MOVEMENT_COMBAT, true, false);
|
||||
}
|
||||
|
||||
if (botHasAura)
|
||||
{
|
||||
float botX = bot->GetPositionX();
|
||||
@@ -1762,7 +1826,7 @@ bool IccBpcMainTankAction::Execute(Event event)
|
||||
if (unit->HasAura(71596))
|
||||
{
|
||||
if (unit->GetEntry() == 37972 || // Keleseth
|
||||
unit->GetEntry() == 37973 || // Taldaram
|
||||
unit->GetEntry() == 37973 || // Taldaram
|
||||
unit->GetEntry() == 37970) // Valanar
|
||||
{
|
||||
empoweredPrince = unit;
|
||||
|
||||
@@ -29,8 +29,7 @@ const Position ICC_FESTERGUT_MELEE_SPORE = Position(4269.1772f, 3144.7673f, 360.
|
||||
const Position ICC_ROTFACE_TANK_POSITION = Position(4447.061f, 3150.9758f, 360.38568f);
|
||||
const Position ICC_ROTFACE_BIG_OOZE_POSITION = Position(4432.687f, 3142.3035f, 360.38623f);
|
||||
const Position ICC_ROTFACE_SAFE_POSITION = Position(4446.557f, 3065.6594f, 360.51843f);
|
||||
//const Position ICC_PUTRICIDE_TANK_OOZE_POSITION = Position(4362.709f, 3229.1448f, 389.4083f);
|
||||
//const Position ICC_PUTRICIDE_TANK_GAS_CLOUD_POSITION = Position(4397.0386f, 3221.385f, 389.3999f);
|
||||
const Position ICC_PUTRICIDE_TANK_POSITION = Position(4393.7676f, 3214.9375f, 389.3997f);
|
||||
//const Position ICC_PUTRICIDE_GAS1_POSITION = Position(4350.772f, 3249.9773f, 389.39508f);
|
||||
//const Position ICC_PUTRICIDE_GAS2_POSITION = Position(4390.002f, 3204.8855f, 389.39938f);
|
||||
//const Position ICC_PUTRICIDE_GAS3_POSITION = Position(4367.753f, 3177.5894f, 389.39575f);
|
||||
|
||||
@@ -9,7 +9,7 @@ void RaidIccStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
||||
NextAction::array(0, new NextAction("icc lm tank position", ACTION_RAID + 5), nullptr)));
|
||||
|
||||
triggers.push_back(new TriggerNode("icc spike near",
|
||||
NextAction::array(0, new NextAction("icc spike", ACTION_RAID + 5), nullptr)));
|
||||
NextAction::array(0, new NextAction("icc spike", ACTION_RAID + 3), nullptr)));
|
||||
|
||||
//Lady Deathwhisper
|
||||
triggers.push_back(new TriggerNode("icc dark reckoning",
|
||||
|
||||
@@ -29,10 +29,15 @@ public:
|
||||
creators["ignis fire resistance action"] = &RaidUlduarActionContext::ignis_fire_resistance_action;
|
||||
creators["iron assembly lightning tendrils action"] = &RaidUlduarActionContext::iron_assembly_lightning_tendrils_action;
|
||||
creators["iron assembly overload action"] = &RaidUlduarActionContext::iron_assembly_overload_action;
|
||||
creators["iron assembly rune of power action"] = &RaidUlduarActionContext::iron_assembly_rune_of_power_action;
|
||||
creators["kologarn mark dps target action"] = &RaidUlduarActionContext::kologarn_mark_dps_target_action;
|
||||
creators["kologarn fall from floor action"] = &RaidUlduarActionContext::kologarn_fall_from_floor_action;
|
||||
creators["kologarn nature resistance action"] = &RaidUlduarActionContext::kologarn_nature_resistance_action;
|
||||
creators["kologarn rubble slowdown action"] = &RaidUlduarActionContext::kologarn_rubble_slowdown_action;
|
||||
creators["kologarn eyebeam action"] = &RaidUlduarActionContext::kologarn_eyebeam_action;
|
||||
creators["kologarn rti target action"] = &RaidUlduarActionContext::kologarn_rti_target_action;
|
||||
creators["kologarn crunch armor action"] = &RaidUlduarActionContext::kologarn_crunch_armor_action;
|
||||
creators["auriaya fall from floor action"] = &RaidUlduarActionContext::auriaya_fall_from_floor_action;
|
||||
creators["hodir move snowpacked icicle"] = &RaidUlduarActionContext::hodir_move_snowpacked_icicle;
|
||||
creators["hodir biting cold jump"] = &RaidUlduarActionContext::hodir_biting_cold_jump;
|
||||
creators["hodir frost resistance action"] = &RaidUlduarActionContext::hodir_frost_resistance_action;
|
||||
@@ -41,8 +46,22 @@ public:
|
||||
creators["freya nature resistance action"] = &RaidUlduarActionContext::freya_nature_resistance_action;
|
||||
creators["freya mark dps target action"] = &RaidUlduarActionContext::freya_mark_dps_target_action;
|
||||
creators["freya move to healing spore action"] = &RaidUlduarActionContext::freya_move_to_healing_spore_action;
|
||||
creators["thorim frost resistance action"] = &RaidUlduarActionContext::thorim_frost_resistance_action;
|
||||
creators["thorim nature resistance action"] = &RaidUlduarActionContext::thorim_nature_resistance_action;
|
||||
creators["thorim unbalancing strike action"] = &RaidUlduarActionContext::thorim_unbalancing_strike_action;
|
||||
creators["thorim mark dps target action"] = &RaidUlduarActionContext::thorim_mark_dps_target_action;
|
||||
creators["thorim arena positioning action"] = &RaidUlduarActionContext::thorim_arena_positioning_action;
|
||||
creators["thorim gauntlet positioning action"] = &RaidUlduarActionContext::thorim_gauntlet_positioning_action;
|
||||
creators["thorim phase 2 positioning action"] = &RaidUlduarActionContext::thorim_phase2_positioning_action;
|
||||
creators["mimiron fire resistance action"] = &RaidUlduarActionContext::mimiron_fire_resistance_action;
|
||||
creators["mimiron shock blast action"] = &RaidUlduarActionContext::mimiron_shock_blast_action;
|
||||
creators["mimiron phase 1 positioning action"] = &RaidUlduarActionContext::mimiron_phase_1_positioning_action;
|
||||
creators["mimiron p3wx2 laser barrage action"] = &RaidUlduarActionContext::mimiron_p3wx2_laser_barrage_action;
|
||||
creators["mimiron rapid burst action"] = &RaidUlduarActionContext::mimiron_rapid_burst_action;
|
||||
creators["mimiron aerial command unit action"] = &RaidUlduarActionContext::mimiron_aerial_command_unit_action;
|
||||
creators["mimiron rocket strike action"] = &RaidUlduarActionContext::mimiron_rocket_strike_action;
|
||||
creators["mimiron phase 4 mark dps action"] = &RaidUlduarActionContext::mimiron_phase_4_mark_dps_action;
|
||||
creators["mimiron cheat action"] = &RaidUlduarActionContext::mimiron_cheat_action;
|
||||
}
|
||||
|
||||
private:
|
||||
@@ -59,10 +78,15 @@ private:
|
||||
static Action* ignis_fire_resistance_action(PlayerbotAI* ai) { return new BossFireResistanceAction(ai, "ignis the furnace master"); }
|
||||
static Action* iron_assembly_lightning_tendrils_action(PlayerbotAI* ai) { return new IronAssemblyLightningTendrilsAction(ai); }
|
||||
static Action* iron_assembly_overload_action(PlayerbotAI* ai) { return new IronAssemblyOverloadAction(ai); }
|
||||
static Action* iron_assembly_rune_of_power_action(PlayerbotAI* ai) { return new IronAssemblyRuneOfPowerAction(ai); }
|
||||
static Action* kologarn_mark_dps_target_action(PlayerbotAI* ai) { return new KologarnMarkDpsTargetAction(ai); }
|
||||
static Action* kologarn_fall_from_floor_action(PlayerbotAI* ai) { return new KologarnFallFromFloorAction(ai); }
|
||||
static Action* kologarn_nature_resistance_action(PlayerbotAI* ai) { return new BossNatureResistanceAction(ai, "kologarn"); }
|
||||
static Action* kologarn_rubble_slowdown_action(PlayerbotAI* ai) { return new KologarnRubbleSlowdownAction(ai); }
|
||||
static Action* kologarn_eyebeam_action(PlayerbotAI* ai) { return new KologarnEyebeamAction(ai); }
|
||||
static Action* kologarn_rti_target_action(PlayerbotAI* ai) { return new KologarnRtiTargetAction(ai); }
|
||||
static Action* kologarn_crunch_armor_action(PlayerbotAI* ai) { return new KologarnCrunchArmorAction(ai); }
|
||||
static Action* auriaya_fall_from_floor_action(PlayerbotAI* ai) { return new AuriayaFallFromFloorAction(ai); }
|
||||
static Action* hodir_move_snowpacked_icicle(PlayerbotAI* ai) { return new HodirMoveSnowpackedIcicleAction(ai); }
|
||||
static Action* hodir_biting_cold_jump(PlayerbotAI* ai) { return new HodirBitingColdJumpAction(ai); }
|
||||
static Action* hodir_frost_resistance_action(PlayerbotAI* ai) { return new BossFrostResistanceAction(ai, "hodir"); }
|
||||
@@ -71,8 +95,22 @@ private:
|
||||
static Action* freya_nature_resistance_action(PlayerbotAI* ai) { return new BossNatureResistanceAction(ai, "freya"); }
|
||||
static Action* freya_mark_dps_target_action(PlayerbotAI* ai) { return new FreyaMarkDpsTargetAction(ai); }
|
||||
static Action* freya_move_to_healing_spore_action(PlayerbotAI* ai) { return new FreyaMoveToHealingSporeAction(ai); }
|
||||
static Action* thorim_frost_resistance_action(PlayerbotAI* ai) { return new BossFrostResistanceAction(ai, "thorim"); }
|
||||
static Action* thorim_nature_resistance_action(PlayerbotAI* ai) { return new BossNatureResistanceAction(ai, "thorim"); }
|
||||
static Action* thorim_unbalancing_strike_action(PlayerbotAI* ai) { return new ThorimUnbalancingStrikeAction(ai); }
|
||||
static Action* thorim_mark_dps_target_action(PlayerbotAI* ai) { return new ThorimMarkDpsTargetAction(ai); }
|
||||
static Action* thorim_arena_positioning_action(PlayerbotAI* ai) { return new ThorimArenaPositioningAction(ai); }
|
||||
static Action* thorim_gauntlet_positioning_action(PlayerbotAI* ai) { return new ThorimGauntletPositioningAction(ai); }
|
||||
static Action* thorim_phase2_positioning_action(PlayerbotAI* ai) { return new ThorimPhase2PositioningAction(ai); }
|
||||
static Action* mimiron_fire_resistance_action(PlayerbotAI* ai) { return new BossFireResistanceAction(ai, "mimiron"); }
|
||||
static Action* mimiron_shock_blast_action(PlayerbotAI* ai) { return new MimironShockBlastAction(ai); }
|
||||
static Action* mimiron_phase_1_positioning_action(PlayerbotAI* ai) { return new MimironPhase1PositioningAction(ai); }
|
||||
static Action* mimiron_p3wx2_laser_barrage_action(PlayerbotAI* ai) { return new MimironP3Wx2LaserBarrageAction(ai); }
|
||||
static Action* mimiron_rapid_burst_action(PlayerbotAI* ai) { return new MimironRapidBurstAction(ai); }
|
||||
static Action* mimiron_aerial_command_unit_action(PlayerbotAI* ai) { return new MimironAerialCommandUnitAction(ai); }
|
||||
static Action* mimiron_rocket_strike_action(PlayerbotAI* ai) { return new MimironRocketStrikeAction(ai); }
|
||||
static Action* mimiron_phase_4_mark_dps_action(PlayerbotAI* ai) { return new MimironPhase4MarkDpsAction(ai); }
|
||||
static Action* mimiron_cheat_action(PlayerbotAI* ai) { return new MimironCheatAction(ai); }
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -130,6 +130,14 @@ public:
|
||||
bool isUseful() override;
|
||||
};
|
||||
|
||||
class IronAssemblyRuneOfPowerAction : public MovementAction
|
||||
{
|
||||
public:
|
||||
IronAssemblyRuneOfPowerAction(PlayerbotAI* botAI) : MovementAction(botAI, "iron assembly rune of power action") {}
|
||||
bool Execute(Event event) override;
|
||||
bool isUseful() override;
|
||||
};
|
||||
|
||||
class KologarnMarkDpsTargetAction : public Action
|
||||
{
|
||||
public:
|
||||
@@ -153,6 +161,38 @@ public:
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
class KologarnEyebeamAction : public MovementAction
|
||||
{
|
||||
public:
|
||||
KologarnEyebeamAction(PlayerbotAI* botAI) : MovementAction(botAI, "kologarn eyebeam action") {}
|
||||
bool Execute(Event event) override;
|
||||
bool isUseful() override;
|
||||
};
|
||||
|
||||
class KologarnRtiTargetAction : public Action
|
||||
{
|
||||
public:
|
||||
KologarnRtiTargetAction(PlayerbotAI* botAI) : Action(botAI, "kologarn rti target action") {}
|
||||
bool Execute(Event event) override;
|
||||
bool isUseful() override;
|
||||
};
|
||||
|
||||
class KologarnCrunchArmorAction : public Action
|
||||
{
|
||||
public:
|
||||
KologarnCrunchArmorAction(PlayerbotAI* botAI) : Action(botAI, "kologarn crunch armor action") {}
|
||||
bool Execute(Event event) override;
|
||||
bool isUseful() override;
|
||||
};
|
||||
|
||||
class AuriayaFallFromFloorAction : public Action
|
||||
{
|
||||
public:
|
||||
AuriayaFallFromFloorAction(PlayerbotAI* botAI) : Action(botAI, "auriaya fall from floor action") {}
|
||||
bool Execute(Event event) override;
|
||||
bool isUseful() override;
|
||||
};
|
||||
|
||||
class HodirBitingColdJumpAction : public MovementAction
|
||||
{
|
||||
public:
|
||||
@@ -185,4 +225,133 @@ public:
|
||||
bool isUseful() override;
|
||||
};
|
||||
|
||||
class ThorimUnbalancingStrikeAction : public Action
|
||||
{
|
||||
public:
|
||||
ThorimUnbalancingStrikeAction(PlayerbotAI* ai) : Action(ai, "thorim unbalancing strike action") {}
|
||||
|
||||
bool Execute(Event event) override;
|
||||
bool isUseful() override;
|
||||
};
|
||||
|
||||
class ThorimMarkDpsTargetAction : public Action
|
||||
{
|
||||
public:
|
||||
ThorimMarkDpsTargetAction(PlayerbotAI* ai) : Action(ai, "thorim mark dps target action") {}
|
||||
|
||||
bool Execute(Event event) override;
|
||||
bool isUseful() override;
|
||||
};
|
||||
|
||||
class ThorimArenaPositioningAction : public MovementAction
|
||||
{
|
||||
public:
|
||||
ThorimArenaPositioningAction(PlayerbotAI* ai) : MovementAction(ai, "thorim arena positioning action") {}
|
||||
|
||||
bool Execute(Event event) override;
|
||||
bool isUseful() override;
|
||||
};
|
||||
|
||||
class ThorimGauntletPositioningAction : public MovementAction
|
||||
{
|
||||
public:
|
||||
ThorimGauntletPositioningAction(PlayerbotAI* ai) : MovementAction(ai, "thorim gauntlet positioning action") {}
|
||||
|
||||
bool Execute(Event event) override;
|
||||
bool isUseful() override;
|
||||
};
|
||||
|
||||
class ThorimFallFromFloorAction : public Action
|
||||
{
|
||||
public:
|
||||
ThorimFallFromFloorAction(PlayerbotAI* botAI) : Action(botAI, "thorim fall from floor action") {}
|
||||
bool Execute(Event event) override;
|
||||
bool isUseful() override;
|
||||
};
|
||||
|
||||
class ThorimPhase2PositioningAction : public MovementAction
|
||||
{
|
||||
public:
|
||||
ThorimPhase2PositioningAction(PlayerbotAI* ai) : MovementAction(ai, "thorim phase 2 positioning action") {}
|
||||
|
||||
bool Execute(Event event) override;
|
||||
bool isUseful() override;
|
||||
};
|
||||
|
||||
class MimironShockBlastAction : public MovementAction
|
||||
{
|
||||
public:
|
||||
MimironShockBlastAction(PlayerbotAI* ai) : MovementAction(ai, "mimiron shock blast action") {}
|
||||
|
||||
bool Execute(Event event) override;
|
||||
bool isUseful() override;
|
||||
};
|
||||
|
||||
class MimironPhase1PositioningAction : public MovementAction
|
||||
{
|
||||
public:
|
||||
MimironPhase1PositioningAction(PlayerbotAI* ai) : MovementAction(ai, "mimiron phase 1 positioning action") {}
|
||||
|
||||
bool Execute(Event event) override;
|
||||
bool isUseful() override;
|
||||
};
|
||||
|
||||
class MimironP3Wx2LaserBarrageAction : public MovementAction
|
||||
{
|
||||
public:
|
||||
MimironP3Wx2LaserBarrageAction(PlayerbotAI* ai, float distance = 24.0f, float delta_angle = M_PI / 8)
|
||||
: MovementAction(ai, "mimiron p3wx2 laser barrage action")
|
||||
{
|
||||
this->distance = distance;
|
||||
this->delta_angle = delta_angle;
|
||||
}
|
||||
virtual bool Execute(Event event);
|
||||
|
||||
protected:
|
||||
float distance, delta_angle;
|
||||
};
|
||||
|
||||
class MimironRapidBurstAction : public MovementAction
|
||||
{
|
||||
public:
|
||||
MimironRapidBurstAction(PlayerbotAI* ai) : MovementAction(ai, "mimiron rapid burst action") {}
|
||||
|
||||
bool Execute(Event event) override;
|
||||
bool isUseful() override;
|
||||
};
|
||||
|
||||
class MimironAerialCommandUnitAction : public Action
|
||||
{
|
||||
public:
|
||||
MimironAerialCommandUnitAction(PlayerbotAI* ai) : Action(ai, "mimiron aerial command unit action") {}
|
||||
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
class MimironRocketStrikeAction : public MovementAction
|
||||
{
|
||||
public:
|
||||
MimironRocketStrikeAction(PlayerbotAI* ai) : MovementAction(ai, "mimiron rocket strike action") {}
|
||||
|
||||
bool Execute(Event event) override;
|
||||
bool isUseful() override;
|
||||
};
|
||||
|
||||
class MimironPhase4MarkDpsAction : public Action
|
||||
{
|
||||
public:
|
||||
MimironPhase4MarkDpsAction(PlayerbotAI* ai) : Action(ai, "mimiron phase 4 mark dps action") {}
|
||||
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
class MimironCheatAction : public Action
|
||||
{
|
||||
public:
|
||||
MimironCheatAction(PlayerbotAI* ai) : Action(ai, "mimiron cheat action") {}
|
||||
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
@@ -68,6 +68,10 @@ void RaidUlduarStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
||||
"iron assembly overload trigger",
|
||||
NextAction::array(0, new NextAction("iron assembly overload action", ACTION_RAID), nullptr)));
|
||||
|
||||
triggers.push_back(new TriggerNode(
|
||||
"iron assembly rune of power trigger",
|
||||
NextAction::array(0, new NextAction("iron assembly rune of power action", ACTION_RAID), nullptr)));
|
||||
|
||||
//
|
||||
// Kologarn
|
||||
//
|
||||
@@ -75,6 +79,18 @@ void RaidUlduarStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
||||
"kologarn fall from floor trigger",
|
||||
NextAction::array(0, new NextAction("kologarn fall from floor action", ACTION_RAID + 1), nullptr)));
|
||||
|
||||
triggers.push_back(new TriggerNode(
|
||||
"kologarn rti target trigger",
|
||||
NextAction::array(0, new NextAction("kologarn rti target action", ACTION_RAID + 1), nullptr)));
|
||||
|
||||
triggers.push_back(new TriggerNode(
|
||||
"kologarn eyebeam trigger",
|
||||
NextAction::array(0, new NextAction("kologarn eyebeam action", ACTION_RAID + 1), nullptr)));
|
||||
|
||||
triggers.push_back(new TriggerNode(
|
||||
"kologarn attack dps target trigger",
|
||||
NextAction::array(0, new NextAction("attack rti target", ACTION_RAID), nullptr)));
|
||||
|
||||
triggers.push_back(new TriggerNode(
|
||||
"kologarn mark dps target trigger",
|
||||
NextAction::array(0, new NextAction("kologarn mark dps target action", ACTION_RAID), nullptr)));
|
||||
@@ -87,6 +103,17 @@ void RaidUlduarStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
||||
"kologarn rubble slowdown trigger",
|
||||
NextAction::array(0, new NextAction("kologarn rubble slowdown action", ACTION_RAID), nullptr)));
|
||||
|
||||
triggers.push_back(new TriggerNode(
|
||||
"kologarn crunch armor trigger",
|
||||
NextAction::array(0, new NextAction("kologarn crunch armor action", ACTION_RAID), nullptr)));
|
||||
|
||||
//
|
||||
// Auriaya
|
||||
//
|
||||
triggers.push_back(new TriggerNode(
|
||||
"auriaya fall from floor trigger",
|
||||
NextAction::array(0, new NextAction("auriaya fall from floor action", ACTION_RAID), nullptr)));
|
||||
|
||||
//
|
||||
// Hodir
|
||||
//
|
||||
@@ -132,12 +159,72 @@ void RaidUlduarStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
||||
"thorim nature resistance trigger",
|
||||
NextAction::array(0, new NextAction("thorim nature resistance action", ACTION_RAID), nullptr)));
|
||||
|
||||
triggers.push_back(new TriggerNode(
|
||||
"thorim frost resistance trigger",
|
||||
NextAction::array(0, new NextAction("thorim frost resistance action", ACTION_RAID), nullptr)));
|
||||
|
||||
triggers.push_back(new TriggerNode(
|
||||
"thorim unbalancing strike trigger",
|
||||
NextAction::array(0, new NextAction("thorim unbalancing strike action", ACTION_RAID), nullptr)));
|
||||
|
||||
triggers.push_back(new TriggerNode(
|
||||
"thorim mark dps target trigger",
|
||||
NextAction::array(0, new NextAction("thorim mark dps target action", ACTION_RAID), nullptr)));
|
||||
|
||||
triggers.push_back(new TriggerNode(
|
||||
"thorim gauntlet positioning trigger",
|
||||
NextAction::array(0, new NextAction("thorim gauntlet positioning action", ACTION_RAID), nullptr)));
|
||||
|
||||
triggers.push_back(new TriggerNode(
|
||||
"thorim arena positioning trigger",
|
||||
NextAction::array(0, new NextAction("thorim arena positioning action", ACTION_RAID), nullptr)));
|
||||
|
||||
triggers.push_back(new TriggerNode(
|
||||
"thorim fall from floor trigger",
|
||||
NextAction::array(0, new NextAction("thorim fall from floor action", ACTION_RAID + 1), nullptr)));
|
||||
|
||||
triggers.push_back(new TriggerNode(
|
||||
"thorim phase 2 positioning trigger",
|
||||
NextAction::array(0, new NextAction("thorim phase 2 positioning action", ACTION_RAID), nullptr)));
|
||||
|
||||
//
|
||||
// Mimiron
|
||||
//
|
||||
triggers.push_back(new TriggerNode(
|
||||
"mimiron p3wx2 laser barrage trigger",
|
||||
NextAction::array(0, new NextAction("mimiron p3wx2 laser barrage action", ACTION_RAID + 2), nullptr)));
|
||||
|
||||
triggers.push_back(new TriggerNode(
|
||||
"mimiron shock blast trigger",
|
||||
NextAction::array(0, new NextAction("mimiron shock blast action", ACTION_RAID + 1), nullptr)));
|
||||
|
||||
triggers.push_back(new TriggerNode(
|
||||
"mimiron fire resistance trigger",
|
||||
NextAction::array(0, new NextAction("mimiron fire resistance action", ACTION_RAID), nullptr)));
|
||||
|
||||
triggers.push_back(new TriggerNode(
|
||||
"mimiron phase 1 positioning trigger",
|
||||
NextAction::array(0, new NextAction("mimiron phase 1 positioning action", ACTION_RAID), nullptr)));
|
||||
|
||||
triggers.push_back(new TriggerNode(
|
||||
"mimiron rapid burst trigger",
|
||||
NextAction::array(0, new NextAction("mimiron rapid burst action", ACTION_RAID), nullptr)));
|
||||
|
||||
triggers.push_back(new TriggerNode(
|
||||
"mimiron aerial command unit trigger",
|
||||
NextAction::array(0, new NextAction("mimiron aerial command unit action", ACTION_RAID), nullptr)));
|
||||
|
||||
triggers.push_back(new TriggerNode(
|
||||
"mimiron rocket strike trigger",
|
||||
NextAction::array(0, new NextAction("mimiron rocket strike action", ACTION_RAID), nullptr)));
|
||||
|
||||
triggers.push_back(new TriggerNode(
|
||||
"mimiron phase 4 mark dps trigger",
|
||||
NextAction::array(0, new NextAction("mimiron phase 4 mark dps action", ACTION_RAID), nullptr)));
|
||||
|
||||
triggers.push_back(new TriggerNode(
|
||||
"mimiron cheat trigger",
|
||||
NextAction::array(0, new NextAction("mimiron cheat action", ACTION_RAID), nullptr)));
|
||||
}
|
||||
|
||||
void RaidUlduarStrategy::InitMultipliers(std::vector<Multiplier*>& multipliers)
|
||||
|
||||
@@ -29,10 +29,16 @@ public:
|
||||
creators["ignis fire resistance trigger"] = &RaidUlduarTriggerContext::ignis_fire_resistance_trigger;
|
||||
creators["iron assembly lightning tendrils trigger"] = &RaidUlduarTriggerContext::iron_assembly_lightning_tendrils_trigger;
|
||||
creators["iron assembly overload trigger"] = &RaidUlduarTriggerContext::iron_assembly_overload_trigger;
|
||||
creators["iron assembly rune of power trigger"] = &RaidUlduarTriggerContext::iron_assembly_rune_of_power_trigger;
|
||||
creators["kologarn mark dps target trigger"] = &RaidUlduarTriggerContext::kologarn_mark_dps_target_trigger;
|
||||
creators["kologarn fall from floor trigger"] = &RaidUlduarTriggerContext::kologarn_fall_from_floor_trigger;
|
||||
creators["kologarn nature resistance trigger"] = &RaidUlduarTriggerContext::kologarn_nature_resistance_trigger;
|
||||
creators["kologarn rubble slowdown trigger"] = &RaidUlduarTriggerContext::kologarn_rubble_slowdown_trigger;
|
||||
creators["kologarn eyebeam trigger"] = &RaidUlduarTriggerContext::kologarn_eyebeam_trigger;
|
||||
creators["kologarn rti target trigger"] = &RaidUlduarTriggerContext::kologarn_rti_target_trigger;
|
||||
creators["kologarn crunch armor trigger"] = &RaidUlduarTriggerContext::kologarn_crunch_armor_trigger;
|
||||
creators["kologarn attack dps target trigger"] = &RaidUlduarTriggerContext::kologarn_attack_dps_target_trigger;
|
||||
creators["auriaya fall from floor trigger"] = &RaidUlduarTriggerContext::auriaya_fall_from_floor_trigger;
|
||||
creators["hodir biting cold"] = &RaidUlduarTriggerContext::hodir_biting_cold;
|
||||
creators["hodir near snowpacked icicle"] = &RaidUlduarTriggerContext::hodir_near_snowpacked_icicle;
|
||||
creators["hodir frost resistance trigger"] = &RaidUlduarTriggerContext::hodir_frost_resistance_trigger;
|
||||
@@ -41,8 +47,23 @@ public:
|
||||
creators["freya nature resistance trigger"] = &RaidUlduarTriggerContext::freya_nature_resistance_trigger;
|
||||
creators["freya mark dps target trigger"] = &RaidUlduarTriggerContext::freya_mark_dps_target_trigger;
|
||||
creators["freya move to healing spore trigger"] = &RaidUlduarTriggerContext::freya_move_to_healing_spore_trigger;
|
||||
creators["thorim frost resistance trigger"] = &RaidUlduarTriggerContext::thorim_frost_resistance_trigger;
|
||||
creators["thorim nature resistance trigger"] = &RaidUlduarTriggerContext::thorim_nature_resistance_trigger;
|
||||
creators["thorim unbalancing strike trigger"] = &RaidUlduarTriggerContext::thorim_unbalancing_strike_trigger;
|
||||
creators["thorim mark dps target trigger"] = &RaidUlduarTriggerContext::thorim_mark_dps_target_trigger;
|
||||
creators["thorim arena positioning trigger"] = &RaidUlduarTriggerContext::thorim_arena_positioning_trigger;
|
||||
creators["thorim gauntlet positioning trigger"] = &RaidUlduarTriggerContext::thorim_gauntlet_positioning_trigger;
|
||||
creators["thorim fall from floor trigger"] = &RaidUlduarTriggerContext::thorim_fall_from_floor_trigger;
|
||||
creators["thorim phase 2 positioning trigger"] = &RaidUlduarTriggerContext::thorim_phase2_positioning_trigger;
|
||||
creators["mimiron fire resistance trigger"] = &RaidUlduarTriggerContext::mimiron_fire_resistance_trigger;
|
||||
creators["mimiron shock blast trigger"] = &RaidUlduarTriggerContext::mimiron_shock_blast_trigger;
|
||||
creators["mimiron phase 1 positioning trigger"] = &RaidUlduarTriggerContext::mimiron_phase_1_positioning_trigger;
|
||||
creators["mimiron p3wx2 laser barrage trigger"] = &RaidUlduarTriggerContext::mimiron_p3wx2_laser_barrage_trigger;
|
||||
creators["mimiron rapid burst trigger"] = &RaidUlduarTriggerContext::mimiron_rapid_burst_trigger;
|
||||
creators["mimiron aerial command unit trigger"] = &RaidUlduarTriggerContext::mimiron_aerial_command_unit_trigger;
|
||||
creators["mimiron rocket strike trigger"] = &RaidUlduarTriggerContext::mimiron_rocket_strike_trigger;
|
||||
creators["mimiron phase 4 mark dps trigger"] = &RaidUlduarTriggerContext::mimiron_phase_4_mark_dps_trigger;
|
||||
creators["mimiron cheat trigger"] = &RaidUlduarTriggerContext::mimiron_cheat_trigger;
|
||||
}
|
||||
|
||||
private:
|
||||
@@ -59,10 +80,16 @@ private:
|
||||
static Trigger* ignis_fire_resistance_trigger(PlayerbotAI* ai) { return new BossFireResistanceTrigger(ai, "ignis the furnace master"); }
|
||||
static Trigger* iron_assembly_lightning_tendrils_trigger(PlayerbotAI* ai) { return new IronAssemblyLightningTendrilsTrigger(ai); }
|
||||
static Trigger* iron_assembly_overload_trigger(PlayerbotAI* ai) { return new IronAssemblyOverloadTrigger(ai); }
|
||||
static Trigger* iron_assembly_rune_of_power_trigger(PlayerbotAI* ai) { return new IronAssemblyRuneOfPowerTrigger(ai); }
|
||||
static Trigger* kologarn_mark_dps_target_trigger(PlayerbotAI* ai) { return new KologarnMarkDpsTargetTrigger(ai); }
|
||||
static Trigger* kologarn_fall_from_floor_trigger(PlayerbotAI* ai) { return new KologarnFallFromFloorTrigger(ai); }
|
||||
static Trigger* kologarn_nature_resistance_trigger(PlayerbotAI* ai) { return new BossNatureResistanceTrigger(ai, "kologarn"); }
|
||||
static Trigger* kologarn_rubble_slowdown_trigger(PlayerbotAI* ai) { return new KologarnRubbleSlowdownTrigger(ai); }
|
||||
static Trigger* kologarn_eyebeam_trigger(PlayerbotAI* ai) { return new KologarnEyebeamTrigger(ai); }
|
||||
static Trigger* kologarn_rti_target_trigger(PlayerbotAI* ai) { return new KologarnRtiTargetTrigger(ai); }
|
||||
static Trigger* kologarn_crunch_armor_trigger(PlayerbotAI* ai) { return new KologarnCrunchArmorTrigger(ai); }
|
||||
static Trigger* kologarn_attack_dps_target_trigger(PlayerbotAI* ai) { return new KologarnAttackDpsTargetTrigger(ai); }
|
||||
static Trigger* auriaya_fall_from_floor_trigger(PlayerbotAI* ai) { return new AuriayaFallFromFloorTrigger(ai); }
|
||||
static Trigger* hodir_biting_cold(PlayerbotAI* ai) { return new HodirBitingColdTrigger(ai); }
|
||||
static Trigger* hodir_near_snowpacked_icicle(PlayerbotAI* ai) { return new HodirNearSnowpackedIcicleTrigger(ai); }
|
||||
static Trigger* hodir_frost_resistance_trigger(PlayerbotAI* ai) { return new BossFrostResistanceTrigger(ai, "hodir"); }
|
||||
@@ -71,8 +98,23 @@ private:
|
||||
static Trigger* freya_nature_resistance_trigger(PlayerbotAI* ai) { return new BossNatureResistanceTrigger(ai, "freya"); }
|
||||
static Trigger* freya_mark_dps_target_trigger(PlayerbotAI* ai) { return new FreyaMarkDpsTargetTrigger(ai); }
|
||||
static Trigger* freya_move_to_healing_spore_trigger(PlayerbotAI* ai) { return new FreyaMoveToHealingSporeTrigger(ai); }
|
||||
static Trigger* thorim_frost_resistance_trigger(PlayerbotAI* ai) { return new BossFrostResistanceTrigger(ai, "thorim"); }
|
||||
static Trigger* thorim_nature_resistance_trigger(PlayerbotAI* ai) { return new BossNatureResistanceTrigger(ai, "thorim"); }
|
||||
static Trigger* thorim_unbalancing_strike_trigger(PlayerbotAI* ai) { return new ThorimUnbalancingStrikeTrigger(ai); }
|
||||
static Trigger* thorim_mark_dps_target_trigger(PlayerbotAI* ai) { return new ThorimMarkDpsTargetTrigger(ai); }
|
||||
static Trigger* thorim_arena_positioning_trigger(PlayerbotAI* ai) { return new ThorimArenaPositioningTrigger(ai); }
|
||||
static Trigger* thorim_gauntlet_positioning_trigger(PlayerbotAI* ai) { return new ThorimGauntletPositioningTrigger(ai); }
|
||||
static Trigger* thorim_fall_from_floor_trigger(PlayerbotAI* ai) { return new ThorimFallFromFloorTrigger(ai); }
|
||||
static Trigger* thorim_phase2_positioning_trigger(PlayerbotAI* ai) { return new ThorimPhase2PositioningTrigger(ai); }
|
||||
static Trigger* mimiron_fire_resistance_trigger(PlayerbotAI* ai) { return new BossFireResistanceTrigger(ai, "mimiron"); }
|
||||
static Trigger* mimiron_shock_blast_trigger(PlayerbotAI* ai) { return new MimironShockBlastTrigger(ai); }
|
||||
static Trigger* mimiron_phase_1_positioning_trigger(PlayerbotAI* ai) { return new MimironPhase1PositioningTrigger(ai); }
|
||||
static Trigger* mimiron_p3wx2_laser_barrage_trigger(PlayerbotAI* ai) { return new MimironP3Wx2LaserBarrageTrigger(ai); }
|
||||
static Trigger* mimiron_rapid_burst_trigger(PlayerbotAI* ai) { return new MimironRapidBurstTrigger(ai); }
|
||||
static Trigger* mimiron_aerial_command_unit_trigger(PlayerbotAI* ai) { return new MimironAerialCommandUnitTrigger(ai); }
|
||||
static Trigger* mimiron_rocket_strike_trigger(PlayerbotAI* ai) { return new MimironRocketStrikeTrigger(ai); }
|
||||
static Trigger* mimiron_phase_4_mark_dps_trigger(PlayerbotAI* ai) { return new MimironPhase4MarkDpsTrigger(ai); }
|
||||
static Trigger* mimiron_cheat_trigger(PlayerbotAI* ai) { return new MimironCheatTrigger(ai); }
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#include "SharedDefines.h"
|
||||
#include "Trigger.h"
|
||||
#include "Vehicle.h"
|
||||
#include <MovementActions.h>
|
||||
|
||||
const std::vector<uint32> availableVehicles = {NPC_VEHICLE_CHOPPER, NPC_SALVAGED_DEMOLISHER,
|
||||
NPC_SALVAGED_DEMOLISHER_TURRET, NPC_SALVAGED_SIEGE_ENGINE,
|
||||
@@ -277,6 +278,21 @@ bool IronAssemblyOverloadTrigger::IsActive()
|
||||
boss->HasAura(SPELL_OVERLOAD_10_MAN_2) || boss->HasAura(SPELL_OVERLOAD_25_MAN_2);
|
||||
}
|
||||
|
||||
bool IronAssemblyRuneOfPowerTrigger::IsActive()
|
||||
{
|
||||
Unit* target = botAI->GetUnit(bot->GetTarget());
|
||||
if (!target || !target->IsAlive())
|
||||
return false;
|
||||
|
||||
if (!target->HasAura(SPELL_RUNE_OF_POWER))
|
||||
return false;
|
||||
|
||||
if (target->GetVictim() != bot)
|
||||
return false;
|
||||
|
||||
return botAI->IsTank(bot);
|
||||
}
|
||||
|
||||
bool KologarnMarkDpsTargetTrigger::IsActive()
|
||||
{
|
||||
// Check boss and it is alive
|
||||
@@ -384,6 +400,105 @@ bool KologarnRubbleSlowdownTrigger::IsActive()
|
||||
return true;
|
||||
}
|
||||
|
||||
bool KologarnEyebeamTrigger::IsActive()
|
||||
{
|
||||
Unit* boss = AI_VALUE2(Unit*, "find target", "kologarn");
|
||||
|
||||
// Check boss and it is alive
|
||||
if (!boss || !boss->IsAlive())
|
||||
return false;
|
||||
|
||||
GuidVector triggers = AI_VALUE(GuidVector, "possible triggers");
|
||||
|
||||
if (!triggers.empty())
|
||||
{
|
||||
for (ObjectGuid const guid : triggers)
|
||||
{
|
||||
if (Unit* unit = botAI->GetUnit(guid))
|
||||
{
|
||||
std::string triggerName = unit->GetNameForLocaleIdx(sWorld->GetDefaultDbcLocale());
|
||||
|
||||
if (triggerName.rfind("Focused Eyebeam", 0) == 0 &&
|
||||
bot->GetDistance2d(unit) < ULDUAR_KOLOGARN_EYEBEAM_RADIUS + 1.0f)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool KologarnAttackDpsTargetTrigger::IsActive()
|
||||
{
|
||||
Unit* boss = AI_VALUE2(Unit*, "find target", "kologarn");
|
||||
|
||||
// Check boss and it is alive
|
||||
if (!boss || !boss->IsAlive())
|
||||
return false;
|
||||
|
||||
// Get bot's current target
|
||||
Unit* currentTarget = botAI->GetUnit(bot->GetTarget());
|
||||
if (!currentTarget || !currentTarget->IsAlive())
|
||||
return false;
|
||||
|
||||
// Get the current raid marker from the group
|
||||
Group* group = bot->GetGroup();
|
||||
if (!group)
|
||||
return false;
|
||||
|
||||
ObjectGuid skullTarget = group->GetTargetIcon(skullIndex);
|
||||
ObjectGuid crossTarget = group->GetTargetIcon(crossIndex);
|
||||
|
||||
if (crossTarget && (botAI->IsMainTank(bot) || botAI->IsAssistTankOfIndex(bot, 0)))
|
||||
{
|
||||
return currentTarget->GetGUID() != crossTarget;
|
||||
}
|
||||
else
|
||||
{
|
||||
return currentTarget->GetGUID() != skullTarget;
|
||||
}
|
||||
}
|
||||
|
||||
bool KologarnRtiTargetTrigger::IsActive()
|
||||
{
|
||||
Unit* boss = AI_VALUE2(Unit*, "find target", "kologarn");
|
||||
|
||||
// Check boss and it is alive
|
||||
if (!boss || !boss->IsAlive())
|
||||
return false;
|
||||
|
||||
std::string rtiMark = AI_VALUE(std::string, "rti");
|
||||
|
||||
if (botAI->IsMainTank(bot) || botAI->IsAssistTankOfIndex(bot, 0))
|
||||
return rtiMark != "cross";
|
||||
|
||||
return rtiMark != "skull";
|
||||
}
|
||||
|
||||
bool KologarnCrunchArmorTrigger::IsActive()
|
||||
{
|
||||
Unit* boss = AI_VALUE2(Unit*, "find target", "kologarn");
|
||||
|
||||
// Check boss and it is alive
|
||||
if (!boss || !boss->IsAlive())
|
||||
return false;
|
||||
|
||||
return bot->HasAura(SPELL_CRUNCH_ARMOR);
|
||||
}
|
||||
|
||||
bool AuriayaFallFromFloorTrigger::IsActive()
|
||||
{
|
||||
// Check boss and it is alive
|
||||
Unit* boss = AI_VALUE2(Unit*, "find target", "auriaya");
|
||||
if (!boss || !boss->IsAlive())
|
||||
return false;
|
||||
|
||||
// Check if bot is on the floor
|
||||
return bot->GetPositionZ() < ULDUAR_AURIAYA_AXIS_Z_PATHING_ISSUE_DETECT;
|
||||
}
|
||||
|
||||
bool HodirBitingColdTrigger::IsActive()
|
||||
{
|
||||
Unit* boss = AI_VALUE2(Unit*, "find target", "hodir");
|
||||
@@ -514,15 +629,13 @@ bool FreyaMarkDpsTargetTrigger::IsActive()
|
||||
}
|
||||
|
||||
// Check that eonars gift is need to be mark
|
||||
if (eonarsGift &&
|
||||
(!currentSkullUnit || currentSkullUnit->GetEntry() != eonarsGift->GetEntry()))
|
||||
if (eonarsGift && (!currentSkullUnit || currentSkullUnit->GetEntry() != eonarsGift->GetEntry()))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check that ancient conservator is need to be mark
|
||||
if (ancientConservator &&
|
||||
(!currentSkullUnit || currentSkullUnit->GetEntry() != ancientConservator->GetEntry()))
|
||||
if (ancientConservator && (!currentSkullUnit || currentSkullUnit->GetEntry() != ancientConservator->GetEntry()))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
@@ -549,8 +662,7 @@ bool FreyaMarkDpsTargetTrigger::IsActive()
|
||||
}
|
||||
|
||||
// If the highest health unit is not already marked, mark it
|
||||
if (highestHealthUnit &&
|
||||
(!currentSkullUnit || currentSkullUnit->GetEntry() != highestHealthUnit->GetEntry()))
|
||||
if (highestHealthUnit && (!currentSkullUnit || currentSkullUnit->GetEntry() != highestHealthUnit->GetEntry()))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
@@ -564,7 +676,7 @@ bool FreyaMarkDpsTargetTrigger::IsActive()
|
||||
if (!map || !map->IsRaid())
|
||||
return false;
|
||||
|
||||
uint32 healthThreshold = map->Is25ManRaid() ? 7200 : 4900; // Detonate maximum damage
|
||||
uint32 healthThreshold = map->Is25ManRaid() ? 7200 : 4900; // Detonate maximum damage
|
||||
|
||||
// Check that detonate lasher dont kill raid members
|
||||
for (GroupReference* gref = group->GetFirstMember(); gref; gref = gref->next())
|
||||
@@ -597,7 +709,6 @@ bool FreyaMoveToHealingSporeTrigger::IsActive()
|
||||
if (!conservatory || !conservatory->IsAlive())
|
||||
return false;
|
||||
|
||||
|
||||
GuidVector targets = AI_VALUE(GuidVector, "nearest npcs");
|
||||
float nearestDistance = std::numeric_limits<float>::max();
|
||||
bool foundSpore = false;
|
||||
@@ -628,3 +739,821 @@ bool FreyaMoveToHealingSporeTrigger::IsActive()
|
||||
// If the nearest spore is farther than 6 yards, a move is required
|
||||
return nearestDistance > 6.0f;
|
||||
}
|
||||
|
||||
bool ThorimUnbalancingStrikeTrigger::IsActive()
|
||||
{
|
||||
Unit* boss = AI_VALUE2(Unit*, "find target", "thorim");
|
||||
|
||||
// Check boss and it is alive
|
||||
if (!boss || !boss->IsAlive())
|
||||
return false;
|
||||
|
||||
return bot->HasAura(SPELL_UNBALANCING_STRIKE);
|
||||
}
|
||||
|
||||
bool ThorimMarkDpsTargetTrigger::IsActive()
|
||||
{
|
||||
if (bot->GetDistance(ULDUAR_THORIM_NEAR_ARENA_CENTER) > 110.0f)
|
||||
return false;
|
||||
|
||||
Group* group = bot->GetGroup();
|
||||
if (!group)
|
||||
return false;
|
||||
|
||||
if (botAI->IsMainTank(bot))
|
||||
{
|
||||
ObjectGuid currentSkullTarget = group->GetTargetIcon(skullIndex);
|
||||
Unit* currentSkullUnit = botAI->GetUnit(currentSkullTarget);
|
||||
if (currentSkullUnit && !currentSkullUnit->IsAlive())
|
||||
{
|
||||
currentSkullUnit = nullptr;
|
||||
}
|
||||
|
||||
Unit* acolyte = AI_VALUE2(Unit*, "find target", "dark rune acolyte");
|
||||
Unit* evoker = AI_VALUE2(Unit*, "find target", "dark rune evoker");
|
||||
|
||||
if (acolyte && acolyte->IsAlive() && bot->GetDistance(acolyte) < 50.0f &&
|
||||
(!currentSkullUnit || currentSkullUnit->GetEntry() != acolyte->GetEntry()))
|
||||
return true;
|
||||
|
||||
if (evoker && evoker->IsAlive() && bot->GetDistance(evoker) < 50.0f &&
|
||||
(!currentSkullUnit || currentSkullUnit->GetEntry() != evoker->GetEntry()))
|
||||
return true;
|
||||
|
||||
Unit* boss = AI_VALUE2(Unit*, "find target", "thorim");
|
||||
|
||||
// Check boss and it is alive
|
||||
if (!boss || !boss->IsAlive())
|
||||
return false;
|
||||
|
||||
|
||||
if (boss->GetPositionZ() < ULDUAR_THORIM_AXIS_Z_FLOOR_THRESHOLD && (!currentSkullUnit || !currentSkullUnit->IsAlive()))
|
||||
{
|
||||
group->SetTargetIcon(skullIndex, bot->GetGUID(), boss->GetGUID());
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
else if (botAI->IsAssistTankOfIndex(bot, 0))
|
||||
{
|
||||
Player* mainTank = nullptr;
|
||||
for (GroupReference* gref = group->GetFirstMember(); gref; gref = gref->next())
|
||||
{
|
||||
Player* member = gref->GetSource();
|
||||
if (member && botAI->IsMainTank(member))
|
||||
{
|
||||
mainTank = member;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (mainTank && bot->GetDistance(mainTank) < 30.0f)
|
||||
return false;
|
||||
|
||||
ObjectGuid currentCrossTarget = group->GetTargetIcon(crossIndex);
|
||||
Unit* currentCrossUnit = botAI->GetUnit(currentCrossTarget);
|
||||
if (currentCrossUnit && !currentCrossUnit->IsAlive())
|
||||
{
|
||||
currentCrossUnit = nullptr;
|
||||
}
|
||||
|
||||
Unit* acolyte = AI_VALUE2(Unit*, "find target", "dark rune acolyte");
|
||||
if (currentCrossUnit && currentCrossUnit->GetEntry() == NPC_DARK_RUNE_ACOLYTE_I)
|
||||
return false;
|
||||
|
||||
Unit* runicColossus = AI_VALUE2(Unit*, "find target", "runic colossus");
|
||||
Unit* ancientRuneGiant = AI_VALUE2(Unit*, "find target", "ancient rune giant");
|
||||
Unit* ironHonorGuard = AI_VALUE2(Unit*, "find target", "iron ring guard");
|
||||
Unit* ironRingGuard = AI_VALUE2(Unit*, "find target", "iron honor guard");
|
||||
|
||||
if (acolyte && acolyte->IsAlive() && (!currentCrossUnit || currentCrossUnit->GetEntry() != acolyte->GetEntry()))
|
||||
return true;
|
||||
|
||||
if (currentCrossUnit && currentCrossUnit->GetEntry() == NPC_RUNIC_COLOSSUS)
|
||||
return false;
|
||||
if (runicColossus && runicColossus->IsAlive() &&
|
||||
(!currentCrossUnit || currentCrossUnit->GetEntry() != runicColossus->GetEntry()))
|
||||
return true;
|
||||
|
||||
if (currentCrossUnit && currentCrossUnit->GetEntry() == NPC_ANCIENT_RUNE_GIANT)
|
||||
return false;
|
||||
if (ancientRuneGiant && ancientRuneGiant->IsAlive() &&
|
||||
(!currentCrossUnit || currentCrossUnit->GetEntry() != ancientRuneGiant->GetEntry()))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ThorimGauntletPositioningTrigger::IsActive()
|
||||
{
|
||||
if (bot->GetDistance(ULDUAR_THORIM_NEAR_ARENA_CENTER) > 110.0f)
|
||||
return false;
|
||||
|
||||
Difficulty raidDifficulty = bot->GetRaidDifficulty();
|
||||
|
||||
Group* group = bot->GetGroup();
|
||||
if (!group)
|
||||
return false;
|
||||
uint32 requiredAssistTankQuantity = 1;
|
||||
uint32 requiredHealerQuantity = 0;
|
||||
uint32 requiredDpsQuantity = 0;
|
||||
|
||||
if (raidDifficulty == Difficulty::RAID_DIFFICULTY_10MAN_NORMAL)
|
||||
{
|
||||
requiredDpsQuantity = 3;
|
||||
requiredHealerQuantity = 1;
|
||||
}
|
||||
else if (raidDifficulty == Difficulty::RAID_DIFFICULTY_25MAN_NORMAL)
|
||||
{
|
||||
requiredDpsQuantity = 7;
|
||||
requiredHealerQuantity = 2;
|
||||
}
|
||||
|
||||
for (GroupReference* gref = group->GetFirstMember(); gref; gref = gref->next())
|
||||
{
|
||||
Player* member = gref->GetSource();
|
||||
if (!member)
|
||||
continue;
|
||||
|
||||
if (requiredDpsQuantity > 0 && botAI->IsDps(member))
|
||||
{
|
||||
requiredDpsQuantity--;
|
||||
if (bot->GetGUID() == member->GetGUID())
|
||||
break;
|
||||
}
|
||||
|
||||
if (requiredAssistTankQuantity > 0 && botAI->IsAssistTankOfIndex(member, 0))
|
||||
{
|
||||
requiredAssistTankQuantity--;
|
||||
if (bot->GetGUID() == member->GetGUID())
|
||||
break;
|
||||
}
|
||||
|
||||
if (requiredHealerQuantity > 0 && botAI->IsHeal(member))
|
||||
{
|
||||
requiredHealerQuantity--;
|
||||
if (bot->GetGUID() == member->GetGUID())
|
||||
break;
|
||||
}
|
||||
|
||||
if (requiredDpsQuantity == 0 && requiredAssistTankQuantity == 0 && requiredHealerQuantity == 0)
|
||||
return false;
|
||||
}
|
||||
|
||||
Unit* master = botAI->GetMaster();
|
||||
if (master->GetDistance(ULDUAR_THORIM_NEAR_ENTRANCE_POSITION) < 10.0f && (bot->GetDistance2d(master) > 5.0f))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if ((master->GetDistance(ULDUAR_THORIM_GAUNTLET_LEFT_SIDE_6_YARDS_1) < 6.0f ||
|
||||
master->GetDistance(ULDUAR_THORIM_GAUNTLET_LEFT_SIDE_6_YARDS_2) < 6.0f ||
|
||||
master->GetDistance(ULDUAR_THORIM_GAUNTLET_LEFT_SIDE_5_YARDS_1) < 5.0f ||
|
||||
master->GetDistance(ULDUAR_THORIM_GAUNTLET_LEFT_SIDE_10_YARDS_1) < 10.0f ||
|
||||
master->GetDistance(ULDUAR_THORIM_GAUNTLET_LEFT_SIDE_10_YARDS_2) < 10.0f ||
|
||||
master->GetDistance(ULDUAR_THORIM_GAUNTLET_LEFT_SIDE_10_YARDS_3) < 10.0f) &&
|
||||
bot->GetDistance(ULDUAR_THORIM_GAUNTLET_LEFT_SIDE_6_YARDS_1) > 6.0f &&
|
||||
bot->GetDistance(ULDUAR_THORIM_GAUNTLET_LEFT_SIDE_6_YARDS_2) > 6.0f &&
|
||||
bot->GetDistance(ULDUAR_THORIM_GAUNTLET_LEFT_SIDE_5_YARDS_1) > 5.0f &&
|
||||
bot->GetDistance(ULDUAR_THORIM_GAUNTLET_LEFT_SIDE_10_YARDS_1) > 10.0f &&
|
||||
bot->GetDistance(ULDUAR_THORIM_GAUNTLET_LEFT_SIDE_10_YARDS_2) > 10.0f &&
|
||||
bot->GetDistance(ULDUAR_THORIM_GAUNTLET_LEFT_SIDE_10_YARDS_3) > 10.0f)
|
||||
{
|
||||
if (bot->GetPositionZ() > ULDUAR_THORIM_AXIS_Z_FLOOR_THRESHOLD)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if ((master->GetDistance(ULDUAR_THORIM_GAUNTLET_RIGHT_SIDE_6_YARDS_1) < 6.0f ||
|
||||
master->GetDistance(ULDUAR_THORIM_GAUNTLET_RIGHT_SIDE_6_YARDS_2) < 6.0f ||
|
||||
master->GetDistance(ULDUAR_THORIM_GAUNTLET_RIGHT_SIDE_5_YARDS_1) < 5.0f ||
|
||||
master->GetDistance(ULDUAR_THORIM_GAUNTLET_RIGHT_SIDE_10_YARDS_1) < 10.0f ||
|
||||
master->GetDistance(ULDUAR_THORIM_GAUNTLET_RIGHT_SIDE_10_YARDS_2) < 10.0f ||
|
||||
master->GetDistance(ULDUAR_THORIM_GAUNTLET_RIGHT_SIDE_10_YARDS_3) < 10.0f) &&
|
||||
bot->GetDistance(ULDUAR_THORIM_GAUNTLET_RIGHT_SIDE_6_YARDS_1) > 6.0f &&
|
||||
bot->GetDistance(ULDUAR_THORIM_GAUNTLET_RIGHT_SIDE_6_YARDS_2) > 6.0f &&
|
||||
bot->GetDistance(ULDUAR_THORIM_GAUNTLET_RIGHT_SIDE_5_YARDS_1) > 5.0f &&
|
||||
bot->GetDistance(ULDUAR_THORIM_GAUNTLET_RIGHT_SIDE_10_YARDS_1) > 10.0f &&
|
||||
bot->GetDistance(ULDUAR_THORIM_GAUNTLET_RIGHT_SIDE_10_YARDS_2) > 10.0f &&
|
||||
bot->GetDistance(ULDUAR_THORIM_GAUNTLET_RIGHT_SIDE_10_YARDS_3) > 10.0f)
|
||||
{
|
||||
if (bot->GetPositionZ() > ULDUAR_THORIM_AXIS_Z_FLOOR_THRESHOLD)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
Unit* boss = AI_VALUE2(Unit*, "find target", "thorim");
|
||||
if (boss && boss->IsAlive() && bot->GetPositionZ() > ULDUAR_THORIM_AXIS_Z_FLOOR_THRESHOLD &&
|
||||
boss->GetPositionZ() < ULDUAR_THORIM_AXIS_Z_FLOOR_THRESHOLD)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ThorimArenaPositioningTrigger::IsActive()
|
||||
{
|
||||
Unit* boss = AI_VALUE2(Unit*, "find target", "thorim");
|
||||
|
||||
// Check boss and it is alive
|
||||
if (!boss || !boss->IsAlive())
|
||||
return false;
|
||||
|
||||
if (boss->GetPositionZ() < ULDUAR_THORIM_AXIS_Z_FLOOR_THRESHOLD)
|
||||
return false;
|
||||
|
||||
Difficulty raidDifficulty = bot->GetRaidDifficulty();
|
||||
|
||||
Group* group = bot->GetGroup();
|
||||
if (!group)
|
||||
return false;
|
||||
uint32 requiredAssistTankQuantity = 1;
|
||||
uint32 requiredHealerQuantity = 0;
|
||||
uint32 requiredDpsQuantity = 0;
|
||||
|
||||
if (raidDifficulty == Difficulty::RAID_DIFFICULTY_10MAN_NORMAL)
|
||||
{
|
||||
requiredDpsQuantity = 3;
|
||||
requiredHealerQuantity = 1;
|
||||
}
|
||||
else if (raidDifficulty == Difficulty::RAID_DIFFICULTY_25MAN_NORMAL)
|
||||
{
|
||||
requiredDpsQuantity = 7;
|
||||
requiredHealerQuantity = 2;
|
||||
}
|
||||
|
||||
for (GroupReference* gref = group->GetFirstMember(); gref; gref = gref->next())
|
||||
{
|
||||
Player* member = gref->GetSource();
|
||||
if (!member)
|
||||
continue;
|
||||
|
||||
if (requiredDpsQuantity > 0 && botAI->IsDps(member))
|
||||
{
|
||||
requiredDpsQuantity--;
|
||||
if (bot->GetGUID() == member->GetGUID())
|
||||
return false;
|
||||
}
|
||||
|
||||
if (requiredAssistTankQuantity > 0 && botAI->IsAssistTankOfIndex(member, 0))
|
||||
{
|
||||
requiredAssistTankQuantity--;
|
||||
if (bot->GetGUID() == member->GetGUID())
|
||||
return false;
|
||||
}
|
||||
|
||||
if (requiredHealerQuantity > 0 && botAI->IsHeal(member))
|
||||
{
|
||||
requiredHealerQuantity--;
|
||||
if (bot->GetGUID() == member->GetGUID())
|
||||
return false;
|
||||
}
|
||||
|
||||
if (requiredDpsQuantity == 0 && requiredAssistTankQuantity == 0 && requiredHealerQuantity == 0)
|
||||
break;
|
||||
}
|
||||
|
||||
GuidVector targets = AI_VALUE(GuidVector, "possible targets");
|
||||
Unit* target = nullptr;
|
||||
for (auto i = targets.begin(); i != targets.end(); ++i)
|
||||
{
|
||||
target = botAI->GetUnit(*i);
|
||||
if (!target || !target->IsAlive())
|
||||
continue;
|
||||
|
||||
uint32 entry = target->GetEntry();
|
||||
|
||||
if (entry == NPC_DARK_RUNE_ACOLYTE_I || entry == NPC_CAPTURED_MERCENARY_SOLDIER_ALLY ||
|
||||
entry == NPC_CAPTURED_MERCENARY_SOLDIER_HORDE || entry == NPC_CAPTURED_MERCENARY_CAPTAIN_ALLY ||
|
||||
entry == NPC_CAPTURED_MERCENARY_CAPTAIN_HORDE || entry == NPC_JORMUNGAR_BEHEMOT ||
|
||||
entry == NPC_DARK_RUNE_WARBRINGER || entry == NPC_DARK_RUNE_EVOKER || entry == NPC_DARK_RUNE_CHAMPION ||
|
||||
entry == NPC_DARK_RUNE_COMMONER)
|
||||
return false;
|
||||
}
|
||||
|
||||
if (bot && bot->GetDistance(ULDUAR_THORIM_NEAR_ARENA_CENTER) > 5.0f)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ThorimFallFromFloorTrigger::IsActive()
|
||||
{
|
||||
if (bot->GetDistance(ULDUAR_THORIM_NEAR_ARENA_CENTER) > 110.0f)
|
||||
return false;
|
||||
|
||||
// Check if bot is on the floor
|
||||
return bot->GetPositionZ() < ULDUAR_THORIM_AXIS_Z_PATHING_ISSUE_DETECT;
|
||||
}
|
||||
|
||||
bool ThorimPhase2PositioningTrigger::IsActive()
|
||||
{
|
||||
if (!botAI->IsRanged(bot) && !botAI->IsMainTank(bot))
|
||||
return false;
|
||||
|
||||
Unit* boss = AI_VALUE2(Unit*, "find target", "thorim");
|
||||
|
||||
// Check boss and it is alive
|
||||
if (!boss || !boss->IsAlive())
|
||||
return false;
|
||||
|
||||
if (boss->GetPositionZ() > ULDUAR_THORIM_AXIS_Z_FLOOR_THRESHOLD)
|
||||
return false;
|
||||
|
||||
if (botAI->IsMainTank(bot))
|
||||
{
|
||||
if (bot->GetDistance(ULDUAR_THORIM_PHASE2_TANK_SPOT) > 1.0f && boss->GetVictim() == bot)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
Group* group = bot->GetGroup();
|
||||
if (!group)
|
||||
return false;
|
||||
|
||||
uint32 memberPositionNumber = 0;
|
||||
for (GroupReference* gref = group->GetFirstMember(); gref; gref = gref->next())
|
||||
{
|
||||
Player* member = gref->GetSource();
|
||||
if (!member)
|
||||
continue;
|
||||
|
||||
if (botAI->IsRanged(member))
|
||||
{
|
||||
if (bot->GetGUID() == member->GetGUID())
|
||||
break;
|
||||
|
||||
memberPositionNumber++;
|
||||
|
||||
if (memberPositionNumber == 3)
|
||||
memberPositionNumber = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (memberPositionNumber == 0 && bot->GetDistance(ULDUAR_THORIM_PHASE2_RANGE1_SPOT) > 1.0f)
|
||||
return true;
|
||||
|
||||
if (memberPositionNumber == 1 && bot->GetDistance(ULDUAR_THORIM_PHASE2_RANGE2_SPOT) > 1.0f)
|
||||
return true;
|
||||
|
||||
if (memberPositionNumber == 2 && bot->GetDistance(ULDUAR_THORIM_PHASE2_RANGE3_SPOT) > 1.0f)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool MimironShockBlastTrigger::IsActive()
|
||||
{
|
||||
Unit* boss = AI_VALUE2(Unit*, "find target", "leviathan mk ii");
|
||||
|
||||
// Check boss and it is alive
|
||||
if (!boss || !boss->IsAlive())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!boss->HasUnitState(UNIT_STATE_CASTING) || !boss->FindCurrentSpellBySpellId(SPELL_SHOCK_BLAST))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (botAI->IsMelee(bot))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return bot->GetDistance2d(boss) < 15.0f;
|
||||
}
|
||||
}
|
||||
|
||||
bool MimironPhase1PositioningTrigger::IsActive()
|
||||
{
|
||||
if (!botAI->IsRanged(bot))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
Unit* leviathanMkII = nullptr;
|
||||
Unit* vx001 = nullptr;
|
||||
Unit* aerialCommandUnit = nullptr;
|
||||
|
||||
GuidVector targets = AI_VALUE(GuidVector, "possible targets");
|
||||
Unit* target = nullptr;
|
||||
for (auto i = targets.begin(); i != targets.end(); ++i)
|
||||
{
|
||||
target = botAI->GetUnit(*i);
|
||||
if (!target || !target->IsAlive())
|
||||
continue;
|
||||
|
||||
if (target->GetEntry() == NPC_LEVIATHAN_MKII)
|
||||
{
|
||||
leviathanMkII = target;
|
||||
}
|
||||
else if (target->GetEntry() == NPC_VX001)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else if (target->GetEntry() == NPC_AERIAL_COMMAND_UNIT)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!leviathanMkII || !leviathanMkII->IsAlive())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return AI_VALUE(float, "disperse distance") != 6.0f;
|
||||
}
|
||||
|
||||
bool MimironP3Wx2LaserBarrageTrigger::IsActive()
|
||||
{
|
||||
Unit* boss = AI_VALUE2(Unit*, "find target", "vx-001");
|
||||
|
||||
// Check boss and it is alive
|
||||
if (!boss || !boss->IsAlive())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool isCasting = boss->HasUnitState(UNIT_STATE_CASTING);
|
||||
bool isP3WX2LaserBarrage = boss->FindCurrentSpellBySpellId(SPELL_SPINNING_UP) ||
|
||||
boss->FindCurrentSpellBySpellId(SPELL_P3WX2_LASER_BARRAGE_1) ||
|
||||
boss->FindCurrentSpellBySpellId(SPELL_P3WX2_LASER_BARRAGE_2) ||
|
||||
boss->FindCurrentSpellBySpellId(SPELL_P3WX2_LASER_BARRAGE_AURA_1) ||
|
||||
boss->FindCurrentSpellBySpellId(SPELL_P3WX2_LASER_BARRAGE_AURA_2) ||
|
||||
boss->FindCurrentSpellBySpellId(SPELL_P3WX2_LASER_BARRAGE_3);
|
||||
bool hasP3WX2LaserBarrageAura =
|
||||
boss->HasAura(SPELL_P3WX2_LASER_BARRAGE_AURA_1) || boss->HasAura(SPELL_P3WX2_LASER_BARRAGE_AURA_2);
|
||||
|
||||
if ((!isCasting && !hasP3WX2LaserBarrageAura) || !isP3WX2LaserBarrage)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MimironRapidBurstTrigger::IsActive()
|
||||
{
|
||||
Unit* leviathanMkII = nullptr;
|
||||
Unit* vx001 = nullptr;
|
||||
Unit* aerialCommandUnit = nullptr;
|
||||
|
||||
GuidVector targets = AI_VALUE(GuidVector, "possible targets");
|
||||
Unit* target = nullptr;
|
||||
for (auto i = targets.begin(); i != targets.end(); ++i)
|
||||
{
|
||||
target = botAI->GetUnit(*i);
|
||||
if (!target || !target->IsAlive())
|
||||
continue;
|
||||
|
||||
if (target->GetEntry() == NPC_LEVIATHAN_MKII)
|
||||
{
|
||||
leviathanMkII = target;
|
||||
}
|
||||
else if (target->GetEntry() == NPC_VX001)
|
||||
{
|
||||
vx001 = target;
|
||||
}
|
||||
else if (target->GetEntry() == NPC_AERIAL_COMMAND_UNIT)
|
||||
{
|
||||
aerialCommandUnit = target;
|
||||
}
|
||||
}
|
||||
|
||||
if (!vx001 || !vx001->IsAlive())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (leviathanMkII && leviathanMkII->HasUnitState(UNIT_STATE_CASTING) &&
|
||||
leviathanMkII->FindCurrentSpellBySpellId(SPELL_SHOCK_BLAST))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (botAI->IsMainTank(bot) && leviathanMkII && leviathanMkII->IsAlive() && leviathanMkII->GetVictim() != bot)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (botAI->IsMelee(bot) && !botAI->IsMainTank(bot) && leviathanMkII && aerialCommandUnit)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
MimironP3Wx2LaserBarrageTrigger mimironP3Wx2LaserBarrageTrigger(botAI);
|
||||
if (mimironP3Wx2LaserBarrageTrigger.IsActive())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
Group* group = bot->GetGroup();
|
||||
if (!group)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32 memberSpotNumber = 0;
|
||||
Position memberPosition;
|
||||
for (GroupReference* gref = group->GetFirstMember(); gref; gref = gref->next())
|
||||
{
|
||||
Player* member = gref->GetSource();
|
||||
if (!member)
|
||||
continue;
|
||||
|
||||
if (bot->GetGUID() == member->GetGUID())
|
||||
{
|
||||
if (botAI->IsRanged(bot))
|
||||
{
|
||||
switch (memberSpotNumber)
|
||||
{
|
||||
case 0:
|
||||
memberPosition = ULDUAR_MIMIRON_PHASE2_SIDE1RANGE_SPOT;
|
||||
break;
|
||||
case 1:
|
||||
memberPosition = ULDUAR_MIMIRON_PHASE2_SIDE2RANGE_SPOT;
|
||||
break;
|
||||
case 2:
|
||||
memberPosition = ULDUAR_MIMIRON_PHASE2_SIDE3RANGE_SPOT;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (botAI->IsMainTank(bot) && leviathanMkII)
|
||||
{
|
||||
memberPosition = ULDUAR_MIMIRON_PHASE4_TANK_SPOT;
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (memberSpotNumber)
|
||||
{
|
||||
case 0:
|
||||
memberPosition = ULDUAR_MIMIRON_PHASE2_SIDE1MELEE_SPOT;
|
||||
break;
|
||||
case 1:
|
||||
memberPosition = ULDUAR_MIMIRON_PHASE2_SIDE2MELEE_SPOT;
|
||||
break;
|
||||
case 2:
|
||||
memberPosition = ULDUAR_MIMIRON_PHASE2_SIDE3MELEE_SPOT;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
memberSpotNumber++;
|
||||
|
||||
if (memberSpotNumber == 3)
|
||||
{
|
||||
memberSpotNumber = 0;
|
||||
}
|
||||
}
|
||||
|
||||
GuidVector npcs = AI_VALUE(GuidVector, "nearest npcs");
|
||||
float nearestRocketStrikeDistance = std::numeric_limits<float>::max();
|
||||
bool rocketStrikeDetected = false;
|
||||
|
||||
for (const ObjectGuid& guid : npcs)
|
||||
{
|
||||
Unit* unit = botAI->GetUnit(guid);
|
||||
if (!unit)
|
||||
continue;
|
||||
|
||||
if (unit->GetEntry() == NPC_ROCKET_STRIKE_N)
|
||||
{
|
||||
rocketStrikeDetected = true;
|
||||
float distance = bot->GetDistance2d(memberPosition.GetPositionX(), memberPosition.GetPositionY());
|
||||
if (distance < nearestRocketStrikeDistance)
|
||||
{
|
||||
nearestRocketStrikeDistance = distance;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return (bot->GetDistance(memberPosition) > 7.0f && !rocketStrikeDetected) ||
|
||||
bot->GetDistance(memberPosition) > 20.0f;
|
||||
}
|
||||
|
||||
bool MimironAerialCommandUnitTrigger::IsActive()
|
||||
{
|
||||
Unit* leviathanMkII = nullptr;
|
||||
Unit* vx001 = nullptr;
|
||||
Unit* aerialCommandUnit = nullptr;
|
||||
//Unit* bombBot = nullptr;
|
||||
Unit* assaultBot = nullptr;
|
||||
|
||||
GuidVector targets = AI_VALUE(GuidVector, "possible targets");
|
||||
Unit* target = nullptr;
|
||||
for (auto i = targets.begin(); i != targets.end(); ++i)
|
||||
{
|
||||
target = botAI->GetUnit(*i);
|
||||
if (!target || !target->IsAlive())
|
||||
continue;
|
||||
|
||||
if (target->GetEntry() == NPC_LEVIATHAN_MKII)
|
||||
{
|
||||
leviathanMkII = target;
|
||||
}
|
||||
else if (target->GetEntry() == NPC_VX001)
|
||||
{
|
||||
vx001 = target;
|
||||
}
|
||||
else if (target->GetEntry() == NPC_AERIAL_COMMAND_UNIT)
|
||||
{
|
||||
aerialCommandUnit = target;
|
||||
}
|
||||
//else if (target->GetEntry() == NPC_BOMB_BOT)
|
||||
//{
|
||||
// bombBot = target;
|
||||
//}
|
||||
else if (target->GetEntry() == NPC_ASSAULT_BOT)
|
||||
{
|
||||
assaultBot = target;
|
||||
}
|
||||
}
|
||||
|
||||
if (!aerialCommandUnit || !aerialCommandUnit->IsAlive() || leviathanMkII || vx001)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!botAI->IsRanged(bot) && !botAI->IsMainTank(bot) && !botAI->IsAssistTankOfIndex(bot, 0))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (botAI->IsMainTank(bot) || botAI->IsAssistTankOfIndex(bot, 0))
|
||||
{
|
||||
Group* group = bot->GetGroup();
|
||||
if (!group)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
ObjectGuid skullTarget = group->GetTargetIcon(skullIndex);
|
||||
ObjectGuid crossTarget = group->GetTargetIcon(crossIndex);
|
||||
|
||||
//if (bombBot && bombBot->GetGUID() != crossTarget)
|
||||
//{
|
||||
// return true;
|
||||
//}
|
||||
if (!crossTarget || aerialCommandUnit->GetGUID() != crossTarget)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else if (assaultBot && (!skullTarget || assaultBot->GetGUID() != skullTarget))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string rtiMark = AI_VALUE(std::string, "rti");
|
||||
if (rtiMark != "cross")
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool MimironRocketStrikeTrigger::IsActive()
|
||||
{
|
||||
Unit* boss = AI_VALUE2(Unit*, "find target", "vx-001");
|
||||
|
||||
// Check boss and it is alive
|
||||
if (!boss || !boss->IsAlive())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
Creature* rocketStrikeN = bot->FindNearestCreature(NPC_ROCKET_STRIKE_N, 100.0f);
|
||||
|
||||
if (!rocketStrikeN)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return bot->GetDistance2d(rocketStrikeN->GetPositionX(), rocketStrikeN->GetPositionY()) <= 10.0f;
|
||||
}
|
||||
|
||||
bool MimironPhase4MarkDpsTrigger::IsActive()
|
||||
{
|
||||
Unit* leviathanMkII = nullptr;
|
||||
Unit* vx001 = nullptr;
|
||||
Unit* aerialCommandUnit = nullptr;
|
||||
|
||||
Group* group = bot->GetGroup();
|
||||
if (!group)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
GuidVector targets = AI_VALUE(GuidVector, "possible targets");
|
||||
Unit* target = nullptr;
|
||||
for (auto i = targets.begin(); i != targets.end(); ++i)
|
||||
{
|
||||
target = botAI->GetUnit(*i);
|
||||
if (!target || !target->IsAlive())
|
||||
continue;
|
||||
|
||||
if (target->GetEntry() == NPC_LEVIATHAN_MKII)
|
||||
{
|
||||
leviathanMkII = target;
|
||||
}
|
||||
else if (target->GetEntry() == NPC_VX001)
|
||||
{
|
||||
vx001 = target;
|
||||
}
|
||||
else if (target->GetEntry() == NPC_AERIAL_COMMAND_UNIT)
|
||||
{
|
||||
aerialCommandUnit = target;
|
||||
}
|
||||
}
|
||||
|
||||
if (!leviathanMkII || !vx001 || !aerialCommandUnit)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (botAI->IsMainTank(bot))
|
||||
{
|
||||
Unit* highestHealthUnit = nullptr;
|
||||
uint32 highestHealth = 0;
|
||||
|
||||
if (leviathanMkII && leviathanMkII->GetHealth() > highestHealth)
|
||||
{
|
||||
highestHealth = leviathanMkII->GetHealth();
|
||||
highestHealthUnit = leviathanMkII;
|
||||
}
|
||||
if (vx001 && vx001->GetHealth() > highestHealth)
|
||||
{
|
||||
highestHealth = vx001->GetHealth();
|
||||
highestHealthUnit = vx001;
|
||||
}
|
||||
if (aerialCommandUnit && aerialCommandUnit->GetHealth() > highestHealth)
|
||||
{
|
||||
highestHealthUnit = aerialCommandUnit;
|
||||
}
|
||||
|
||||
ObjectGuid skullTarget = group->GetTargetIcon(skullIndex);
|
||||
if (!skullTarget)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return highestHealthUnit->GetGUID() != skullTarget;
|
||||
}
|
||||
else
|
||||
{
|
||||
return AI_VALUE(std::string, "rti") != "skull";
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool MimironCheatTrigger::IsActive()
|
||||
{
|
||||
if (!botAI->IsMainTank(bot))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
GuidVector targets = AI_VALUE(GuidVector, "nearest npcs");
|
||||
for (const ObjectGuid& guid : targets)
|
||||
{
|
||||
Unit* unit = botAI->GetUnit(guid);
|
||||
if (!unit || !unit->IsAlive())
|
||||
continue;
|
||||
|
||||
if (unit->GetEntry() == NPC_PROXIMITY_MINE)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else if (unit->GetEntry() == NPC_BOMB_BOT)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@ enum UlduarIDs
|
||||
SPELL_OVERLOAD_25_MAN = 63481,
|
||||
SPELL_OVERLOAD_10_MAN_2 = 63485,
|
||||
SPELL_OVERLOAD_25_MAN_2 = 61886,
|
||||
SPELL_RUNE_OF_POWER = 64320,
|
||||
|
||||
//Kologarn
|
||||
NPC_RIGHT_ARM = 32934,
|
||||
@@ -43,11 +44,79 @@ enum UlduarIDs
|
||||
NPC_EONARS_GIFT = 33228,
|
||||
GOBJECT_NATURE_BOMB = 194902,
|
||||
|
||||
//Thorim
|
||||
NPC_DARK_RUNE_ACOLYTE_I = 32886,
|
||||
NPC_CAPTURED_MERCENARY_SOLDIER_ALLY = 32885,
|
||||
NPC_CAPTURED_MERCENARY_SOLDIER_HORDE = 32883,
|
||||
NPC_CAPTURED_MERCENARY_CAPTAIN_ALLY = 32908,
|
||||
NPC_CAPTURED_MERCENARY_CAPTAIN_HORDE = 32907,
|
||||
NPC_JORMUNGAR_BEHEMOT = 32882,
|
||||
NPC_DARK_RUNE_WARBRINGER = 32877,
|
||||
NPC_DARK_RUNE_EVOKER = 32878,
|
||||
NPC_DARK_RUNE_CHAMPION = 32876,
|
||||
NPC_DARK_RUNE_COMMONER = 32904,
|
||||
NPC_IRON_RING_GUARD = 32874,
|
||||
NPC_RUNIC_COLOSSUS = 32872,
|
||||
NPC_ANCIENT_RUNE_GIANT = 32873,
|
||||
NPC_DARK_RUNE_ACOLYTE_G = 33110,
|
||||
NPC_IRON_HONOR_GUARD = 32875,
|
||||
SPELL_UNBALANCING_STRIKE = 62130,
|
||||
|
||||
//Mimiron
|
||||
NPC_LEVIATHAN_MKII = 33432,
|
||||
NPC_VX001 = 33651,
|
||||
NPC_AERIAL_COMMAND_UNIT = 33670,
|
||||
NPC_BOMB_BOT = 33836,
|
||||
NPC_ROCKET_STRIKE_N = 34047,
|
||||
NPC_ASSAULT_BOT = 34057,
|
||||
NPC_PROXIMITY_MINE = 34362,
|
||||
SPELL_P3WX2_LASER_BARRAGE_1 = 63293,
|
||||
SPELL_P3WX2_LASER_BARRAGE_2 = 63297,
|
||||
SPELL_SPINNING_UP = 63414,
|
||||
SPELL_SHOCK_BLAST = 63631,
|
||||
SPELL_P3WX2_LASER_BARRAGE_3 = 64042,
|
||||
SPELL_P3WX2_LASER_BARRAGE_AURA_1 = 63274,
|
||||
SPELL_P3WX2_LASER_BARRAGE_AURA_2 = 63300,
|
||||
|
||||
// Buffs
|
||||
SPELL_FROST_TRAP = 13809
|
||||
};
|
||||
|
||||
const int8 skullIndex = 7; // Skull
|
||||
const int8 crossIndex = 6; // Cross
|
||||
const int8 moonIndex = 4; // Moon
|
||||
|
||||
const float ULDUAR_KOLOGARN_AXIS_Z_PATHING_ISSUE_DETECT = 420.0f;
|
||||
const float ULDUAR_KOLOGARN_EYEBEAM_RADIUS = 3.0f;
|
||||
const float ULDUAR_THORIM_AXIS_Z_FLOOR_THRESHOLD = 429.6094f;
|
||||
const float ULDUAR_THORIM_AXIS_Z_PATHING_ISSUE_DETECT = 410.0f;
|
||||
const float ULDUAR_AURIAYA_AXIS_Z_PATHING_ISSUE_DETECT = 410.0f;
|
||||
|
||||
const Position ULDUAR_THORIM_NEAR_ARENA_CENTER = Position(2134.9854f, -263.11853f, 419.8465f);
|
||||
const Position ULDUAR_THORIM_NEAR_ENTRANCE_POSITION = Position(2172.4355f, -258.27957f, 418.47162f);
|
||||
const Position ULDUAR_THORIM_GAUNTLET_LEFT_SIDE_6_YARDS_1 = Position(2237.6187f, -265.08844f, 412.17548f);
|
||||
const Position ULDUAR_THORIM_GAUNTLET_LEFT_SIDE_6_YARDS_2 = Position(2237.2498f, -275.81122f, 412.17548f);
|
||||
const Position ULDUAR_THORIM_GAUNTLET_LEFT_SIDE_5_YARDS_1 = Position(2236.895f, -294.62448f, 412.1348f);
|
||||
const Position ULDUAR_THORIM_GAUNTLET_LEFT_SIDE_10_YARDS_1 = Position(2242.1162f, -310.15308f, 412.1348f);
|
||||
const Position ULDUAR_THORIM_GAUNTLET_LEFT_SIDE_10_YARDS_2 = Position(2242.018f, -318.66003f, 412.1348f);
|
||||
const Position ULDUAR_THORIM_GAUNTLET_LEFT_SIDE_10_YARDS_3 = Position(2242.1904f, -329.0533f, 412.1348f);
|
||||
const Position ULDUAR_THORIM_GAUNTLET_RIGHT_SIDE_6_YARDS_1 = Position(2219.5417f, -264.77167f, 412.17548f);
|
||||
const Position ULDUAR_THORIM_GAUNTLET_RIGHT_SIDE_6_YARDS_2 = Position(2217.446f, -275.85248f, 412.17548f);
|
||||
const Position ULDUAR_THORIM_GAUNTLET_RIGHT_SIDE_5_YARDS_1 = Position(2217.8877f, -295.01193f, 412.13434f);
|
||||
const Position ULDUAR_THORIM_GAUNTLET_RIGHT_SIDE_10_YARDS_1 = Position(2212.193f, -307.44992f, 412.1348f);
|
||||
const Position ULDUAR_THORIM_GAUNTLET_RIGHT_SIDE_10_YARDS_2 = Position(2212.1353f, -318.20795f, 412.1348f);
|
||||
const Position ULDUAR_THORIM_GAUNTLET_RIGHT_SIDE_10_YARDS_3 = Position(2212.1956f, -328.0144f, 412.1348f);
|
||||
const Position ULDUAR_THORIM_PHASE2_TANK_SPOT = Position(2134.8572f, -287.0291f, 419.4935f);
|
||||
const Position ULDUAR_THORIM_PHASE2_RANGE1_SPOT = Position(2112.8752f, -267.69305f, 419.52814f);
|
||||
const Position ULDUAR_THORIM_PHASE2_RANGE2_SPOT = Position(2134.1296f, -257.3316f, 419.8462f);
|
||||
const Position ULDUAR_THORIM_PHASE2_RANGE3_SPOT = Position(2156.798f, -267.57434f, 419.52722f);
|
||||
const Position ULDUAR_MIMIRON_PHASE2_SIDE1RANGE_SPOT = Position(2753.708f, 2583.9617f, 364.31357f);
|
||||
const Position ULDUAR_MIMIRON_PHASE2_SIDE1MELEE_SPOT = Position(2746.9792f, 2573.6716f, 364.31357f);
|
||||
const Position ULDUAR_MIMIRON_PHASE2_SIDE2RANGE_SPOT = Position(2727.7224f, 2569.527f, 364.31357f);
|
||||
const Position ULDUAR_MIMIRON_PHASE2_SIDE2MELEE_SPOT = Position(2739.4746f, 2569.4106f, 364.31357f);
|
||||
const Position ULDUAR_MIMIRON_PHASE2_SIDE3RANGE_SPOT = Position(2754.1294f, 2553.9954f, 364.31357f);
|
||||
const Position ULDUAR_MIMIRON_PHASE2_SIDE3MELEE_SPOT = Position(2746.8513f, 2565.4263f, 364.31357f);
|
||||
const Position ULDUAR_MIMIRON_PHASE4_TANK_SPOT = Position(2744.5754f, 2570.8657f, 364.3138f);
|
||||
|
||||
//
|
||||
// Flame Levi
|
||||
@@ -135,6 +204,13 @@ public:
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class IronAssemblyRuneOfPowerTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
IronAssemblyRuneOfPowerTrigger(PlayerbotAI* ai) : Trigger(ai, "iron assembly rune of power trigger") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
//
|
||||
// Kologarn
|
||||
//
|
||||
@@ -159,6 +235,44 @@ public:
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class KologarnEyebeamTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
KologarnEyebeamTrigger(PlayerbotAI* ai) : Trigger(ai, "kologarn eyebeam trigger") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class KologarnAttackDpsTargetTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
KologarnAttackDpsTargetTrigger(PlayerbotAI* ai) : Trigger(ai, "kologarn attack dps target trigger") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class KologarnRtiTargetTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
KologarnRtiTargetTrigger(PlayerbotAI* ai) : Trigger(ai, "kologarn rti target trigger") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class KologarnCrunchArmorTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
KologarnCrunchArmorTrigger(PlayerbotAI* ai) : Trigger(ai, "kologarn crunch armor trigger") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
//
|
||||
// Auriaya
|
||||
//
|
||||
class AuriayaFallFromFloorTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
AuriayaFallFromFloorTrigger(PlayerbotAI* ai) : Trigger(ai, "auriaya fall from floor trigger") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
//
|
||||
// Hodir
|
||||
//
|
||||
@@ -200,4 +314,108 @@ public:
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
//
|
||||
// Thorim
|
||||
//
|
||||
class ThorimUnbalancingStrikeTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
ThorimUnbalancingStrikeTrigger(PlayerbotAI* ai) : Trigger(ai, "thorim unbalancing strike trigger") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class ThorimMarkDpsTargetTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
ThorimMarkDpsTargetTrigger(PlayerbotAI* ai) : Trigger(ai, "thorim mark dps target trigger") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class ThorimGauntletPositioningTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
ThorimGauntletPositioningTrigger(PlayerbotAI* ai) : Trigger(ai, "thorim gauntlet positioning trigger") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class ThorimArenaPositioningTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
ThorimArenaPositioningTrigger(PlayerbotAI* ai) : Trigger(ai, "thorim arena positioning trigger") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class ThorimFallFromFloorTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
ThorimFallFromFloorTrigger(PlayerbotAI* ai) : Trigger(ai, "thorim fall from floor trigger") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class ThorimPhase2PositioningTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
ThorimPhase2PositioningTrigger(PlayerbotAI* ai) : Trigger(ai, "thorim phase 2 positioning trigger") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
//
|
||||
// Mimiron
|
||||
//
|
||||
class MimironShockBlastTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
MimironShockBlastTrigger(PlayerbotAI* ai) : Trigger(ai, "mimiron shock blast trigger") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class MimironPhase1PositioningTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
MimironPhase1PositioningTrigger(PlayerbotAI* ai) : Trigger(ai, "mimiron phase 1 positioning trigger") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class MimironP3Wx2LaserBarrageTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
MimironP3Wx2LaserBarrageTrigger(PlayerbotAI* ai) : Trigger(ai, "mimiron p3wx2 laser barrage trigger") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class MimironRapidBurstTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
MimironRapidBurstTrigger(PlayerbotAI* ai) : Trigger(ai, "mimiron rapid burst trigger") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class MimironAerialCommandUnitTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
MimironAerialCommandUnitTrigger(PlayerbotAI* ai) : Trigger(ai, "mimiron aerial command unit trigger") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class MimironRocketStrikeTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
MimironRocketStrikeTrigger(PlayerbotAI* ai) : Trigger(ai, "mimiron rocket strike trigger") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class MimironPhase4MarkDpsTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
MimironPhase4MarkDpsTrigger(PlayerbotAI* ai) : Trigger(ai, "mimiron phase 4 mark dps trigger") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class MimironCheatTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
MimironCheatTrigger(PlayerbotAI* ai) : Trigger(ai, "mimiron cheat trigger") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -88,6 +88,7 @@ public:
|
||||
creators["cast"] = &ChatTriggerContext::cast;
|
||||
creators["castnc"] = &ChatTriggerContext::castnc;
|
||||
creators["invite"] = &ChatTriggerContext::invite;
|
||||
creators["lfg"] = &ChatTriggerContext::lfg;
|
||||
creators["spell"] = &ChatTriggerContext::spell;
|
||||
creators["rti"] = &ChatTriggerContext::rti;
|
||||
creators["revive"] = &ChatTriggerContext::revive;
|
||||
@@ -164,6 +165,7 @@ private:
|
||||
static Trigger* revive(PlayerbotAI* botAI) { return new ChatCommandTrigger(botAI, "revive"); }
|
||||
static Trigger* rti(PlayerbotAI* botAI) { return new ChatCommandTrigger(botAI, "rti"); }
|
||||
static Trigger* invite(PlayerbotAI* botAI) { return new ChatCommandTrigger(botAI, "invite"); }
|
||||
static Trigger* lfg(PlayerbotAI* botAI) { return new ChatCommandTrigger(botAI, "lfg"); }
|
||||
static Trigger* cast(PlayerbotAI* botAI) { return new ChatCommandTrigger(botAI, "cast"); }
|
||||
static Trigger* castnc(PlayerbotAI* botAI) { return new ChatCommandTrigger(botAI, "castnc"); }
|
||||
static Trigger* talk(PlayerbotAI* botAI) { return new ChatCommandTrigger(botAI, "talk"); }
|
||||
|
||||
@@ -37,6 +37,7 @@ public:
|
||||
creators["random"] = &TriggerContext::Random;
|
||||
creators["seldom"] = &TriggerContext::seldom;
|
||||
creators["often"] = &TriggerContext::often;
|
||||
creators["very often"] = &TriggerContext::very_often;
|
||||
|
||||
creators["target critical health"] = &TriggerContext::TargetCriticalHealth;
|
||||
|
||||
@@ -314,6 +315,7 @@ private:
|
||||
static Trigger* Random(PlayerbotAI* botAI) { return new RandomTrigger(botAI, "random", 20); }
|
||||
static Trigger* seldom(PlayerbotAI* botAI) { return new RandomTrigger(botAI, "seldom", 300); }
|
||||
static Trigger* often(PlayerbotAI* botAI) { return new RandomTrigger(botAI, "often", 5); }
|
||||
static Trigger* very_often(PlayerbotAI* botAI) { return new RandomTrigger(botAI, "often", 3); }
|
||||
static Trigger* EnemyOutOfMelee(PlayerbotAI* botAI) { return new EnemyOutOfMeleeTrigger(botAI); }
|
||||
static Trigger* EnemyOutOfSpell(PlayerbotAI* botAI) { return new EnemyOutOfSpellRangeTrigger(botAI); }
|
||||
static Trigger* enemy_too_close_for_spell(PlayerbotAI* botAI) { return new EnemyTooCloseForSpellTrigger(botAI); }
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include "AiFactory.h"
|
||||
#include "ChatHelper.h"
|
||||
#include "GuildTaskMgr.h"
|
||||
#include "Item.h"
|
||||
#include "LootObjectStack.h"
|
||||
#include "PlayerbotAIConfig.h"
|
||||
#include "PlayerbotFactory.h"
|
||||
@@ -18,7 +19,16 @@
|
||||
|
||||
ItemUsage ItemUsageValue::Calculate()
|
||||
{
|
||||
uint32 itemId = atoi(qualifier.c_str());
|
||||
uint32 itemId = 0;
|
||||
uint32 randomPropertyId = 0;
|
||||
size_t pos = qualifier.find(",");
|
||||
if (pos != std::string::npos) {
|
||||
itemId = atoi(qualifier.substr(0, pos).c_str());
|
||||
randomPropertyId = atoi(qualifier.substr(pos + 1).c_str());
|
||||
} else {
|
||||
itemId = atoi(qualifier.c_str());
|
||||
}
|
||||
|
||||
if (!itemId)
|
||||
return ITEM_USAGE_NONE;
|
||||
|
||||
@@ -89,7 +99,7 @@ ItemUsage ItemUsageValue::Calculate()
|
||||
if (bot->GetGuildId() && sGuildTaskMgr->IsGuildTaskItem(itemId, bot->GetGuildId()))
|
||||
return ITEM_USAGE_GUILD_TASK;
|
||||
|
||||
ItemUsage equip = QueryItemUsageForEquip(proto);
|
||||
ItemUsage equip = QueryItemUsageForEquip(proto, randomPropertyId);
|
||||
if (equip != ITEM_USAGE_NONE)
|
||||
return equip;
|
||||
|
||||
@@ -224,7 +234,7 @@ ItemUsage ItemUsageValue::Calculate()
|
||||
return ITEM_USAGE_NONE;
|
||||
}
|
||||
|
||||
ItemUsage ItemUsageValue::QueryItemUsageForEquip(ItemTemplate const* itemProto)
|
||||
ItemUsage ItemUsageValue::QueryItemUsageForEquip(ItemTemplate const* itemProto, int32 randomPropertyId)
|
||||
{
|
||||
if (bot->CanUseItem(itemProto) != EQUIP_ERR_OK)
|
||||
return ITEM_USAGE_NONE;
|
||||
@@ -296,7 +306,8 @@ ItemUsage ItemUsageValue::QueryItemUsageForEquip(ItemTemplate const* itemProto)
|
||||
calculator.SetItemSetBonus(false);
|
||||
calculator.SetOverflowPenalty(false);
|
||||
|
||||
float itemScore = calculator.CalculateItem(itemProto->ItemId);
|
||||
float itemScore = calculator.CalculateItem(itemProto->ItemId, randomPropertyId);
|
||||
|
||||
if (itemScore)
|
||||
shouldEquip = true;
|
||||
|
||||
@@ -380,7 +391,7 @@ ItemUsage ItemUsageValue::QueryItemUsageForEquip(ItemTemplate const* itemProto)
|
||||
}
|
||||
|
||||
ItemTemplate const* oldItemProto = oldItem->GetTemplate();
|
||||
float oldScore = calculator.CalculateItem(oldItemProto->ItemId);
|
||||
float oldScore = calculator.CalculateItem(oldItemProto->ItemId, oldItem->GetInt32Value(ITEM_FIELD_RANDOM_PROPERTIES_ID));
|
||||
if (oldItem)
|
||||
{
|
||||
// uint32 oldStatWeight = sRandomItemMgr->GetLiveStatWeight(bot, oldItemProto->ItemId);
|
||||
|
||||
@@ -43,7 +43,7 @@ public:
|
||||
ItemUsage Calculate() override;
|
||||
|
||||
private:
|
||||
ItemUsage QueryItemUsageForEquip(ItemTemplate const* proto);
|
||||
ItemUsage QueryItemUsageForEquip(ItemTemplate const* proto, int32 randomPropertyId = 0);
|
||||
uint32 GetSmallestBagSize();
|
||||
bool IsItemUsefulForQuest(Player* player, ItemTemplate const* proto);
|
||||
bool IsItemNeededForSkill(ItemTemplate const* proto);
|
||||
|
||||
@@ -18,7 +18,7 @@ void NearestNpcsValue::FindUnits(std::list<Unit*>& targets)
|
||||
Cell::VisitAllObjects(bot, searcher, range);
|
||||
}
|
||||
|
||||
bool NearestNpcsValue::AcceptUnit(Unit* unit) { return !unit->IsHostileTo(bot) && !unit->IsPlayer(); }
|
||||
bool NearestNpcsValue::AcceptUnit(Unit* unit) { return !unit->IsPlayer(); }
|
||||
|
||||
void NearestHostileNpcsValue::FindUnits(std::list<Unit*>& targets)
|
||||
{
|
||||
@@ -64,4 +64,4 @@ void NearestTotemsValue::FindUnits(std::list<Unit*>& targets)
|
||||
Cell::VisitAllObjects(bot, searcher, range);
|
||||
}
|
||||
|
||||
bool NearestTotemsValue::AcceptUnit(Unit* unit) { return unit->IsTotem(); }
|
||||
bool NearestTotemsValue::AcceptUnit(Unit* unit) { return unit->IsTotem(); }
|
||||
|
||||
Reference in New Issue
Block a user