mirror of
https://github.com/mod-playerbots/mod-playerbots
synced 2025-11-29 15:58:20 +08:00
Merge branch 'master' into pmon-enhancements
This commit is contained in:
@@ -274,11 +274,12 @@ void AiFactory::AddDefaultCombatStrategies(Player* player, PlayerbotAI* const fa
|
||||
{
|
||||
engine->addStrategy("avoid aoe");
|
||||
}
|
||||
engine->addStrategy("combat formation");
|
||||
switch (player->getClass())
|
||||
{
|
||||
case CLASS_PRIEST:
|
||||
if (tab == 2) {
|
||||
engine->addStrategies("dps", "shadow debuff", "shadow aoe", "threat", nullptr);
|
||||
engine->addStrategies("dps", "shadow debuff", "shadow aoe", nullptr);
|
||||
} else if (tab == PRIEST_TAB_DISIPLINE) {
|
||||
engine->addStrategies("heal", nullptr);
|
||||
} else {
|
||||
@@ -289,11 +290,11 @@ void AiFactory::AddDefaultCombatStrategies(Player* player, PlayerbotAI* const fa
|
||||
break;
|
||||
case CLASS_MAGE:
|
||||
if (tab == 0)
|
||||
engine->addStrategies("arcane", "arcane aoe", "threat", nullptr);
|
||||
engine->addStrategies("arcane", "arcane aoe", nullptr);
|
||||
else if (tab == 1)
|
||||
engine->addStrategies("fire", "fire aoe", "threat", nullptr);
|
||||
engine->addStrategies("fire", "fire aoe", nullptr);
|
||||
else
|
||||
engine->addStrategies("frost", "frost aoe", "threat", nullptr);
|
||||
engine->addStrategies("frost", "frost aoe", nullptr);
|
||||
|
||||
engine->addStrategies("dps", "dps assist", "cure", nullptr);
|
||||
break;
|
||||
@@ -301,17 +302,17 @@ void AiFactory::AddDefaultCombatStrategies(Player* player, PlayerbotAI* const fa
|
||||
if (tab == 2)
|
||||
engine->addStrategies("tank", "tank assist", "aoe", "mark rti", nullptr);
|
||||
else if (player->getLevel() < 36 || tab == 0)
|
||||
engine->addStrategies("arms", "aoe", "dps assist", "threat", /*"behind",*/ nullptr);
|
||||
engine->addStrategies("arms", "aoe", "dps assist",/*"behind",*/ nullptr);
|
||||
else
|
||||
engine->addStrategies("fury", "aoe", "dps assist", "threat", /*"behind",*/ nullptr);
|
||||
engine->addStrategies("fury", "aoe", "dps assist",/*"behind",*/ nullptr);
|
||||
break;
|
||||
case CLASS_SHAMAN:
|
||||
if (tab == 0)
|
||||
engine->addStrategies("caster", "caster aoe", "bmana", "threat", nullptr);
|
||||
engine->addStrategies("caster", "caster aoe", "bmana",nullptr);
|
||||
else if (tab == 2)
|
||||
engine->addStrategies("heal", "bmana", nullptr);
|
||||
else
|
||||
engine->addStrategies("melee", "melee aoe", "bdps", "threat", nullptr);
|
||||
engine->addStrategies("melee", "melee aoe", "bdps", nullptr);
|
||||
|
||||
engine->addStrategies("dps assist", "cure", "totems", nullptr);
|
||||
break;
|
||||
@@ -327,38 +328,41 @@ void AiFactory::AddDefaultCombatStrategies(Player* player, PlayerbotAI* const fa
|
||||
case CLASS_DRUID:
|
||||
if (tab == 0)
|
||||
{
|
||||
engine->addStrategies("caster", "cure", "caster aoe", "threat", "dps assist", nullptr);
|
||||
engine->addStrategies("caster", "cure", "caster aoe", "dps assist", nullptr);
|
||||
engine->addStrategy("caster debuff");
|
||||
}
|
||||
else if (tab == 2)
|
||||
engine->addStrategies("heal", "cure", "dps assist", nullptr);
|
||||
else
|
||||
{
|
||||
engine->removeStrategy("flee");
|
||||
engine->addStrategies("bear", "tank assist", nullptr);
|
||||
if (player->GetLevel() >= 20 && !player->HasAura(16931)/*thick hide*/) {
|
||||
engine->addStrategies("cat", "dps assist", nullptr);
|
||||
} else {
|
||||
engine->addStrategies("bear", "tank assist", nullptr);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case CLASS_HUNTER:
|
||||
engine->addStrategies("dps", "aoe", "bdps", "threat", "dps assist", nullptr);
|
||||
engine->addStrategies("dps", "aoe", "bdps", "dps assist", nullptr);
|
||||
engine->addStrategy("dps debuff");
|
||||
break;
|
||||
case CLASS_ROGUE:
|
||||
if (tab == ROGUE_TAB_ASSASSINATION) {
|
||||
engine->addStrategies("melee", "threat", "dps assist", "aoe", /*"behind",*/ nullptr);
|
||||
engine->addStrategies("melee", "dps assist", "aoe", /*"behind",*/ nullptr);
|
||||
} else {
|
||||
engine->addStrategies("dps", "threat", "dps assist", "aoe", /*"behind",*/ nullptr);
|
||||
engine->addStrategies("dps", "dps assist", "aoe", /*"behind",*/ nullptr);
|
||||
}
|
||||
break;
|
||||
case CLASS_WARLOCK:
|
||||
engine->addStrategies("dps assist", "dps", "dps debuff", "aoe", "threat", nullptr);
|
||||
engine->addStrategies("dps assist", "dps", "dps debuff", "aoe", nullptr);
|
||||
break;
|
||||
case CLASS_DEATH_KNIGHT:
|
||||
if (tab == 0)
|
||||
engine->addStrategies("blood", "tank assist", nullptr);
|
||||
else if (tab == 1)
|
||||
engine->addStrategies("frost", "frost aoe", "dps assist", "threat", nullptr);
|
||||
engine->addStrategies("frost", "frost aoe", "dps assist", nullptr);
|
||||
else
|
||||
engine->addStrategies("unholy", "unholy aoe", "dps assist", "threat", nullptr);
|
||||
engine->addStrategies("unholy", "unholy aoe", "dps assist", nullptr);
|
||||
|
||||
break;
|
||||
}
|
||||
@@ -504,8 +508,13 @@ void AiFactory::AddDefaultNonCombatStrategies(Player* player, PlayerbotAI* const
|
||||
nonCombatEngine->addStrategies("dps assist", "cure", nullptr);
|
||||
break;
|
||||
case CLASS_DRUID:
|
||||
if (tab == 1)
|
||||
nonCombatEngine->addStrategy("tank assist");
|
||||
if (tab == 1) {
|
||||
if (player->GetLevel() >= 20 && !player->HasAura(16931)/*thick hide*/) {
|
||||
nonCombatEngine->addStrategy("dps assist");
|
||||
} else {
|
||||
nonCombatEngine->addStrategy("tank assist");
|
||||
}
|
||||
}
|
||||
else
|
||||
nonCombatEngine->addStrategies("dps assist", "cure", nullptr);
|
||||
break;
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
#include <sstream>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <algorithm>
|
||||
|
||||
void split(std::vector<std::string>& dest, std::string const str, char const* delim)
|
||||
{
|
||||
|
||||
@@ -352,10 +352,16 @@ void PlayerbotAI::UpdateAIInternal([[maybe_unused]] uint32 elapsed, bool minimal
|
||||
Player* owner = holder.GetOwner();
|
||||
if (!helper.ParseChatCommand(command, owner) && holder.GetType() == CHAT_MSG_WHISPER)
|
||||
{
|
||||
std::ostringstream out;
|
||||
out << "Unknown command " << command;
|
||||
TellMaster(out);
|
||||
helper.ParseChatCommand("help");
|
||||
// To prevent spam caused by WIM
|
||||
if (!(command.rfind("WIM", 0) == 0) &&
|
||||
!(command.rfind("QHpr", 0) == 0)
|
||||
)
|
||||
{
|
||||
std::ostringstream out;
|
||||
out << "Unknown command " << command;
|
||||
TellMaster(out);
|
||||
helper.ParseChatCommand("help");
|
||||
}
|
||||
}
|
||||
|
||||
chatCommands.pop();
|
||||
@@ -1810,7 +1816,7 @@ Player* PlayerbotAI::GetPlayer(ObjectGuid guid)
|
||||
|
||||
uint32 GetCreatureIdForCreatureTemplateId(uint32 creatureTemplateId)
|
||||
{
|
||||
QueryResult results = WorldDatabase.Query("SELECT guid FROM `acore_world`.`creature` WHERE id1 = {} LIMIT 1;", creatureTemplateId);
|
||||
QueryResult results = WorldDatabase.Query("SELECT guid FROM `creature` WHERE id1 = {} LIMIT 1;", creatureTemplateId);
|
||||
if (results) {
|
||||
Field* fields = results->Fetch();
|
||||
return fields[0].Get<uint32>();
|
||||
@@ -1900,19 +1906,19 @@ bool PlayerbotAI::TellMasterNoFacing(std::string const text, PlayerbotSecurityLe
|
||||
return false;
|
||||
|
||||
time_t lastSaid = whispers[text];
|
||||
// Yunfan: Remove tell cooldown
|
||||
// if (!lastSaid || (time(nullptr) - lastSaid) >= sPlayerbotAIConfig->repeatDelay / 1000)
|
||||
// {
|
||||
whispers[text] = time(nullptr);
|
||||
|
||||
if (!lastSaid || (time(nullptr) - lastSaid) >= sPlayerbotAIConfig->repeatDelay / 1000)
|
||||
{
|
||||
whispers[text] = time(nullptr);
|
||||
|
||||
ChatMsg type = CHAT_MSG_WHISPER;
|
||||
if (currentChat.second - time(nullptr) >= 1)
|
||||
type = currentChat.first;
|
||||
ChatMsg type = CHAT_MSG_WHISPER;
|
||||
if (currentChat.second - time(nullptr) >= 1)
|
||||
type = currentChat.first;
|
||||
|
||||
WorldPacket data;
|
||||
ChatHandler::BuildChatPacket(data, type == CHAT_MSG_ADDON ? CHAT_MSG_PARTY : type, type == CHAT_MSG_ADDON ? LANG_ADDON : LANG_UNIVERSAL, bot, nullptr, text.c_str());
|
||||
master->SendDirectMessage(&data);
|
||||
// }
|
||||
WorldPacket data;
|
||||
ChatHandler::BuildChatPacket(data, type == CHAT_MSG_ADDON ? CHAT_MSG_PARTY : type, type == CHAT_MSG_ADDON ? LANG_ADDON : LANG_UNIVERSAL, bot, nullptr, text.c_str());
|
||||
master->SendDirectMessage(&data);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -53,12 +53,13 @@ bool PlayerbotAIConfig::Initialize()
|
||||
|
||||
globalCoolDown = sConfigMgr->GetOption<int32>("AiPlayerbot.GlobalCooldown", 1500);
|
||||
maxWaitForMove = sConfigMgr->GetOption<int32>("AiPlayerbot.MaxWaitForMove", 5000);
|
||||
disableMoveSplinePath = sConfigMgr->GetOption<int32>("AiPlayerbot.DisableMoveSplinePath", 0);
|
||||
maxMovementSearchTime = sConfigMgr->GetOption<int32>("AiPlayerbot.MaxMovementSearchTime", 3);
|
||||
expireActionTime = sConfigMgr->GetOption<int32>("AiPlayerbot.ExpireActionTime", 5000);
|
||||
dispelAuraDuration = sConfigMgr->GetOption<int32>("AiPlayerbot.DispelAuraDuration", 7000);
|
||||
reactDelay = sConfigMgr->GetOption<int32>("AiPlayerbot.ReactDelay", 500);
|
||||
passiveDelay = sConfigMgr->GetOption<int32>("AiPlayerbot.PassiveDelay", 10000);
|
||||
repeatDelay = sConfigMgr->GetOption<int32>("AiPlayerbot.RepeatDelay", 5000);
|
||||
repeatDelay = sConfigMgr->GetOption<int32>("AiPlayerbot.RepeatDelay", 2000);
|
||||
errorDelay = sConfigMgr->GetOption<int32>("AiPlayerbot.ErrorDelay", 5000);
|
||||
rpgDelay = sConfigMgr->GetOption<int32>("AiPlayerbot.RpgDelay", 10000);
|
||||
sitDelay = sConfigMgr->GetOption<int32>("AiPlayerbot.SitDelay", 30000);
|
||||
@@ -94,7 +95,10 @@ bool PlayerbotAIConfig::Initialize()
|
||||
autoAvoidAoe = sConfigMgr->GetOption<bool>("AiPlayerbot.AutoAvoidAoe", true);
|
||||
tellWhenAvoidAoe = sConfigMgr->GetOption<bool>("AiPlayerbot.TellWhenAvoidAoe", true);
|
||||
|
||||
randomGearLoweringChance = sConfigMgr->GetOption<float>("AiPlayerbot.RandomGearLoweringChance", 0.15f);
|
||||
randomGearLoweringChance = sConfigMgr->GetOption<float>("AiPlayerbot.RandomGearLoweringChance", 0.0f);
|
||||
randomGearQualityLimit = sConfigMgr->GetOption<int32>("AiPlayerbot.RandomGearQualityLimit", 3);
|
||||
randomGearScoreLimit = sConfigMgr->GetOption<int32>("AiPlayerbot.RandomGearScoreLimit", 0);
|
||||
|
||||
randomBotMaxLevelChance = sConfigMgr->GetOption<float>("AiPlayerbot.RandomBotMaxLevelChance", 0.15f);
|
||||
randomBotRpgChance = sConfigMgr->GetOption<float>("AiPlayerbot.RandomBotRpgChance", 0.20f);
|
||||
|
||||
@@ -123,7 +127,7 @@ bool PlayerbotAIConfig::Initialize()
|
||||
minRandomBotInWorldTime = sConfigMgr->GetOption<int32>("AiPlayerbot.MinRandomBotInWorldTime", 2 * HOUR);
|
||||
maxRandomBotInWorldTime = sConfigMgr->GetOption<int32>("AiPlayerbot.MaxRandomBotInWorldTime", 12 * HOUR);
|
||||
minRandomBotRandomizeTime = sConfigMgr->GetOption<int32>("AiPlayerbot.MinRandomBotRandomizeTime", 2 * HOUR);
|
||||
maxRandomBotRandomizeTime = sConfigMgr->GetOption<int32>("AiPlayerbot.MaxRandomRandomizeTime", 14 * 24 * HOUR);
|
||||
maxRandomBotRandomizeTime = sConfigMgr->GetOption<int32>("AiPlayerbot.MaxRandomBotRandomizeTime", 14 * 24 * HOUR);
|
||||
minRandomBotChangeStrategyTime = sConfigMgr->GetOption<int32>("AiPlayerbot.MinRandomBotChangeStrategyTime", 30 * MINUTE);
|
||||
maxRandomBotChangeStrategyTime = sConfigMgr->GetOption<int32>("AiPlayerbot.MaxRandomBotChangeStrategyTime", 2 * HOUR);
|
||||
minRandomBotReviveTime = sConfigMgr->GetOption<int32>("AiPlayerbot.MinRandomBotReviveTime", MINUTE);
|
||||
@@ -272,7 +276,11 @@ bool PlayerbotAIConfig::Initialize()
|
||||
equipmentPersistence = sConfigMgr->GetOption<bool>("AiPlayerbot.EquipmentPersistence", false);
|
||||
equipmentPersistenceLevel = sConfigMgr->GetOption<int32>("AiPlayerbot.EquipmentPersistenceLevel", 80);
|
||||
groupInvitationPermission = sConfigMgr->GetOption<int32>("AiPlayerbot.GroupInvitationPermission", 1);
|
||||
botReviveWhenSummon = sConfigMgr->GetOption<int>("AiPlayerbot.BotReviveWhenSummon", 1);
|
||||
allowSummonInCombat = sConfigMgr->GetOption<bool>("AiPlayerbot.AllowSummonInCombat", true);
|
||||
allowSummonWhenMasterIsDead = sConfigMgr->GetOption<bool>("AiPlayerbot.AllowSummonWhenMasterIsDead", true);
|
||||
allowSummonWhenBotIsDead = sConfigMgr->GetOption<bool>("AiPlayerbot.AllowSummonWhenBotIsDead", true);
|
||||
reviveBotWhenSummoned = sConfigMgr->GetOption<bool>("AiPlayerbot.ReviveBotWhenSummoned", true);
|
||||
botRepairWhenSummon = sConfigMgr->GetOption<bool>("AiPlayerbot.BotRepairWhenSummon", true);
|
||||
autoInitOnly = sConfigMgr->GetOption<bool>("AiPlayerbot.AutoInitOnly", false);
|
||||
autoInitEquipLevelLimitRatio = sConfigMgr->GetOption<float>("AiPlayerbot.AutoInitEquipLevelLimitRatio", 1.0);
|
||||
addClassCommand = sConfigMgr->GetOption<int32>("AiPlayerbot.AddClassCommand", 1);
|
||||
@@ -292,7 +300,8 @@ bool PlayerbotAIConfig::Initialize()
|
||||
randombotsWalkingRPG = sConfigMgr->GetOption<bool>("AiPlayerbot.RandombotsWalkingRPG", false);
|
||||
randombotsWalkingRPGInDoors = sConfigMgr->GetOption<bool>("AiPlayerbot.RandombotsWalkingRPG.InDoors", false);
|
||||
minEnchantingBotLevel = sConfigMgr->GetOption<int32>("AiPlayerbot.MinEnchantingBotLevel", 60);
|
||||
limitEnchantExpansion = sConfigMgr->GetOption<int32>("AiPlayerbot.LimitEnchantExpansion", 1);
|
||||
limitEnchantExpansion = sConfigMgr->GetOption<int32>("AiPlayerbot.LimitEnchantExpansion", 0);
|
||||
limitGearExpansion = sConfigMgr->GetOption<int32>("AiPlayerbot.LimitGearExpansion", 0);
|
||||
randombotStartingLevel = sConfigMgr->GetOption<int32>("AiPlayerbot.RandombotStartingLevel", 5);
|
||||
enableRotation = sConfigMgr->GetOption<bool>("AiPlayerbot.EnableRotation", false);
|
||||
rotationPoolSize = sConfigMgr->GetOption<int32>("AiPlayerbot.RotationPoolSize", 500);
|
||||
@@ -327,6 +336,9 @@ bool PlayerbotAIConfig::Initialize()
|
||||
selfBotLevel = sConfigMgr->GetOption<int32>("AiPlayerbot.SelfBotLevel", 1);
|
||||
|
||||
RandomPlayerbotFactory::CreateRandomBots();
|
||||
if (World::IsStopped()) {
|
||||
return true;
|
||||
}
|
||||
PlayerbotFactory::Init();
|
||||
sRandomItemMgr->Init();
|
||||
sRandomItemMgr->InitAfterAhBot();
|
||||
|
||||
@@ -54,8 +54,8 @@ class PlayerbotAIConfig
|
||||
|
||||
bool enabled;
|
||||
bool allowGuildBots, allowPlayerBots;
|
||||
uint32 globalCoolDown, reactDelay, maxWaitForMove, maxMovementSearchTime, expireActionTime,
|
||||
dispelAuraDuration, passiveDelay, repeatDelay,
|
||||
uint32 globalCoolDown, reactDelay, maxWaitForMove, disableMoveSplinePath, maxMovementSearchTime,
|
||||
expireActionTime, dispelAuraDuration, passiveDelay, repeatDelay,
|
||||
errorDelay, rpgDelay, sitDelay, returnDelay, lootDelay;
|
||||
float sightDistance, spellDistance, reactDistance, grindDistance, lootDistance, shootDistance,
|
||||
fleeDistance, tooCloseDistance, meleeDistance, followDistance, whisperDistance, contactDistance,
|
||||
@@ -79,6 +79,8 @@ class PlayerbotAIConfig
|
||||
std::vector<uint32> randomBotQuestIds;
|
||||
uint32 randomBotTeleportDistance;
|
||||
float randomGearLoweringChance;
|
||||
int32 randomGearQualityLimit;
|
||||
int32 randomGearScoreLimit;
|
||||
float randomBotMaxLevelChance;
|
||||
float randomBotRpgChance;
|
||||
uint32 minRandomBots, maxRandomBots;
|
||||
@@ -134,6 +136,7 @@ class PlayerbotAIConfig
|
||||
bool randombotsWalkingRPGInDoors;
|
||||
uint32 minEnchantingBotLevel;
|
||||
uint32 limitEnchantExpansion;
|
||||
uint32 limitGearExpansion;
|
||||
uint32 randombotStartingLevel;
|
||||
bool enableRotation;
|
||||
uint32 rotationPoolSize;
|
||||
@@ -214,7 +217,11 @@ class PlayerbotAIConfig
|
||||
bool equipmentPersistence;
|
||||
int32 equipmentPersistenceLevel;
|
||||
int32 groupInvitationPermission;
|
||||
int32 botReviveWhenSummon;
|
||||
bool allowSummonInCombat;
|
||||
bool allowSummonWhenMasterIsDead;
|
||||
bool allowSummonWhenBotIsDead;
|
||||
bool reviveBotWhenSummoned;
|
||||
bool botRepairWhenSummon;
|
||||
bool autoInitOnly;
|
||||
float autoInitEquipLevelLimitRatio;
|
||||
int32 addClassCommand;
|
||||
|
||||
@@ -149,7 +149,10 @@ void PlayerbotFactory::Prepare()
|
||||
{
|
||||
if (!itemQuality)
|
||||
{
|
||||
itemQuality = ITEM_QUALITY_RARE;
|
||||
uint32 gs = sPlayerbotAIConfig->randomGearScoreLimit == 0 ? 0 :
|
||||
PlayerbotFactory::CalcMixedGearScore(sPlayerbotAIConfig->randomGearScoreLimit, sPlayerbotAIConfig->randomGearQualityLimit);
|
||||
itemQuality = sPlayerbotAIConfig->randomGearQualityLimit;
|
||||
gearScoreLimit = gs;
|
||||
}
|
||||
|
||||
if (bot->isDead())
|
||||
@@ -1453,6 +1456,14 @@ void PlayerbotFactory::InitEquipment(bool incremental)
|
||||
if (itemId == 46978) { // shaman earth ring totem
|
||||
continue;
|
||||
}
|
||||
|
||||
// disable next expansion gear
|
||||
if (sPlayerbotAIConfig->limitGearExpansion && bot->GetLevel() <= 60 && itemId >= 23728)
|
||||
continue;
|
||||
|
||||
if (sPlayerbotAIConfig->limitGearExpansion && bot->GetLevel() <= 70 && itemId >= 35570 && itemId != 36737 && itemId != 37739 && itemId != 37740)//transition point from TBC -> WOTLK isn't as clear, and there are other wearable TBC items above 35570 but nothing of significance
|
||||
continue;
|
||||
|
||||
ItemTemplate const* proto = sObjectMgr->GetItemTemplate(itemId);
|
||||
if (!proto)
|
||||
continue;
|
||||
@@ -3451,15 +3462,12 @@ void PlayerbotFactory::ApplyEnchantAndGemsNew(bool destoryOld)
|
||||
continue;
|
||||
}
|
||||
|
||||
// disable next expansion
|
||||
if (sPlayerbotAIConfig->limitEnchantExpansion && bot->GetLevel() <= 69 && enchantSpell >= 25072) {
|
||||
// disable next expansion enchantments
|
||||
if (sPlayerbotAIConfig->limitEnchantExpansion && bot->GetLevel() <= 60 && enchantSpell >= 25072)
|
||||
continue;
|
||||
}
|
||||
|
||||
if (sPlayerbotAIConfig->limitEnchantExpansion && bot->GetLevel() <= 79 && enchantSpell > 48557) {
|
||||
if (sPlayerbotAIConfig->limitEnchantExpansion && bot->GetLevel() <= 70 && enchantSpell > 48557)
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
for (uint8 j = 0; j < MAX_SPELL_EFFECTS; ++j)
|
||||
{
|
||||
@@ -3821,9 +3829,10 @@ float PlayerbotFactory::CalculateItemScore(uint32 item_id, Player* bot)
|
||||
score += (agility + strength + intellect + spirit + stamina + defense + dodge + parry + block +
|
||||
resilience + hit + crit + haste + expertise + attack_power + mana_regeneration + spell_power + armor_penetration +
|
||||
spell_penetration + armor + rangeDps + meleeDps) * 0.001;
|
||||
// todo: remove duplicate code
|
||||
if (cls == CLASS_HUNTER) {
|
||||
// AGILITY only
|
||||
score += agility * 2.5 + attack_power + armor_penetration * 2 + rangeDps * 5 + hit * 2.5 + crit * 2 + haste * 2.5 + intellect;
|
||||
score += agility * 2.5 + attack_power + armor_penetration * 2 + rangeDps * 5 + hit * 2 + crit * 2 + haste * 2 + intellect;
|
||||
} else if (cls == CLASS_WARLOCK ||
|
||||
cls == CLASS_MAGE ||
|
||||
(cls == CLASS_PRIEST && tab == 2) || // shadow
|
||||
@@ -3832,7 +3841,7 @@ float PlayerbotFactory::CalculateItemScore(uint32 item_id, Player* bot)
|
||||
) {
|
||||
// SPELL DPS
|
||||
score += intellect * 0.5 + spirit * 0.5 + spell_power + spell_penetration
|
||||
+ hit * 1.2 + crit * 0.7 + haste * 1 + rangeDps;
|
||||
+ hit * 1 + crit * 0.7 + haste * 1 + rangeDps;
|
||||
} else if ((cls == CLASS_PALADIN && tab == 0) || // holy
|
||||
(cls == CLASS_PRIEST && tab != 2) || // discipline / holy
|
||||
(cls == CLASS_SHAMAN && tab == 2) || // heal
|
||||
@@ -3840,9 +3849,9 @@ float PlayerbotFactory::CalculateItemScore(uint32 item_id, Player* bot)
|
||||
) {
|
||||
// HEALER
|
||||
score += intellect * 0.5 + spirit * 0.5 + spell_power + mana_regeneration * 0.5 + crit * 0.5 + haste * 1 + rangeDps;
|
||||
} else if (cls == CLASS_ROGUE) {
|
||||
} else if (cls == CLASS_ROGUE || (cls == CLASS_DRUID && tab == 2 && !PlayerbotAI::IsTank(bot))) {
|
||||
// AGILITY mainly (STRENGTH also)
|
||||
score += agility * 2 + strength + attack_power + armor_penetration * 1 + meleeDps * 5 + hit * 2 + crit * 1.5 + haste * 1.5 + expertise * 2.5;
|
||||
score += agility * 2 + strength + attack_power + armor_penetration * 1 + meleeDps * 5 + hit * 1.5 + crit * 1.5 + haste * 1.5 + expertise * 2.5;
|
||||
} else if ((cls == CLASS_PALADIN && tab == 2) || // retribution
|
||||
(cls == CLASS_WARRIOR && tab != 2) || // arm / fury
|
||||
(cls == CLASS_DEATH_KNIGHT && tab != 0) // ice / unholy
|
||||
@@ -3852,20 +3861,20 @@ float PlayerbotFactory::CalculateItemScore(uint32 item_id, Player* bot)
|
||||
} else if ((cls == CLASS_SHAMAN && tab == 1)) { // enhancement
|
||||
// STRENGTH mainly (AGILITY, INTELLECT also)
|
||||
score += strength * 1 + agility * 1.5 + intellect * 1.5 + attack_power + spell_power * 1.5 + armor_penetration * 0.5 + meleeDps * 5
|
||||
+ hit * 2 + crit * 1.5 + haste * 1.5 + expertise * 2;
|
||||
+ hit * 1.5 + crit * 1.5 + haste * 1.5 + expertise * 2;
|
||||
} else if ((cls == CLASS_WARRIOR && tab == 2) ||
|
||||
(cls == CLASS_PALADIN && tab == 1)) {
|
||||
// TANK WITH SHIELD
|
||||
score += strength * 1 + agility * 2 + attack_power * 0.2
|
||||
+ defense * 2.5 + parry * 2 + dodge * 2 + resilience * 2 + block * 2 + armor * 0.3 + stamina * 3
|
||||
+ hit * 1 + crit * 0.2 + haste * 0.5 + expertise * 3;
|
||||
+ hit * 0.5 + crit * 0.2 + haste * 0.5 + expertise * 3;
|
||||
} else if (cls == CLASS_DEATH_KNIGHT && tab == 0){
|
||||
// BLOOD DK TANK
|
||||
score += strength * 1 + agility * 2 + attack_power * 0.2
|
||||
+ defense * 3.5 + parry * 2 + dodge * 2 + resilience * 2 + armor * 0.3 + stamina * 2.5
|
||||
+ hit * 2 + crit * 0.5 + haste * 0.5 + expertise * 3.5;
|
||||
+ hit * 0.5 + crit * 0.5 + haste * 0.5 + expertise * 3.5;
|
||||
} else {
|
||||
// BEAR DRUID TANK (AND FERAL DRUID...?)
|
||||
// BEAR DRUID TANK
|
||||
score += agility * 1.5 + strength * 1 + attack_power * 0.5 + armor_penetration * 0.5 + meleeDps * 2
|
||||
+ defense * 0.25 + dodge * 0.25 + armor * 0.3 + stamina * 1.5
|
||||
+ hit * 1 + crit * 1 + haste * 0.5 + expertise * 3;
|
||||
@@ -4242,9 +4251,10 @@ float PlayerbotFactory::CalculateEnchantScore(uint32 enchant_id, Player* bot)
|
||||
float score = (agility + strength + intellect + spirit + stamina + defense + dodge + parry + block +
|
||||
resilience + hit + crit + haste + expertise + attack_power + mana_regeneration + spell_power + armor_penetration +
|
||||
spell_penetration + armor + dps) * 0.001;
|
||||
// todo: remove duplicate code
|
||||
if (cls == CLASS_HUNTER) {
|
||||
// AGILITY only
|
||||
score += agility * 2.5 + attack_power + armor_penetration * 2 + dps * 5 + hit * 2.5 + crit * 2 + haste * 2.5 + intellect;
|
||||
score += agility * 2.5 + attack_power + armor_penetration * 2 + dps * 5 + hit * 2 + crit * 2 + haste * 2.5 + intellect;
|
||||
} else if (cls == CLASS_WARLOCK ||
|
||||
cls == CLASS_MAGE ||
|
||||
(cls == CLASS_PRIEST && tab == 2) || // shadow
|
||||
@@ -4253,7 +4263,7 @@ float PlayerbotFactory::CalculateEnchantScore(uint32 enchant_id, Player* bot)
|
||||
) {
|
||||
// SPELL DPS
|
||||
score += intellect * 0.5 + spirit * 0.5 + spell_power + spell_penetration
|
||||
+ hit * 1.2 + crit * 0.7 + haste * 1;
|
||||
+ hit * 1 + crit * 0.7 + haste * 1;
|
||||
} else if ((cls == CLASS_PALADIN && tab == 0) || // holy
|
||||
(cls == CLASS_PRIEST && tab != 2) || // discipline / holy
|
||||
(cls == CLASS_SHAMAN && tab == 2) || // heal
|
||||
@@ -4261,9 +4271,9 @@ float PlayerbotFactory::CalculateEnchantScore(uint32 enchant_id, Player* bot)
|
||||
) {
|
||||
// HEALER
|
||||
score += intellect * 0.5 + spirit * 0.5 + spell_power + mana_regeneration * 0.5 + crit * 0.5 + haste * 1;
|
||||
} else if (cls == CLASS_ROGUE) {
|
||||
} else if (cls == CLASS_ROGUE || (cls == CLASS_DRUID && tab == 2 && !PlayerbotAI::IsTank(bot))) {
|
||||
// AGILITY mainly (STRENGTH also)
|
||||
score += agility * 2 + strength + attack_power + armor_penetration * 1 + dps * 5 + hit * 2 + crit * 1.5 + haste * 1.5 + expertise * 2.5;
|
||||
score += agility * 2 + strength + attack_power + armor_penetration * 1 + dps * 5 + hit * 1.5 + crit * 1.5 + haste * 1.5 + expertise * 2.5;
|
||||
} else if ((cls == CLASS_PALADIN && tab == 2) || // retribution
|
||||
(cls == CLASS_WARRIOR && tab != 2) || // arm / fury
|
||||
(cls == CLASS_DEATH_KNIGHT && tab != 0) // ice / unholy
|
||||
@@ -4273,20 +4283,20 @@ float PlayerbotFactory::CalculateEnchantScore(uint32 enchant_id, Player* bot)
|
||||
} else if ((cls == CLASS_SHAMAN && tab == 1)) { // enhancement
|
||||
// STRENGTH mainly (AGILITY, INTELLECT also)
|
||||
score += strength * 1 + agility * 1.5 + intellect * 1.5 + attack_power + spell_power * 1.5 + armor_penetration * 0.5 + dps * 5
|
||||
+ hit * 2 + crit * 1.5 + haste * 1.5 + expertise * 2;
|
||||
+ hit * 1.5 + crit * 1.5 + haste * 1.5 + expertise * 2;
|
||||
} else if ((cls == CLASS_WARRIOR && tab == 2) ||
|
||||
(cls == CLASS_PALADIN && tab == 1)) {
|
||||
// TANK WITH SHIELD
|
||||
score += strength * 1 + agility * 2 + attack_power * 0.2
|
||||
+ defense * 2.5 + parry * 2 + dodge * 2 + resilience * 2 + block * 2 + armor * 0.3 + stamina * 3
|
||||
+ hit * 1 + crit * 0.2 + haste * 0.5 + expertise * 3;
|
||||
+ hit * 0.5 + crit * 0.2 + haste * 0.5 + expertise * 3;
|
||||
} else if (cls == CLASS_DEATH_KNIGHT && tab == 0){
|
||||
// BLOOD DK TANK
|
||||
score += strength * 1 + agility * 2 + attack_power * 0.2
|
||||
+ defense * 3.5 + parry * 2 + dodge * 2 + resilience * 2 + armor * 0.3 + stamina * 2.5
|
||||
+ hit * 2 + crit * 0.5 + haste * 0.5 + expertise * 3.5;
|
||||
+ hit * 0.5 + crit * 0.5 + haste * 0.5 + expertise * 3.5;
|
||||
} else {
|
||||
// BEAR DRUID TANK (AND FERAL DRUID...?)
|
||||
// BEAR DRUID TANK
|
||||
score += agility * 1.5 + strength * 1 + attack_power * 0.5 + armor_penetration * 0.5 + dps * 2
|
||||
+ defense * 0.25 + dodge * 0.25 + armor * 0.3 + stamina * 1.5
|
||||
+ hit * 1 + crit * 1 + haste * 0.5 + expertise * 3;
|
||||
@@ -4521,9 +4531,10 @@ float PlayerbotFactory::CalculateSpellScore(uint32 spell_id, Player* bot, uint32
|
||||
}
|
||||
}
|
||||
float score = 0;
|
||||
// todo: remove duplicate code
|
||||
if (cls == CLASS_HUNTER) {
|
||||
// AGILITY only
|
||||
score += agility * 2.5 + attack_power + armor_penetration * 2 + rangeDps * 5 + hit * 2.5 + crit * 2 + haste * 2.5 + intellect;
|
||||
score += agility * 2.5 + attack_power + armor_penetration * 2 + rangeDps * 5 + hit * 2 + crit * 2 + haste * 2.5 + intellect;
|
||||
} else if (cls == CLASS_WARLOCK ||
|
||||
cls == CLASS_MAGE ||
|
||||
(cls == CLASS_PRIEST && tab == 2) || // shadow
|
||||
@@ -4532,7 +4543,7 @@ float PlayerbotFactory::CalculateSpellScore(uint32 spell_id, Player* bot, uint32
|
||||
) {
|
||||
// SPELL DPS
|
||||
score += intellect * 0.5 + spirit * 0.5 + spell_power + spell_penetration
|
||||
+ hit * 1.2 + crit * 0.7 + haste * 1 + rangeDps;
|
||||
+ hit * 1 + crit * 0.7 + haste * 1 + rangeDps;
|
||||
} else if ((cls == CLASS_PALADIN && tab == 0) || // holy
|
||||
(cls == CLASS_PRIEST && tab != 2) || // discipline / holy
|
||||
(cls == CLASS_SHAMAN && tab == 2) || // heal
|
||||
@@ -4540,9 +4551,9 @@ float PlayerbotFactory::CalculateSpellScore(uint32 spell_id, Player* bot, uint32
|
||||
) {
|
||||
// HEALER
|
||||
score += intellect * 0.5 + spirit * 0.5 + spell_power + mana_regeneration * 0.5 + crit * 0.5 + haste * 1 + rangeDps;
|
||||
} else if (cls == CLASS_ROGUE) {
|
||||
} else if (cls == CLASS_ROGUE || (cls == CLASS_DRUID && tab == 2 && !PlayerbotAI::IsTank(bot))) {
|
||||
// AGILITY mainly (STRENGTH also)
|
||||
score += agility * 2 + strength + attack_power + armor_penetration * 1 + meleeDps * 5 + hit * 2 + crit * 1.5 + haste * 1.5 + expertise * 2.5;
|
||||
score += agility * 2 + strength + attack_power + armor_penetration * 1 + meleeDps * 5 + hit * 1.5 + crit * 1.5 + haste * 1.5 + expertise * 2.5;
|
||||
} else if ((cls == CLASS_PALADIN && tab == 2) || // retribution
|
||||
(cls == CLASS_WARRIOR && tab != 2) || // arm / fury
|
||||
(cls == CLASS_DEATH_KNIGHT && tab != 0) // ice / unholy
|
||||
@@ -4552,20 +4563,20 @@ float PlayerbotFactory::CalculateSpellScore(uint32 spell_id, Player* bot, uint32
|
||||
} else if ((cls == CLASS_SHAMAN && tab == 1)) { // enhancement
|
||||
// STRENGTH mainly (AGILITY, INTELLECT also)
|
||||
score += strength * 1 + agility * 1.5 + intellect * 1.5 + attack_power + spell_power * 1.5 + armor_penetration * 0.5 + meleeDps * 5
|
||||
+ hit * 2 + crit * 1.5 + haste * 1.5 + expertise * 2;
|
||||
+ hit * 1.5 + crit * 1.5 + haste * 1.5 + expertise * 2;
|
||||
} else if ((cls == CLASS_WARRIOR && tab == 2) ||
|
||||
(cls == CLASS_PALADIN && tab == 1)) {
|
||||
// TANK WITH SHIELD
|
||||
score += strength * 1 + agility * 2 + attack_power * 0.2
|
||||
+ defense * 2.5 + parry * 2 + dodge * 2 + resilience * 2 + block * 2 + armor * 0.3 + stamina * 3
|
||||
+ hit * 1 + crit * 0.2 + haste * 0.5 + expertise * 3;
|
||||
+ hit * 0.5 + crit * 0.2 + haste * 0.5 + expertise * 3;
|
||||
} else if (cls == CLASS_DEATH_KNIGHT && tab == 0){
|
||||
// BLOOD DK TANK
|
||||
score += strength * 1 + agility * 2 + attack_power * 0.2
|
||||
+ defense * 3.5 + parry * 2 + dodge * 2 + resilience * 2 + armor * 0.3 + stamina * 2.5
|
||||
+ hit * 2 + crit * 0.5 + haste * 0.5 + expertise * 3.5;
|
||||
+ hit * 0.5 + crit * 0.5 + haste * 0.5 + expertise * 3.5;
|
||||
} else {
|
||||
// BEAR DRUID TANK (AND FERAL DRUID...?)
|
||||
// BEAR DRUID TANK
|
||||
score += agility * 1.5 + strength * 1 + attack_power * 0.5 + armor_penetration * 0.5 + meleeDps * 2
|
||||
+ defense * 0.25 + dodge * 0.25 + armor * 0.3 + stamina * 1.5
|
||||
+ hit * 1 + crit * 1 + haste * 0.5 + expertise * 3;
|
||||
|
||||
@@ -74,6 +74,12 @@ void PlayerbotHolder::AddPlayerBot(ObjectGuid playerGuid, uint32 masterAccountId
|
||||
|
||||
void PlayerbotHolder::HandlePlayerBotLoginCallback(PlayerbotLoginQueryHolder const& holder)
|
||||
{
|
||||
// has bot already been added?
|
||||
Player* loginBot = ObjectAccessor::FindConnectedPlayer(holder.GetGuid());
|
||||
if (loginBot && loginBot->IsInWorld()) {
|
||||
return;
|
||||
}
|
||||
|
||||
uint32 botAccountId = holder.GetAccountId();
|
||||
|
||||
WorldSession* botSession = new WorldSession(botAccountId, "", nullptr, SEC_PLAYER, EXPANSION_WRATH_OF_THE_LICH_KING, time_t(0), LOCALE_enUS, 0, false, false, 0, true);
|
||||
@@ -363,6 +369,11 @@ Player* PlayerbotHolder::GetPlayerBot(ObjectGuid::LowType lowGuid) const
|
||||
|
||||
void PlayerbotHolder::OnBotLogin(Player* const bot)
|
||||
{
|
||||
// Prevent duplicate login
|
||||
if (playerBots.find(bot->GetGUID()) != playerBots.end()) {
|
||||
return;
|
||||
}
|
||||
|
||||
sPlayerbotsMgr->AddPlayerbotData(bot, true);
|
||||
playerBots[bot->GetGUID()] = bot;
|
||||
OnBotLoginInternal(bot);
|
||||
@@ -564,6 +575,13 @@ std::string const PlayerbotHolder::ProcessBotCommand(std::string const cmd, Obje
|
||||
return "ERROR: You can not use this command on non-summoned random bot.";
|
||||
}
|
||||
|
||||
if (!admin) {
|
||||
Player* master = ObjectAccessor::FindConnectedPlayer(masterguid);
|
||||
if (master && (master->IsInCombat() || bot->IsInCombat())) {
|
||||
return "ERROR: You can not use this command during combat.";
|
||||
}
|
||||
}
|
||||
|
||||
if (GET_PLAYERBOT_AI(bot)) {
|
||||
if (Player* master = GET_PLAYERBOT_AI(bot)->GetMaster())
|
||||
{
|
||||
|
||||
@@ -34,6 +34,8 @@ int strcmpi(char const* s1, char const* s2);
|
||||
#define AI_VALUE_LAZY(type, name) context->GetValue<type>(name)->LazyGet()
|
||||
#define AI_VALUE2_LAZY(type, name, param) context->GetValue<type>(name, param)->LazyGet()
|
||||
|
||||
#define AI_VALUE_REF(type, name) context->GetValue<type>(name)->RefGet()
|
||||
|
||||
#define SET_AI_VALUE(type, name, value) context->GetValue<type>(name)->Set(value)
|
||||
#define SET_AI_VALUE2(type, name, param, value) context->GetValue<type>(name, param)->Set(value)
|
||||
#define RESET_AI_VALUE(type, name) context->GetValue<type>(name)->Reset()
|
||||
|
||||
@@ -55,9 +55,9 @@ RandomPlayerbotFactory::RandomPlayerbotFactory(uint32 accountId) : accountId(acc
|
||||
availableRaces[CLASS_PRIEST].push_back(RACE_NIGHTELF);
|
||||
availableRaces[CLASS_PRIEST].push_back(RACE_TROLL);
|
||||
availableRaces[CLASS_PRIEST].push_back(RACE_UNDEAD_PLAYER);
|
||||
availableRaces[CLASS_PRIEST].push_back(RACE_DRAENEI);
|
||||
if(expansion >= EXPANSION_THE_BURNING_CRUSADE)
|
||||
{
|
||||
availableRaces[CLASS_PRIEST].push_back(RACE_DRAENEI);
|
||||
availableRaces[CLASS_PRIEST].push_back(RACE_BLOODELF);
|
||||
}
|
||||
|
||||
|
||||
@@ -384,6 +384,12 @@ void RandomPlayerbotMgr::UpdateAIInternal(uint32 elapsed, bool /*minimal*/)
|
||||
activateCheckBgQueueThread();
|
||||
}
|
||||
|
||||
if (sPlayerbotAIConfig->randomBotJoinLfg/* && !players.empty()*/)
|
||||
{
|
||||
if (time(nullptr) > (LfgCheckTimer + 30))
|
||||
activateCheckLfgQueueThread();
|
||||
}
|
||||
|
||||
uint32 updateBots = sPlayerbotAIConfig->randomBotsPerInterval * onlineBotFocus / 100;
|
||||
uint32 maxNewBots = onlineBotCount < maxAllowedBotCount ? maxAllowedBotCount - onlineBotCount : 0;
|
||||
uint32 loginBots = std::min(sPlayerbotAIConfig->randomBotsPerInterval - updateBots, maxNewBots);
|
||||
|
||||
@@ -112,6 +112,7 @@ class StrategyContext : public NamedObjectContext<Strategy>
|
||||
creators["grind"] = &StrategyContext::grind;
|
||||
creators["avoid aoe"] = &StrategyContext::avoid_aoe;
|
||||
creators["move random"] = &StrategyContext::move_random;
|
||||
creators["combat formation"] = &StrategyContext::combat_formation;
|
||||
}
|
||||
|
||||
private:
|
||||
@@ -174,6 +175,7 @@ class StrategyContext : public NamedObjectContext<Strategy>
|
||||
static Strategy* grind(PlayerbotAI* botAI) { return new GrindingStrategy(botAI); }
|
||||
static Strategy* avoid_aoe(PlayerbotAI* botAI) { return new AvoidAoeStrategy(botAI); }
|
||||
static Strategy* move_random(PlayerbotAI* ai) { return new MoveRandomStrategy(ai); }
|
||||
static Strategy* combat_formation(PlayerbotAI* ai) { return new CombatFormationStrategy(ai); }
|
||||
};
|
||||
|
||||
class MovementStrategyContext : public NamedObjectContext<Strategy>
|
||||
|
||||
@@ -16,6 +16,16 @@
|
||||
class PlayerbotAI;
|
||||
class Unit;
|
||||
|
||||
class FleeInfo
|
||||
{
|
||||
public:
|
||||
Position fromPos;
|
||||
float radius;
|
||||
float angle;
|
||||
uint32 timestamp;
|
||||
int GetAngleRangeIndex() { return (angle + 2 * M_PI) / (M_PI / 2); } // [0, 7)
|
||||
};
|
||||
|
||||
struct CreatureData;
|
||||
|
||||
class UntypedValue : public AiNamedObject
|
||||
@@ -37,6 +47,7 @@ class Value
|
||||
virtual ~Value() { }
|
||||
virtual T Get() = 0;
|
||||
virtual T LazyGet() = 0;
|
||||
virtual T& RefGet() = 0;
|
||||
virtual void Reset() { }
|
||||
virtual void Set(T value) = 0;
|
||||
operator T() { return Get(); }
|
||||
@@ -79,7 +90,26 @@ class CalculatedValue : public UntypedValue, public Value<T>
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
T& RefGet() override
|
||||
{
|
||||
if (checkInterval < 2) {
|
||||
// PerformanceMonitorOperation* pmo = sPerformanceMonitor->start(PERF_MON_VALUE, this->getName(), this->context ? &this->context->performanceStack : nullptr);
|
||||
value = Calculate();
|
||||
// if (pmo)
|
||||
// pmo->finish();
|
||||
} else {
|
||||
time_t now = getMSTime();
|
||||
if (!lastCheckTime || now - lastCheckTime >= checkInterval)
|
||||
{
|
||||
lastCheckTime = now;
|
||||
// PerformanceMonitorOperation* pmo = sPerformanceMonitor->start(PERF_MON_VALUE, this->getName(), this->context ? &this->context->performanceStack : nullptr);
|
||||
value = Calculate();
|
||||
// if (pmo)
|
||||
// pmo->finish();
|
||||
}
|
||||
}
|
||||
return value;
|
||||
}
|
||||
void Set(T val) override { value = val; }
|
||||
void Update() override {}
|
||||
void Reset() override { lastCheckTime = 0; }
|
||||
@@ -304,6 +334,7 @@ class ManualSetValue : public UntypedValue, public Value<T>
|
||||
|
||||
T Get() override { return value; }
|
||||
T LazyGet() override { return value; }
|
||||
T& RefGet() override { return value; }
|
||||
void Set(T val) override { value = val; }
|
||||
void Update() override {}
|
||||
void Reset() override
|
||||
@@ -326,4 +357,32 @@ class UnitManualSetValue : public ManualSetValue<Unit*>
|
||||
Unit* Get() override;
|
||||
};
|
||||
|
||||
class DisperseDistanceValue : public ManualSetValue<float>
|
||||
{
|
||||
public:
|
||||
DisperseDistanceValue(PlayerbotAI* botAI, float defaultValue = -1.0f, std::string const name = "disperse distance") :
|
||||
ManualSetValue<float>(botAI, defaultValue, name) { }
|
||||
};
|
||||
|
||||
class LastFleeAngleValue : public ManualSetValue<float>
|
||||
{
|
||||
public:
|
||||
LastFleeAngleValue(PlayerbotAI* botAI, float defaultValue = 0.0f, std::string const name = "last flee angle") :
|
||||
ManualSetValue<float>(botAI, defaultValue, name) { }
|
||||
};
|
||||
|
||||
class LastFleeTimestampValue : public ManualSetValue<uint32>
|
||||
{
|
||||
public:
|
||||
LastFleeTimestampValue(PlayerbotAI* botAI, uint32 defaultValue = 0, std::string const name = "last flee timestamp") :
|
||||
ManualSetValue<uint32>(botAI, defaultValue, name) { }
|
||||
};
|
||||
|
||||
class RecentlyFleeInfo : public ManualSetValue<std::list<FleeInfo>>
|
||||
{
|
||||
public:
|
||||
RecentlyFleeInfo(PlayerbotAI* botAI, std::list<FleeInfo> defaultValue = {}, std::string const name = "recently flee info") :
|
||||
ManualSetValue<std::list<FleeInfo>>(botAI, defaultValue, name) { }
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -89,6 +89,8 @@ class ActionContext : public NamedObjectContext<Action>
|
||||
creators["flee"] = &ActionContext::flee;
|
||||
creators["flee with pet"] = &ActionContext::flee_with_pet;
|
||||
creators["avoid aoe"] = &ActionContext::avoid_aoe;
|
||||
creators["combat formation move"] = &ActionContext::combat_formation_move;
|
||||
creators["disperse set"] = &ActionContext::disperse_set;
|
||||
creators["gift of the naaru"] = &ActionContext::gift_of_the_naaru;
|
||||
creators["shoot"] = &ActionContext::shoot;
|
||||
creators["lifeblood"] = &ActionContext::lifeblood;
|
||||
@@ -150,6 +152,7 @@ class ActionContext : public NamedObjectContext<Action>
|
||||
creators["auto talents"] = &ActionContext::auto_talents;
|
||||
creators["auto learn spell"] = &ActionContext::auto_learn_spell;
|
||||
creators["auto teleport for level"] = &ActionContext::auto_teleport_for_level;
|
||||
creators["auto upgrade equip"] = &ActionContext::auto_upgrade_equip;
|
||||
creators["xp gain"] = &ActionContext::xp_gain;
|
||||
creators["invite nearby"] = &ActionContext::invite_nearby;
|
||||
creators["invite guild"] = &ActionContext::invite_guild;
|
||||
@@ -265,6 +268,8 @@ class ActionContext : public NamedObjectContext<Action>
|
||||
static Action* flee(PlayerbotAI* botAI) { return new FleeAction(botAI); }
|
||||
static Action* flee_with_pet(PlayerbotAI* botAI) { return new FleeWithPetAction(botAI); }
|
||||
static Action* avoid_aoe(PlayerbotAI* botAI) { return new AvoidAoeAction(botAI); }
|
||||
static Action* combat_formation_move(PlayerbotAI* botAI) { return new CombatFormationMoveAction(botAI); }
|
||||
static Action* disperse_set(PlayerbotAI* botAI) { return new DisperseSetAction(botAI); }
|
||||
static Action* gift_of_the_naaru(PlayerbotAI* botAI) { return new CastGiftOfTheNaaruAction(botAI); }
|
||||
static Action* lifeblood(PlayerbotAI* botAI) { return new CastLifeBloodAction(botAI); }
|
||||
static Action* arcane_torrent(PlayerbotAI* botAI) { return new CastArcaneTorrentAction(botAI); }
|
||||
@@ -315,6 +320,7 @@ class ActionContext : public NamedObjectContext<Action>
|
||||
static Action* auto_talents(PlayerbotAI* botAI) { return new AutoSetTalentsAction(botAI); }
|
||||
static Action* auto_learn_spell(PlayerbotAI* botAI) { return new AutoLearnSpellAction(botAI); }
|
||||
static Action* auto_teleport_for_level(PlayerbotAI* botAI) { return new AutoTeleportForLevelAction(botAI); }
|
||||
static Action* auto_upgrade_equip(PlayerbotAI* botAI) { return new AutoUpgradeEquipAction(botAI); }
|
||||
static Action* xp_gain(PlayerbotAI* botAI) { return new XpGainAction(botAI); }
|
||||
static Action* invite_nearby(PlayerbotAI* botAI) { return new InviteNearbyToGroupAction(botAI); }
|
||||
static Action* invite_guild(PlayerbotAI* botAI) { return new InviteGuildToGroupAction(botAI); }
|
||||
|
||||
@@ -95,7 +95,7 @@ bool AttackAction::Attack(Unit* target, bool with_pet /*true*/)
|
||||
return false;
|
||||
}
|
||||
|
||||
if (bot->IsMounted() && bot->IsWithinLOSInMap(target) && sServerFacade->GetDistance2d(bot, target) < 40.0f)
|
||||
if (bot->IsMounted() && bot->IsWithinLOSInMap(target))
|
||||
{
|
||||
WorldPacket emptyPacket;
|
||||
bot->GetSession()->HandleCancelMountAuraOpcode(emptyPacket);
|
||||
|
||||
@@ -24,7 +24,6 @@ bool AutoLearnSpellAction::Execute(Event event)
|
||||
out << ".";
|
||||
botAI->TellMaster(out);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -181,3 +180,15 @@ void AutoLearnSpellAction::LearnSpell(uint32 spellId, std::ostringstream* out)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool AutoUpgradeEquipAction::Execute(Event event) {
|
||||
if (!sPlayerbotAIConfig->autoUpgradeEquip || !sRandomPlayerbotMgr->IsRandomBot(bot)) {
|
||||
return false;
|
||||
}
|
||||
PlayerbotFactory factory(bot, bot->GetLevel(), ITEM_QUALITY_RARE);
|
||||
if (!sPlayerbotAIConfig->equipmentPersistence || bot->GetLevel() < sPlayerbotAIConfig->equipmentPersistenceLevel) {
|
||||
factory.InitEquipment(true);
|
||||
}
|
||||
factory.InitAmmo();
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -23,4 +23,12 @@ class AutoLearnSpellAction : public Action
|
||||
void LearnSpell(uint32 spellId, std::ostringstream* out);
|
||||
};
|
||||
|
||||
class AutoUpgradeEquipAction : public Action
|
||||
{
|
||||
public:
|
||||
AutoUpgradeEquipAction(PlayerbotAI* botAI, std::string const name = "auto upgrade equip") : Action(botAI, name) { }
|
||||
|
||||
bool Execute(Event event);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -6,8 +6,6 @@
|
||||
#include "SharedDefines.h"
|
||||
|
||||
bool AutoTeleportForLevelAction::Execute(Event event) {
|
||||
AutoUpgradeEquip();
|
||||
|
||||
if (!sPlayerbotAIConfig->autoTeleportForLevel || !sRandomPlayerbotMgr->IsRandomBot(bot)) {
|
||||
return false;
|
||||
}
|
||||
@@ -16,15 +14,4 @@ bool AutoTeleportForLevelAction::Execute(Event event) {
|
||||
}
|
||||
sRandomPlayerbotMgr->RandomTeleportForLevel(bot);
|
||||
return true;
|
||||
}
|
||||
|
||||
void AutoTeleportForLevelAction::AutoUpgradeEquip() {
|
||||
if (!sPlayerbotAIConfig->autoUpgradeEquip || !sRandomPlayerbotMgr->IsRandomBot(bot)) {
|
||||
return;
|
||||
}
|
||||
PlayerbotFactory factory(bot, bot->GetLevel(), ITEM_QUALITY_RARE);
|
||||
if (!sPlayerbotAIConfig->equipmentPersistence || bot->GetLevel() < sPlayerbotAIConfig->equipmentPersistenceLevel) {
|
||||
factory.InitEquipment(true);
|
||||
}
|
||||
factory.InitAmmo();
|
||||
}
|
||||
@@ -15,8 +15,6 @@ class AutoTeleportForLevelAction : public Action
|
||||
AutoTeleportForLevelAction(PlayerbotAI* botAI, std::string const name = "auto teleport for level") : Action(botAI, name) { }
|
||||
|
||||
bool Execute(Event event);
|
||||
private:
|
||||
void AutoUpgradeEquip();
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -459,7 +459,7 @@ bool BGJoinAction::JoinQueue(uint32 type)
|
||||
// get BG MapId
|
||||
uint32 bgTypeId_ = bgTypeId;
|
||||
uint32 instanceId = 0; // 0 = First Available
|
||||
bool joinAsGroup = bot->GetGroup() && bot->GetGroup()->GetLeaderGUID() == bot->GetGUID();
|
||||
|
||||
bool isPremade = false;
|
||||
bool isArena = false;
|
||||
bool isRated = false;
|
||||
@@ -483,6 +483,11 @@ bool BGJoinAction::JoinQueue(uint32 type)
|
||||
return false;
|
||||
}
|
||||
|
||||
// refresh food/regs
|
||||
sRandomPlayerbotMgr->Refresh(bot);
|
||||
|
||||
bool joinAsGroup = bot->GetGroup() && bot->GetGroup()->GetLeaderGUID() == bot->GetGUID();
|
||||
|
||||
// in wotlk only arena requires battlemaster guid
|
||||
ObjectGuid guid = isArena ? unit->GetGUID() : bot->GetGUID();
|
||||
|
||||
@@ -546,8 +551,6 @@ bool BGJoinAction::JoinQueue(uint32 type)
|
||||
bot->GetGUID().ToString().c_str(), bot->GetTeamId() == TEAM_ALLIANCE ? "A" : "H", bot->getLevel(), bot->GetName().c_str(), _bgType.c_str(),
|
||||
isRated ? "Rated Arena" : isArena ? "Arena" : "");
|
||||
|
||||
// refresh food/regs
|
||||
sRandomPlayerbotMgr->Refresh(bot);
|
||||
|
||||
if (isArena)
|
||||
{
|
||||
|
||||
@@ -40,12 +40,16 @@ Position const WS_FLAG_HIDE_ALLIANCE_2 = { 1540.286f, 1476.026f, 352.692f, 2.91f
|
||||
Position const WS_FLAG_HIDE_ALLIANCE_3 = { 1495.807f, 1466.774f, 352.350f, 1.50f };
|
||||
std::vector<Position> const WS_FLAG_HIDE_HORDE = { WS_FLAG_HIDE_HORDE_1 , WS_FLAG_HIDE_HORDE_2, WS_FLAG_HIDE_HORDE_3 };
|
||||
std::vector<Position> const WS_FLAG_HIDE_ALLIANCE = { WS_FLAG_HIDE_ALLIANCE_1 , WS_FLAG_HIDE_ALLIANCE_2, WS_FLAG_HIDE_ALLIANCE_3 };
|
||||
|
||||
Position const AB_WAITING_POS_HORDE = { 702.884f, 703.045f, -16.115f, 0.77f };
|
||||
Position const AB_WAITING_POS_ALLIANCE = { 1286.054f, 1282.500f, -15.697f, 3.95f };
|
||||
|
||||
Position const AV_WAITING_POS_ALLIANCE = { 793.627f, -493.814f, 99.689f, 3.09f };
|
||||
Position const AV_WAITING_POS_HORDE = { -1381.865f, -544.872f, 54.773f, 0.76f };
|
||||
Position const AV_ICEBLOOD_GARRISON_WAITING_ALLIANCE = { -492.17f, -187.077f, 57.1342f, 2.77f };
|
||||
Position const AV_STONEHEARTH_WAITING_HORDE = { 28.1264f, -302.593f, 15.076f, 2.96f };
|
||||
Position const AV_ICEBLOOD_GARRISON_WAITING_ALLIANCE = { -523.105f, -182.178f, 57.956f, 2.77f };
|
||||
Position const AV_ICEBLOOD_GARRISON_ATTACKING_ALLIANCE = { -545.288f, -167.932f, 57.012f, 2.77f };
|
||||
Position const AV_STONEHEARTH_WAITING_HORDE = { -36.399f, -306.403f, 15.565f, 2.96f };
|
||||
Position const AV_STONEHEARTH_ATTACKING_HORDE = { -55.210f, -288.546f, 15.578f, 2.96f };
|
||||
|
||||
Position const EY_WAITING_POS_HORDE = { 1809.102f, 1540.854f, 1267.142f, 6.18f };
|
||||
Position const EY_WAITING_POS_ALLIANCE = { 2526.020f, 1596.787f, 1270.127f, 3.14f };
|
||||
@@ -799,7 +803,16 @@ BattleBotPath vPath_AB_Farm_to_LumberMill =
|
||||
|
||||
BattleBotPath vPath_AV_Horde_Cave_to_Tower_Point_Crossroad =
|
||||
{
|
||||
{ -885.928f, -536.612f, 55.1936f, nullptr },
|
||||
{ -1362.395f, -529.615f, 52.636f, nullptr },
|
||||
{ -1327.036f, -511.374f, 51.138f, nullptr },
|
||||
{ -1277.047f, -516.327f, 50.667f, nullptr },
|
||||
{ -1214.901f, -529.350f, 52.251f, nullptr },
|
||||
{ -1151.129f, -545.598f, 51.990f, nullptr },
|
||||
{ -1085.775f, -538.719f, 47.905f, nullptr },
|
||||
{ -1038.552f, -517.946f, 43.876f, nullptr },
|
||||
{ -981.371f, -494.593f, 41.127f, nullptr },
|
||||
{ -930.598f, -463.751f, 43.060f, nullptr },
|
||||
{ -887.138f, -475.816f, 44.374f, nullptr },
|
||||
{ -880.957f, -525.119f, 53.6791f, nullptr },
|
||||
{ -839.408f, -499.746f, 49.7505f, nullptr },
|
||||
{ -820.21f, -469.193f, 49.4085f, nullptr },
|
||||
@@ -2262,48 +2275,48 @@ std::vector<BattleBotPath*> const vPaths_HordeMine =
|
||||
&vPath_AV_Coldtooth_Mine_Entrance_to_Coldtooth_Mine_Boss,
|
||||
};
|
||||
|
||||
static uint32 AV_HordeAttackObjectives[] =
|
||||
static std::pair<uint32, uint32> AV_HordeAttackObjectives[] =
|
||||
{
|
||||
// Attack
|
||||
{ BG_AV_NODES_STONEHEART_BUNKER },
|
||||
{ BG_AV_NODES_STONEHEART_GRAVE },
|
||||
{ BG_AV_NODES_ICEWING_BUNKER },
|
||||
{ BG_AV_NODES_STORMPIKE_GRAVE },
|
||||
{ BG_AV_NODES_DUNBALDAR_SOUTH },
|
||||
{ BG_AV_NODES_DUNBALDAR_NORTH },
|
||||
{ BG_AV_NODES_FIRSTAID_STATION }
|
||||
{ BG_AV_NODES_STONEHEART_BUNKER, BG_AV_OBJECT_FLAG_A_STONEHEART_BUNKER },
|
||||
{ BG_AV_NODES_STONEHEART_GRAVE, BG_AV_OBJECT_FLAG_A_STONEHEART_GRAVE },
|
||||
{ BG_AV_NODES_ICEWING_BUNKER, BG_AV_OBJECT_FLAG_A_ICEWING_BUNKER },
|
||||
{ BG_AV_NODES_STORMPIKE_GRAVE, BG_AV_OBJECT_FLAG_A_STORMPIKE_GRAVE },
|
||||
{ BG_AV_NODES_DUNBALDAR_SOUTH, BG_AV_OBJECT_FLAG_A_DUNBALDAR_SOUTH },
|
||||
{ BG_AV_NODES_DUNBALDAR_NORTH, BG_AV_OBJECT_FLAG_A_DUNBALDAR_NORTH },
|
||||
{ BG_AV_NODES_FIRSTAID_STATION, BG_AV_OBJECT_FLAG_A_FIRSTAID_STATION }
|
||||
};
|
||||
|
||||
static uint32 AV_HordeDefendObjectives[] =
|
||||
static std::pair<uint32, uint32> AV_HordeDefendObjectives[] =
|
||||
{
|
||||
// Defend
|
||||
{ BG_AV_NODES_FROSTWOLF_GRAVE },
|
||||
{ BG_AV_NODES_FROSTWOLF_ETOWER },
|
||||
{ BG_AV_NODES_FROSTWOLF_WTOWER },
|
||||
{ BG_AV_NODES_TOWER_POINT },
|
||||
{ BG_AV_NODES_ICEBLOOD_TOWER },
|
||||
{ BG_AV_NODES_FROSTWOLF_GRAVE, BG_AV_OBJECT_FLAG_H_FROSTWOLF_GRAVE },
|
||||
{ BG_AV_NODES_FROSTWOLF_ETOWER, BG_AV_OBJECT_FLAG_H_FROSTWOLF_ETOWER },
|
||||
{ BG_AV_NODES_FROSTWOLF_WTOWER, BG_AV_OBJECT_FLAG_H_FROSTWOLF_WTOWER },
|
||||
{ BG_AV_NODES_TOWER_POINT, BG_AV_OBJECT_FLAG_H_TOWER_POINT },
|
||||
{ BG_AV_NODES_ICEBLOOD_TOWER, BG_AV_OBJECT_FLAG_H_ICEBLOOD_TOWER },
|
||||
};
|
||||
|
||||
static uint32 AV_AllianceAttackObjectives[] =
|
||||
static std::pair<uint32, uint32> AV_AllianceAttackObjectives[] =
|
||||
{
|
||||
// Attack
|
||||
{ BG_AV_NODES_ICEBLOOD_TOWER },
|
||||
{ BG_AV_NODES_ICEBLOOD_GRAVE },
|
||||
{ BG_AV_NODES_TOWER_POINT },
|
||||
{ BG_AV_NODES_FROSTWOLF_GRAVE },
|
||||
{ BG_AV_NODES_FROSTWOLF_ETOWER },
|
||||
{ BG_AV_NODES_FROSTWOLF_WTOWER },
|
||||
{ BG_AV_NODES_FROSTWOLF_HUT },
|
||||
{ BG_AV_NODES_ICEBLOOD_TOWER, BG_AV_OBJECT_FLAG_H_ICEBLOOD_TOWER},
|
||||
{ BG_AV_NODES_ICEBLOOD_GRAVE, BG_AV_OBJECT_FLAG_H_ICEBLOOD_GRAVE},
|
||||
{ BG_AV_NODES_TOWER_POINT, BG_AV_OBJECT_FLAG_H_TOWER_POINT },
|
||||
{ BG_AV_NODES_FROSTWOLF_GRAVE, BG_AV_OBJECT_FLAG_H_FROSTWOLF_GRAVE },
|
||||
{ BG_AV_NODES_FROSTWOLF_ETOWER, BG_AV_OBJECT_FLAG_H_FROSTWOLF_ETOWER },
|
||||
{ BG_AV_NODES_FROSTWOLF_WTOWER, BG_AV_OBJECT_FLAG_H_FROSTWOLF_WTOWER },
|
||||
{ BG_AV_NODES_FROSTWOLF_HUT, BG_AV_OBJECT_FLAG_H_FROSTWOLF_HUT },
|
||||
};
|
||||
|
||||
static uint32 AV_AllianceDefendObjectives[] =
|
||||
static std::pair<uint32, uint32> AV_AllianceDefendObjectives[] =
|
||||
{
|
||||
// Defend
|
||||
{ BG_AV_NODES_STORMPIKE_GRAVE },
|
||||
{ BG_AV_NODES_DUNBALDAR_SOUTH },
|
||||
{ BG_AV_NODES_DUNBALDAR_NORTH },
|
||||
{ BG_AV_NODES_ICEWING_BUNKER },
|
||||
{ BG_AV_NODES_STONEHEART_BUNKER },
|
||||
{ BG_AV_NODES_STORMPIKE_GRAVE, BG_AV_OBJECT_FLAG_A_STORMPIKE_GRAVE },
|
||||
{ BG_AV_NODES_DUNBALDAR_SOUTH, BG_AV_OBJECT_FLAG_A_DUNBALDAR_SOUTH },
|
||||
{ BG_AV_NODES_DUNBALDAR_NORTH, BG_AV_OBJECT_FLAG_A_DUNBALDAR_NORTH },
|
||||
{ BG_AV_NODES_ICEWING_BUNKER, BG_AV_OBJECT_FLAG_A_ICEWING_BUNKER },
|
||||
{ BG_AV_NODES_STONEHEART_BUNKER, BG_AV_OBJECT_FLAG_A_STONEHEART_BUNKER },
|
||||
};
|
||||
|
||||
static uint32 AB_AttackObjectives[] =
|
||||
@@ -2937,7 +2950,7 @@ bool BGTactics::selectObjective(bool reset)
|
||||
alterValleyBG->GetAVNodeInfo(BG_AV_NODES_STONEHEART_BUNKER).TotalOwnerId != TEAM_ALLIANCE &&
|
||||
alterValleyBG->GetAVNodeInfo(BG_AV_NODES_FIRSTAID_STATION).TotalOwnerId != TEAM_ALLIANCE)
|
||||
{
|
||||
if (Creature* pVanndar = bg->GetBGCreature(AV_NPC_A_BOSS))
|
||||
if (Creature* pVanndar = bg->GetBGCreature(AV_CPLACE_TRIGGER17))
|
||||
{
|
||||
BgObjective = pVanndar;
|
||||
endBoss = true;
|
||||
@@ -2952,8 +2965,9 @@ bool BGTactics::selectObjective(bool reset)
|
||||
|
||||
// Only go to Snowfall Graveyard if already close to it.
|
||||
// Need to fix AV script
|
||||
if (!BgObjective && supporter && (alterValleyBG->GetAVNodeInfo(BG_AV_NODES_SNOWFALL_GRAVE).OwnerId == TEAM_ALLIANCE ||
|
||||
alterValleyBG->GetAVNodeInfo(BG_AV_NODES_SNOWFALL_GRAVE).OwnerId == TEAM_HORDE || alterValleyBG->GetAVNodeInfo(BG_AV_NODES_SNOWFALL_GRAVE).OwnerId == TEAM_OTHER))
|
||||
if (!BgObjective && supporter &&
|
||||
(alterValleyBG->GetAVNodeInfo(BG_AV_NODES_SNOWFALL_GRAVE).OwnerId == TEAM_ALLIANCE ||
|
||||
alterValleyBG->GetAVNodeInfo(BG_AV_NODES_SNOWFALL_GRAVE).OwnerId == TEAM_OTHER))
|
||||
{
|
||||
if (GameObject* pGO = bg->GetBGObject(BG_AV_NODES_SNOWFALL_GRAVE))
|
||||
if (bot->IsWithinDist(pGO, 200.f))
|
||||
@@ -2967,19 +2981,20 @@ bool BGTactics::selectObjective(bool reset)
|
||||
|
||||
if (!BgObjective && alterValleyBG->IsCaptainAlive(0))
|
||||
{
|
||||
if (Creature* pBalinda = bg->GetBGCreature(AV_NPC_A_CAPTAIN))
|
||||
if (Creature* pBalinda = bg->GetBGCreature(AV_CPLACE_TRIGGER16))
|
||||
{
|
||||
if (pBalinda->getDeathState() != DeathState::Dead)
|
||||
{
|
||||
uint32 attackCount = 0;
|
||||
attackCount += getDefendersCount(AV_STONEHEARTH_WAITING_HORDE, 10.0f, false);
|
||||
uint32 attackCount = getDefendersCount(AV_STONEHEARTH_WAITING_HORDE, 10.0f, false) + getDefendersCount(AV_STONEHEARTH_ATTACKING_HORDE, 10.0f, false);
|
||||
|
||||
// prepare to attack Captain
|
||||
if (attackCount < 10 && !pBalinda->IsInCombat())
|
||||
{
|
||||
// get in position to attack Captain
|
||||
pos.Set(AV_STONEHEARTH_WAITING_HORDE.GetPositionX(), AV_STONEHEARTH_WAITING_HORDE.GetPositionY(),
|
||||
AV_STONEHEARTH_WAITING_HORDE.GetPositionZ(), bg->GetMapId());
|
||||
pos.Set(AV_STONEHEARTH_WAITING_HORDE.GetPositionX(),
|
||||
AV_STONEHEARTH_WAITING_HORDE.GetPositionY(),
|
||||
AV_STONEHEARTH_WAITING_HORDE.GetPositionZ(),
|
||||
bg->GetMapId());
|
||||
|
||||
std::ostringstream out;
|
||||
out << "Taking position at Stonehearth!";
|
||||
@@ -2987,6 +3002,12 @@ bool BGTactics::selectObjective(bool reset)
|
||||
}
|
||||
else
|
||||
{
|
||||
// they need help getting there (or did before I fixed the target creature, will leave in anyway, as it probably makes it more robust)
|
||||
pos.Set(AV_STONEHEARTH_ATTACKING_HORDE.GetPositionX(),
|
||||
AV_STONEHEARTH_ATTACKING_HORDE.GetPositionY(),
|
||||
AV_STONEHEARTH_ATTACKING_HORDE.GetPositionZ(),
|
||||
bg->GetMapId());
|
||||
|
||||
std::ostringstream out;
|
||||
out << "Attacking Balinda!";
|
||||
//bot->Say(out.str(), LANG_UNIVERSAL);
|
||||
@@ -3007,9 +3028,9 @@ bool BGTactics::selectObjective(bool reset)
|
||||
{
|
||||
for (const auto& objective : AV_HordeDefendObjectives)
|
||||
{
|
||||
if (!BgObjective && alterValleyBG->GetAVNodeInfo(objective).OwnerId == TEAM_ALLIANCE)
|
||||
if (!BgObjective && alterValleyBG->GetAVNodeInfo(objective.first).OwnerId == TEAM_ALLIANCE)
|
||||
{
|
||||
if (GameObject* pGO = bg->GetBGObject(objective))
|
||||
if (GameObject* pGO = bg->GetBGObject(objective.second))
|
||||
if (bot->IsWithinDist(pGO, 400.0f))
|
||||
{
|
||||
BgObjective = pGO;
|
||||
@@ -3022,7 +3043,8 @@ bool BGTactics::selectObjective(bool reset)
|
||||
}
|
||||
|
||||
// Mine capture (need paths & script fix)
|
||||
if (!BgObjective && supporter && !endBoss && (alterValleyBG->GetMineOwner(AV_NORTH_MINE) == TEAM_ALLIANCE || alterValleyBG->GetMineOwner(AV_NORTH_MINE) == TEAM_OTHER) &&
|
||||
if (!BgObjective && supporter && !endBoss &&
|
||||
(alterValleyBG->GetMineOwner(AV_NORTH_MINE) == TEAM_ALLIANCE || alterValleyBG->GetMineOwner(AV_NORTH_MINE) == TEAM_OTHER) &&
|
||||
alterValleyBG->GetAVNodeInfo(BG_AV_NODES_STORMPIKE_GRAVE).OwnerId != TEAM_ALLIANCE)
|
||||
{
|
||||
if (Creature* mBossNeutral = bg->GetBGCreature(AV_CPLACE_MINE_N_3))
|
||||
@@ -3055,10 +3077,9 @@ bool BGTactics::selectObjective(bool reset)
|
||||
for (const auto& objective : AV_HordeAttackObjectives)
|
||||
{
|
||||
if ((!BgObjective/* || (supporter && objective.first == BG_AV_NODES_STONEHEART_BUNKER && !bg->IsActiveEvent(BG_AV_NODE_CAPTAIN_DEAD_A, 0))*/) &&
|
||||
(alterValleyBG->GetAVNodeInfo(objective).OwnerId == TEAM_ALLIANCE || alterValleyBG->GetAVNodeInfo(objective).TotalOwnerId == TEAM_ALLIANCE ||
|
||||
alterValleyBG->GetAVNodeInfo(objective).OwnerId == TEAM_OTHER))
|
||||
alterValleyBG->GetAVNodeInfo(objective.first).TotalOwnerId == TEAM_ALLIANCE)//need to check TotalOwnerId for attack objectives
|
||||
{
|
||||
if (GameObject* pGO = bg->GetBGObject(objective))
|
||||
if (GameObject* pGO = bg->GetBGObject(objective.second))
|
||||
{
|
||||
BgObjective = pGO;
|
||||
//std::ostringstream out;
|
||||
@@ -3073,11 +3094,13 @@ bool BGTactics::selectObjective(bool reset)
|
||||
{
|
||||
bool endBoss = false;
|
||||
// End boss
|
||||
if (alterValleyBG->GetAVNodeInfo(BG_AV_NODES_ICEBLOOD_TOWER).OwnerId != TEAM_HORDE && alterValleyBG->GetAVNodeInfo(BG_AV_NODES_TOWER_POINT).OwnerId != TEAM_HORDE &&
|
||||
alterValleyBG->GetAVNodeInfo(BG_AV_NODES_FROSTWOLF_ETOWER).OwnerId != TEAM_HORDE && alterValleyBG->GetAVNodeInfo(BG_AV_NODES_FROSTWOLF_WTOWER).OwnerId != TEAM_HORDE &&
|
||||
alterValleyBG->GetAVNodeInfo(BG_AV_NODES_FROSTWOLF_HUT).OwnerId != TEAM_HORDE)
|
||||
if (alterValleyBG->GetAVNodeInfo(BG_AV_NODES_ICEBLOOD_TOWER).TotalOwnerId != TEAM_HORDE &&
|
||||
alterValleyBG->GetAVNodeInfo(BG_AV_NODES_TOWER_POINT).TotalOwnerId != TEAM_HORDE &&
|
||||
alterValleyBG->GetAVNodeInfo(BG_AV_NODES_FROSTWOLF_ETOWER).TotalOwnerId != TEAM_HORDE &&
|
||||
alterValleyBG->GetAVNodeInfo(BG_AV_NODES_FROSTWOLF_WTOWER).TotalOwnerId != TEAM_HORDE &&
|
||||
alterValleyBG->GetAVNodeInfo(BG_AV_NODES_FROSTWOLF_HUT).TotalOwnerId != TEAM_HORDE)
|
||||
{
|
||||
if (Creature* pDrek = bg->GetBGCreature(AV_NPC_H_BOSS))
|
||||
if (Creature* pDrek = bg->GetBGCreature(AV_CPLACE_TRIGGER19))
|
||||
{
|
||||
BgObjective = pDrek;
|
||||
endBoss = true;
|
||||
@@ -3091,9 +3114,9 @@ bool BGTactics::selectObjective(bool reset)
|
||||
bool supporter = role < 3;
|
||||
|
||||
// Only go to Snowfall Graveyard if already close to it.
|
||||
if (!BgObjective && supporter && (alterValleyBG->GetAVNodeInfo(BG_AV_NODES_SNOWFALL_GRAVE).OwnerId == TEAM_HORDE ||
|
||||
alterValleyBG->GetAVNodeInfo(BG_AV_NODES_SNOWFALL_GRAVE).TotalOwnerId == TEAM_HORDE ||
|
||||
alterValleyBG->GetAVNodeInfo(BG_AV_NODES_SNOWFALL_GRAVE).TotalOwnerId == TEAM_OTHER))
|
||||
if (!BgObjective && supporter &&
|
||||
(alterValleyBG->GetAVNodeInfo(BG_AV_NODES_SNOWFALL_GRAVE).OwnerId == TEAM_HORDE ||
|
||||
alterValleyBG->GetAVNodeInfo(BG_AV_NODES_SNOWFALL_GRAVE).OwnerId == TEAM_OTHER))
|
||||
{
|
||||
if (GameObject* pGO = bg->GetBGObject(BG_AV_NODES_SNOWFALL_GRAVE))
|
||||
if (bot->IsWithinDist(pGO, 200.f))
|
||||
@@ -3110,9 +3133,9 @@ bool BGTactics::selectObjective(bool reset)
|
||||
{
|
||||
for (const auto& objective : AV_AllianceDefendObjectives)
|
||||
{
|
||||
if (!BgObjective && alterValleyBG->GetAVNodeInfo(objective).OwnerId == TEAM_HORDE)
|
||||
if (!BgObjective && alterValleyBG->GetAVNodeInfo(objective.first).OwnerId == TEAM_HORDE)
|
||||
{
|
||||
if (GameObject* pGO = bg->GetBGObject(objective))
|
||||
if (GameObject* pGO = bg->GetBGObject(objective.second))
|
||||
{
|
||||
BgObjective = pGO;
|
||||
//std::ostringstream out; out << "Defending Node #" << objective.first;
|
||||
@@ -3123,8 +3146,8 @@ bool BGTactics::selectObjective(bool reset)
|
||||
}
|
||||
|
||||
// Mine capture (need paths & script fix)
|
||||
if (!BgObjective && supporter && !endBoss && (alterValleyBG->GetMineOwner(AV_SOUTH_MINE) == TEAM_HORDE ||
|
||||
alterValleyBG->GetMineOwner(AV_SOUTH_MINE) == TEAM_OTHER) &&
|
||||
if (!BgObjective && supporter && !endBoss &&
|
||||
(alterValleyBG->GetMineOwner(AV_SOUTH_MINE) == TEAM_HORDE || alterValleyBG->GetMineOwner(AV_SOUTH_MINE) == TEAM_OTHER) &&
|
||||
alterValleyBG->GetAVNodeInfo(BG_AV_NODES_FROSTWOLF_GRAVE).TotalOwnerId != TEAM_HORDE)
|
||||
{
|
||||
if (Creature* mBossNeutral = bg->GetBGCreature(AV_CPLACE_MINE_S_3))
|
||||
@@ -3158,17 +3181,18 @@ bool BGTactics::selectObjective(bool reset)
|
||||
|
||||
if (alterValleyBG->IsCaptainAlive(1))
|
||||
{
|
||||
if (Creature* pGalvangar = bg->GetBGCreature(AV_NPC_H_CAPTAIN))
|
||||
if (Creature* pGalvangar = bg->GetBGCreature(AV_CPLACE_TRIGGER18))
|
||||
{
|
||||
uint32 attackCount = 0;
|
||||
attackCount += getDefendersCount(AV_ICEBLOOD_GARRISON_WAITING_ALLIANCE, 10.0f, false);
|
||||
uint32 attackCount = getDefendersCount(AV_ICEBLOOD_GARRISON_WAITING_ALLIANCE, 10.0f, false) + getDefendersCount(AV_ICEBLOOD_GARRISON_ATTACKING_ALLIANCE, 10.0f, false);
|
||||
|
||||
// prepare to attack Captain
|
||||
if (attackCount < 10 && !pGalvangar->IsInCombat())
|
||||
{
|
||||
// get in position to attack Captain
|
||||
pos.Set(AV_ICEBLOOD_GARRISON_WAITING_ALLIANCE.GetPositionX(), AV_ICEBLOOD_GARRISON_WAITING_ALLIANCE.GetPositionY(),
|
||||
AV_ICEBLOOD_GARRISON_WAITING_ALLIANCE.GetPositionZ(), bg->GetMapId());
|
||||
pos.Set(AV_ICEBLOOD_GARRISON_WAITING_ALLIANCE.GetPositionX(),
|
||||
AV_ICEBLOOD_GARRISON_WAITING_ALLIANCE.GetPositionY(),
|
||||
AV_ICEBLOOD_GARRISON_WAITING_ALLIANCE.GetPositionZ(),
|
||||
bg->GetMapId());
|
||||
|
||||
//std::ostringstream out;
|
||||
//out << "Taking position at Iceblood Outpost!";
|
||||
@@ -3176,6 +3200,12 @@ bool BGTactics::selectObjective(bool reset)
|
||||
}
|
||||
else
|
||||
{
|
||||
// they need help getting there (or did before I fixed the target creature, will leave in anyway, as it probably makes it more robust)
|
||||
pos.Set(AV_ICEBLOOD_GARRISON_ATTACKING_ALLIANCE.GetPositionX(),
|
||||
AV_ICEBLOOD_GARRISON_ATTACKING_ALLIANCE.GetPositionY(),
|
||||
AV_ICEBLOOD_GARRISON_ATTACKING_ALLIANCE.GetPositionZ(),
|
||||
bg->GetMapId());
|
||||
|
||||
//std::ostringstream out;
|
||||
// out << "Attacking Galvangar!";
|
||||
//bot->Say(out.str(), LANG_UNIVERSAL);
|
||||
@@ -3187,11 +3217,9 @@ bool BGTactics::selectObjective(bool reset)
|
||||
|
||||
for (const auto& objective : AV_AllianceAttackObjectives)
|
||||
{
|
||||
if (alterValleyBG->GetAVNodeInfo(objective).OwnerId == TEAM_HORDE ||
|
||||
alterValleyBG->GetAVNodeInfo(objective).TotalOwnerId == TEAM_HORDE ||
|
||||
alterValleyBG->GetAVNodeInfo(objective).TotalOwnerId == TEAM_OTHER)
|
||||
if (alterValleyBG->GetAVNodeInfo(objective.first).TotalOwnerId == TEAM_HORDE)//need to check TotalOwnerId for attack objectives
|
||||
{
|
||||
if (GameObject* pGO = bg->GetBGObject(objective))
|
||||
if (GameObject* pGO = bg->GetBGObject(objective.second))
|
||||
{
|
||||
float const distance = sqrt(bot->GetDistance(pGO));
|
||||
if (attackObjectiveDistance > distance)
|
||||
@@ -4087,7 +4115,8 @@ bool BGTactics::selectObjective(bool reset)
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return false;
|
||||
@@ -4124,7 +4153,7 @@ bool BGTactics::moveToObjective()
|
||||
}
|
||||
|
||||
// don't try to move if already close
|
||||
if (sqrt(bot->GetDistance(pos.x, pos.y, pos.z)) < 5.0f)
|
||||
if (sqrt(bot->GetDistance(pos.x, pos.y, pos.z)) < 2.0f)
|
||||
{
|
||||
resetObjective();
|
||||
|
||||
@@ -4134,8 +4163,8 @@ bool BGTactics::moveToObjective()
|
||||
//std::ostringstream out; out << "Moving to objective " << pos.x << ", " << pos.y << ", Distance: " << sServerFacade->GetDistance2d(bot, pos.x, pos.y);
|
||||
//bot->Say(out.str(), LANG_UNIVERSAL);
|
||||
|
||||
// more precise position for wsg
|
||||
if (bgType == BATTLEGROUND_WS)
|
||||
// more precise position for wsg and AV (flags in AV towers require precision)
|
||||
if (bgType == BATTLEGROUND_WS || bgType == BATTLEGROUND_AV)
|
||||
return MoveTo(bot->GetMapId(), pos.x, pos.y, pos.z);
|
||||
else
|
||||
return MoveNear(bot->GetMapId(), pos.x, pos.y, pos.z, 3.0f);
|
||||
@@ -4162,99 +4191,84 @@ bool BGTactics::selectObjectiveWp(std::vector<BattleBotPath*> const& vPaths)
|
||||
if (bgType == BATTLEGROUND_WS /* && (bot->HasAura(BG_WS_SPELL_WARSONG_FLAG) || bot->HasAura(BG_WS_SPELL_SILVERWING_FLAG))*/)
|
||||
return wsgPaths();
|
||||
|
||||
BattleBotPath* pClosestPath = nullptr;
|
||||
uint32 closestPoint = 0;
|
||||
float closestDistanceToTarget = FLT_MAX;
|
||||
bool reverse = false;
|
||||
float maxDistanceToPoint = 50.0f;
|
||||
if (bgType == BATTLEGROUND_IC)
|
||||
maxDistanceToPoint = 80.0f;
|
||||
float chosenPathScore = FLT_MAX;//lower score is better
|
||||
BattleBotPath* chosenPath = nullptr;
|
||||
uint32 chosenPathPoint = 0;
|
||||
bool chosenPathReverse = false;
|
||||
|
||||
for (auto const& pPath : vPaths)
|
||||
float botDistanceLimit = 50.0f; // limit for how far path can be from bot
|
||||
float botDistanceScoreSubtract = 8.0f; // path score modifier - lower = less likely to chose a further path (it's basically the distance from bot that's ignored)
|
||||
float botDistanceScoreMultiply = 3.0f; // path score modifier - higher = less likely to chose a further path (it's basically a multiplier on distance from bot - makes distance from bot more signifcant than distance from destination)
|
||||
|
||||
if (bgType == BATTLEGROUND_IC)
|
||||
botDistanceLimit = 80.0f;
|
||||
else if (bgType == BATTLEGROUND_AB)
|
||||
{
|
||||
botDistanceScoreSubtract = 2.0f;
|
||||
botDistanceScoreMultiply = 4.0f;
|
||||
}
|
||||
|
||||
for (auto const& path : vPaths)
|
||||
{
|
||||
// skip mine paths of own faction
|
||||
if (bot->GetTeamId() == TEAM_ALLIANCE && std::find(vPaths_AllyMine.begin(), vPaths_AllyMine.end(), pPath) != vPaths_AllyMine.end())
|
||||
if (bot->GetTeamId() == TEAM_ALLIANCE && std::find(vPaths_AllyMine.begin(), vPaths_AllyMine.end(), path) != vPaths_AllyMine.end())
|
||||
continue;
|
||||
|
||||
if (bot->GetTeamId() == TEAM_HORDE && std::find(vPaths_HordeMine.begin(), vPaths_HordeMine.end(), pPath) != vPaths_HordeMine.end())
|
||||
if (bot->GetTeamId() == TEAM_HORDE && std::find(vPaths_HordeMine.begin(), vPaths_HordeMine.end(), path) != vPaths_HordeMine.end())
|
||||
continue;
|
||||
|
||||
BattleBotWaypoint& lastPoint = ((*pPath)[pPath->size() - 1]);
|
||||
float const distanceFromPathEndToTarget = sqrt(Position(pos.x, pos.y, pos.z, 0.f).GetExactDist(lastPoint.x, lastPoint.y, lastPoint.z));
|
||||
if (closestDistanceToTarget > distanceFromPathEndToTarget)
|
||||
BattleBotWaypoint& startPoint = ((*path)[0]);
|
||||
float const startPointDistToDestination = sqrt(Position(pos.x, pos.y, pos.z, 0.f).GetExactDist(startPoint.x, startPoint.y, startPoint.z));
|
||||
BattleBotWaypoint& endPoint = ((*path)[path->size() - 1]);
|
||||
float const endPointDistToDestination = sqrt(Position(pos.x, pos.y, pos.z, 0.f).GetExactDist(endPoint.x, endPoint.y, endPoint.z));
|
||||
|
||||
bool reverse = startPointDistToDestination < endPointDistToDestination;
|
||||
|
||||
// dont travel reverse if it's a reverse paths
|
||||
if (reverse && std::find(vPaths_NoReverseAllowed.begin(), vPaths_NoReverseAllowed.end(), path) != vPaths_NoReverseAllowed.end())
|
||||
continue;
|
||||
|
||||
int closestPointIndex = -1;
|
||||
float closestPointDistToBot = FLT_MAX;
|
||||
for (uint32 i = 0; i < path->size(); i++)
|
||||
{
|
||||
float closestDistanceFromMeToPoint = FLT_MAX;
|
||||
|
||||
for (uint32 i = 0; i < pPath->size(); i++)
|
||||
BattleBotWaypoint& waypoint = ((*path)[i]);
|
||||
float const distToBot = sqrt(bot->GetDistance(waypoint.x, waypoint.y, waypoint.z));
|
||||
if (closestPointDistToBot > distToBot)
|
||||
{
|
||||
BattleBotWaypoint& waypoint = ((*pPath)[i]);
|
||||
float const distanceFromMeToPoint = sqrt(bot->GetDistance(waypoint.x, waypoint.y, waypoint.z));
|
||||
if (distanceFromMeToPoint < maxDistanceToPoint && closestDistanceFromMeToPoint > distanceFromMeToPoint)
|
||||
{
|
||||
reverse = false;
|
||||
pClosestPath = pPath;
|
||||
closestPoint = i;
|
||||
closestDistanceToTarget = distanceFromPathEndToTarget;
|
||||
closestDistanceFromMeToPoint = distanceFromMeToPoint;
|
||||
}
|
||||
closestPointDistToBot = distToBot;
|
||||
closestPointIndex = i;
|
||||
}
|
||||
}
|
||||
|
||||
// skip no reverse paths
|
||||
if (std::find(vPaths_NoReverseAllowed.begin(), vPaths_NoReverseAllowed.end(), pPath) != vPaths_NoReverseAllowed.end())
|
||||
// don't pick path where bot is already closest to the paths closest point to target (it means path cant lead it anywhere)
|
||||
// don't pick path where closest point is too far away
|
||||
if (closestPointIndex == (reverse ? 0 : path->size() - 1) || closestPointDistToBot > botDistanceLimit)
|
||||
continue;
|
||||
|
||||
// skip mine paths of own faction
|
||||
if (bot->GetTeamId() == TEAM_ALLIANCE && std::find(vPaths_AllyMine.begin(), vPaths_AllyMine.end(), pPath) != vPaths_AllyMine.end())
|
||||
continue;
|
||||
|
||||
if (bot->GetTeamId() == TEAM_HORDE && std::find(vPaths_HordeMine.begin(), vPaths_HordeMine.end(), pPath) != vPaths_HordeMine.end())
|
||||
continue;
|
||||
// creates a score based on dist-to-bot and dist-to-destination, where lower is better, and dist-to-bot is more important (when its beyond a certain distance)
|
||||
// dist-to-bot is more important because otherwise they cant reach it at all (or will fly through air with MM::MovePoint()), also bot may need to use multiple
|
||||
// paths (one after another) anyway
|
||||
float distToDestination = reverse ? startPointDistToDestination : endPointDistToDestination;
|
||||
float pathScore = (closestPointDistToBot < botDistanceScoreSubtract ? 0.0f : ((closestPointDistToBot - botDistanceScoreSubtract) * botDistanceScoreMultiply)) + distToDestination;
|
||||
|
||||
{
|
||||
BattleBotWaypoint& firstPoint = ((*pPath)[0]);
|
||||
float const distanceFromPathBeginToTarget = sqrt(Position(pos.x, pos.y, pos.z, 0).GetExactDist(firstPoint.x, firstPoint.y, firstPoint.z));
|
||||
if (closestDistanceToTarget > distanceFromPathBeginToTarget)
|
||||
{
|
||||
float closestDistanceFromMeToPoint = FLT_MAX;
|
||||
//LOG_INFO("playerbots", "bot={}\t{:6.1f}\t{:4.1f}\t{:4.1f}\t{}", bot->GetName(), pathScore, closestPointDistToBot, distToDestination, vPaths_AB_name[pathNum]);
|
||||
|
||||
for (uint32 i = 0; i < pPath->size(); i++)
|
||||
{
|
||||
BattleBotWaypoint& waypoint = ((*pPath)[i]);
|
||||
float const distanceFromMeToPoint = sqrt(bot->GetDistance(waypoint.x, waypoint.y, waypoint.z));
|
||||
if (distanceFromMeToPoint < maxDistanceToPoint && closestDistanceFromMeToPoint > distanceFromMeToPoint)
|
||||
{
|
||||
reverse = true;
|
||||
pClosestPath = pPath;
|
||||
closestPoint = i;
|
||||
closestDistanceToTarget = distanceFromPathBeginToTarget;
|
||||
closestDistanceFromMeToPoint = distanceFromMeToPoint;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (chosenPathScore > pathScore) {
|
||||
chosenPathScore = pathScore;
|
||||
chosenPath = path;
|
||||
chosenPathPoint = closestPointIndex;
|
||||
chosenPathReverse = reverse;
|
||||
}
|
||||
}
|
||||
|
||||
if (!pClosestPath)
|
||||
if (!chosenPath)
|
||||
return false;
|
||||
|
||||
// Prevent picking last point of path.
|
||||
// It means we are already there.
|
||||
if (reverse)
|
||||
{
|
||||
if (closestPoint == 0)
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (closestPoint == pClosestPath->size() - 1)
|
||||
return false;
|
||||
}
|
||||
//LOG_INFO("playerbots", "bot={} {}", bot->GetName(), vPaths_AB_name[chosenPathNum]);
|
||||
|
||||
BattleBotPath* currentPath = pClosestPath;
|
||||
uint32 currentPoint = reverse ? closestPoint + 1 : closestPoint - 1;
|
||||
|
||||
return moveToObjectiveWp(currentPath, currentPoint, reverse);
|
||||
return moveToObjectiveWp(chosenPath, chosenPathPoint, chosenPathReverse);
|
||||
|
||||
return false;
|
||||
}
|
||||
@@ -4490,7 +4504,7 @@ bool BGTactics::atFlag(std::vector<BattleBotPath*> const& vPaths, std::vector<ui
|
||||
if (f == vFlagIds.end())
|
||||
continue;
|
||||
|
||||
if (!go->isSpawned() || !go->GetGoState() == GO_STATE_READY)
|
||||
if (!go->isSpawned() || go->GetGoState() != GO_STATE_READY)
|
||||
continue;
|
||||
|
||||
if (!bot->CanUseBattlegroundObject(go) && bgType != BATTLEGROUND_WS)
|
||||
@@ -4631,6 +4645,8 @@ bool BGTactics::atFlag(std::vector<BattleBotPath*> const& vPaths, std::vector<ui
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4961,9 +4977,9 @@ bool ArenaTactics::moveToCenter(Battleground* bg)
|
||||
case BATTLEGROUND_DS:
|
||||
if (!MoveTo(bg->GetMapId(), 1291.58f + frand(-5, +5), 790.87f + frand(-5, +5), 7.8f, false, true)) {
|
||||
// they like to hang around at the tip of the pipes doing nothing, so we just teleport them down
|
||||
if (bot->GetDistance(1333.07f, 817.18f, 13.35f) < 2)
|
||||
if (bot->GetDistance(1333.07f, 817.18f, 13.35f) < 4)
|
||||
bot->TeleportTo(bg->GetMapId(), 1330.96f, 816.75f, 3.2f, bot->GetOrientation());
|
||||
if (bot->GetDistance(1250.13f, 764.79f, 13.34f) < 2)
|
||||
if (bot->GetDistance(1250.13f, 764.79f, 13.34f) < 4)
|
||||
bot->TeleportTo(bg->GetMapId(), 1252.19f, 765.41f, 3.2f, bot->GetOrientation());
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -91,6 +91,8 @@ bool BuyAction::Execute(Event event)
|
||||
case ITEM_USAGE_SKILL:
|
||||
needMoneyFor = NeedMoneyFor::tradeskill;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (needMoneyFor == NeedMoneyFor::none)
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
|
||||
uint32 FindLastSeparator(std::string const text, std::string const sep)
|
||||
{
|
||||
uint32 pos = text.rfind(sep);
|
||||
size_t pos = text.rfind(sep);
|
||||
if (pos == std::string::npos)
|
||||
return pos;
|
||||
|
||||
@@ -64,7 +64,7 @@ bool CastCustomSpellAction::Execute(Event event)
|
||||
|
||||
Item* itemTarget = nullptr;
|
||||
|
||||
uint32 pos = FindLastSeparator(text, " ");
|
||||
size_t pos = FindLastSeparator(text, " ");
|
||||
uint32 castCount = 1;
|
||||
if (pos != std::string::npos)
|
||||
{
|
||||
|
||||
@@ -44,9 +44,11 @@ bool ChangeTalentsAction::Execute(Event event)
|
||||
} else if (param.find("spec ") != std::string::npos) {
|
||||
param = param.substr(5);
|
||||
out << SpecPick(param);
|
||||
botAI->ResetStrategies();
|
||||
} else if (param.find("apply ") != std::string::npos) {
|
||||
param = param.substr(6);
|
||||
out << SpecApply(param);
|
||||
botAI->ResetStrategies();
|
||||
} else {
|
||||
out << "Unknown command.";
|
||||
}
|
||||
|
||||
@@ -46,19 +46,19 @@ void ChooseTravelTargetAction::getNewTarget(TravelTarget* newTarget, TravelTarge
|
||||
foundTarget = SetNpcFlagTarget(newTarget, { UNIT_NPC_FLAG_BANKER,UNIT_NPC_FLAG_BATTLEMASTER,UNIT_NPC_FLAG_AUCTIONEER });
|
||||
|
||||
//Grind for money
|
||||
if (!foundTarget && AI_VALUE(bool, "should get money"))
|
||||
if (!foundTarget && AI_VALUE(bool, "should get money")) {
|
||||
if (urand(1, 100) > 66)
|
||||
{
|
||||
foundTarget = SetQuestTarget(newTarget, true); //Turn in quests for money.
|
||||
|
||||
if (!foundTarget)
|
||||
foundTarget = SetQuestTarget(newTarget); //Do low level quests
|
||||
}
|
||||
else if (urand(1, 100) > 50)
|
||||
} else if (urand(1, 100) > 50) {
|
||||
foundTarget = SetGrindTarget(newTarget); //Go grind mobs for money
|
||||
else
|
||||
} else {
|
||||
foundTarget = SetNewQuestTarget(newTarget); //Find a low level quest to do
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
//Continue
|
||||
if (!foundTarget && urand(1, 100) > 10) //90% chance
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
bool CustomStrategyEditAction::Execute(Event event)
|
||||
{
|
||||
std::string text = event.getParam();
|
||||
uint32 pos = text.find(" ");
|
||||
size_t pos = text.find(" ");
|
||||
if (pos == std::string::npos)
|
||||
return PrintHelp();
|
||||
|
||||
|
||||
@@ -147,6 +147,8 @@ RollVote LootRollAction::CalculateRollVote(ItemTemplate const* proto)
|
||||
case ITEM_USAGE_VENDOR:
|
||||
needVote = GREED;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return StoreLootAction::IsLootAllowed(proto->ItemId, GET_PLAYERBOT_AI(bot)) ? needVote : PASS;
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
#include "MovementActions.h"
|
||||
#include "GameObject.h"
|
||||
#include "Geometry.h"
|
||||
#include "Map.h"
|
||||
#include "MotionMaster.h"
|
||||
#include "MoveSplineInitArgs.h"
|
||||
@@ -12,6 +13,7 @@
|
||||
#include "ObjectGuid.h"
|
||||
#include "PathGenerator.h"
|
||||
#include "PlayerbotAIConfig.h"
|
||||
#include "Position.h"
|
||||
#include "Random.h"
|
||||
#include "SharedDefines.h"
|
||||
#include "SpellAuraEffects.h"
|
||||
@@ -25,10 +27,15 @@
|
||||
#include "LootObjectStack.h"
|
||||
#include "Playerbots.h"
|
||||
#include "ServerFacade.h"
|
||||
#include "Timer.h"
|
||||
#include "Transport.h"
|
||||
#include "Unit.h"
|
||||
#include "Vehicle.h"
|
||||
#include "WaypointMovementGenerator.h"
|
||||
#include <cmath>
|
||||
#include <cstdlib>
|
||||
#include <iomanip>
|
||||
#include <string>
|
||||
|
||||
MovementAction::MovementAction(PlayerbotAI* botAI, std::string const name) : Action(botAI, name)
|
||||
{
|
||||
@@ -156,7 +163,9 @@ bool MovementAction::MoveTo(uint32 mapId, float x, float y, float z, bool idle,
|
||||
// }
|
||||
bool generatePath = !bot->HasAuraType(SPELL_AURA_MOD_INCREASE_MOUNTED_FLIGHT_SPEED) &&
|
||||
!bot->IsFlying() && !bot->HasUnitMovementFlag(MOVEMENTFLAG_SWIMMING) && !bot->IsInWater();
|
||||
if (!generatePath) {
|
||||
bool disableMoveSplinePath = sPlayerbotAIConfig->disableMoveSplinePath >= 2 ||
|
||||
(sPlayerbotAIConfig->disableMoveSplinePath == 1 && bot->InBattleground());
|
||||
if (disableMoveSplinePath || !generatePath) {
|
||||
float distance = bot->GetExactDist(x, y, z);
|
||||
if (distance > sPlayerbotAIConfig->contactDistance)
|
||||
{
|
||||
@@ -1495,6 +1504,9 @@ bool FleeWithPetAction::Execute(Event event)
|
||||
|
||||
bool AvoidAoeAction::isUseful()
|
||||
{
|
||||
if (getMSTime() - moveInterval < lastMoveTimer) {
|
||||
return false;
|
||||
}
|
||||
GuidVector traps = AI_VALUE(GuidVector, "nearest trap with damage");
|
||||
GuidVector triggers = AI_VALUE(GuidVector, "possible triggers");
|
||||
return AI_VALUE(Aura*, "area debuff") || !traps.empty() || !triggers.empty();
|
||||
@@ -1523,6 +1535,9 @@ bool AvoidAoeAction::AvoidAuraWithDynamicObj()
|
||||
if (!aura || aura->IsRemoved() || aura->IsExpired()) {
|
||||
return false;
|
||||
}
|
||||
if (!aura->GetOwner() || !aura->GetOwner()->IsInWorld()) {
|
||||
return false;
|
||||
}
|
||||
// Crash fix: maybe change owner due to check interval
|
||||
if (aura->GetType() != DYNOBJ_AURA_TYPE) {
|
||||
return false;
|
||||
@@ -1540,8 +1555,15 @@ bool AvoidAoeAction::AvoidAuraWithDynamicObj()
|
||||
return false;
|
||||
}
|
||||
std::ostringstream name;
|
||||
name << spellInfo->SpellName[0]; // << "] (aura)";
|
||||
if (FleePosition(dynOwner->GetPosition(), radius, name.str())) {
|
||||
name << spellInfo->SpellName[sWorld->GetDefaultDbcLocale()]; // << "] (aura)";
|
||||
if (FleePosition(dynOwner->GetPosition(), radius)) {
|
||||
if (sPlayerbotAIConfig->tellWhenAvoidAoe && lastTellTimer < time(NULL) - 10) {
|
||||
lastTellTimer = time(NULL);
|
||||
lastMoveTimer = getMSTime();
|
||||
std::ostringstream out;
|
||||
out << "I'm avoiding " << name.str() << "...";
|
||||
bot->Say(out.str(), LANG_UNIVERSAL);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@@ -1591,8 +1613,15 @@ bool AvoidAoeAction::AvoidGameObjectWithDamage()
|
||||
continue;
|
||||
}
|
||||
std::ostringstream name;
|
||||
name << spellInfo->SpellName[0]; // << "] (object)";
|
||||
if (FleePosition(go->GetPosition(), radius, name.str())) {
|
||||
name << spellInfo->SpellName[sWorld->GetDefaultDbcLocale()]; // << "] (object)";
|
||||
if (FleePosition(go->GetPosition(), radius)) {
|
||||
if (sPlayerbotAIConfig->tellWhenAvoidAoe && lastTellTimer < time(NULL) - 10) {
|
||||
lastTellTimer = time(NULL);
|
||||
lastMoveTimer = getMSTime();
|
||||
std::ostringstream out;
|
||||
out << "I'm avoiding " << name.str() << "...";
|
||||
bot->Say(out.str(), LANG_UNIVERSAL);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1633,9 +1662,15 @@ bool AvoidAoeAction::AvoidUnitWithDamageAura()
|
||||
break;
|
||||
}
|
||||
std::ostringstream name;
|
||||
name << triggerSpellInfo->SpellName[0]; //<< "] (unit)";
|
||||
if (FleePosition(unit->GetPosition(), radius, name.str())) {
|
||||
return true;
|
||||
name << triggerSpellInfo->SpellName[sWorld->GetDefaultDbcLocale()]; //<< "] (unit)";
|
||||
if (FleePosition(unit->GetPosition(), radius)) {
|
||||
if (sPlayerbotAIConfig->tellWhenAvoidAoe && lastTellTimer < time(NULL) - 10) {
|
||||
lastTellTimer = time(NULL);
|
||||
lastMoveTimer = getMSTime();
|
||||
std::ostringstream out;
|
||||
out << "I'm avoiding " << name.str() << "...";
|
||||
bot->Say(out.str(), LANG_UNIVERSAL);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1645,7 +1680,7 @@ bool AvoidAoeAction::AvoidUnitWithDamageAura()
|
||||
return false;
|
||||
}
|
||||
|
||||
Position AvoidAoeAction::BestPositionForMelee(Position pos, float radius)
|
||||
Position MovementAction::BestPositionForMeleeToFlee(Position pos, float radius)
|
||||
{
|
||||
Unit* currentTarget = AI_VALUE(Unit*, "current target");
|
||||
std::vector<CheckAngle> possibleAngles;
|
||||
@@ -1670,13 +1705,18 @@ Position AvoidAoeAction::BestPositionForMelee(Position pos, float radius)
|
||||
Position bestPos;
|
||||
for (CheckAngle &checkAngle : possibleAngles) {
|
||||
float angle = checkAngle.angle;
|
||||
auto& infoList = AI_VALUE_REF(std::list<FleeInfo>, "recently flee info");
|
||||
if (!CheckLastFlee(angle, infoList)) {
|
||||
continue;
|
||||
}
|
||||
bool strict = checkAngle.strict;
|
||||
float fleeDis = sPlayerbotAIConfig->fleeDistance;
|
||||
float fleeDis = std::min(radius + 1.0f, sPlayerbotAIConfig->fleeDistance);
|
||||
Position fleePos{bot->GetPositionX() + cos(angle) * fleeDis,
|
||||
bot->GetPositionY() + sin(angle) * fleeDis,
|
||||
bot->GetPositionZ()};
|
||||
if (strict && currentTarget
|
||||
&& fleePos.GetExactDist(currentTarget) - currentTarget->GetCombatReach() > sPlayerbotAIConfig->tooCloseDistance) {
|
||||
&& fleePos.GetExactDist(currentTarget) - currentTarget->GetCombatReach() > sPlayerbotAIConfig->tooCloseDistance
|
||||
&& bot->IsWithinMeleeRange(currentTarget)) {
|
||||
continue;
|
||||
}
|
||||
if (pos.GetExactDist(fleePos) > farestDis) {
|
||||
@@ -1690,7 +1730,7 @@ Position AvoidAoeAction::BestPositionForMelee(Position pos, float radius)
|
||||
return Position();
|
||||
}
|
||||
|
||||
Position AvoidAoeAction::BestPositionForRanged(Position pos, float radius)
|
||||
Position MovementAction::BestPositionForRangedToFlee(Position pos, float radius)
|
||||
{
|
||||
Unit* currentTarget = AI_VALUE(Unit*, "current target");
|
||||
std::vector<CheckAngle> possibleAngles;
|
||||
@@ -1713,8 +1753,12 @@ Position AvoidAoeAction::BestPositionForRanged(Position pos, float radius)
|
||||
Position bestPos;
|
||||
for (CheckAngle &checkAngle : possibleAngles) {
|
||||
float angle = checkAngle.angle;
|
||||
auto& infoList = AI_VALUE_REF(std::list<FleeInfo>, "recently flee info");
|
||||
if (!CheckLastFlee(angle, infoList)) {
|
||||
continue;
|
||||
}
|
||||
bool strict = checkAngle.strict;
|
||||
float fleeDis = sPlayerbotAIConfig->fleeDistance;
|
||||
float fleeDis = std::min(radius + 1.0f, sPlayerbotAIConfig->fleeDistance);
|
||||
Position fleePos{bot->GetPositionX() + cos(angle) * fleeDis,
|
||||
bot->GetPositionY() + sin(angle) * fleeDis,
|
||||
bot->GetPositionZ()};
|
||||
@@ -1737,28 +1781,197 @@ Position AvoidAoeAction::BestPositionForRanged(Position pos, float radius)
|
||||
return Position();
|
||||
}
|
||||
|
||||
bool AvoidAoeAction::FleePosition(Position pos, float radius, std::string name)
|
||||
bool MovementAction::FleePosition(Position pos, float radius)
|
||||
{
|
||||
Position bestPos;
|
||||
if (botAI->IsMelee(bot)) {
|
||||
bestPos = BestPositionForMelee(pos, radius);
|
||||
bestPos = BestPositionForMeleeToFlee(pos, radius);
|
||||
} else {
|
||||
bestPos = BestPositionForRanged(pos, radius);
|
||||
bestPos = BestPositionForRangedToFlee(pos, radius);
|
||||
}
|
||||
if (bestPos != Position()) {
|
||||
if (MoveTo(bot->GetMapId(), bestPos.GetPositionX(), bestPos.GetPositionY(), bestPos.GetPositionZ(), false, false, true)) {
|
||||
if (sPlayerbotAIConfig->tellWhenAvoidAoe && lastTellTimer < time(NULL) - 10) {
|
||||
lastTellTimer = time(NULL);
|
||||
std::ostringstream out;
|
||||
out << "I'm avoiding " << name << "...";
|
||||
bot->Say(out.str(), LANG_UNIVERSAL);
|
||||
auto& infoList = AI_VALUE_REF(std::list<FleeInfo>, "recently flee info");
|
||||
uint32 curTS = getMSTime();
|
||||
while (!infoList.empty()) {
|
||||
if (infoList.size() > 10 || infoList.front().timestamp + 5000 < curTS) {
|
||||
infoList.pop_front();
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
infoList.push_back({pos, radius, bot->GetAngle(&bestPos), curTS});
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool MovementAction::CheckLastFlee(float curAngle, std::list<FleeInfo>& infoList)
|
||||
{
|
||||
uint32 curTS = getMSTime();
|
||||
curAngle = fmod(curAngle, 2 * M_PI);
|
||||
while (!infoList.empty()) {
|
||||
if (infoList.size() > 10 || infoList.front().timestamp + 5000 < curTS) {
|
||||
infoList.pop_front();
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
for (FleeInfo& info : infoList) {
|
||||
// more than 5 sec
|
||||
if (info.timestamp + 5000 < curTS) {
|
||||
continue;
|
||||
}
|
||||
float revAngle = fmod(info.angle + M_PI, 2 * M_PI);
|
||||
// angle too close
|
||||
if (fabs(revAngle - curAngle) < M_PI / 4) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CombatFormationMoveAction::isUseful()
|
||||
{
|
||||
if (getMSTime() - moveInterval < lastMoveTimer) {
|
||||
return false;
|
||||
}
|
||||
if (bot->GetCurrentSpell(CURRENT_CHANNELED_SPELL) != nullptr) {
|
||||
return false;
|
||||
}
|
||||
float dis = AI_VALUE(float, "disperse distance");
|
||||
return dis > 0.0f;
|
||||
}
|
||||
|
||||
bool CombatFormationMoveAction::Execute(Event event)
|
||||
{
|
||||
float dis = AI_VALUE(float, "disperse distance");
|
||||
Player* playerToLeave = NearestGroupMember(dis);
|
||||
if (playerToLeave && bot->GetExactDist(playerToLeave) < dis) {
|
||||
if (FleePosition(playerToLeave->GetPosition(), dis)) {
|
||||
lastMoveTimer = getMSTime();
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
Position CombatFormationMoveAction::AverageGroupPos(float dis)
|
||||
{
|
||||
float averageX = 0, averageY = 0, averageZ = 0;
|
||||
int cnt = 0;
|
||||
Group* group = bot->GetGroup();
|
||||
if (!group) {
|
||||
return Position();
|
||||
}
|
||||
Group::MemberSlotList const& groupSlot = group->GetMemberSlots();
|
||||
for (Group::member_citerator itr = groupSlot.begin(); itr != groupSlot.end(); itr++)
|
||||
{
|
||||
Player *member = ObjectAccessor::FindPlayer(itr->guid);
|
||||
if (!member || !member->IsAlive() || member->GetMapId() != bot->GetMapId() || member->IsCharmed() || sServerFacade->GetDistance2d(bot, member) > dis)
|
||||
continue;
|
||||
cnt++;
|
||||
averageX += member->GetPositionX();
|
||||
averageY += member->GetPositionY();
|
||||
averageZ += member->GetPositionZ();
|
||||
}
|
||||
averageX /= cnt;
|
||||
averageY /= cnt;
|
||||
averageZ /= cnt;
|
||||
return Position(averageX, averageY, averageZ);
|
||||
}
|
||||
|
||||
Player* CombatFormationMoveAction::NearestGroupMember(float dis)
|
||||
{
|
||||
float nearestDis = 10000.0f;
|
||||
Player* result = nullptr;
|
||||
Group* group = bot->GetGroup();
|
||||
if (!group) {
|
||||
return result;
|
||||
}
|
||||
Group::MemberSlotList const& groupSlot = group->GetMemberSlots();
|
||||
for (Group::member_citerator itr = groupSlot.begin(); itr != groupSlot.end(); itr++)
|
||||
{
|
||||
Player *member = ObjectAccessor::FindPlayer(itr->guid);
|
||||
if (!member || !member->IsAlive() || member == bot || member->GetMapId() != bot->GetMapId() || member->IsCharmed() || sServerFacade->GetDistance2d(bot, member) > dis)
|
||||
continue;
|
||||
if (nearestDis > bot->GetExactDist(member)) {
|
||||
result = member;
|
||||
nearestDis = bot->GetExactDist(member);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
bool DisperseSetAction::Execute(Event event)
|
||||
{
|
||||
std::string const text = event.getParam();
|
||||
if (text == "disable") {
|
||||
RESET_AI_VALUE(float, "disperse distance");
|
||||
botAI->TellMasterNoFacing("Disable disperse");
|
||||
return true;
|
||||
}
|
||||
if (text == "enable" || text == "reset") {
|
||||
if (botAI->IsMelee(bot)) {
|
||||
SET_AI_VALUE(float, "disperse distance", DEFAULT_DISPERSE_DISTANCE_MELEE);
|
||||
} else {
|
||||
SET_AI_VALUE(float, "disperse distance", DEFAULT_DISPERSE_DISTANCE_RANGED);
|
||||
}
|
||||
float dis = AI_VALUE(float, "disperse distance");
|
||||
std::ostringstream out;
|
||||
out << "Enable disperse distance " << std::setprecision(2) << dis;
|
||||
botAI->TellMasterNoFacing(out.str());
|
||||
return true;
|
||||
}
|
||||
if (text == "increase") {
|
||||
float dis = AI_VALUE(float, "disperse distance");
|
||||
std::ostringstream out;
|
||||
if (dis <= 0.0f) {
|
||||
out << "Enable disperse first";
|
||||
botAI->TellMasterNoFacing(out.str());
|
||||
return true;
|
||||
}
|
||||
dis += 1.0f;
|
||||
SET_AI_VALUE(float, "disperse distance", dis);
|
||||
out << "Increase disperse distance to " << std::setprecision(2) << dis;
|
||||
botAI->TellMasterNoFacing(out.str());
|
||||
return true;
|
||||
}
|
||||
if (text == "decrease") {
|
||||
float dis = AI_VALUE(float, "disperse distance");
|
||||
dis -= 1.0f;
|
||||
if (dis <= 0.0f) {
|
||||
dis += 1.0f;
|
||||
}
|
||||
SET_AI_VALUE(float, "disperse distance", dis);
|
||||
std::ostringstream out;
|
||||
out << "Increase disperse distance to " << std::setprecision(2) << dis;
|
||||
botAI->TellMasterNoFacing(out.str());
|
||||
return true;
|
||||
}
|
||||
if (text.starts_with("set")) {
|
||||
float dis = -1.0f;;
|
||||
sscanf(text.c_str(), "set %f", &dis);
|
||||
std::ostringstream out;
|
||||
if (dis < 0 || dis > 100.0f) {
|
||||
out << "Invalid disperse distance " << std::setprecision(2) << dis;
|
||||
} else {
|
||||
SET_AI_VALUE(float, "disperse distance", dis);
|
||||
out << "Set disperse distance to " << std::setprecision(2) << dis;
|
||||
}
|
||||
botAI->TellMasterNoFacing(out.str());
|
||||
return true;
|
||||
}
|
||||
std::ostringstream out;
|
||||
out << "Usage: disperse [enable | disable | increase | decrease | set {distance}]";
|
||||
float dis = AI_VALUE(float, "disperse distance");
|
||||
if (dis > 0.0f) {
|
||||
out << "(Current disperse distance: " << std::setprecision(2) << dis << ")";
|
||||
}
|
||||
botAI->TellMasterNoFacing(out.str());
|
||||
return true;
|
||||
}
|
||||
|
||||
bool RunAwayAction::Execute(Event event)
|
||||
{
|
||||
return Flee(AI_VALUE(Unit*, "master target"));
|
||||
|
||||
@@ -13,6 +13,7 @@ class Player;
|
||||
class PlayerbotAI;
|
||||
class Unit;
|
||||
class WorldObject;
|
||||
class Position;
|
||||
|
||||
class MovementAction : public Action
|
||||
{
|
||||
@@ -41,6 +42,15 @@ class MovementAction : public Action
|
||||
bool MoveAway(Unit* target);
|
||||
bool MoveInside(uint32 mapId, float x, float y, float z, float distance = sPlayerbotAIConfig->followDistance);
|
||||
void CreateWp(Player* wpOwner, float x, float y, float z, float o, uint32 entry, bool important = false);
|
||||
Position BestPositionForMeleeToFlee(Position pos, float radius);
|
||||
Position BestPositionForRangedToFlee(Position pos, float radius);
|
||||
bool FleePosition(Position pos, float radius);
|
||||
bool CheckLastFlee(float curAngle, std::list<FleeInfo>& infoList);
|
||||
protected:
|
||||
struct CheckAngle {
|
||||
float angle;
|
||||
bool strict;
|
||||
};
|
||||
private:
|
||||
// float SearchBestGroundZForPath(float x, float y, float z, bool generatePath, float range = 20.0f, bool normal_only = false, float step = 8.0f);
|
||||
const Movement::PointsArray SearchForBestPath(float x, float y, float z, float &modified_z, int maxSearchCount = 5, bool normal_only = false, float step = 8.0f);
|
||||
@@ -69,7 +79,8 @@ class FleeWithPetAction : public MovementAction
|
||||
class AvoidAoeAction : public MovementAction
|
||||
{
|
||||
public:
|
||||
AvoidAoeAction(PlayerbotAI* botAI) : MovementAction(botAI, "avoid aoe") { }
|
||||
AvoidAoeAction(PlayerbotAI* botAI, int moveInterval = 1000) : MovementAction(botAI, "avoid aoe"),
|
||||
moveInterval(moveInterval) { }
|
||||
|
||||
bool isUseful() override;
|
||||
bool Execute(Event event) override;
|
||||
@@ -78,14 +89,36 @@ class AvoidAoeAction : public MovementAction
|
||||
bool AvoidAuraWithDynamicObj();
|
||||
bool AvoidGameObjectWithDamage();
|
||||
bool AvoidUnitWithDamageAura();
|
||||
Position BestPositionForMelee(Position pos, float radius);
|
||||
Position BestPositionForRanged(Position pos, float radius);
|
||||
bool FleePosition(Position pos, float radius, std::string name);
|
||||
time_t lastTellTimer = 0;
|
||||
struct CheckAngle {
|
||||
float angle;
|
||||
bool strict;
|
||||
};
|
||||
int lastMoveTimer = 0;
|
||||
int moveInterval;
|
||||
|
||||
};
|
||||
|
||||
class CombatFormationMoveAction : public MovementAction
|
||||
{
|
||||
public:
|
||||
CombatFormationMoveAction(PlayerbotAI* botAI, int moveInterval = 1000) : MovementAction(botAI, "combat formation move"),
|
||||
moveInterval(moveInterval) { }
|
||||
|
||||
bool isUseful() override;
|
||||
bool Execute(Event event) override;
|
||||
|
||||
protected:
|
||||
Position AverageGroupPos(float dis = sPlayerbotAIConfig->sightDistance);
|
||||
Player* NearestGroupMember(float dis = sPlayerbotAIConfig->sightDistance);
|
||||
int lastMoveTimer = 0;
|
||||
int moveInterval;
|
||||
};
|
||||
|
||||
class DisperseSetAction : public Action
|
||||
{
|
||||
public:
|
||||
DisperseSetAction(PlayerbotAI* botAI, std::string const name = "disperse set") : Action(botAI, name) { }
|
||||
|
||||
bool Execute(Event event) override;
|
||||
float DEFAULT_DISPERSE_DISTANCE_RANGED = 5.0f;
|
||||
float DEFAULT_DISPERSE_DISTANCE_MELEE = 2.0f;
|
||||
};
|
||||
|
||||
class RunAwayAction : public MovementAction
|
||||
|
||||
@@ -17,7 +17,7 @@ bool RangeAction::Execute(Event event)
|
||||
PrintRange("flee");
|
||||
}
|
||||
|
||||
uint32 pos = param.find(" ");
|
||||
size_t pos = param.find(" ");
|
||||
if (pos == std::string::npos)
|
||||
return false;
|
||||
|
||||
|
||||
@@ -13,7 +13,6 @@ bool StayActionBase::Stay()
|
||||
|
||||
//if (!urand(0, 10))
|
||||
//botAI->PlaySound(TEXT_EMOTE_YAWN);
|
||||
|
||||
if (bot->GetMotionMaster()->GetCurrentMovementGeneratorType() == FLIGHT_MOTION_TYPE)
|
||||
return false;
|
||||
|
||||
@@ -26,12 +25,13 @@ bool StayActionBase::Stay()
|
||||
context->GetValue<time_t>("stay time")->Set(stayTime);
|
||||
}
|
||||
|
||||
if (!bot->isMoving())
|
||||
return false;
|
||||
|
||||
bot->StopMoving();
|
||||
bot->ClearUnitState(UNIT_STATE_CHASE);
|
||||
bot->ClearUnitState(UNIT_STATE_FOLLOW);
|
||||
// Stop the bot from moving immediately when action is called
|
||||
if (bot->isMoving())
|
||||
{
|
||||
bot->StopMoving();
|
||||
bot->ClearUnitState(UNIT_STATE_CHASE);
|
||||
bot->ClearUnitState(UNIT_STATE_FOLLOW);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -43,7 +43,8 @@ bool StayAction::Execute(Event event)
|
||||
|
||||
bool StayAction::isUseful()
|
||||
{
|
||||
return !AI_VALUE2(bool, "moving", "self target");
|
||||
// Only useful if the bot is currently moving
|
||||
return AI_VALUE2(bool, "moving", "self target");
|
||||
}
|
||||
|
||||
bool SitAction::Execute(Event event)
|
||||
|
||||
@@ -53,6 +53,8 @@ void TalkToQuestGiverAction::ProcessQuest(Quest const* quest, Object* questGiver
|
||||
case QUEST_STATUS_FAILED:
|
||||
out << "|cffff0000Failed|r";
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
out << ": " << chat->FormatQuest(quest);
|
||||
@@ -257,6 +259,8 @@ bool TurnInQueryQuestAction::Execute(Event event)
|
||||
case QUEST_STATUS_REWARDED:
|
||||
out << "|cffff0000Rewarded|r";
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
out << ": " << chat->FormatQuest(quest);
|
||||
|
||||
@@ -198,6 +198,7 @@ bool AutoGearAction::Execute(Event event)
|
||||
sPlayerbotAIConfig->autoGearQualityLimit,
|
||||
gs);
|
||||
factory.InitEquipment(true);
|
||||
factory.InitAmmo();
|
||||
if (bot->getLevel() >= sPlayerbotAIConfig->minEnchantingBotLevel) {
|
||||
factory.ApplyEnchantAndGemsNew();
|
||||
}
|
||||
|
||||
@@ -82,7 +82,8 @@ bool SummonAction::Execute(Event event)
|
||||
}
|
||||
|
||||
if (master->GetSession()->GetSecurity() >= SEC_PLAYER) {
|
||||
botAI->GetAiObjectContext()->GetValue<GuidVector>("prioritized targets")->Set({});
|
||||
// botAI->GetAiObjectContext()->GetValue<GuidVector>("prioritized targets")->Set({});
|
||||
SET_AI_VALUE(std::list<FleeInfo>, "recently flee info", {});
|
||||
return Teleport(master, bot);
|
||||
}
|
||||
|
||||
@@ -175,11 +176,30 @@ bool SummonAction::Teleport(Player* summoner, Player* player)
|
||||
|
||||
if (summoner->IsWithinLOS(x, y, z))
|
||||
{
|
||||
bool allowed = sPlayerbotAIConfig->botReviveWhenSummon == 2 || (sPlayerbotAIConfig->botReviveWhenSummon == 1 && !master->IsInCombat() && master->IsAlive());
|
||||
if (allowed && bot->isDead())
|
||||
if (sPlayerbotAIConfig->botRepairWhenSummon) // .conf option to repair bot gear when summoned 0 = off, 1 = on
|
||||
bot->DurabilityRepairAll(false, 1.0f, false);
|
||||
|
||||
if (master->IsInCombat() && !sPlayerbotAIConfig->allowSummonInCombat)
|
||||
{
|
||||
botAI->TellError("You cannot summon me while you're in combat");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!master->IsAlive() && !sPlayerbotAIConfig->allowSummonWhenMasterIsDead)
|
||||
{
|
||||
botAI->TellError("You cannot summon me while you're dead");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (bot->isDead() && !bot->HasPlayerFlag(PLAYER_FLAGS_GHOST) && !sPlayerbotAIConfig->allowSummonWhenBotIsDead)
|
||||
{
|
||||
botAI->TellError("You cannot summon me while I'm dead, you need to release my spirit first");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (bot->isDead() && sPlayerbotAIConfig->reviveBotWhenSummoned)
|
||||
{
|
||||
bot->ResurrectPlayer(1.0f, false);
|
||||
bot->DurabilityRepairAll(false, 1.0f, false);
|
||||
botAI->TellMasterNoFacing("I live, again!");
|
||||
}
|
||||
|
||||
|
||||
@@ -11,8 +11,8 @@
|
||||
#ifndef WIN32
|
||||
inline int strcmpi(char const* s1, char const* s2)
|
||||
{
|
||||
for (; *s1 && *s2 && (toupper(*s1) == toupper(*s2)); ++s1, ++s2);
|
||||
return *s1 - *s2;
|
||||
for (; *s1 && *s2 && (toupper(*s1) == toupper(*s2)); ++s1, ++s2) {}
|
||||
return *s1 - *s2;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
@@ -234,8 +234,6 @@ class CastDeathAndDecayAction : public CastSpellAction
|
||||
{
|
||||
public:
|
||||
CastDeathAndDecayAction(PlayerbotAI* botAI) : CastSpellAction(botAI, "death and decay") { }
|
||||
|
||||
ActionThreatType getThreatType() override { return ActionThreatType::Aoe; }
|
||||
};
|
||||
|
||||
class CastHornOfWinterAction : public CastSpellAction
|
||||
|
||||
@@ -67,7 +67,7 @@ class DeathKnightTriggerFactoryInternal : public NamedObjectContext<Trigger>
|
||||
DeathKnightTriggerFactoryInternal()
|
||||
{
|
||||
creators["bone shield"] = &DeathKnightTriggerFactoryInternal::bone_shield;
|
||||
creators["pestilence"] = &DeathKnightTriggerFactoryInternal::pestilence;
|
||||
creators["pestilence glyph"] = &DeathKnightTriggerFactoryInternal::pestilence_glyph;
|
||||
creators["blood strike"] = &DeathKnightTriggerFactoryInternal::blood_strike;
|
||||
creators["plague strike"] = &DeathKnightTriggerFactoryInternal::plague_strike;
|
||||
creators["plague strike on attacker"] = &DeathKnightTriggerFactoryInternal::plague_strike_on_attacker;
|
||||
@@ -94,7 +94,7 @@ class DeathKnightTriggerFactoryInternal : public NamedObjectContext<Trigger>
|
||||
|
||||
private:
|
||||
static Trigger* bone_shield(PlayerbotAI* botAI) { return new BoneShieldTrigger(botAI); }
|
||||
static Trigger* pestilence(PlayerbotAI* botAI) { return new PestilenceTrigger(botAI); }
|
||||
static Trigger* pestilence_glyph(PlayerbotAI* botAI) { return new PestilenceGlyphTrigger(botAI); }
|
||||
static Trigger* blood_strike(PlayerbotAI* botAI) { return new BloodStrikeTrigger(botAI); }
|
||||
static Trigger* plague_strike(PlayerbotAI* botAI) { return new PlagueStrikeDebuffTrigger(botAI); }
|
||||
static Trigger* plague_strike_on_attacker(PlayerbotAI* botAI) { return new PlagueStrikeDebuffOnAttackerTrigger(botAI); }
|
||||
|
||||
@@ -14,7 +14,7 @@ bool DKPresenceTrigger::IsActive()
|
||||
return !botAI->HasAura("blood presence", target) && !botAI->HasAura("unholy presence", target) && !botAI->HasAura("frost presence", target);
|
||||
}
|
||||
|
||||
bool PestilenceTrigger::IsActive() {
|
||||
bool PestilenceGlyphTrigger::IsActive() {
|
||||
if (!SpellTrigger::IsActive()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -71,10 +71,10 @@ class DeathCoilTrigger : public SpellCanBeCastTrigger
|
||||
DeathCoilTrigger(PlayerbotAI* botAI) : SpellCanBeCastTrigger(botAI, "death coil") { }
|
||||
};
|
||||
|
||||
class PestilenceTrigger : public DebuffTrigger
|
||||
class PestilenceGlyphTrigger : public SpellTrigger
|
||||
{
|
||||
public:
|
||||
PestilenceTrigger(PlayerbotAI* botAI) : DebuffTrigger(botAI, "pestilence") { }
|
||||
PestilenceGlyphTrigger(PlayerbotAI* botAI) : SpellTrigger(botAI, "pestilence") { }
|
||||
virtual bool IsActive() override;
|
||||
};
|
||||
|
||||
|
||||
@@ -192,5 +192,5 @@ void GenericDKStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
||||
// triggers.push_back(new TriggerNode("light aoe", NextAction::array(0,
|
||||
// new NextAction("pestilence", ACTION_NORMAL + 4),
|
||||
// nullptr)));
|
||||
triggers.push_back(new TriggerNode("pestilence", NextAction::array(0, new NextAction("pestilence", ACTION_HIGH + 9), NULL)));
|
||||
triggers.push_back(new TriggerNode("pestilence glyph", NextAction::array(0, new NextAction("pestilence", ACTION_HIGH + 9), NULL)));
|
||||
}
|
||||
|
||||
@@ -69,7 +69,7 @@ class BearTankDruidStrategyActionNodeFactory : public NamedObjectFactory<ActionN
|
||||
static ActionNode* dire_bear_form([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode ("dire bear form",
|
||||
/*P*/ nullptr,
|
||||
/*P*/ NextAction::array(0, new NextAction("caster form"), nullptr),
|
||||
/*A*/ NextAction::array(0, new NextAction("bear form"), nullptr),
|
||||
/*C*/ nullptr);
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@ class CasterDruidStrategyActionNodeFactory : public NamedObjectFactory<ActionNod
|
||||
creators["insect swarm"] = &insect_swarm;
|
||||
creators["moonfire"] = &moonfire;
|
||||
creators["starfire"] = &starfire;
|
||||
creators["moonkin form"] = &moonkin_form;
|
||||
}
|
||||
|
||||
private:
|
||||
@@ -94,6 +95,15 @@ class CasterDruidStrategyActionNodeFactory : public NamedObjectFactory<ActionNod
|
||||
/*A*/ nullptr,
|
||||
/*C*/ nullptr);
|
||||
}
|
||||
|
||||
static ActionNode* moonkin_form([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode ("moonkin form",
|
||||
/*P*/ NextAction::array(0, new NextAction("caster form"), nullptr),
|
||||
/*A*/ nullptr,
|
||||
/*C*/ nullptr);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
CasterDruidStrategy::CasterDruidStrategy(PlayerbotAI* botAI) : GenericDruidStrategy(botAI)
|
||||
|
||||
@@ -51,7 +51,7 @@ class CatDpsDruidStrategyActionNodeFactory : public NamedObjectFactory<ActionNod
|
||||
static ActionNode* cat_form([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode ("cat form",
|
||||
/*P*/ nullptr,
|
||||
/*P*/ NextAction::array(0, new NextAction("caster form"), nullptr),
|
||||
/*A*/ nullptr,
|
||||
/*C*/ nullptr);
|
||||
}
|
||||
|
||||
@@ -61,6 +61,7 @@ void ChatCommandHandlerStrategy::InitTriggers(std::vector<TriggerNode*>& trigger
|
||||
triggers.push_back(new TriggerNode("naxx", NextAction::array(0, new NextAction("naxx chat shortcut", relevance), NULL)));
|
||||
triggers.push_back(new TriggerNode("bwl", NextAction::array(0, new NextAction("bwl chat shortcut", relevance), NULL)));
|
||||
triggers.push_back(new TriggerNode("dps", NextAction::array(0, new NextAction("tell expected dps", relevance), NULL)));
|
||||
triggers.push_back(new TriggerNode("disperse", NextAction::array(0, new NextAction("disperse set", relevance), NULL)));
|
||||
}
|
||||
|
||||
ChatCommandHandlerStrategy::ChatCommandHandlerStrategy(PlayerbotAI* botAI) : PassTroughStrategy(botAI)
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
|
||||
void CombatStrategy::InitTriggers(std::vector<TriggerNode*> &triggers)
|
||||
{
|
||||
triggers.push_back(new TriggerNode("enemy out of spell", NextAction::array(0, new NextAction("reach spell", ACTION_MOVE + 11), nullptr)));
|
||||
triggers.push_back(new TriggerNode("enemy out of spell", NextAction::array(0, new NextAction("reach spell", ACTION_HIGH), nullptr)));
|
||||
triggers.push_back(new TriggerNode("invalid target", NextAction::array(0, new NextAction("drop target", 100), nullptr)));
|
||||
triggers.push_back(new TriggerNode("mounted", NextAction::array(0, new NextAction("check mount state", 54), nullptr)));
|
||||
// triggers.push_back(new TriggerNode("out of react range", NextAction::array(0, new NextAction("flee to master", 55), nullptr)));
|
||||
@@ -24,44 +24,44 @@ AvoidAoeStrategy::AvoidAoeStrategy(PlayerbotAI* botAI) : Strategy(botAI)
|
||||
}
|
||||
|
||||
|
||||
class AvoidAoeStrategyMultiplier : public Multiplier
|
||||
{
|
||||
public:
|
||||
AvoidAoeStrategyMultiplier(PlayerbotAI* botAI) : Multiplier(botAI, "run away on area debuff") {}
|
||||
// class AvoidAoeStrategyMultiplier : public Multiplier
|
||||
// {
|
||||
// public:
|
||||
// AvoidAoeStrategyMultiplier(PlayerbotAI* botAI) : Multiplier(botAI, "run away on area debuff") {}
|
||||
|
||||
public:
|
||||
virtual float GetValue(Action* action);
|
||||
// public:
|
||||
// virtual float GetValue(Action* action);
|
||||
|
||||
private:
|
||||
};
|
||||
// private:
|
||||
// };
|
||||
|
||||
float AvoidAoeStrategyMultiplier::GetValue(Action* action)
|
||||
{
|
||||
if (!action)
|
||||
return 1.0f;
|
||||
// float AvoidAoeStrategyMultiplier::GetValue(Action* action)
|
||||
// {
|
||||
// if (!action)
|
||||
// return 1.0f;
|
||||
|
||||
std::string name = action->getName();
|
||||
if (name == "follow" || name == "co" || name == "nc" || name == "drop target")
|
||||
return 1.0f;
|
||||
// std::string name = action->getName();
|
||||
// if (name == "follow" || name == "co" || name == "nc" || name == "drop target")
|
||||
// return 1.0f;
|
||||
|
||||
uint32 spellId = AI_VALUE2(uint32, "spell id", name);
|
||||
const SpellInfo* const pSpellInfo = sSpellMgr->GetSpellInfo(spellId);
|
||||
if (!pSpellInfo) return 1.0f;
|
||||
// uint32 spellId = AI_VALUE2(uint32, "spell id", name);
|
||||
// const SpellInfo* const pSpellInfo = sSpellMgr->GetSpellInfo(spellId);
|
||||
// if (!pSpellInfo) return 1.0f;
|
||||
|
||||
if (spellId && pSpellInfo->Targets & TARGET_FLAG_DEST_LOCATION)
|
||||
return 1.0f;
|
||||
else if (spellId && pSpellInfo->Targets & TARGET_FLAG_SOURCE_LOCATION)
|
||||
return 1.0f;
|
||||
// if (spellId && pSpellInfo->Targets & TARGET_FLAG_DEST_LOCATION)
|
||||
// return 1.0f;
|
||||
// else if (spellId && pSpellInfo->Targets & TARGET_FLAG_SOURCE_LOCATION)
|
||||
// return 1.0f;
|
||||
|
||||
uint32 castTime = pSpellInfo->CalcCastTime(bot);
|
||||
// uint32 castTime = pSpellInfo->CalcCastTime(bot);
|
||||
|
||||
if (AI_VALUE2(bool, "has area debuff", "self target") && spellId && castTime > 0)
|
||||
{
|
||||
return 0.0f;
|
||||
}
|
||||
// if (AI_VALUE2(bool, "has area debuff", "self target") && spellId && castTime > 0)
|
||||
// {
|
||||
// return 0.0f;
|
||||
// }
|
||||
|
||||
return 1.0f;
|
||||
}
|
||||
// return 1.0f;
|
||||
// }
|
||||
|
||||
NextAction** AvoidAoeStrategy::getDefaultActions()
|
||||
{
|
||||
@@ -81,4 +81,11 @@ void AvoidAoeStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
||||
void AvoidAoeStrategy::InitMultipliers(std::vector<Multiplier*>& multipliers)
|
||||
{
|
||||
// multipliers.push_back(new AvoidAoeStrategyMultiplier(botAI));
|
||||
}
|
||||
}
|
||||
|
||||
NextAction** CombatFormationStrategy::getDefaultActions()
|
||||
{
|
||||
return NextAction::array(0,
|
||||
new NextAction("combat formation move", ACTION_NORMAL),
|
||||
nullptr);
|
||||
}
|
||||
|
||||
@@ -28,4 +28,12 @@ public:
|
||||
void InitTriggers(std::vector<TriggerNode*>& triggers) override;
|
||||
};
|
||||
|
||||
class CombatFormationStrategy : public Strategy
|
||||
{
|
||||
public:
|
||||
CombatFormationStrategy(PlayerbotAI* ai): Strategy(ai) {}
|
||||
const std::string getName() override { return "combat formation"; }
|
||||
NextAction** getDefaultActions() override;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -37,9 +37,10 @@ void WorldPacketHandlerStrategy::InitTriggers(std::vector<TriggerNode*>& trigger
|
||||
triggers.push_back(new TriggerNode("bg status", NextAction::array(0, new NextAction("bg status", relevance), nullptr)));
|
||||
triggers.push_back(new TriggerNode("xpgain", NextAction::array(0, new NextAction("xp gain", relevance), nullptr)));
|
||||
triggers.push_back(new TriggerNode("levelup", NextAction::array(0,
|
||||
new NextAction("auto talents", relevance),
|
||||
new NextAction("auto learn spell", relevance),
|
||||
new NextAction("auto teleport for level", relevance),
|
||||
new NextAction("auto teleport for level", relevance + 3),
|
||||
new NextAction("auto talents", relevance + 2),
|
||||
new NextAction("auto learn spell", relevance + 1),
|
||||
new NextAction("auto upgrade equip", relevance),
|
||||
nullptr)));
|
||||
// triggers.push_back(new TriggerNode("group destroyed", NextAction::array(0, new NextAction("reset botAI", relevance), nullptr)));
|
||||
triggers.push_back(new TriggerNode("questgiver quest details", NextAction::array(0, new NextAction("turn in query quest", relevance), nullptr)));
|
||||
|
||||
@@ -203,12 +203,14 @@ class CastDragonsBreathAction : public CastSpellAction
|
||||
{
|
||||
public:
|
||||
CastDragonsBreathAction(PlayerbotAI* botAI) : CastSpellAction(botAI, "dragon's breath") { }
|
||||
ActionThreatType getThreatType() override { return ActionThreatType::Aoe; }
|
||||
};
|
||||
|
||||
class CastBlastWaveAction : public CastSpellAction
|
||||
{
|
||||
public:
|
||||
CastBlastWaveAction(PlayerbotAI* botAI) : CastSpellAction(botAI, "blast wave") { }
|
||||
ActionThreatType getThreatType() override { return ActionThreatType::Aoe; }
|
||||
};
|
||||
|
||||
class CastInvisibilityAction : public CastBuffSpellAction
|
||||
|
||||
@@ -153,7 +153,7 @@ bool CastMeleeConsecrationAction::isUseful()
|
||||
{
|
||||
Unit* target = GetTarget();
|
||||
// float dis = distance + CONTACT_DISTANCE;
|
||||
return target && bot->IsWithinCombatRange(target, sPlayerbotAIConfig->meleeDistance); // sServerFacade->IsDistanceGreaterThan(AI_VALUE2(float, "distance", GetTargetName()), distance);
|
||||
return target && bot->IsWithinMeleeRange(target); // sServerFacade->IsDistanceGreaterThan(AI_VALUE2(float, "distance", GetTargetName()), distance);
|
||||
}
|
||||
|
||||
bool CastDivineSacrificeAction::isUseful()
|
||||
|
||||
@@ -94,6 +94,9 @@ void TankPaladinStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
||||
triggers.push_back(new TriggerNode(
|
||||
"medium group heal occasion",
|
||||
NextAction::array(0, new NextAction("divine sacrifice", ACTION_HIGH + 5), nullptr)));
|
||||
triggers.push_back(new TriggerNode(
|
||||
"enough mana",
|
||||
NextAction::array(0, new NextAction("melee consecration", ACTION_HIGH + 4), nullptr)));
|
||||
triggers.push_back(new TriggerNode(
|
||||
"not facing target",
|
||||
NextAction::array(0, new NextAction("set facing", ACTION_NORMAL + 7), nullptr)));
|
||||
|
||||
@@ -161,5 +161,6 @@ class CastMindSearAction : public CastSpellAction
|
||||
{
|
||||
public:
|
||||
CastMindSearAction(PlayerbotAI* ai) : CastSpellAction(ai, "mind sear") {}
|
||||
ActionThreatType getThreatType() override { return ActionThreatType::Aoe; }
|
||||
};
|
||||
#endif
|
||||
|
||||
@@ -49,9 +49,11 @@ void CasterShamanStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
||||
GenericShamanStrategy::InitTriggers(triggers);
|
||||
|
||||
// triggers.push_back(new TriggerNode("enemy out of spell", NextAction::array(0, new NextAction("reach spell", ACTION_NORMAL + 9), nullptr)));
|
||||
triggers.push_back(new TriggerNode("shaman weapon", NextAction::array(0, new NextAction("flametongue weapon", 23.0f), nullptr)));
|
||||
// triggers.push_back(new TriggerNode("shaman weapon", NextAction::array(0, new NextAction("flametongue weapon", 23.0f), nullptr)));
|
||||
triggers.push_back(new TriggerNode("main hand weapon no imbue", NextAction::array(0, new NextAction("flametongue weapon", 22.0f), nullptr)));
|
||||
// triggers.push_back(new TriggerNode("searing totem", NextAction::array(0, new NextAction("searing totem", 19.0f), nullptr)));
|
||||
triggers.push_back(new TriggerNode("flame shock", NextAction::array(0, new NextAction("flame shock", 20.0f), nullptr)));
|
||||
triggers.push_back(new TriggerNode("elemental mastery", NextAction::array(0, new NextAction("elemental mastery", 27.0f), nullptr)));
|
||||
// triggers.push_back(new TriggerNode("frost shock snare", NextAction::array(0, new NextAction("frost shock", 21.0f), nullptr)));
|
||||
triggers.push_back(new TriggerNode(
|
||||
"no fire totem",
|
||||
|
||||
@@ -35,7 +35,7 @@ class GenericShamanStrategyActionNodeFactory : public NamedObjectFactory<ActionN
|
||||
{
|
||||
return new ActionNode ("flametongue weapon",
|
||||
/*P*/ nullptr,
|
||||
/*A*/ NextAction::array(0, new NextAction("frostbrand weapon"), nullptr),
|
||||
/*A*/ NextAction::array(0, new NextAction("flametongue weapon"), nullptr),
|
||||
/*C*/ nullptr);
|
||||
}
|
||||
|
||||
@@ -43,7 +43,7 @@ class GenericShamanStrategyActionNodeFactory : public NamedObjectFactory<ActionN
|
||||
{
|
||||
return new ActionNode ("frostbrand weapon",
|
||||
/*P*/ nullptr,
|
||||
/*A*/ NextAction::array(0, new NextAction("rockbiter weapon"), nullptr),
|
||||
/*A*/ NextAction::array(0, new NextAction("frostbrand weapon"), nullptr),
|
||||
/*C*/ nullptr);
|
||||
}
|
||||
|
||||
@@ -51,7 +51,7 @@ class GenericShamanStrategyActionNodeFactory : public NamedObjectFactory<ActionN
|
||||
{
|
||||
return new ActionNode ("windfury weapon",
|
||||
/*P*/ nullptr,
|
||||
/*A*/ NextAction::array(0, new NextAction("rockbiter weapon"), nullptr),
|
||||
/*A*/ NextAction::array(0, new NextAction("windfury weapon"), nullptr),
|
||||
/*C*/ nullptr);
|
||||
}
|
||||
|
||||
|
||||
@@ -42,7 +42,8 @@ void HealShamanStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
||||
GenericShamanStrategy::InitTriggers(triggers);
|
||||
|
||||
// triggers.push_back(new TriggerNode("enemy out of spell", NextAction::array(0, new NextAction("reach spell", ACTION_NORMAL + 9), nullptr)));
|
||||
triggers.push_back(new TriggerNode("shaman weapon", NextAction::array(0, new NextAction("earthliving weapon", 22.0f), nullptr)));
|
||||
// triggers.push_back(new TriggerNode("shaman weapon", NextAction::array(0, new NextAction("earthliving weapon", 22.0f), nullptr)));
|
||||
triggers.push_back(new TriggerNode("main hand weapon no imbue", NextAction::array(0, new NextAction("earthliving weapon", 22.0f), nullptr)));
|
||||
triggers.push_back(new TriggerNode(
|
||||
"group heal occasion",
|
||||
NextAction::array(0, new NextAction("riptide on party", 23.0f), new NextAction("chain heal", 22.0f), NULL)));
|
||||
|
||||
@@ -63,7 +63,9 @@ void MeleeShamanStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
||||
{
|
||||
GenericShamanStrategy::InitTriggers(triggers);
|
||||
|
||||
triggers.push_back(new TriggerNode("shaman weapon", NextAction::array(0, new NextAction("flametongue weapon", 22.0f), nullptr)));
|
||||
//triggers.push_back(new TriggerNode("shaman weapon", NextAction::array(0, new NextAction("flametongue weapon", 22.0f), nullptr)));
|
||||
triggers.push_back(new TriggerNode("main hand weapon no imbue", NextAction::array(0, new NextAction("windfury weapon", 22.0f), nullptr)));
|
||||
triggers.push_back(new TriggerNode("off hand weapon no imbue", NextAction::array(0, new NextAction("flametongue weapon", 21.0f), nullptr)));
|
||||
// triggers.push_back(new TriggerNode("searing totem", NextAction::array(0, new NextAction("reach melee", 22.0f), new NextAction("searing totem", 22.0f), nullptr)));
|
||||
triggers.push_back(new TriggerNode("flame shock", NextAction::array(0, new NextAction("flame shock", 20.0f), nullptr)));
|
||||
triggers.push_back(new TriggerNode(
|
||||
|
||||
@@ -323,6 +323,7 @@ class CastChainLightningAction : public CastSpellAction
|
||||
{
|
||||
public:
|
||||
CastChainLightningAction(PlayerbotAI* botAI) : CastSpellAction(botAI, "chain lightning") { }
|
||||
ActionThreatType getThreatType() override { return ActionThreatType::Aoe; }
|
||||
};
|
||||
|
||||
class CastLightningBoltAction : public CastSpellAction
|
||||
@@ -349,6 +350,12 @@ class CastBloodlustAction : public CastBuffSpellAction
|
||||
CastBloodlustAction(PlayerbotAI* botAI) : CastBuffSpellAction(botAI, "bloodlust") { }
|
||||
};
|
||||
|
||||
class CastElementalMasteryAction : public CastBuffSpellAction
|
||||
{
|
||||
public:
|
||||
CastElementalMasteryAction(PlayerbotAI* botAI) : CastBuffSpellAction(botAI, "elemental mastery") { }
|
||||
};
|
||||
|
||||
class CastWindShearOnEnemyHealerAction : public CastSpellOnEnemyHealerAction
|
||||
{
|
||||
public:
|
||||
|
||||
@@ -79,7 +79,9 @@ class ShamanATriggerFactoryInternal : public NamedObjectContext<Trigger>
|
||||
creators["searing totem"] = &ShamanATriggerFactoryInternal::searing_totem;
|
||||
creators["wind shear"] = &ShamanATriggerFactoryInternal::wind_shear;
|
||||
creators["purge"] = &ShamanATriggerFactoryInternal::purge;
|
||||
creators["shaman weapon"] = &ShamanATriggerFactoryInternal::shaman_weapon;
|
||||
//creators["shaman weapon"] = &ShamanATriggerFactoryInternal::shaman_weapon;
|
||||
creators["main hand weapon no imbue"] = &ShamanATriggerFactoryInternal::main_hand_weapon_no_imbue;
|
||||
creators["off hand weapon no imbue"] = &ShamanATriggerFactoryInternal::off_hand_weapon_no_imbue;
|
||||
creators["water shield"] = &ShamanATriggerFactoryInternal::water_shield;
|
||||
creators["lightning shield"] = &ShamanATriggerFactoryInternal::lightning_shield;
|
||||
creators["water breathing"] = &ShamanATriggerFactoryInternal::water_breathing;
|
||||
@@ -96,6 +98,7 @@ class ShamanATriggerFactoryInternal : public NamedObjectContext<Trigger>
|
||||
creators["frost shock snare"] = &ShamanATriggerFactoryInternal::frost_shock_snare;
|
||||
creators["heroism"] = &ShamanATriggerFactoryInternal::heroism;
|
||||
creators["bloodlust"] = &ShamanATriggerFactoryInternal::bloodlust;
|
||||
creators["elemental mastery"] = &ShamanATriggerFactoryInternal::elemental_mastery;
|
||||
creators["wind shear on enemy healer"] = &ShamanATriggerFactoryInternal::wind_shear_on_enemy_healer;
|
||||
creators["cure poison"] = &ShamanATriggerFactoryInternal::cure_poison;
|
||||
creators["party member cure poison"] = &ShamanATriggerFactoryInternal::party_member_cure_poison;
|
||||
@@ -114,6 +117,7 @@ class ShamanATriggerFactoryInternal : public NamedObjectContext<Trigger>
|
||||
static Trigger* maelstrom_weapon(PlayerbotAI* botAI) { return new MaelstromWeaponTrigger(botAI); }
|
||||
static Trigger* heroism(PlayerbotAI* botAI) { return new HeroismTrigger(botAI); }
|
||||
static Trigger* bloodlust(PlayerbotAI* botAI) { return new BloodlustTrigger(botAI); }
|
||||
static Trigger* elemental_mastery(PlayerbotAI* botAI) { return new ElementalMasteryTrigger(botAI); }
|
||||
static Trigger* party_member_cleanse_disease(PlayerbotAI* botAI) { return new PartyMemberCleanseSpiritDiseaseTrigger(botAI); }
|
||||
static Trigger* party_member_cleanse_curse(PlayerbotAI* botAI) { return new PartyMemberCleanseSpiritCurseTrigger(botAI); }
|
||||
static Trigger* party_member_cleanse_poison(PlayerbotAI* botAI) { return new PartyMemberCleanseSpiritPoisonTrigger(botAI); }
|
||||
@@ -134,7 +138,9 @@ class ShamanATriggerFactoryInternal : public NamedObjectContext<Trigger>
|
||||
static Trigger* searing_totem(PlayerbotAI* botAI) { return new SearingTotemTrigger(botAI); }
|
||||
static Trigger* wind_shear(PlayerbotAI* botAI) { return new WindShearInterruptSpellTrigger(botAI); }
|
||||
static Trigger* purge(PlayerbotAI* botAI) { return new PurgeTrigger(botAI); }
|
||||
static Trigger* shaman_weapon(PlayerbotAI* botAI) { return new ShamanWeaponTrigger(botAI); }
|
||||
//static Trigger* shaman_weapon(PlayerbotAI* botAI) { return new ShamanWeaponTrigger(botAI); }
|
||||
static Trigger* main_hand_weapon_no_imbue(PlayerbotAI* botAI) { return new MainHandWeaponNoImbueTrigger(botAI); }
|
||||
static Trigger* off_hand_weapon_no_imbue(PlayerbotAI* botAI) { return new OffHandWeaponNoImbueTrigger(botAI); }
|
||||
static Trigger* water_shield(PlayerbotAI* botAI) { return new WaterShieldTrigger(botAI); }
|
||||
static Trigger* lightning_shield(PlayerbotAI* botAI) { return new LightningShieldTrigger(botAI); }
|
||||
static Trigger* shock(PlayerbotAI* botAI) { return new ShockTrigger(botAI); }
|
||||
@@ -206,6 +212,7 @@ class ShamanAiObjectContextInternal : public NamedObjectContext<Action>
|
||||
creators["thunderstorm"] = &ShamanAiObjectContextInternal::thunderstorm;
|
||||
creators["heroism"] = &ShamanAiObjectContextInternal::heroism;
|
||||
creators["bloodlust"] = &ShamanAiObjectContextInternal::bloodlust;
|
||||
creators["elemental mastery"] = &ShamanAiObjectContextInternal::elemental_mastery;
|
||||
// creators["cure disease"] = &ShamanAiObjectContextInternal::cure_disease;
|
||||
// creators["cure disease on party"] = &ShamanAiObjectContextInternal::cure_disease_on_party;
|
||||
// creators["cure poison"] = &ShamanAiObjectContextInternal::cure_poison;
|
||||
@@ -222,6 +229,7 @@ class ShamanAiObjectContextInternal : public NamedObjectContext<Action>
|
||||
private:
|
||||
static Action* heroism(PlayerbotAI* botAI) { return new CastHeroismAction(botAI); }
|
||||
static Action* bloodlust(PlayerbotAI* botAI) { return new CastBloodlustAction(botAI); }
|
||||
static Action* elemental_mastery(PlayerbotAI* botAI) { return new CastElementalMasteryAction(botAI); }
|
||||
static Action* thunderstorm(PlayerbotAI* botAI) { return new CastThunderstormAction(botAI); }
|
||||
static Action* lightning_bolt(PlayerbotAI* botAI) { return new CastLightningBoltAction(botAI); }
|
||||
static Action* chain_lightning(PlayerbotAI* botAI) { return new CastChainLightningAction(botAI); }
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include "ShamanTriggers.h"
|
||||
#include "Playerbots.h"
|
||||
|
||||
/*
|
||||
std::vector<std::string> ShamanWeaponTrigger::spells;
|
||||
|
||||
bool ShamanWeaponTrigger::IsActive()
|
||||
@@ -30,6 +31,21 @@ bool ShamanWeaponTrigger::IsActive()
|
||||
|
||||
return false;
|
||||
}
|
||||
*/
|
||||
|
||||
bool MainHandWeaponNoImbueTrigger::IsActive() {
|
||||
Item* const itemForSpell = bot->GetItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_MAINHAND );
|
||||
if (!itemForSpell || itemForSpell->GetEnchantmentId(TEMP_ENCHANTMENT_SLOT))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool OffHandWeaponNoImbueTrigger::IsActive() {
|
||||
Item* const itemForSpell = bot->GetItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_OFFHAND );
|
||||
if (!itemForSpell || itemForSpell->GetEnchantmentId(TEMP_ENCHANTMENT_SLOT))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ShockTrigger::IsActive()
|
||||
{
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
|
||||
class PlayerbotAI;
|
||||
|
||||
/*
|
||||
class ShamanWeaponTrigger : public BuffTrigger
|
||||
{
|
||||
public:
|
||||
@@ -21,6 +22,21 @@ class ShamanWeaponTrigger : public BuffTrigger
|
||||
private:
|
||||
static std::vector<std::string> spells;
|
||||
};
|
||||
*/
|
||||
|
||||
class MainHandWeaponNoImbueTrigger : public BuffTrigger
|
||||
{
|
||||
public:
|
||||
MainHandWeaponNoImbueTrigger(PlayerbotAI* ai) : BuffTrigger(ai, "main hand", 1) {}
|
||||
virtual bool IsActive();
|
||||
};
|
||||
|
||||
class OffHandWeaponNoImbueTrigger : public BuffTrigger
|
||||
{
|
||||
public:
|
||||
OffHandWeaponNoImbueTrigger(PlayerbotAI* ai) : BuffTrigger(ai, "off hand", 1) {}
|
||||
virtual bool IsActive();
|
||||
};
|
||||
|
||||
class TotemTrigger : public Trigger
|
||||
{
|
||||
@@ -201,6 +217,12 @@ class BloodlustTrigger : public BoostTrigger
|
||||
BloodlustTrigger(PlayerbotAI* botAI) : BoostTrigger(botAI, "bloodlust") { }
|
||||
};
|
||||
|
||||
class ElementalMasteryTrigger : public BoostTrigger
|
||||
{
|
||||
public:
|
||||
ElementalMasteryTrigger(PlayerbotAI* botAI) : BoostTrigger(botAI, "elemental mastery") { }
|
||||
};
|
||||
|
||||
class MaelstromWeaponTrigger : public HasAuraStackTrigger
|
||||
{
|
||||
public:
|
||||
|
||||
@@ -119,6 +119,7 @@ class ChatTriggerContext : public NamedObjectContext<Trigger>
|
||||
creators["naxx"] = &ChatTriggerContext::naxx;
|
||||
creators["bwl"] = &ChatTriggerContext::bwl;
|
||||
creators["dps"] = &ChatTriggerContext::dps;
|
||||
creators["disperse"] = &ChatTriggerContext::disperse;
|
||||
}
|
||||
|
||||
private:
|
||||
@@ -218,6 +219,7 @@ class ChatTriggerContext : public NamedObjectContext<Trigger>
|
||||
static Trigger* naxx(PlayerbotAI* ai) { return new ChatCommandTrigger(ai, "naxx"); }
|
||||
static Trigger* bwl(PlayerbotAI* ai) { return new ChatCommandTrigger(ai, "bwl"); }
|
||||
static Trigger* dps(PlayerbotAI* ai) { return new ChatCommandTrigger(ai, "dps"); }
|
||||
static Trigger* disperse(PlayerbotAI* ai) { return new ChatCommandTrigger(ai, "disperse"); }
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -64,6 +64,11 @@ bool AlmostFullManaTrigger::IsActive()
|
||||
return AI_VALUE2(bool, "has mana", "self target") && AI_VALUE2(uint8, "mana", "self target") > 85;
|
||||
}
|
||||
|
||||
bool EnoughManaTrigger::IsActive()
|
||||
{
|
||||
return AI_VALUE2(bool, "has mana", "self target") && AI_VALUE2(uint8, "mana", "self target") > 65;
|
||||
}
|
||||
|
||||
bool RageAvailable::IsActive()
|
||||
{
|
||||
return AI_VALUE2(uint8, "rage", "self target") >= amount;
|
||||
|
||||
@@ -30,6 +30,14 @@ class HighManaTrigger : public Trigger
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class EnoughManaTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
EnoughManaTrigger(PlayerbotAI* botAI) : Trigger(botAI, "enough mana") { }
|
||||
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class AlmostFullManaTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
|
||||
@@ -45,6 +45,7 @@ class TriggerContext : public NamedObjectContext<Trigger>
|
||||
creators["medium mana"] = &TriggerContext::MediumMana;
|
||||
creators["high mana"] = &TriggerContext::HighMana;
|
||||
creators["almost full mana"] = &TriggerContext::AlmostFullMana;
|
||||
creators["enough mana"] = &TriggerContext::EnoughMana;
|
||||
|
||||
creators["party member critical health"] = &TriggerContext::PartyMemberCriticalHealth;
|
||||
creators["party member low health"] = &TriggerContext::PartyMemberLowHealth;
|
||||
@@ -253,6 +254,7 @@ class TriggerContext : public NamedObjectContext<Trigger>
|
||||
static Trigger* MediumMana(PlayerbotAI* botAI) { return new MediumManaTrigger(botAI); }
|
||||
static Trigger* HighMana(PlayerbotAI* botAI) { return new HighManaTrigger(botAI); }
|
||||
static Trigger* AlmostFullMana(PlayerbotAI* botAI) { return new AlmostFullManaTrigger(botAI); }
|
||||
static Trigger* EnoughMana(PlayerbotAI* botAI) { return new EnoughManaTrigger(botAI); }
|
||||
static Trigger* LightRageAvailable(PlayerbotAI* botAI) { return new LightRageAvailableTrigger(botAI); }
|
||||
static Trigger* MediumRageAvailable(PlayerbotAI* botAI) { return new MediumRageAvailableTrigger(botAI); }
|
||||
static Trigger* HighRageAvailable(PlayerbotAI* botAI) { return new HighRageAvailableTrigger(botAI); }
|
||||
|
||||
@@ -47,8 +47,21 @@ GuidVector AttackersValue::Calculate()
|
||||
|
||||
if (bot->duel && bot->duel->Opponent)
|
||||
result.push_back(bot->duel->Opponent->GetGUID());
|
||||
|
||||
return result;
|
||||
|
||||
// workaround for bots of same faction not fighting in arena
|
||||
if (bot->InArena())
|
||||
{
|
||||
GuidVector possibleTargets = AI_VALUE(GuidVector, "possible targets");
|
||||
for (ObjectGuid const guid : possibleTargets)
|
||||
{
|
||||
Unit* unit = botAI->GetUnit(guid);
|
||||
if (unit && unit->IsPlayer() && IsValidTarget(unit, bot)) {
|
||||
result.push_back(unit->GetGUID());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void AttackersValue::AddAttackersOf(Group* group, std::unordered_set<Unit*>& targets)
|
||||
@@ -159,7 +172,7 @@ bool AttackersValue::IsPossibleTarget(Unit* attacker, Player* bot, float range)
|
||||
// (inCannon || !attacker->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE)) && attacker->CanSeeOrDetect(bot) &&
|
||||
// !(attacker->HasUnitState(UNIT_STATE_STUNNED) && botAI->HasAura("shackle undead", attacker)) && !((attacker->IsPolymorphed() || botAI->HasAura("sap", attacker) || /*attacker->IsCharmed() ||*/ attacker->isFeared()) && !rti) &&
|
||||
/*!sServerFacade->IsInRoots(attacker) &&*/
|
||||
!attacker->IsFriendlyTo(bot) && bot->IsWithinDistInMap(attacker, range) &&
|
||||
!attacker->IsFriendlyTo(bot) &&
|
||||
!attacker->HasAuraType(SPELL_AURA_SPIRIT_OF_REDEMPTION) &&
|
||||
// !(attacker->GetGUID().IsPet() && enemy) &&
|
||||
!(attacker->GetCreatureType() == CREATURE_TYPE_CRITTER && !attacker->IsInCombat()) &&
|
||||
|
||||
@@ -180,6 +180,8 @@ uint32 MoneyNeededForValue::Calculate()
|
||||
case NeedMoneyFor::tradeskill:
|
||||
moneyWanted = (level * level * level); //Or level^3 (10s @ lvl10, 3g @ lvl30, 20g @ lvl60, 50g @ lvl80): Todo replace (Should be buyable reagents that combined allow crafting of usefull items)
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return moneyWanted;
|
||||
|
||||
@@ -8,8 +8,8 @@
|
||||
#ifndef WIN32
|
||||
inline int strcmpi(char const* s1, char const* s2)
|
||||
{
|
||||
for (; *s1 && *s2 && (toupper(*s1) == toupper(*s2)); ++s1, ++s2);
|
||||
return *s1 - *s2;
|
||||
for (; *s1 && *s2 && (toupper(*s1) == toupper(*s2)); ++s1, ++s2) {}
|
||||
return *s1 - *s2;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ class Unit;
|
||||
class PartyMemberToDispel : public PartyMemberValue, public Qualified
|
||||
{
|
||||
public:
|
||||
PartyMemberToDispel(PlayerbotAI* botAI, std::string const name = "party member to dispel") : PartyMemberValue(botAI, name, 2 * 1000), Qualified() { }
|
||||
PartyMemberToDispel(PlayerbotAI* botAI, std::string const name = "party member to dispel") : PartyMemberValue(botAI, name, 1000), Qualified() { }
|
||||
|
||||
protected:
|
||||
Unit* Calculate() override;
|
||||
|
||||
@@ -74,7 +74,7 @@ bool PartyMemberToHeal::Check(Unit* player)
|
||||
{
|
||||
// return player && player != bot && player->GetMapId() == bot->GetMapId() && player->IsInWorld() &&
|
||||
// sServerFacade->GetDistance2d(bot, player) < (player->IsPlayer() && botAI->IsTank((Player*)player) ? 50.0f : 40.0f);
|
||||
return player->GetMapId() == bot->GetMapId() &&
|
||||
return player->GetMapId() == bot->GetMapId() && !player->IsCharmed() &&
|
||||
bot->GetDistance2d(player) < sPlayerbotAIConfig->healDistance * 2 &&
|
||||
bot->IsWithinLOS(player->GetPositionX(), player->GetPositionY(), player->GetPositionZ());
|
||||
}
|
||||
|
||||
@@ -301,6 +301,10 @@ class ValueContext : public NamedObjectContext<UntypedValue>
|
||||
creators["expected group dps"] = &ValueContext::expected_group_dps;
|
||||
creators["area debuff"] = &ValueContext::area_debuff;
|
||||
creators["nearest trap with damage"] = &ValueContext::nearest_trap_with_damange;
|
||||
creators["disperse distance"] = &ValueContext::disperse_distance;
|
||||
creators["last flee angle"] = &ValueContext::last_flee_angle;
|
||||
creators["last flee timestamp"] = &ValueContext::last_flee_timestamp;
|
||||
creators["recently flee info"] = &ValueContext::recently_flee_info;
|
||||
}
|
||||
|
||||
private:
|
||||
@@ -505,6 +509,10 @@ class ValueContext : public NamedObjectContext<UntypedValue>
|
||||
static UntypedValue* expected_group_dps(PlayerbotAI* ai) { return new ExpectedGroupDpsValue(ai); }
|
||||
static UntypedValue* area_debuff(PlayerbotAI* ai) { return new AreaDebuffValue(ai); }
|
||||
static UntypedValue* nearest_trap_with_damange(PlayerbotAI* ai) { return new NearestTrapWithDamageValue(ai); }
|
||||
static UntypedValue* disperse_distance(PlayerbotAI* ai) { return new DisperseDistanceValue(ai); }
|
||||
static UntypedValue* last_flee_angle(PlayerbotAI* ai) { return new LastFleeAngleValue(ai); }
|
||||
static UntypedValue* last_flee_timestamp(PlayerbotAI* ai) { return new LastFleeTimestampValue(ai); }
|
||||
static UntypedValue* recently_flee_info(PlayerbotAI* ai) { return new RecentlyFleeInfo(ai); }
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -87,7 +87,10 @@ void DpsWarlockStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
||||
|
||||
void DpsAoeWarlockStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
||||
{
|
||||
triggers.push_back(new TriggerNode("medium aoe", NextAction::array(0, new NextAction("rain of fire", 37.0f), nullptr)));
|
||||
triggers.push_back(new TriggerNode("medium aoe", NextAction::array(0,
|
||||
new NextAction("seed of corruption", 39.0f),
|
||||
new NextAction("seed of corruption on attacker", 38.0f),
|
||||
new NextAction("rain of fire", 37.0f), nullptr)));
|
||||
triggers.push_back(new TriggerNode("corruption on attacker", NextAction::array(0, new NextAction("corruption on attacker", 27.0f), nullptr)));
|
||||
triggers.push_back(new TriggerNode("unstable affliction on attacker", NextAction::array(0, new NextAction("unstable affliction on attacker", 26.0f), NULL)));
|
||||
triggers.push_back(new TriggerNode("curse of agony on attacker", NextAction::array(0, new NextAction("curse of agony on attacker", 25.0f), nullptr)));
|
||||
|
||||
@@ -67,12 +67,18 @@ class CastCorruptionAction : public CastDebuffSpellAction
|
||||
{
|
||||
public:
|
||||
CastCorruptionAction(PlayerbotAI* botAI) : CastDebuffSpellAction(botAI, "corruption", true) { }
|
||||
bool isUseful() override {
|
||||
return CastDebuffSpellAction::isUseful() && !botAI->HasAura("seed of corruption", GetTarget(), false, true) ;
|
||||
}
|
||||
};
|
||||
|
||||
class CastCorruptionOnAttackerAction : public CastDebuffSpellOnAttackerAction
|
||||
{
|
||||
public:
|
||||
CastCorruptionOnAttackerAction(PlayerbotAI* botAI) : CastDebuffSpellOnAttackerAction(botAI, "corruption", true) { }
|
||||
bool isUseful() override {
|
||||
return CastDebuffSpellOnAttackerAction::isUseful() && !botAI->HasAura("seed of corruption", GetTarget(), false, true) ;
|
||||
}
|
||||
};
|
||||
|
||||
class CastCurseOfAgonyOnAttackerAction : public CastDebuffSpellOnAttackerAction
|
||||
@@ -142,6 +148,20 @@ class CastSeedOfCorruptionAction : public CastDebuffSpellAction
|
||||
{
|
||||
public:
|
||||
CastSeedOfCorruptionAction(PlayerbotAI* botAI) : CastDebuffSpellAction(botAI, "seed of corruption", true, 0) { }
|
||||
bool isUseful() override {
|
||||
return CastDebuffSpellAction::isUseful() && !botAI->HasAura("corruption", GetTarget(), false, true) ;
|
||||
}
|
||||
ActionThreatType getThreatType() override { return ActionThreatType::Aoe; }
|
||||
};
|
||||
|
||||
class CastSeedOfCorruptionOnAttackerAction : public CastDebuffSpellOnAttackerAction
|
||||
{
|
||||
public:
|
||||
CastSeedOfCorruptionOnAttackerAction(PlayerbotAI* botAI) : CastDebuffSpellOnAttackerAction(botAI, "seed of corruption", true, 0) { }
|
||||
bool isUseful() override {
|
||||
return CastDebuffSpellOnAttackerAction::isUseful() && !botAI->HasAura("corruption", GetTarget(), false, true) ;
|
||||
}
|
||||
ActionThreatType getThreatType() override { return ActionThreatType::Aoe; }
|
||||
};
|
||||
|
||||
class CastRainOfFireAction : public CastSpellAction
|
||||
|
||||
@@ -158,6 +158,7 @@ class WarlockAiObjectContextInternal : public NamedObjectContext<Action>
|
||||
creators["banish"] = &WarlockAiObjectContextInternal::banish;
|
||||
creators["banish on cc"] = &WarlockAiObjectContextInternal::banish_on_cc;
|
||||
creators["seed of corruption"] = &WarlockAiObjectContextInternal::seed_of_corruption;
|
||||
creators["seed of corruption on attacker"] = &WarlockAiObjectContextInternal::seed_of_corruption_on_attacker;
|
||||
creators["rain of fire"] = &WarlockAiObjectContextInternal::rain_of_fire;
|
||||
creators["shadowfury"] = &WarlockAiObjectContextInternal::shadowfury;
|
||||
creators["life tap"] = &WarlockAiObjectContextInternal::life_tap;
|
||||
@@ -209,6 +210,7 @@ class WarlockAiObjectContextInternal : public NamedObjectContext<Action>
|
||||
static Action* banish(PlayerbotAI* botAI) { return new CastBanishAction(botAI); }
|
||||
static Action* banish_on_cc(PlayerbotAI* botAI) { return new CastBanishAction(botAI); }
|
||||
static Action* seed_of_corruption(PlayerbotAI* botAI) { return new CastSeedOfCorruptionAction(botAI); }
|
||||
static Action* seed_of_corruption_on_attacker(PlayerbotAI* botAI) { return new CastSeedOfCorruptionOnAttackerAction(botAI); }
|
||||
static Action* rain_of_fire(PlayerbotAI* botAI) { return new CastRainOfFireAction(botAI); }
|
||||
static Action* shadowfury(PlayerbotAI* botAI) { return new CastShadowfuryAction(botAI); }
|
||||
static Action* life_tap(PlayerbotAI* botAI) { return new CastLifeTapAction(botAI); }
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#define _PLAYERBOT_WARLOCKTRIGGERS_H
|
||||
|
||||
#include "GenericTriggers.h"
|
||||
#include "PlayerbotAI.h"
|
||||
|
||||
class PlayerbotAI;
|
||||
|
||||
@@ -32,13 +33,24 @@ class CurseOfAgonyTrigger : public DebuffTrigger
|
||||
CurseOfAgonyTrigger(PlayerbotAI* botAI) : DebuffTrigger(botAI, "curse of agony", 1, true, 20.0f) { }
|
||||
};
|
||||
|
||||
DEBUFF_CHECKISOWNER_TRIGGER(CorruptionTrigger, "corruption");
|
||||
class CorruptionTrigger : public DebuffTrigger
|
||||
{
|
||||
public:
|
||||
CorruptionTrigger(PlayerbotAI* botAI) : DebuffTrigger(botAI, "corruption", 1, true) { } \
|
||||
bool IsActive() override {
|
||||
return DebuffTrigger::IsActive() && !botAI->HasAura("seed of corruption", GetTarget(), false, true) ;
|
||||
}
|
||||
};
|
||||
|
||||
DEBUFF_CHECKISOWNER_TRIGGER(SiphonLifeTrigger, "siphon life");
|
||||
|
||||
class CorruptionOnAttackerTrigger : public DebuffOnAttackerTrigger
|
||||
{
|
||||
public:
|
||||
CorruptionOnAttackerTrigger(PlayerbotAI* botAI) : DebuffOnAttackerTrigger(botAI, "corruption", true) { }
|
||||
bool IsActive() override {
|
||||
return DebuffOnAttackerTrigger::IsActive() && !botAI->HasAura("seed of corruption", GetTarget(), false, true) ;
|
||||
}
|
||||
};
|
||||
|
||||
class CastCurseOfAgonyOnAttackerTrigger : public DebuffOnAttackerTrigger
|
||||
|
||||
@@ -32,7 +32,9 @@ ArmsWarriorStrategy::ArmsWarriorStrategy(PlayerbotAI* botAI) : GenericWarriorStr
|
||||
|
||||
NextAction** ArmsWarriorStrategy::getDefaultActions()
|
||||
{
|
||||
return NextAction::array(0, new NextAction("heroic strike", ACTION_DEFAULT), nullptr);
|
||||
return NextAction::array(0,
|
||||
new NextAction("bladestorm", ACTION_DEFAULT + 0.1f),
|
||||
new NextAction("melee", ACTION_DEFAULT), nullptr);
|
||||
}
|
||||
|
||||
void ArmsWarriorStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
||||
@@ -56,4 +58,5 @@ void ArmsWarriorStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
||||
triggers.push_back(new TriggerNode("rend", NextAction::array(0, new NextAction("rend", ACTION_HIGH + 5), nullptr)));
|
||||
triggers.push_back(new TriggerNode("rend on attacker", NextAction::array(0, new NextAction("rend on attacker", ACTION_HIGH + 5), nullptr)));
|
||||
triggers.push_back(new TriggerNode("critical health", NextAction::array(0, new NextAction("intimidating shout", ACTION_EMERGENCY), nullptr)));
|
||||
triggers.push_back(new TriggerNode("medium rage available", NextAction::array(0, new NextAction("thunder clap", ACTION_HIGH + 1), nullptr)));
|
||||
}
|
||||
|
||||
@@ -61,7 +61,7 @@ void FuryWarriorStrategy::InitTriggers(std::vector<TriggerNode*> &triggers)
|
||||
triggers.push_back(new TriggerNode("bloodthirst", NextAction::array(0, new NextAction("bloodthirst", ACTION_HIGH + 7), nullptr)));
|
||||
triggers.push_back(new TriggerNode("instant slam", NextAction::array(0, new NextAction("slam", ACTION_HIGH + 5), nullptr)));
|
||||
triggers.push_back(new TriggerNode("bloodrage", NextAction::array(0, new NextAction("bloodrage", ACTION_HIGH + 2), nullptr)));
|
||||
triggers.push_back(new TriggerNode("high rage available", NextAction::array(0, new NextAction("heroic strike", ACTION_HIGH + 1), NULL)));
|
||||
triggers.push_back(new TriggerNode("medium rage available", NextAction::array(0, new NextAction("heroic strike", ACTION_HIGH + 1), NULL)));
|
||||
// triggers.push_back(new TriggerNode("berserker rage", NextAction::array(0, new NextAction("berserker rage", ACTION_HIGH + 2), nullptr)));
|
||||
// triggers.push_back(new TriggerNode("light aoe", NextAction::array(0,
|
||||
// new NextAction("whirlwind", ACTION_HIGH + 2),
|
||||
|
||||
@@ -88,4 +88,6 @@ void TankWarriorStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
||||
triggers.push_back(new TriggerNode("rend", NextAction::array(0, new NextAction("rend", ACTION_NORMAL + 1), nullptr)));
|
||||
triggers.push_back(new TriggerNode("rend on attacker", NextAction::array(0, new NextAction("rend on attacker", ACTION_NORMAL + 1), nullptr)));
|
||||
triggers.push_back(new TriggerNode("protect party member", NextAction::array(0, new NextAction("intervene", ACTION_EMERGENCY), nullptr)));
|
||||
triggers.push_back(new TriggerNode("high rage available", NextAction::array(0, new NextAction("heroic strike", ACTION_HIGH + 1), nullptr)));
|
||||
triggers.push_back(new TriggerNode("medium rage available", NextAction::array(0, new NextAction("thunder clap", ACTION_HIGH + 1), nullptr)));
|
||||
}
|
||||
|
||||
@@ -117,7 +117,7 @@ class WarriorTriggerFactoryInternal : public NamedObjectContext<Trigger>
|
||||
static Trigger* SwordAndBoard(PlayerbotAI* botAI) { return new SwordAndBoardTrigger(botAI); }
|
||||
static Trigger* shield_bash_on_enemy_healer(PlayerbotAI* botAI) { return new ShieldBashInterruptEnemyHealerSpellTrigger(botAI); }
|
||||
|
||||
static Trigger* thunderclap_and_rage(PlayerbotAI* botAI) { return new TwoTriggers(botAI, "thunderclap", "light rage available"); }
|
||||
static Trigger* thunderclap_and_rage(PlayerbotAI* botAI) { return new TwoTriggers(botAI, "thunder clap", "light rage available"); }
|
||||
static Trigger* intercept_can_cast(PlayerbotAI* botAI) { return new InterceptCanCastTrigger(botAI); }
|
||||
static Trigger* intercept_and_far_enemy(PlayerbotAI* botAI) { return new TwoTriggers(botAI, "enemy is out of melee", "intercept can cast"); }
|
||||
static Trigger* intercept_and_rage(PlayerbotAI* botAI) { return new TwoTriggers(botAI, "intercept and far enemy", "light rage available"); }
|
||||
|
||||
Reference in New Issue
Block a user