Compare commits

..

1 Commits

Author SHA1 Message Date
bash
ad6c9cb1bc Increased updateInterval booster time
from botAmounts * 0.13 to 0.15
2024-11-19 00:24:43 +01:00
96 changed files with 435 additions and 3123 deletions

View File

@@ -67,7 +67,7 @@
###################################
# #
# GENERAL SETTINGS #
# GENERAL SETTINGS #
# #
###################################
@@ -368,10 +368,6 @@ AiPlayerbot.SyncQuestWithPlayer = 1
# Default: 0 (disabled)
AiPlayerbot.SyncQuestForPlayer = 0
# Bots will drop obsolete quests
# Default: 1 (enabled)
AiPlayerbot.DropObsoleteQuests = 1
#
#
#
@@ -424,10 +420,6 @@ AiPlayerbot.MaintenanceCommand = 1
# default: 1 (enable)
AiPlayerbot.AutoGearCommand = 1
# Enable/Disable autogear command on player alt bots
# Default: 1 (enable)
AiPlayerbot.AutoGearCommandAltBots = 1
# Equips quality limitation for auto gear command (1 = normal, 2 = uncommon, 3 = rare, 4 = epic, 5 = legendary)
# default: 3 (rare)
AiPlayerbot.AutoGearQualityLimit = 3
@@ -596,11 +588,6 @@ AiPlayerbot.RandomBotGroupNearby = 0
# Default: 1 (enabled)
AiPlayerbot.AutoDoQuests = 1
# Random Bots will behave more like real players (exprimental)
# This option will override AutoDoQuests
# Default: 0 (disabled)
AiPlayerbot.EnableNewRpgStrategy = 0
# Quest items to leave (do not destroy)
AiPlayerbot.RandomBotQuestItems = "6948,5175,5176,5177,5178,16309,12382,13704,11000"
@@ -722,10 +709,10 @@ AiPlayerbot.RandomBotArenaTeamMinRating = 1000
AiPlayerbot.DeleteRandomBotArenaTeams = 0
# PvP Restricted Zones (bots don't pvp)
AiPlayerbot.PvpProhibitedZoneIds = "2255,656,2361,2362,2363,976,35,2268,3425,392,541,1446,3828,3712,3738,3565,3539,3623,4152,3988,4658,4284,4418,4436,4275,4323,4395,3703,4298,139"
AiPlayerbot.PvpProhibitedZoneIds = "2255,656,2361,2362,2363,976,35,2268,3425,392,541,1446,3828,3712,3738,3565,3539,3623,4152,3988,4658,4284,4418,4436,4275,4323,4395,3703,4298"
# PvP Restricted Areas (bots don't pvp)
AiPlayerbot.PvpProhibitedAreaIds = "976,35,392,2268"
AiPlayerbot.PvpProhibitedAreaIds = "976,35,392"
# Improve react speed in battleground and arena (may cause lag)
AiPlayerbot.FastReactInBG = 1
@@ -1035,56 +1022,6 @@ AiPlayerbot.PremadeSpecLink.11.3.80 = -553202032322010053100030310511-205503012
#
###################################################################################################
###################################
# #
# WORLD BUFFS #
# #
###################################
####################################################################################################
#
#
#
# Applies a permanent buff to all bots when not in combat simulating flasks, food, rune etc.
# WorldBuff.Faction.Class.Spec.MinLevel.MaxLevel
AiPlayerbot.WorldBuff.0.1.0.80.80 = 53760,57358 #WARRIOR ARMS
AiPlayerbot.WorldBuff.0.1.1.80.80 = 53760,57358 #WARRIOR FURY
AiPlayerbot.WorldBuff.0.1.2.80.80 = 53758,57356 #WARRIOR PROTECTION
AiPlayerbot.WorldBuff.0.2.0.80.80 = 60347,53749,57332 #PALADIN HOLY
AiPlayerbot.WorldBuff.0.2.1.80.80 = 53758,57356 #PALADIN PROTECTION
AiPlayerbot.WorldBuff.0.2.2.80.80 = 53760,57371 #PALADIN RETRIBUTION
AiPlayerbot.WorldBuff.0.3.0.80.80 = 53760,57325 #HUNTER BEAST
AiPlayerbot.WorldBuff.0.3.1.80.80 = 53760,57358 #HUNTER MARKSMANSHIP
AiPlayerbot.WorldBuff.0.3.2.80.80 = 53760,57367 #HUNTER SURVIVAL
AiPlayerbot.WorldBuff.0.4.0.80.80 = 53760,57325 #ROGUE ASSASINATION
AiPlayerbot.WorldBuff.0.4.1.80.80 = 53760,57358 #ROGUE COMBAT
AiPlayerbot.WorldBuff.0.4.2.80.80 = 53760,57367 #ROGUE SUBTLETY
AiPlayerbot.WorldBuff.0.5.0.80.80 = 53755,57327 #PRIEST DISCIPLINE
AiPlayerbot.WorldBuff.0.5.1.80.80 = 53755,57327 #PRIEST HOLY
AiPlayerbot.WorldBuff.0.5.2.80.80 = 53755,57327 #PRIEST SHADOW
AiPlayerbot.WorldBuff.0.6.0.80.80 = 53758,57356 #DEATH KNIGHT BLOOD
AiPlayerbot.WorldBuff.0.6.1.80.80 = 53760,57358 #DEATH KNIGHT FROST
AiPlayerbot.WorldBuff.0.6.2.80.80 = 53760,57358 #DEATH KNIGHT UNHOLY
AiPlayerbot.WorldBuff.0.7.0.80.80 = 53755,57327 #SHAMAN ELEMENTAL
AiPlayerbot.WorldBuff.0.7.1.80.80 = 53760,57325 #SHAMAN ENHANCEMENT
AiPlayerbot.WorldBuff.0.7.2.80.80 = 53755,57327 #SHAMAN RESTORATION
AiPlayerbot.WorldBuff.0.8.0.80.80 = 53755,57327 #MAGE ARCANE
AiPlayerbot.WorldBuff.0.8.1.80.80 = 53755,57327 #MAGE FIRE
AiPlayerbot.WorldBuff.0.8.2.80.80 = 53755,57327 #MAGE FROST
AiPlayerbot.WorldBuff.0.9.0.80.80 = 53755,57327 #WARLOCK AFFLICTION
AiPlayerbot.WorldBuff.0.9.1.80.80 = 53755,57327 #WARLOCK DEMONOLOGY
AiPlayerbot.WorldBuff.0.9.2.80.80 = 53755,57327 #WARLOCK DESTRUCTION
AiPlayerbot.WorldBuff.0.11.0.80.80 = 53755,57327 #DRUID BALANCE
AiPlayerbot.WorldBuff.0.11.1.80.80 = 53749,53763,57367 #DRUID FERAL
AiPlayerbot.WorldBuff.0.11.2.80.80 = 54212,57334 #DRUID RESTORATION
#
#
#
###################################################################################################
###################################
# #
# RANDOM BOT DEFAULT TALENT SPEC #
@@ -1474,6 +1411,9 @@ AiPlayerbot.BroadcastChanceGuildManagement = 30000
# Example: AiPlayerbot.AllowedLogFiles = travelNodes.csv,travelPaths.csv,TravelNodeStore.h,bot_movement.csv,bot_location.csv
AiPlayerbot.AllowedLogFiles = ""
Appender.Playerbots=2,5,0,Playerbots.log,w
Logger.playerbots=5,Console Playerbots
#
#
#
@@ -1520,13 +1460,6 @@ AiPlayerbot.RandombotsWalkingRPG.InDoors = 0
# enforced to 100%
AiPlayerbot.BotActiveAlone = 100
# Force botActiveAlone when bot is ... of real player
AiPlayerbot.BotActiveAloneForceWhenInRadius = 150
AiPlayerbot.BotActiveAloneForceWhenInZone = 1
AiPlayerbot.BotActiveAloneForceWhenInMap = 0
AiPlayerbot.BotActiveAloneForceWhenIsFriend = 1
AiPlayerbot.BotActiveAloneForceWhenInGuild = 1
# Specify smart scaling is enabled or not.
# The default is 1. When enabled (smart) scales the 'BotActiveAlone' value.
# Only when botLevel is between WhenMinLevel and WhenMaxLevel.

View File

@@ -1,11 +0,0 @@
UPDATE `updates_include`
SET `path` = '$/data/sql/playerbots/updates'
WHERE `state` = 'RELEASED';
UPDATE `updates_include`
SET `path` = '$/data/sql/playerbots/custom'
WHERE `state` = 'CUSTOM';
UPDATE `updates_include`
SET `path` = '$/data/sql/playerbots/archive'
WHERE `state` = 'ARCHIVED';

View File

@@ -631,11 +631,7 @@ void AiFactory::AddDefaultNonCombatStrategies(Player* player, PlayerbotAI* const
// nonCombatEngine->addStrategy("group");
// nonCombatEngine->addStrategy("guild");
if (sPlayerbotAIConfig->enableNewRpgStrategy)
{
nonCombatEngine->addStrategy("new rpg", false);
}
else if (sPlayerbotAIConfig->autoDoQuests)
if (sPlayerbotAIConfig->autoDoQuests)
{
// nonCombatEngine->addStrategy("travel");
nonCombatEngine->addStrategy("rpg", false);

View File

@@ -29,7 +29,6 @@
#include "MotionMaster.h"
#include "MoveSpline.h"
#include "MoveSplineInit.h"
#include "NewRpgStrategy.h"
#include "ObjectGuid.h"
#include "PerformanceMonitor.h"
#include "Player.h"
@@ -39,7 +38,6 @@
#include "Playerbots.h"
#include "PointMovementGenerator.h"
#include "PositionValue.h"
#include "RandomPlayerbotMgr.h"
#include "SayAction.h"
#include "ScriptMgr.h"
#include "ServerFacade.h"
@@ -720,7 +718,7 @@ void PlayerbotAI::HandleTeleportAck()
// SetNextCheckDelay(urand(2000, 5000));
if (sPlayerbotAIConfig->applyInstanceStrategies)
ApplyInstanceStrategies(bot->GetMapId(), true);
Reset(true);
Reset();
}
SetNextCheckDelay(sPlayerbotAIConfig->globalCoolDown);
@@ -769,15 +767,14 @@ void PlayerbotAI::Reset(bool full)
->setTarget(sTravelMgr->nullTravelDestination, sTravelMgr->nullWorldPosition, true);
aiObjectContext->GetValue<TravelTarget*>("travel target")->Get()->setStatus(TRAVEL_STATUS_EXPIRED);
aiObjectContext->GetValue<TravelTarget*>("travel target")->Get()->setExpireIn(1000);
rpgInfo = NewRpgInfo();
}
aiObjectContext->GetValue<GuidSet&>("ignore rpg target")->Get().clear();
bot->GetMotionMaster()->Clear();
// bot->CleanupAfterTaxiFlight();
InterruptSpell();
if (full)
{
for (uint8 i = 0; i < BOT_STATE_MAX; i++)
@@ -1606,12 +1603,6 @@ void PlayerbotAI::ApplyInstanceStrategies(uint32 mapId, bool tellMaster)
case 608:
strategyName = "wotlk-vh"; // Violet Hold
break;
case 615:
strategyName = "wotlk-os"; // Obsidian Sanctum
break;
case 616:
strategyName = "wotlk-eoe"; // Eye Of Eternity
break;
case 619:
strategyName = "wotlk-ok"; // Ahn'kahet: The Old Kingdom
break;
@@ -4114,11 +4105,20 @@ inline bool ZoneHasRealPlayers(Player* bot)
{
return false;
}
for (auto& player : sRandomPlayerbotMgr->GetPlayers())
Map::PlayerList const& players = bot->GetMap()->GetPlayers();
if (players.IsEmpty())
{
if (player->GetMapId() != bot->GetMapId())
return false;
}
for (auto const& itr : players)
{
Player* player = itr.GetSource();
if (!player || !player->IsVisible())
{
continue;
}
if (player->GetZoneId() == bot->GetZoneId())
{
@@ -4135,15 +4135,6 @@ inline bool ZoneHasRealPlayers(Player* bot)
bool PlayerbotAI::AllowActive(ActivityType activityType)
{
// Is in combat. Always defend yourself.
if (activityType != OUT_OF_PARTY_ACTIVITY && activityType != PACKET_ACTIVITY)
{
if (bot->IsInCombat())
{
return true;
}
}
// only keep updating till initializing time has completed,
// which prevents unneeded expensive GameTime calls.
if (_isBotInitializing)
@@ -4163,53 +4154,35 @@ bool PlayerbotAI::AllowActive(ActivityType activityType)
return true;
}
// when botActiveAlone is 100% and smartScale disabled
if (sPlayerbotAIConfig->botActiveAlone >= 100 && !sPlayerbotAIConfig->botActiveAloneSmartScale)
{
return true;
}
// bg, raid, dungeon
if (!WorldPosition(bot).isOverworld())
{
return true;
}
// bot map has active players.
if (sPlayerbotAIConfig->BotActiveAloneForceWhenInMap)
// Is in combat. Defend yourself.
if (activityType != OUT_OF_PARTY_ACTIVITY && activityType != PACKET_ACTIVITY)
{
if (HasRealPlayers(bot->GetMap()))
if (bot->IsInCombat())
{
return true;
}
}
// bot zone has active players.
if (sPlayerbotAIConfig->BotActiveAloneForceWhenInZone)
if (ZoneHasRealPlayers(bot))
{
if (ZoneHasRealPlayers(bot))
{
return true;
}
return true;
}
// when in real guild
if (sPlayerbotAIConfig->BotActiveAloneForceWhenInGuild)
{
if (IsInRealGuild())
{
return true;
}
}
// Player is near. Always active.
if (HasPlayerNearby(sPlayerbotAIConfig->BotActiveAloneForceWhenInRadius))
if (IsInRealGuild())
{
return true;
}
// Has player master. Always active.
if (GetMaster())
if (GetMaster())
{
PlayerbotAI* masterBotAI = GET_PLAYERBOT_AI(GetMaster());
if (!masterBotAI || masterBotAI->IsRealPlayer())
@@ -4277,27 +4250,30 @@ bool PlayerbotAI::AllowActive(ActivityType activityType)
return true;
}
// HasFriend
if (sPlayerbotAIConfig->BotActiveAloneForceWhenIsFriend)
// Player is near. Always active.
if (HasPlayerNearby(300.f))
{
for (auto& player : sRandomPlayerbotMgr->GetPlayers())
{
if (!player || !player->IsInWorld() || !player->GetSocial() || !bot->GetGUID())
{
continue;
}
return true;
}
if (player->GetSocial()->HasFriend(bot->GetGUID()))
{
return true;
}
// HasFriend
for (auto& player : sRandomPlayerbotMgr->GetPlayers())
{
if (!player || !player->IsInWorld() || !player->GetSocial() || !bot->GetGUID())
{
continue;
}
if (player->GetSocial()->HasFriend(bot->GetGUID()))
{
return true;
}
}
// Force the bots to spread
if (activityType == OUT_OF_PARTY_ACTIVITY || activityType == GRIND_ACTIVITY)
{
if (HasManyPlayersNearby(10, 40))
if (HasManyPlayersNearby(10, sPlayerbotAIConfig->sightDistance))
{
return true;
}
@@ -4313,7 +4289,11 @@ bool PlayerbotAI::AllowActive(ActivityType activityType)
{
return false;
}
if (sPlayerbotAIConfig->botActiveAlone >= 100 && !sPlayerbotAIConfig->botActiveAloneSmartScale)
{
return true;
}
// #######################################################################################
// All mandatory conditations are checked to be active or not, from here the remaining
// situations are usable for scaling when enabled.
@@ -5492,10 +5472,10 @@ bool PlayerbotAI::CanMove()
if (IsInVehicle() && !IsInVehicle(true))
return false;
if (bot->isFrozen() || bot->IsPolymorphed() || (bot->isDead() && !bot->HasPlayerFlag(PLAYER_FLAGS_GHOST)) ||
bot->IsBeingTeleported() || bot->HasRootAura() || bot->HasSpiritOfRedemptionAura() ||
bot->HasConfuseAura() || bot->IsCharmed() || bot->HasStunAura() ||
bot->IsInFlight() || bot->HasUnitState(UNIT_STATE_LOST_CONTROL))
if (bot->isFrozen() || bot->IsPolymorphed() || (bot->isDead() && !bot->HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_GHOST)) ||
bot->IsBeingTeleported() || bot->isInRoots() || bot->HasAuraType(SPELL_AURA_SPIRIT_OF_REDEMPTION) ||
bot->HasAuraType(SPELL_AURA_MOD_CONFUSE) || bot->IsCharmed() || bot->HasAuraType(SPELL_AURA_MOD_STUN) ||
bot->HasUnitState(UNIT_STATE_IN_FLIGHT) || bot->HasUnitState(UNIT_STATE_LOST_CONTROL))
return false;
return bot->GetMotionMaster()->GetCurrentMovementGeneratorType() != FLIGHT_MOTION_TYPE;

View File

@@ -21,7 +21,6 @@
#include "PlayerbotTextMgr.h"
#include "SpellAuras.h"
#include "WorldPacket.h"
#include "NewRpgStrategy.h"
class AiObjectContext;
class Creature;
@@ -433,7 +432,7 @@ public:
std::vector<Player*> GetPlayersInGroup();
const AreaTableEntry* GetCurrentArea();
const AreaTableEntry* GetCurrentZone();
static std::string GetLocalizedAreaName(const AreaTableEntry* entry);
std::string GetLocalizedAreaName(const AreaTableEntry* entry);
bool TellMaster(std::ostringstream& stream, PlayerbotSecurityLevel securityLevel = PLAYERBOT_SECURITY_ALLOW_ALL);
bool TellMaster(std::string const text, PlayerbotSecurityLevel securityLevel = PLAYERBOT_SECURITY_ALLOW_ALL);
@@ -573,7 +572,6 @@ public:
std::set<uint32> GetCurrentIncompleteQuestIds();
void PetFollow();
static float GetItemScoreMultiplier(ItemQualities quality);
NewRpgInfo rpgInfo;
private:
static void _fillGearScoreData(Player* player, Item* item, std::vector<uint32>* gearScore, uint32& twoHandScore,
@@ -582,7 +580,7 @@ private:
void HandleCommands();
void HandleCommand(uint32 type, const std::string& text, Player& fromPlayer, const uint32 lang = LANG_UNIVERSAL);
bool _isBotInitializing = false;
bool _isBotInitializing = true;
protected:
Player* bot;

View File

@@ -399,19 +399,15 @@ bool PlayerbotAIConfig::Initialize()
worldBuffs.clear();
LOG_INFO("playerbots", "Loading Worldbuff...");
for (uint32 factionId = 0; factionId < 3; factionId++)
{
for (uint32 classId = 0; classId < MAX_CLASSES; classId++)
{
for (uint32 specId = 0; specId < MAX_SPECNO; specId++)
for (uint32 minLevel = 0; minLevel < MAX_LEVEL; minLevel++)
{
for (uint32 minLevel = 0; minLevel < MAX_LEVEL; minLevel++)
for (uint32 maxLevel = 0; maxLevel < MAX_LEVEL; maxLevel++)
{
for (uint32 maxLevel = 0; maxLevel < MAX_LEVEL; maxLevel++)
{
loadWorldBuf(factionId, classId, specId, minLevel, maxLevel);
}
loadWorldBuf(factionId, classId, minLevel, maxLevel);
}
}
}
@@ -463,18 +459,12 @@ bool PlayerbotAIConfig::Initialize()
addClassAccountPoolSize = sConfigMgr->GetOption<int32>("AiPlayerbot.AddClassAccountPoolSize", 50);
maintenanceCommand = sConfigMgr->GetOption<int32>("AiPlayerbot.MaintenanceCommand", 1);
autoGearCommand = sConfigMgr->GetOption<int32>("AiPlayerbot.AutoGearCommand", 1);
autoGearCommandAltBots = sConfigMgr->GetOption<int32>("AiPlayerbot.AutoGearCommandAltBots", 1);
autoGearQualityLimit = sConfigMgr->GetOption<int32>("AiPlayerbot.AutoGearQualityLimit", 3);
autoGearScoreLimit = sConfigMgr->GetOption<int32>("AiPlayerbot.AutoGearScoreLimit", 0);
playerbotsXPrate = sConfigMgr->GetOption<int32>("AiPlayerbot.KillXPRate", 1);
disableDeathKnightLogin = sConfigMgr->GetOption<bool>("AiPlayerbot.DisableDeathKnightLogin", 0);
botActiveAlone = sConfigMgr->GetOption<int32>("AiPlayerbot.BotActiveAlone", 100);
BotActiveAloneForceWhenInRadius = sConfigMgr->GetOption<uint32>("AiPlayerbot.BotActiveAloneForceWhenInRadius", 150);
BotActiveAloneForceWhenInZone = sConfigMgr->GetOption<bool>("AiPlayerbot.BotActiveAloneForceWhenInZone", 1);
BotActiveAloneForceWhenInMap = sConfigMgr->GetOption<bool>("AiPlayerbot.BotActiveAloneForceWhenInMap", 0);
BotActiveAloneForceWhenIsFriend = sConfigMgr->GetOption<bool>("AiPlayerbot.BotActiveAloneForceWhenIsFriend", 1);
BotActiveAloneForceWhenInGuild = sConfigMgr->GetOption<bool>("AiPlayerbot.BotActiveAloneForceWhenInGuild", 1);
botActiveAloneSmartScale = sConfigMgr->GetOption<bool>("AiPlayerbot.botActiveAloneSmartScale", 1);
botActiveAloneSmartScaleWhenMinLevel =
sConfigMgr->GetOption<uint32>("AiPlayerbot.botActiveAloneSmartScaleWhenMinLevel", 1);
@@ -501,15 +491,13 @@ bool PlayerbotAIConfig::Initialize()
twoRoundsGearInit = sConfigMgr->GetOption<bool>("AiPlayerbot.TwoRoundsGearInit", false);
syncQuestWithPlayer = sConfigMgr->GetOption<bool>("AiPlayerbot.SyncQuestWithPlayer", true);
syncQuestForPlayer = sConfigMgr->GetOption<bool>("AiPlayerbot.SyncQuestForPlayer", false);
dropObsoleteQuests = sConfigMgr->GetOption<bool>("AiPlayerbot.DropObsoleteQuests", true);
autoTrainSpells = sConfigMgr->GetOption<std::string>("AiPlayerbot.AutoTrainSpells", "yes");
autoPickTalents = sConfigMgr->GetOption<bool>("AiPlayerbot.AutoPickTalents", true);
autoUpgradeEquip = sConfigMgr->GetOption<bool>("AiPlayerbot.AutoUpgradeEquip", false);
autoLearnTrainerSpells = sConfigMgr->GetOption<bool>("AiPlayerbot.AutoLearnTrainerSpells", true);
autoLearnQuestSpells = sConfigMgr->GetOption<bool>("AiPlayerbot.AutoLearnQuestSpells", false);
autoTeleportForLevel = sConfigMgr->GetOption<bool>("AiPlayerbot.AutoTeleportForLevel", false);
autoDoQuests = sConfigMgr->GetOption<bool>("AiPlayerbot.AutoDoQuests", true);
enableNewRpgStrategy = sConfigMgr->GetOption<bool>("AiPlayerbot.EnableNewRpgStrategy", false);
autoDoQuests = sConfigMgr->GetOption<bool>("AiPlayerbot.AutoDoQuests", false);
syncLevelWithPlayers = sConfigMgr->GetOption<bool>("AiPlayerbot.SyncLevelWithPlayers", false);
freeFood = sConfigMgr->GetOption<bool>("AiPlayerbot.FreeFood", true);
randomBotGroupNearby = sConfigMgr->GetOption<bool>("AiPlayerbot.RandomBotGroupNearby", true);
@@ -571,7 +559,7 @@ bool PlayerbotAIConfig::IsInRandomQuestItemList(uint32 id)
bool PlayerbotAIConfig::IsPvpProhibited(uint32 zoneId, uint32 areaId)
{
return IsInPvpProhibitedZone(zoneId) || IsInPvpProhibitedArea(areaId) || IsInPvpProhibitedZone(areaId);
return IsInPvpProhibitedZone(zoneId) || IsInPvpProhibitedArea(areaId);
}
bool PlayerbotAIConfig::IsInPvpProhibitedZone(uint32 id)
@@ -656,50 +644,36 @@ void PlayerbotAIConfig::log(std::string const fileName, char const* str, ...)
fflush(stdout);
}
void PlayerbotAIConfig::loadWorldBuf(uint32 factionId1, uint32 classId1, uint32 specId1, uint32 minLevel1, uint32 maxLevel1)
void PlayerbotAIConfig::loadWorldBuf(uint32 factionId1, uint32 classId1, uint32 minLevel1, uint32 maxLevel1)
{
std::vector<uint32> buffs;
std::ostringstream os;
os << "AiPlayerbot.WorldBuff." << factionId1 << "." << classId1 << "." << specId1 << "." << minLevel1 << "." << maxLevel1;
os << "AiPlayerbot.WorldBuff." << factionId1 << "." << classId1 << "." << minLevel1 << "." << maxLevel1;
LoadList<std::vector<uint32>>(sConfigMgr->GetOption<std::string>(os.str().c_str(), "", false), buffs);
for (auto buff : buffs)
{
worldBuff wb = {buff, factionId1, classId1, specId1, minLevel1, maxLevel1};
worldBuff wb = {buff, factionId1, classId1, minLevel1, maxLevel1};
worldBuffs.push_back(wb);
}
if (maxLevel1 == 0)
{
std::ostringstream os;
os << "AiPlayerbot.WorldBuff." << factionId1 << "." << classId1 << "." << specId1 << "." << minLevel1;
os << "AiPlayerbot.WorldBuff." << factionId1 << "." << classId1 << "." << minLevel1;
LoadList<std::vector<uint32>>(sConfigMgr->GetOption<std::string>(os.str().c_str(), "", false), buffs);
for (auto buff : buffs)
{
worldBuff wb = {buff, factionId1, classId1, specId1, minLevel1, maxLevel1};
worldBuff wb = {buff, factionId1, classId1, minLevel1, maxLevel1};
worldBuffs.push_back(wb);
}
}
if (maxLevel1 == 0 && minLevel1 == 0)
{
std::ostringstream os;
os << "AiPlayerbot.WorldBuff." << factionId1 << "." << factionId1 << "." << classId1 << "." << specId1;
LoadList<std::vector<uint32>>(sConfigMgr->GetOption<std::string>(os.str().c_str(), "", false), buffs);
for (auto buff : buffs)
{
worldBuff wb = {buff, factionId1, classId1, specId1, minLevel1, maxLevel1};
worldBuffs.push_back(wb);
}
}
if (maxLevel1 == 0 && minLevel1 == 0 && specId1 == 0)
{
std::ostringstream os;
os << "AiPlayerbot.WorldBuff." << factionId1 << "." << factionId1 << "." << classId1;
@@ -708,12 +682,12 @@ void PlayerbotAIConfig::loadWorldBuf(uint32 factionId1, uint32 classId1, uint32
for (auto buff : buffs)
{
worldBuff wb = {buff, factionId1, classId1, specId1, minLevel1, maxLevel1};
worldBuff wb = {buff, factionId1, classId1, minLevel1, maxLevel1};
worldBuffs.push_back(wb);
}
}
if (classId1 == 0 && maxLevel1 == 0 && minLevel1 == 0 && specId1 == 0)
if (classId1 == 0 && maxLevel1 == 0 && minLevel1 == 0)
{
std::ostringstream os;
os << "AiPlayerbot.WorldBuff." << factionId1;
@@ -722,12 +696,12 @@ void PlayerbotAIConfig::loadWorldBuf(uint32 factionId1, uint32 classId1, uint32
for (auto buff : buffs)
{
worldBuff wb = {buff, factionId1, classId1, specId1, minLevel1, maxLevel1};
worldBuff wb = {buff, factionId1, classId1, minLevel1, maxLevel1};
worldBuffs.push_back(wb);
}
}
if (factionId1 == 0 && classId1 == 0 && maxLevel1 == 0 && minLevel1 == 0 && specId1 == 0)
if (factionId1 == 0 && classId1 == 0 && maxLevel1 == 0 && minLevel1 == 0)
{
std::ostringstream os;
os << "AiPlayerbot.WorldBuff";
@@ -736,7 +710,7 @@ void PlayerbotAIConfig::loadWorldBuf(uint32 factionId1, uint32 classId1, uint32
for (auto buff : buffs)
{
worldBuff wb = {buff, factionId1, classId1, specId1, minLevel1, maxLevel1};
worldBuff wb = {buff, factionId1, classId1, minLevel1, maxLevel1};
worldBuffs.push_back(wb);
}
}

View File

@@ -247,7 +247,6 @@ public:
uint32 spellId;
uint32 factionId = 0;
uint32 classId = 0;
uint32 specId = 0;
uint32 minLevel = 0;
uint32 maxLevel = 0;
};
@@ -264,11 +263,6 @@ public:
uint32 playerbotsXPrate;
bool disableDeathKnightLogin;
uint32 botActiveAlone;
uint32 BotActiveAloneForceWhenInRadius;
bool BotActiveAloneForceWhenInZone;
bool BotActiveAloneForceWhenInMap;
bool BotActiveAloneForceWhenIsFriend;
bool BotActiveAloneForceWhenInGuild;
bool botActiveAloneSmartScale;
uint32 botActiveAloneSmartScaleWhenMinLevel;
uint32 botActiveAloneSmartScaleWhenMaxLevel;
@@ -281,13 +275,11 @@ public:
bool twoRoundsGearInit;
bool syncQuestWithPlayer;
bool syncQuestForPlayer;
bool dropObsoleteQuests;
std::string autoTrainSpells;
bool autoPickTalents;
bool autoUpgradeEquip;
bool autoLearnTrainerSpells;
bool autoDoQuests;
bool enableNewRpgStrategy;
bool syncLevelWithPlayers;
bool freeFood;
bool autoLearnQuestSpells;
@@ -320,7 +312,7 @@ public:
int32 addClassCommand;
int32 addClassAccountPoolSize;
int32 maintenanceCommand;
int32 autoGearCommand, autoGearCommandAltBots, autoGearQualityLimit, autoGearScoreLimit;
int32 autoGearCommand, autoGearQualityLimit, autoGearScoreLimit;
std::string const GetTimestampStr();
bool hasLog(std::string const fileName)
@@ -335,7 +327,7 @@ public:
}
void log(std::string const fileName, const char* str, ...);
void loadWorldBuf(uint32 factionId, uint32 classId, uint32 specId, uint32 minLevel, uint32 maxLevel);
void loadWorldBuf(uint32 factionId, uint32 classId, uint32 minLevel, uint32 maxLevel);
static std::vector<std::vector<uint32>> ParseTempTalentsOrder(uint32 cls, std::string temp_talents_order);
static std::vector<std::vector<uint32>> ParseTempPetTalentsOrder(uint32 spec, std::string temp_talents_order);
};

View File

@@ -958,17 +958,9 @@ std::vector<std::string> PlayerbotHolder::HandlePlayerbotCommand(char const* arg
if (!strcmp(cmd, "reload"))
{
if (master->GetSession()->GetSecurity() >= SEC_GAMEMASTER)
{
sPlayerbotAIConfig->Initialize();
messages.push_back("Config reloaded.");
return messages;
}
else
{
messages.push_back("ERROR: Only GM can use this command.");
return messages;
}
messages.push_back("Reloading config");
sPlayerbotAIConfig->Initialize();
return messages;
}
if (!strcmp(cmd, "tweak"))

View File

@@ -52,7 +52,7 @@ public:
void OnDatabaseSelectIndexLogout(Player* player, uint32& statementIndex, uint32& statementParam) override
{
statementIndex = CHAR_UPD_CHAR_OFFLINE;
statementIndex = CHAR_UPD_CHAR_ONLINE;
statementParam = player->GetGUID().GetCounter();
}

View File

@@ -556,7 +556,7 @@ void RandomPlayerbotFactory::CreateRandomBots()
totalRandomBotChars += AccountMgr::GetCharactersCount(accountId);
}
LOG_INFO("server.loading", ">> {} random bot accounts with {} characters available",
LOG_INFO("server.loading", "{} random bot accounts with {} characters available",
sPlayerbotAIConfig->randomBotAccounts.size(), totalRandomBotChars);
}

View File

@@ -8,7 +8,6 @@
#include <algorithm>
#include <boost/thread/thread.hpp>
#include <cstdlib>
#include <ctime>
#include <iomanip>
#include <random>
@@ -19,8 +18,6 @@
#include "BattlegroundMgr.h"
#include "CellImpl.h"
#include "ChannelMgr.h"
#include "DBCStores.h"
#include "DBCStructure.h"
#include "DatabaseEnv.h"
#include "Define.h"
#include "FleeManager.h"
@@ -31,19 +28,15 @@
#include "GuildTaskMgr.h"
#include "LFGMgr.h"
#include "MapMgr.h"
#include "NewRpgStrategy.h"
#include "PerformanceMonitor.h"
#include "Player.h"
#include "PlayerbotAI.h"
#include "PlayerbotAIConfig.h"
#include "PlayerbotCommandServer.h"
#include "PlayerbotFactory.h"
#include "Playerbots.h"
#include "Position.h"
#include "Random.h"
#include "ServerFacade.h"
#include "SharedDefines.h"
#include "TravelMgr.h"
#include "Unit.h"
#include "UpdateTime.h"
#include "World.h"
@@ -172,13 +165,6 @@ RandomPlayerbotMgr::RandomPlayerbotMgr() : PlayerbotHolder(), processTicks(0)
}
BattlegroundData.clear();
for (int bracket = BG_BRACKET_ID_FIRST; bracket < MAX_BATTLEGROUND_BRACKETS; ++bracket)
{
for (int queueType = BATTLEGROUND_QUEUE_AV; queueType < MAX_BATTLEGROUND_QUEUE_TYPES; ++queueType)
{
BattlegroundData[queueType][bracket] = BattlegroundInfo();
}
}
BgCheckTimer = 0;
LfgCheckTimer = 0;
PlayersCheckTimer = 0;
@@ -355,32 +341,21 @@ void RandomPlayerbotMgr::UpdateAIInternal(uint32 elapsed, bool /*minimal*/)
if (sPlayerbotAIConfig->syncLevelWithPlayers && !players.empty())
{
if (time(nullptr) > (PlayersCheckTimer + 60))
sRandomPlayerbotMgr->CheckPlayers();
activateCheckPlayersThread();
}
if (sPlayerbotAIConfig->randomBotJoinBG /* && !players.empty()*/)
{
if (time(nullptr) > (BgCheckTimer + 30))
sRandomPlayerbotMgr->CheckBgQueue();
activateCheckBgQueueThread();
}
if (sPlayerbotAIConfig->randomBotJoinLfg /* && !players.empty()*/)
{
if (time(nullptr) > (LfgCheckTimer + 30))
sRandomPlayerbotMgr->CheckLfgQueue();
activateCheckLfgQueueThread();
}
if (time(nullptr) > (printStatsTimer + 300))
{
if (!printStatsTimer)
{
printStatsTimer = time(nullptr);
}
else
{
activatePrintStatsThread();
}
}
uint32 updateBots = sPlayerbotAIConfig->randomBotsPerInterval * onlineBotFocus / 100;
uint32 maxNewBots = onlineBotCount < maxAllowedBotCount ? maxAllowedBotCount - onlineBotCount : 0;
uint32 loginBots = std::min(sPlayerbotAIConfig->randomBotsPerInterval - updateBots, maxNewBots);
@@ -435,25 +410,25 @@ void RandomPlayerbotMgr::UpdateAIInternal(uint32 elapsed, bool /*minimal*/)
}
}
// void RandomPlayerbotMgr::ScaleBotActivity()
//void RandomPlayerbotMgr::ScaleBotActivity()
//{
// float activityPercentage = getActivityPercentage();
// float activityPercentage = getActivityPercentage();
//
// // if (activityPercentage >= 100.0f || activityPercentage <= 0.0f) pid.reset(); //Stop integer buildup during
// // max/min activity
// // if (activityPercentage >= 100.0f || activityPercentage <= 0.0f) pid.reset(); //Stop integer buildup during
// // max/min activity
//
// // % increase/decrease wanted diff , avg diff
// float activityPercentageMod = pid.calculate(
// sRandomPlayerbotMgr->GetPlayers().empty() ? sPlayerbotAIConfig->diffEmpty :
// sPlayerbotAIConfig->diffWithPlayer, sWorldUpdateTime.GetAverageUpdateTime());
// // % increase/decrease wanted diff , avg diff
// float activityPercentageMod = pid.calculate(
// sRandomPlayerbotMgr->GetPlayers().empty() ? sPlayerbotAIConfig->diffEmpty : sPlayerbotAIConfig->diffWithPlayer,
// sWorldUpdateTime.GetAverageUpdateTime());
//
// activityPercentage = activityPercentageMod + 50;
// activityPercentage = activityPercentageMod + 50;
//
// // Cap the percentage between 0 and 100.
// activityPercentage = std::max(0.0f, std::min(100.0f, activityPercentage));
// // Cap the percentage between 0 and 100.
// activityPercentage = std::max(0.0f, std::min(100.0f, activityPercentage));
//
// setActivityPercentage(activityPercentage);
// }
// setActivityPercentage(activityPercentage);
//}
uint32 RandomPlayerbotMgr::AddRandomBots()
{
@@ -539,9 +514,7 @@ uint32 RandomPlayerbotMgr::AddRandomBots()
}
if (maxAllowedBotCount)
LOG_ERROR("playerbots",
"Not enough random bot accounts available. Need {} more, try to increase RandomBotAccountCount "
"in your conf file",
LOG_ERROR("playerbots", "Not enough random bot accounts available. Need {} more!!",
ceil(maxAllowedBotCount / 10));
}
@@ -617,18 +590,10 @@ void RandomPlayerbotMgr::CheckBgQueue()
BgCheckTimer = time(nullptr);
LOG_DEBUG("playerbots", "Checking BG Queue...");
LOG_INFO("playerbots", "Checking BG Queue...");
BattlegroundData.clear();
for (int bracket = BG_BRACKET_ID_FIRST; bracket < MAX_BATTLEGROUND_BRACKETS; ++bracket)
{
for (int queueType = BATTLEGROUND_QUEUE_AV; queueType < MAX_BATTLEGROUND_QUEUE_TYPES; ++queueType)
{
BattlegroundData[queueType][bracket] = BattlegroundInfo();
}
}
for (Player* player : players)
{
if (!player->InBattlegroundQueue())
@@ -875,8 +840,7 @@ void RandomPlayerbotMgr::LogBattlegroundInfo()
for (const auto& bracketIdPair : queueTypePair.second)
{
auto& bgInfo = bracketIdPair.second;
if (bgInfo.minLevel == 0)
continue;
LOG_INFO("playerbots",
"ARENA:{} {}: Player (Skirmish:{}, Rated:{}) Bots (Skirmish:{}, Rated:{}) Total (Skirmish:{} "
"Rated:{}), Instances (Skirmish:{} Rated:{})",
@@ -925,8 +889,6 @@ void RandomPlayerbotMgr::LogBattlegroundInfo()
for (const auto& bracketIdPair : queueTypePair.second)
{
auto& bgInfo = bracketIdPair.second;
if (bgInfo.minLevel == 0)
continue;
LOG_INFO("playerbots", "BG:{} {}: Player ({}:{}) Bot ({}:{}) Total (A:{} H:{}), Instances {}", _bgType,
std::to_string(bgInfo.minLevel) + "-" + std::to_string(bgInfo.maxLevel),
@@ -935,7 +897,7 @@ void RandomPlayerbotMgr::LogBattlegroundInfo()
bgInfo.bgHordePlayerCount + bgInfo.bgHordeBotCount, bgInfo.bgInstanceCount);
}
}
LOG_DEBUG("playerbots", "BG Queue check finished");
LOG_INFO("playerbots", "BG Queue check finished");
}
void RandomPlayerbotMgr::CheckLfgQueue()
@@ -943,7 +905,7 @@ void RandomPlayerbotMgr::CheckLfgQueue()
if (!LfgCheckTimer || time(nullptr) > (LfgCheckTimer + 30))
LfgCheckTimer = time(nullptr);
LOG_DEBUG("playerbots", "Checking LFG Queue...");
LOG_INFO("playerbots", "Checking LFG Queue...");
// Clear LFG list
LfgDungeons[TEAM_ALLIANCE].clear();
@@ -973,7 +935,7 @@ void RandomPlayerbotMgr::CheckLfgQueue()
}
}
LOG_DEBUG("playerbots", "LFG Queue check finished");
LOG_INFO("playerbots", "LFG Queue check finished");
}
void RandomPlayerbotMgr::CheckPlayers()
@@ -1003,7 +965,10 @@ void RandomPlayerbotMgr::CheckPlayers()
LOG_INFO("playerbots", "Max player level is {}, max bot level set to {}", playersLevel - 3, playersLevel);
}
void RandomPlayerbotMgr::ScheduleRandomize(uint32 bot, uint32 time) { SetEventValue(bot, "randomize", 1, time); }
void RandomPlayerbotMgr::ScheduleRandomize(uint32 bot, uint32 time)
{
SetEventValue(bot, "randomize", 1, time);
}
void RandomPlayerbotMgr::ScheduleTeleport(uint32 bot, uint32 time)
{
@@ -1119,7 +1084,9 @@ bool RandomPlayerbotMgr::ProcessBot(uint32 bot)
if (update)
ProcessBot(player);
randomTime = urand(sPlayerbotAIConfig->minRandomBotReviveTime, sPlayerbotAIConfig->maxRandomBotReviveTime);
randomTime = urand(
sPlayerbotAIConfig->minRandomBotReviveTime,
sPlayerbotAIConfig->maxRandomBotReviveTime);
SetEventValue(bot, "update", 1, randomTime);
return true;
@@ -1132,8 +1099,9 @@ bool RandomPlayerbotMgr::ProcessBot(uint32 bot)
player->GetLevel(), player->GetName().c_str());
LogoutPlayerBot(botGUID);
currentBots.remove(bot);
SetEventValue(bot, "logout", 1,
urand(sPlayerbotAIConfig->minRandomBotInWorldTime, sPlayerbotAIConfig->maxRandomBotInWorldTime));
SetEventValue(bot, "logout", 1, urand(
sPlayerbotAIConfig->minRandomBotInWorldTime,
sPlayerbotAIConfig->maxRandomBotInWorldTime));
return true;
}
@@ -1155,10 +1123,11 @@ bool RandomPlayerbotMgr::ProcessBot(Player* player)
{
if (!GetEventValue(bot, "dead"))
{
uint32 randomTime =
urand(sPlayerbotAIConfig->minRandomBotReviveTime, sPlayerbotAIConfig->maxRandomBotReviveTime);
LOG_DEBUG("playerbots", "Mark bot {} as dead, will be revived in {}s.", player->GetName().c_str(),
randomTime);
uint32 randomTime = urand(
sPlayerbotAIConfig->minRandomBotReviveTime,
sPlayerbotAIConfig->maxRandomBotReviveTime);
LOG_INFO("playerbots", "Mark bot {} as dead, will be revived in {}s.",
player->GetName().c_str(), randomTime);
SetEventValue(bot, "dead", 1, sPlayerbotAIConfig->maxRandomBotInWorldTime);
SetEventValue(bot, "revive", 1, randomTime);
return false;
@@ -1181,10 +1150,11 @@ bool RandomPlayerbotMgr::ProcessBot(Player* player)
LOG_INFO("playerbots", "Bot {} remove from group since leader is random bot.", player->GetName().c_str());
}
// only randomize and teleport idle bots
bool idleBot = false;
PlayerbotAI* botAI = GET_PLAYERBOT_AI(player);
if (botAI)
if (botAI)
{
if (TravelTarget* target = botAI->GetAiObjectContext()->GetValue<TravelTarget*>("travel target")->Get())
{
@@ -1193,10 +1163,10 @@ bool RandomPlayerbotMgr::ProcessBot(Player* player)
idleBot = true;
}
}
else
else
{
idleBot = true;
}
}
}
if (idleBot)
{
@@ -1204,7 +1174,7 @@ bool RandomPlayerbotMgr::ProcessBot(Player* player)
uint32 randomize = GetEventValue(bot, "randomize");
if (!randomize)
{
// bool randomiser = true;
// bool randomiser = true;
// if (player->GetGuildId())
// {
// if (Guild* guild = sGuildMgr->GetGuildById(player->GetGuildId()))
@@ -1226,10 +1196,11 @@ bool RandomPlayerbotMgr::ProcessBot(Player* player)
// if (randomiser)
// {
Randomize(player);
LOG_DEBUG("playerbots", "Bot #{} {}:{} <{}>: randomized", bot,
player->GetTeamId() == TEAM_ALLIANCE ? "A" : "H", player->GetLevel(), player->GetName());
uint32 randomTime =
urand(sPlayerbotAIConfig->minRandomBotRandomizeTime, sPlayerbotAIConfig->maxRandomBotRandomizeTime);
LOG_INFO("playerbots", "Bot #{} {}:{} <{}>: randomized",
bot, player->GetTeamId() == TEAM_ALLIANCE ? "A" : "H", player->GetLevel(), player->GetName());
uint32 randomTime = urand(
sPlayerbotAIConfig->minRandomBotRandomizeTime,
sPlayerbotAIConfig->maxRandomBotRandomizeTime);
ScheduleRandomize(bot, randomTime);
return true;
}
@@ -1245,11 +1216,12 @@ bool RandomPlayerbotMgr::ProcessBot(Player* player)
uint32 teleport = GetEventValue(bot, "teleport");
if (!teleport)
{
LOG_DEBUG("playerbots", "Bot #{} <{}>: teleport for level and refresh", bot, player->GetName());
LOG_INFO("playerbots", "Bot #{} <{}>: teleport for level and refresh", bot, player->GetName());
Refresh(player);
RandomTeleportForLevel(player);
uint32 time = urand(sPlayerbotAIConfig->minRandomBotTeleportInterval,
sPlayerbotAIConfig->maxRandomBotTeleportInterval);
uint32 time = urand(
sPlayerbotAIConfig->minRandomBotTeleportInterval,
sPlayerbotAIConfig->maxRandomBotTeleportInterval);
ScheduleTeleport(bot, time);
return true;
}
@@ -1274,7 +1246,7 @@ void RandomPlayerbotMgr::RandomTeleport(Player* bot, std::vector<WorldLocation>&
{
// ignore when alrdy teleported or not in the world yet.
if (bot->IsBeingTeleported() || !bot->IsInWorld())
return;
return;
// ignore when in queue for battle grounds.
if (bot->InBattlegroundQueue())
@@ -1290,10 +1262,11 @@ void RandomPlayerbotMgr::RandomTeleport(Player* bot, std::vector<WorldLocation>&
PlayerbotAI* botAI = GET_PLAYERBOT_AI(bot);
if (botAI)
{
{
// ignore when in when taxi with boat/zeppelin and has players nearby
if (bot->HasUnitMovementFlag(MOVEMENTFLAG_ONTRANSPORT) && bot->HasUnitState(UNIT_STATE_IGNORE_PATHFINDING) &&
botAI->HasPlayerNearby())
if (bot->HasUnitMovementFlag(MOVEMENTFLAG_ONTRANSPORT) &&
bot->HasUnitState(UNIT_STATE_IGNORE_PATHFINDING) &&
botAI->HasPlayerNearby())
return;
}
@@ -1382,19 +1355,17 @@ void RandomPlayerbotMgr::RandomTeleport(Player* bot, std::vector<WorldLocation>&
{
continue;
}
if (bot->GetLevel() <= 16 && (loc.GetMapId() != pInfo->mapId || dis > 10000.0f))
if (bot->GetLevel() <= 18 && (loc.GetMapId() != pInfo->mapId || dis > 10000.0f))
{
continue;
}
const LocaleConstant& locale = sWorld->GetDefaultDbcLocale();
LOG_DEBUG("playerbots",
"Random teleporting bot {} (level {}) to Map: {} ({}) Zone: {} ({}) Area: {} ({}) ZoneLevel: {} "
"AreaLevel: {} {},{},{} ({}/{} "
"locations)",
bot->GetName().c_str(), bot->GetLevel(), map->GetId(), map->GetMapName(), zone->ID,
zone->area_name[locale], area->ID, area->area_name[locale], zone->area_level, area->area_level, x, y,
z, i + 1, tlocs.size());
LOG_INFO("playerbots",
"Random teleporting bot {} (level {}) to Map: {} ({}) Zone: {} ({}) Area: {} ({}) {},{},{} ({}/{} "
"locations)",
bot->GetName().c_str(), bot->GetLevel(), map->GetId(), map->GetMapName(), zone->ID,
zone->area_name[locale], area->ID, area->area_name[locale], x, y, z, i + 1, tlocs.size());
if (hearth)
{
@@ -1433,35 +1404,32 @@ void RandomPlayerbotMgr::PrepareTeleportCache()
"position_x, "
"position_y, "
"position_z, "
"t.minlevel, "
"t.maxlevel "
"t.minlevel "
"FROM "
"(SELECT "
"map, "
"MIN( c.guid ) guid "
"MIN( c.guid ) guid, "
"t.entry "
"FROM "
"creature c "
"INNER JOIN creature_template t ON c.id1 = t.entry "
"WHERE "
"t.npcflag = 0 "
"AND t.lootid != 0 "
"AND t.unit_flags != 768 "
"AND t.maxlevel - t.minlevel < 3 "
"AND map IN ({}) "
"AND t.entry not in (32820, 24196, 30627, 30617) "
"AND c.id1 != 32820 "
"AND c.spawntimesecs < 1000 "
"AND t.faction not in (11, 71, 79, 85, 188, 1575) "
"AND (t.unit_flags & 256) = 0 "
"AND (t.unit_flags & 4096) = 0 "
"AND t.rank = 0 "
// "AND (t.flags_extra & 32768) = 0 "
"AND t.faction != 188 "
"GROUP BY "
"map, "
"ROUND(position_x / 50), "
"ROUND(position_y / 50), "
"ROUND(position_z / 50) "
"ROUND( position_x / 500 ), "
"ROUND( position_y / 500 ), "
"ROUND( position_z / 50), "
"t.entry "
"HAVING "
"count(*) >= 2) "
"AS g "
"count(*) > 7) AS g "
"INNER JOIN creature c ON g.guid = c.guid "
"INNER JOIN creature_template t on c.id1 = t.entry "
"ORDER BY "
@@ -1477,9 +1445,7 @@ void RandomPlayerbotMgr::PrepareTeleportCache()
float x = fields[1].Get<float>();
float y = fields[2].Get<float>();
float z = fields[3].Get<float>();
uint32 min_level = fields[4].Get<uint32>();
uint32 max_level = fields[5].Get<uint32>();
uint32 level = (min_level + max_level + 1) / 2;
uint32 level = fields[4].Get<uint32>();
WorldLocation loc(mapId, x, y, z, 0);
collected_locs++;
for (int32 l = (int32)level - (int32)sPlayerbotAIConfig->randomBotTeleHigherLevel;
@@ -1493,117 +1459,8 @@ void RandomPlayerbotMgr::PrepareTeleportCache()
}
} while (results->NextRow());
}
LOG_INFO("playerbots", ">> {} locations for level collected.", collected_locs);
LOG_INFO("playerbots", "{} locations for level collected.", collected_locs);
LOG_INFO("playerbots", "Preparing innkeepers locations for level collected...");
if (sPlayerbotAIConfig->enableNewRpgStrategy)
{
results = WorldDatabase.Query(
"SELECT "
"map, "
"position_x, "
"position_y, "
"position_z, "
"orientation, "
"t.faction, "
"t.entry "
"FROM "
"creature c "
"INNER JOIN creature_template t on c.id1 = t.entry "
"WHERE "
"t.npcflag & 73728 "
"AND map IN ({}) "
"ORDER BY "
"t.minlevel;",
sPlayerbotAIConfig->randomBotMapsAsString.c_str());
collected_locs = 0;
if (results)
{
do
{
Field* fields = results->Fetch();
uint16 mapId = fields[0].Get<uint16>();
float x = fields[1].Get<float>();
float y = fields[2].Get<float>();
float z = fields[3].Get<float>();
float orient = fields[4].Get<float>();
uint32 faction = fields[5].Get<uint32>();
uint32 c_entry = fields[6].Get<uint32>();
const FactionTemplateEntry* entry = sFactionTemplateStore.LookupEntry(faction);
WorldLocation loc(mapId, x + cos(orient) * 5.0f, y + sin(orient) * 5.0f, z + 0.5f, orient + M_PI);
collected_locs++;
Map* map = sMapMgr->FindMap(loc.GetMapId(), 0);
if (!map)
continue;
const AreaTableEntry* area = sAreaTableStore.LookupEntry(map->GetAreaId(1, x, y, z));
uint32 zoneId = area->zone ? area->zone : area->ID;
uint32 level = area->area_level;
for (int i = 5; i <= maxLevel; i++)
{
std::vector<WorldLocation>& locs = locsPerLevelCache[i];
int counter = 0;
WorldLocation levelLoc;
for (auto& checkLoc : locs)
{
if (loc.GetMapId() != checkLoc.GetMapId())
continue;
if (loc.GetExactDist(checkLoc) > 1500.0f)
continue;
if (zoneId !=
map->GetZoneId(1, checkLoc.GetPositionX(), checkLoc.GetPositionY(), checkLoc.GetPositionZ()))
continue;
counter++;
levelLoc = checkLoc;
if (counter >= 15)
break;
}
if (counter < 15)
continue;
if (!(entry->hostileMask & 4))
{
hordeStarterPerLevelCache[i].push_back(loc);
}
if (!(entry->hostileMask & 2))
{
allianceStarterPerLevelCache[i].push_back(loc);
}
LOG_DEBUG("playerbots", "Area: {} Level: {} creature_entry: {} add to: {} {}({},{},{},{})", area->ID,
level, c_entry, i, counter, levelLoc.GetPositionX(), levelLoc.GetPositionY(),
levelLoc.GetPositionZ(), levelLoc.GetMapId());
}
} while (results->NextRow());
}
// add all initial position
for (uint32 i = 1; i < MAX_RACES; i++)
{
for (uint32 j = 1; j < MAX_CLASSES; j++)
{
PlayerInfo const* info = sObjectMgr->GetPlayerInfo(i, j);
if (!info)
continue;
WorldPosition pos(info->mapId, info->positionX, info->positionY, info->positionZ, info->orientation);
for (int32 l = 1; l <= 5; l++)
{
if ((1 << (i - 1)) & RACEMASK_ALLIANCE)
allianceStarterPerLevelCache[(uint8)l].push_back(pos);
else
hordeStarterPerLevelCache[(uint8)l].push_back(pos);
}
break;
}
}
LOG_INFO("playerbots", ">> {} innkeepers locations for level collected.", collected_locs);
}
results = WorldDatabase.Query(
"SELECT "
"map, "
@@ -1625,7 +1482,6 @@ void RandomPlayerbotMgr::PrepareTeleportCache()
"AND t.faction != 69 "
"AND t.entry != 30606 "
"AND t.entry != 30608 "
"AND t.entry != 29282 "
"AND t.faction != 69 "
"AND map IN ({}) "
"ORDER BY "
@@ -1667,15 +1523,15 @@ void RandomPlayerbotMgr::PrepareTeleportCache()
}
} while (results->NextRow());
}
LOG_INFO("playerbots", ">> {} banker locations for level collected.", collected_locs);
LOG_INFO("playerbots", "{} banker locations for level collected.", collected_locs);
}
void RandomPlayerbotMgr::PrepareAddclassCache()
{
int32 maxAccountId = sPlayerbotAIConfig->randomBotAccounts.back();
int32 minIdx = sPlayerbotAIConfig->randomBotAccounts.size() - 1 >= sPlayerbotAIConfig->addClassAccountPoolSize
? sPlayerbotAIConfig->randomBotAccounts.size() - sPlayerbotAIConfig->addClassAccountPoolSize
: 0;
int32 minIdx =
sPlayerbotAIConfig->randomBotAccounts.size() - 1 >= sPlayerbotAIConfig->addClassAccountPoolSize
? sPlayerbotAIConfig->randomBotAccounts.size() - sPlayerbotAIConfig->addClassAccountPoolSize : 0;
int32 minAccountId = sPlayerbotAIConfig->randomBotAccounts[minIdx];
if (minAccountId < 0)
{
@@ -1705,7 +1561,7 @@ void RandomPlayerbotMgr::PrepareAddclassCache()
} while (results->NextRow());
}
}
LOG_INFO("playerbots", ">> {} characters collected for addclass command.", collected);
LOG_INFO("playerbots", "{} characters collected for addclass command.", collected);
}
void RandomPlayerbotMgr::RandomTeleportForLevel(Player* bot)
@@ -1715,20 +1571,15 @@ void RandomPlayerbotMgr::RandomTeleportForLevel(Player* bot)
uint32 level = bot->GetLevel();
uint8 race = bot->getRace();
std::vector<WorldLocation>* locs = nullptr;
if (sPlayerbotAIConfig->enableNewRpgStrategy)
locs = IsAlliance(race) ? &allianceStarterPerLevelCache[level] : &hordeStarterPerLevelCache[level];
else
locs = &locsPerLevelCache[level];
LOG_DEBUG("playerbots", "Random teleporting bot {} for level {} ({} locations available)", bot->GetName().c_str(),
bot->GetLevel(), locs->size());
if (level >= 10 && urand(0, 100) < sPlayerbotAIConfig->probTeleToBankers * 100)
bot->GetLevel(), locsPerLevelCache[level].size());
if (level > 10 && urand(0, 100) < sPlayerbotAIConfig->probTeleToBankers * 100)
{
RandomTeleport(bot, bankerLocsPerLevelCache[level], true);
}
else
{
RandomTeleport(bot, *locs);
RandomTeleport(bot, locsPerLevelCache[level]);
}
}
@@ -1739,15 +1590,10 @@ void RandomPlayerbotMgr::RandomTeleportGrindForLevel(Player* bot)
uint32 level = bot->GetLevel();
uint8 race = bot->getRace();
std::vector<WorldLocation>* locs = nullptr;
if (sPlayerbotAIConfig->enableNewRpgStrategy)
locs = IsAlliance(race) ? &allianceStarterPerLevelCache[level] : &hordeStarterPerLevelCache[level];
else
locs = &locsPerLevelCache[level];
LOG_DEBUG("playerbots", "Random teleporting bot {} for level {} ({} locations available)", bot->GetName().c_str(),
bot->GetLevel(), locs->size());
bot->GetLevel(), locsPerLevelCache[level].size());
RandomTeleport(bot, *locs);
RandomTeleport(bot, locsPerLevelCache[level]);
}
void RandomPlayerbotMgr::RandomTeleport(Player* bot)
@@ -2397,7 +2243,7 @@ void RandomPlayerbotMgr::OnBotLoginInternal(Player* const bot)
{
LOG_INFO("playerbots", "{}/{} Bot {} logged in", playerBots.size(), sRandomPlayerbotMgr->GetMaxAllowedBotCount(),
bot->GetName().c_str());
if (sPlayerbotAIConfig->randomBotFixedLevel)
{
bot->SetPlayerFlag(PLAYER_FLAGS_NO_XP_GAIN);
@@ -2515,8 +2361,7 @@ Player* RandomPlayerbotMgr::GetRandomPlayer()
void RandomPlayerbotMgr::PrintStats()
{
printStatsTimer = time(nullptr);
LOG_INFO("playerbots", "Random Bots Stats: {} online", playerBots.size());
LOG_INFO("playerbots", "{} Random Bots online", playerBots.size());
std::map<uint8, uint32> alliance, horde;
for (uint32 i = 0; i < 10; ++i)
@@ -2558,17 +2403,13 @@ void RandomPlayerbotMgr::PrintStats()
uint32 engine_dead = 0;
uint32 stateCount[MAX_TRAVEL_STATE + 1] = {0};
std::vector<std::pair<Quest const*, int32>> questCount;
std::unordered_map<NewRpgStatus, int> rpgStatusCount;
std::unordered_map<uint32, int> zoneCount;
uint8 maxBotLevel = 0;
for (PlayerBotMap::iterator i = playerBots.begin(); i != playerBots.end(); ++i)
{
Player* bot = i->second;
if (IsAlliance(bot->getRace()))
++alliance[bot->GetLevel()];
++alliance[bot->GetLevel() / 10];
else
++horde[bot->GetLevel()];
maxBotLevel = std::max(maxBotLevel, bot->GetLevel());
++horde[bot->GetLevel() / 10];
++perRace[bot->getRace()];
++perClass[bot->getClass()];
@@ -2622,7 +2463,7 @@ void RandomPlayerbotMgr::PrintStats()
++engine_combat;
else
++engine_dead;
if (botAI->IsHeal(bot, true))
++heal;
else if (botAI->IsTank(bot, true))
@@ -2630,11 +2471,6 @@ void RandomPlayerbotMgr::PrintStats()
else
++dps;
zoneCount[bot->GetZoneId()]++;
if (sPlayerbotAIConfig->enableNewRpgStrategy)
rpgStatusCount[botAI->rpgInfo.status]++;
if (TravelTarget* target = botAI->GetAiObjectContext()->GetValue<TravelTarget*>("travel target")->Get())
{
TravelState state = target->getTravelState();
@@ -2664,22 +2500,18 @@ void RandomPlayerbotMgr::PrintStats()
}
LOG_INFO("playerbots", "Bots level:");
// uint32 maxLevel = sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL);
uint32 currentAlliance = 0, currentHorde = 0;
uint32 step = std::max(1, (maxBotLevel + 4) / 8);
uint32 from = 1;
for (uint8 i = 1; i <= maxBotLevel; ++i)
uint32 maxLevel = sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL);
for (uint8 i = 0; i < 10; ++i)
{
currentAlliance += alliance[i];
currentHorde += horde[i];
if (!alliance[i] && !horde[i])
continue;
if (((i + 1) % step == 0) || i == maxBotLevel)
{
LOG_INFO("playerbots", " {}..{}: {} alliance, {} horde", from, i, currentAlliance, currentHorde);
currentAlliance = 0;
currentHorde = 0;
from = i + 1;
}
uint32 from = i * 10;
uint32 to = std::min(from + 9, maxLevel);
if (!from)
from = 1;
LOG_INFO("playerbots", " {}..{}: {} alliance, {} horde", from, to, alliance[i], horde[i]);
}
LOG_INFO("playerbots", "Bots race:");
@@ -2716,39 +2548,20 @@ void RandomPlayerbotMgr::PrintStats()
LOG_INFO("playerbots", " In BG: {}", inBg);
LOG_INFO("playerbots", " In Rest: {}", rest);
LOG_INFO("playerbots", " Dead: {}", dead);
// LOG_INFO("playerbots", "Bots zone:");
// for (auto &[zond_id, counter] : zoneCount)
// {
// const AreaTableEntry* entry = sAreaTableStore.LookupEntry(zond_id);
// std::string name = PlayerbotAI::GetLocalizedAreaName(entry);
// LOG_INFO("playerbots", " {}: {}", name, counter);
// }
if (sPlayerbotAIConfig->enableNewRpgStrategy)
{
LOG_INFO("playerbots", "Bots rpg status:", dead);
LOG_INFO("playerbots", " IDLE: {}", rpgStatusCount[NewRpgStatus::IDLE]);
LOG_INFO("playerbots", " REST: {}", rpgStatusCount[NewRpgStatus::REST]);
LOG_INFO("playerbots", " GO_GRIND: {}", rpgStatusCount[NewRpgStatus::GO_GRIND]);
LOG_INFO("playerbots", " GO_INNKEEPER: {}", rpgStatusCount[NewRpgStatus::GO_INNKEEPER]);
LOG_INFO("playerbots", " NEAR_RANDOM: {}", rpgStatusCount[NewRpgStatus::NEAR_RANDOM]);
LOG_INFO("playerbots", " NEAR_NPC: {}", rpgStatusCount[NewRpgStatus::NEAR_NPC]);
}
LOG_INFO("playerbots", "Bots engine:", dead);
LOG_INFO("playerbots", " Non-combat: {}", engine_noncombat);
LOG_INFO("playerbots", " Combat: {}", engine_combat);
LOG_INFO("playerbots", " Dead: {}", engine_dead);
// LOG_INFO("playerbots", "Bots questing:");
// LOG_INFO("playerbots", " Picking quests: {}",
// stateCount[TRAVEL_STATE_TRAVEL_PICK_UP_QUEST] + stateCount[TRAVEL_STATE_WORK_PICK_UP_QUEST]);
// LOG_INFO("playerbots", " Doing quests: {}",
// stateCount[TRAVEL_STATE_TRAVEL_DO_QUEST] + stateCount[TRAVEL_STATE_WORK_DO_QUEST]);
// LOG_INFO("playerbots", " Completing quests: {}",
// stateCount[TRAVEL_STATE_TRAVEL_HAND_IN_QUEST] + stateCount[TRAVEL_STATE_WORK_HAND_IN_QUEST]);
// LOG_INFO("playerbots", " Idling: {}", stateCount[TRAVEL_STATE_IDLE]);
LOG_INFO("playerbots", "Bots questing:");
LOG_INFO("playerbots", " Picking quests: {}",
stateCount[TRAVEL_STATE_TRAVEL_PICK_UP_QUEST] + stateCount[TRAVEL_STATE_WORK_PICK_UP_QUEST]);
LOG_INFO("playerbots", " Doing quests: {}",
stateCount[TRAVEL_STATE_TRAVEL_DO_QUEST] + stateCount[TRAVEL_STATE_WORK_DO_QUEST]);
LOG_INFO("playerbots", " Completing quests: {}",
stateCount[TRAVEL_STATE_TRAVEL_HAND_IN_QUEST] + stateCount[TRAVEL_STATE_WORK_HAND_IN_QUEST]);
LOG_INFO("playerbots", " Idling: {}", stateCount[TRAVEL_STATE_IDLE]);
/*sort(questCount.begin(), questCount.end(), [](std::pair<Quest const*, int32> i, std::pair<Quest const*, int32> j)
{return i.second > j.second; });

View File

@@ -171,10 +171,6 @@ public:
void PrepareAddclassCache();
std::map<uint8, std::vector<ObjectGuid>> addclassCache;
std::map<uint8, std::vector<WorldLocation>> locsPerLevelCache;
std::map<uint8, std::vector<WorldLocation>> allianceStarterPerLevelCache;
std::map<uint8, std::vector<WorldLocation>> hordeStarterPerLevelCache;
std::map<uint8, std::vector<WorldLocation>> bankerLocsPerLevelCache;
protected:
void OnBotLoginInternal(Player* const bot) override;
@@ -192,7 +188,6 @@ private:
time_t BgCheckTimer;
time_t LfgCheckTimer;
time_t PlayersCheckTimer;
time_t printStatsTimer;
uint32 AddRandomBots();
bool ProcessBot(uint32 bot);
void ScheduleRandomize(uint32 bot, uint32 time);
@@ -204,7 +199,8 @@ private:
std::vector<Player*> players;
uint32 processTicks;
std::map<uint8, std::vector<WorldLocation>> locsPerLevelCache;
std::map<uint8, std::vector<WorldLocation>> bankerLocsPerLevelCache;
// std::map<uint32, std::vector<WorldLocation>> rpgLocsCache;
std::map<uint32, std::map<uint32, std::vector<WorldLocation>>> rpgLocsCacheLevel;

View File

@@ -207,10 +207,7 @@ void PlayerbotFactory::Randomize(bool incremental)
Prepare();
LOG_DEBUG("playerbots", "Resetting player...");
PerformanceMonitorOperation* pmo = sPerformanceMonitor->start(PERF_MON_RNDBOT, "PlayerbotFactory_Reset");
if (!sPlayerbotAIConfig->equipmentPersistence || level < sPlayerbotAIConfig->equipmentPersistenceLevel)
{
bot->resetTalents(true);
}
bot->resetTalents(true);
// bot->SaveToDB(false, false);
ClearSkills();
// bot->SaveToDB(false, false);
@@ -270,7 +267,7 @@ void PlayerbotFactory::Randomize(bool incremental)
pmo = sPerformanceMonitor->start(PERF_MON_RNDBOT, "PlayerbotFactory_Talents");
LOG_DEBUG("playerbots", "Initializing talents...");
if (!incremental || !sPlayerbotAIConfig->equipmentPersistence || bot->GetLevel() < sPlayerbotAIConfig->equipmentPersistenceLevel)
if (!sPlayerbotAIConfig->equipmentPersistence || bot->GetLevel() < sPlayerbotAIConfig->equipmentPersistenceLevel)
{
InitTalentsTree();
}
@@ -305,7 +302,7 @@ void PlayerbotFactory::Randomize(bool incremental)
pmo = sPerformanceMonitor->start(PERF_MON_RNDBOT, "PlayerbotFactory_Equip");
LOG_DEBUG("playerbots", "Initializing equipmemt...");
if (!incremental || !sPlayerbotAIConfig->equipmentPersistence || bot->GetLevel() < sPlayerbotAIConfig->equipmentPersistenceLevel)
if (!sPlayerbotAIConfig->equipmentPersistence || bot->GetLevel() < sPlayerbotAIConfig->equipmentPersistenceLevel)
{
InitEquipment(incremental, incremental ? false : sPlayerbotAIConfig->twoRoundsGearInit);
}
@@ -427,7 +424,7 @@ void PlayerbotFactory::Randomize(bool incremental)
bot->SetHealth(bot->GetMaxHealth());
bot->SetPower(POWER_MANA, bot->GetMaxPower(POWER_MANA));
bot->SaveToDB(false, false);
// LOG_INFO("playerbots", "Initialization Done.");
LOG_INFO("playerbots", "Initialization Done.");
if (pmo)
pmo->finish();
}

View File

@@ -24,10 +24,6 @@
#include "raids/blackwinglair/RaidBwlTriggerContext.h"
#include "raids/naxxramas/RaidNaxxActionContext.h"
#include "raids/naxxramas/RaidNaxxTriggerContext.h"
#include "raids/obsidiansanctum/RaidOsActionContext.h"
#include "raids/obsidiansanctum/RaidOsTriggerContext.h"
#include "raids/eyeofeternity/RaidEoEActionContext.h"
#include "raids/eyeofeternity/RaidEoETriggerContext.h"
#include "raids/moltencore/RaidMcActionContext.h"
#include "raids/moltencore/RaidMcTriggerContext.h"
#include "raids/aq20/RaidAq20ActionContext.h"
@@ -52,8 +48,6 @@ AiObjectContext::AiObjectContext(PlayerbotAI* botAI) : PlayerbotAIAware(botAI)
actionContexts.Add(new RaidBwlActionContext());
actionContexts.Add(new RaidAq20ActionContext());
actionContexts.Add(new RaidNaxxActionContext());
actionContexts.Add(new RaidOsActionContext());
actionContexts.Add(new RaidEoEActionContext());
actionContexts.Add(new RaidUlduarActionContext());
actionContexts.Add(new RaidIccActionContext());
actionContexts.Add(new WotlkDungeonUKActionContext());
@@ -77,8 +71,6 @@ AiObjectContext::AiObjectContext(PlayerbotAI* botAI) : PlayerbotAIAware(botAI)
triggerContexts.Add(new RaidBwlTriggerContext());
triggerContexts.Add(new RaidAq20TriggerContext());
triggerContexts.Add(new RaidNaxxTriggerContext());
triggerContexts.Add(new RaidOsTriggerContext());
triggerContexts.Add(new RaidEoETriggerContext());
triggerContexts.Add(new RaidUlduarTriggerContext());
triggerContexts.Add(new RaidIccTriggerContext());
triggerContexts.Add(new WotlkDungeonUKTriggerContext());

View File

@@ -31,7 +31,6 @@
#include "MeleeCombatStrategy.h"
#include "MoveFromGroupStrategy.h"
#include "NamedObjectContext.h"
#include "NewRpgStrategy.h"
#include "NonCombatStrategy.h"
#include "PassiveStrategy.h"
#include "PullStrategy.h"
@@ -83,7 +82,6 @@ public:
creators["reveal"] = &StrategyContext::reveal;
creators["collision"] = &StrategyContext::collision;
creators["rpg"] = &StrategyContext::rpg;
creators["new rpg"] = &StrategyContext::new_rpg;
creators["travel"] = &StrategyContext::travel;
creators["explore"] = &StrategyContext::explore;
creators["map"] = &StrategyContext::map;
@@ -154,7 +152,6 @@ private:
static Strategy* reveal(PlayerbotAI* botAI) { return new RevealStrategy(botAI); }
static Strategy* collision(PlayerbotAI* botAI) { return new CollisionStrategy(botAI); }
static Strategy* rpg(PlayerbotAI* botAI) { return new RpgStrategy(botAI); }
static Strategy* new_rpg(PlayerbotAI* botAI) { return new NewRpgStrategy(botAI); }
static Strategy* travel(PlayerbotAI* botAI) { return new TravelStrategy(botAI); }
static Strategy* explore(PlayerbotAI* botAI) { return new ExploreStrategy(botAI); }
static Strategy* map(PlayerbotAI* botAI) { return new MapStrategy(botAI); }

View File

@@ -17,8 +17,8 @@ bool AcceptAllQuestsAction::ProcessQuest(Quest const* quest, Object* questGiver)
if (botAI->HasStrategy("debug quest", BotState::BOT_STATE_NON_COMBAT) || botAI->HasStrategy("debug rpg", BotState::BOT_STATE_COMBAT))
{
LOG_INFO("playerbots", "{} => Quest [{}] accepted", bot->GetName(), quest->GetTitle());
bot->Say("Quest [" + text_quest + "] accepted", LANG_UNIVERSAL);
LOG_INFO("playerbots", "{} => Quest [ {} ] accepted", bot->GetName(), quest->GetTitle());
bot->Say("Quest [ " + text_quest + " ] accepted", LANG_UNIVERSAL);
}
return true;
@@ -86,7 +86,7 @@ bool AcceptQuestAction::Execute(Event event)
if (hasAccept)
{
std::stringstream ss;
ss << "AcceptQuestAction [" << qInfo->GetTitle() << "] - [" << std::to_string(qInfo->GetQuestId()) << "]";
ss << "AcceptQuestAction {" << qInfo->GetTitle() << "} - {" << std::to_string(qInfo->GetQuestId()) << "}";
LOG_INFO("playerbots", "{}", ss.str().c_str());
// botAI->TellMaster(ss.str());
}

View File

@@ -62,7 +62,6 @@
#include "VehicleActions.h"
#include "WorldBuffAction.h"
#include "XpGainAction.h"
#include "NewRpgAction.h"
class PlayerbotAI;
@@ -241,12 +240,6 @@ public:
creators["toggle pet spell"] = &ActionContext::toggle_pet_spell;
creators["pet attack"] = &ActionContext::pet_attack;
creators["new rpg status update"] = &ActionContext::new_rpg_status_update;
creators["new rpg go grind"] = &ActionContext::new_rpg_go_grind;
creators["new rpg go innkeeper"] = &ActionContext::new_rpg_go_innkeeper;
creators["new rpg move random"] = &ActionContext::new_rpg_move_random;
creators["new rpg move npc"] = &ActionContext::new_rpg_move_npc;
}
private:
@@ -422,12 +415,6 @@ private:
static Action* toggle_pet_spell(PlayerbotAI* ai) { return new TogglePetSpellAutoCastAction(ai); }
static Action* pet_attack(PlayerbotAI* ai) { return new PetAttackAction(ai); }
static Action* new_rpg_status_update(PlayerbotAI* ai) { return new NewRpgStatusUpdateAction(ai); }
static Action* new_rpg_go_grind(PlayerbotAI* ai) { return new NewRpgGoGrindAction(ai); }
static Action* new_rpg_go_innkeeper(PlayerbotAI* ai) { return new NewRpgGoInnKeeperAction(ai); }
static Action* new_rpg_move_random(PlayerbotAI* ai) { return new NewRpgMoveRandomAction(ai); }
static Action* new_rpg_move_npc(PlayerbotAI* ai) { return new NewRpgMoveNpcAction(ai); }
};
#endif

View File

@@ -74,7 +74,6 @@ bool AttackAction::Attack(Unit* target, bool with_pet /*true*/)
{
return false;
}
std::ostringstream msg;
msg << target->GetName();
@@ -89,11 +88,9 @@ bool AttackAction::Attack(Unit* target, bool with_pet /*true*/)
if (!bot->IsWithinLOSInMap(target))
{
msg << " is not in my sight";
msg << " is not on my sight";
if (verbose)
botAI->TellError(msg.str());
return false;
}
if (target->isDead())
@@ -105,15 +102,6 @@ bool AttackAction::Attack(Unit* target, bool with_pet /*true*/)
return false;
}
if (sPlayerbotAIConfig->IsInPvpProhibitedZone(bot->GetZoneId())
&& (target->IsPlayer() || target->IsPet() || !bot->IsValidAttackTarget(target)))
{
if (verbose)
botAI->TellError("I cannot attack others in PvP prohibited zones");
return false;
}
// if (bot->IsMounted() && bot->IsWithinLOSInMap(target))
// {
// WorldPacket emptyPacket;

View File

@@ -38,7 +38,6 @@
#include "LootStrategyAction.h"
#include "MailAction.h"
#include "NamedObjectContext.h"
#include "NewRpgAction.h"
#include "PassLeadershipToMasterAction.h"
#include "PositionAction.h"
#include "QueryItemUsageAction.h"
@@ -89,7 +88,6 @@ public:
creators["reputation"] = &ChatActionContext::reputation;
creators["log"] = &ChatActionContext::log;
creators["los"] = &ChatActionContext::los;
creators["rpg status"] = &ChatActionContext::rpg_status;
creators["aura"] = &ChatActionContext::aura;
creators["drop"] = &ChatActionContext::drop;
creators["clean quest log"] = &ChatActionContext::clean_quest_log;
@@ -260,7 +258,6 @@ private:
static Action* reputation(PlayerbotAI* botAI) { return new TellReputationAction(botAI); }
static Action* log(PlayerbotAI* botAI) { return new LogLevelAction(botAI); }
static Action* los(PlayerbotAI* botAI) { return new TellLosAction(botAI); }
static Action* rpg_status(PlayerbotAI* botAI) { return new TellRpgStatusAction(botAI); }
static Action* aura(PlayerbotAI* ai) { return new TellAuraAction(ai); }
static Action* ll(PlayerbotAI* botAI) { return new LootStrategyAction(botAI); }
static Action* ss(PlayerbotAI* botAI) { return new SkipSpellsListAction(botAI); }

View File

@@ -10,8 +10,8 @@
#include "LootObjectStack.h"
#include "Playerbots.h"
#include "PossibleRpgTargetsValue.h"
#include "PvpTriggers.h"
#include "ServerFacade.h"
#include "PvpTriggers.h"
bool AttackEnemyPlayerAction::isUseful()
{
@@ -49,15 +49,8 @@ bool AttackAnythingAction::isUseful()
return false;
std::string const name = std::string(target->GetName());
// Check for invalid targets: Dummy, Charge Target, Melee Target, Ranged Target
if (!name.empty() &&
(name.find("Dummy") != std::string::npos ||
name.find("Charge Target") != std::string::npos ||
name.find("Melee Target") != std::string::npos ||
name.find("Ranged Target") != std::string::npos))
{
return false; // Target is one of the disallowed types
}
if (!name.empty() && name.find("Dummy") != std::string::npos) // Target is not a targetdummy
return false;
// if (!ChooseRpgTargetAction::isFollowValid(bot, target)) //Do not grind mobs far
// away from master.
@@ -136,33 +129,3 @@ bool DpsAssistAction::isUseful()
return true;
}
bool AttackRtiTargetAction::Execute(Event event)
{
Unit* rtiTarget = AI_VALUE(Unit*, "rti target");
if (rtiTarget && rtiTarget->IsInWorld() && rtiTarget->GetMapId() == bot->GetMapId())
{
botAI->GetAiObjectContext()->GetValue<GuidVector>("prioritized targets")->Set({rtiTarget->GetGUID()});
bool result = Attack(botAI->GetUnit(rtiTarget->GetGUID()));
if (result)
{
context->GetValue<ObjectGuid>("pull target")->Set(rtiTarget->GetGUID());
return true;
}
}
else
{
botAI->TellError("I dont see my rti attack target");
}
return false;
}
bool AttackRtiTargetAction::isUseful()
{
if (botAI->ContainsStrategy(STRATEGY_TYPE_HEAL))
return false;
return true;
}

View File

@@ -69,8 +69,6 @@ public:
AttackRtiTargetAction(PlayerbotAI* botAI) : AttackAction(botAI, "attack rti target") {}
std::string const GetTargetName() override { return "rti target"; }
bool Execute(Event event) override;
bool isUseful() override;
};
class AttackEnemyFlagCarrierAction : public AttackAction

View File

@@ -68,11 +68,6 @@ bool CleanQuestLogAction::Execute(Event event)
return false;
}
if (!sPlayerbotAIConfig->dropObsoleteQuests)
{
return false;
}
// Only output this message if "debug rpg" strategy is enabled
if (botAI->HasStrategy("debug rpg", BotState::BOT_STATE_COMBAT))
{

View File

@@ -65,210 +65,88 @@ void EquipAction::EquipItem(Item* item)
uint8 slot = item->GetSlot();
const ItemTemplate* itemProto = item->GetTemplate();
uint32 itemId = itemProto->ItemId;
uint8 invType = itemProto->InventoryType;
// Handle ammunition separately
if (invType == INVTYPE_AMMO)
if (itemProto->InventoryType == INVTYPE_AMMO)
{
bot->SetAmmo(itemId);
std::ostringstream out;
out << "equipping " << chat->FormatItem(itemProto);
botAI->TellMaster(out);
return;
}
// Handle bags first
bool equippedBag = false;
if (itemProto->Class == ITEM_CLASS_CONTAINER)
else
{
// Attempt to equip as a bag
Bag* pBag = reinterpret_cast<Bag*>(item);
uint8 newBagSlot = GetSmallestBagSlot();
if (newBagSlot > 0)
bool equippedBag = false;
if (itemProto->Class == ITEM_CLASS_CONTAINER)
{
uint16 src = ((bagIndex << 8) | slot);
uint16 dst = ((INVENTORY_SLOT_BAG_0 << 8) | newBagSlot);
bot->SwapItem(src, dst);
equippedBag = true;
}
}
// If we didn't equip as a bag, try to equip as gear
if (!equippedBag)
{
uint8 dstSlot = botAI->FindEquipSlot(itemProto, NULL_SLOT, true);
// Check if the item is a weapon and whether the bot can dual wield or use Titan Grip
bool isWeapon = (itemProto->Class == ITEM_CLASS_WEAPON);
bool canTitanGrip = bot->CanTitanGrip();
bool canDualWield = bot->CanDualWield();
bool isTwoHander = (invType == INVTYPE_2HWEAPON);
bool isValidTGWeapon = false;
if (canTitanGrip && isTwoHander)
{
// Titan Grip-valid 2H weapon subclasses: Axe2, Mace2, Sword2
isValidTGWeapon = (itemProto->SubClass == ITEM_SUBCLASS_WEAPON_AXE2 ||
itemProto->SubClass == ITEM_SUBCLASS_WEAPON_MACE2 ||
itemProto->SubClass == ITEM_SUBCLASS_WEAPON_SWORD2);
}
// Check if the main hand currently has a 2H weapon equipped
Item* currentMHItem = bot->GetItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_MAINHAND);
bool have2HWeaponEquipped = (currentMHItem && currentMHItem->GetTemplate()->InventoryType == INVTYPE_2HWEAPON);
bool canDualWieldOrTG = (canDualWield || (canTitanGrip && isTwoHander));
// If this is a weapon and we can dual wield or Titan Grip, check if we can improve main/off-hand setup
if (isWeapon && canDualWieldOrTG)
{
// Fetch current main hand and offhand items
Item* mainHandItem = bot->GetItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_MAINHAND);
Item* offHandItem = bot->GetItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_OFFHAND);
// Set up the stats calculator once and reuse results for performance
StatsWeightCalculator calculator(bot);
calculator.SetItemSetBonus(false);
calculator.SetOverflowPenalty(false);
// Calculate item scores once and store them
float newItemScore = calculator.CalculateItem(itemId);
float mainHandScore = mainHandItem ? calculator.CalculateItem(mainHandItem->GetTemplate()->ItemId) : 0.0f;
float offHandScore = offHandItem ? calculator.CalculateItem(offHandItem->GetTemplate()->ItemId) : 0.0f;
// Determine where this weapon can go
bool canGoMain = (invType == INVTYPE_WEAPON ||
invType == INVTYPE_WEAPONMAINHAND ||
(canTitanGrip && isTwoHander));
bool canTGOff = false;
if (canTitanGrip && isTwoHander && isValidTGWeapon)
canTGOff = true;
bool canGoOff = (invType == INVTYPE_WEAPON ||
invType == INVTYPE_WEAPONOFFHAND ||
canTGOff);
// Check if the main hand item can go to offhand if needed
bool mainHandCanGoOff = false;
if (mainHandItem)
Bag* pBag = (Bag*)&item;
uint8 newBagSlot = GetSmallestBagSlot();
if (newBagSlot > 0)
{
const ItemTemplate* mhProto = mainHandItem->GetTemplate();
bool mhIsValidTG = false;
if (canTitanGrip && mhProto->InventoryType == INVTYPE_2HWEAPON)
{
mhIsValidTG = (mhProto->SubClass == ITEM_SUBCLASS_WEAPON_AXE2 ||
mhProto->SubClass == ITEM_SUBCLASS_WEAPON_MACE2 ||
mhProto->SubClass == ITEM_SUBCLASS_WEAPON_SWORD2);
}
mainHandCanGoOff = (mhProto->InventoryType == INVTYPE_WEAPON ||
mhProto->InventoryType == INVTYPE_WEAPONOFFHAND ||
(mhProto->InventoryType == INVTYPE_2HWEAPON && mhIsValidTG));
}
// Priority 1: Replace main hand if the new weapon is strictly better
// and if conditions allow (e.g. no conflicting 2H logic)
bool betterThanMH = (newItemScore > mainHandScore);
bool mhConditionOK = ((invType != INVTYPE_2HWEAPON && !have2HWeaponEquipped) ||
(canTitanGrip && isValidTGWeapon));
if (canGoMain && betterThanMH && mhConditionOK)
{
// Equip new weapon in main hand
{
WorldPacket eqPacket(CMSG_AUTOEQUIP_ITEM_SLOT, 2);
ObjectGuid newItemGuid = item->GetGUID();
eqPacket << newItemGuid << uint8(EQUIPMENT_SLOT_MAINHAND);
bot->GetSession()->HandleAutoEquipItemSlotOpcode(eqPacket);
}
// Try moving old main hand weapon to offhand if beneficial
if (mainHandItem && mainHandCanGoOff && (!offHandItem || mainHandScore > offHandScore))
{
const ItemTemplate* oldMHProto = mainHandItem->GetTemplate();
WorldPacket offhandPacket(CMSG_AUTOEQUIP_ITEM_SLOT, 2);
ObjectGuid oldMHGuid = mainHandItem->GetGUID();
offhandPacket << oldMHGuid << uint8(EQUIPMENT_SLOT_OFFHAND);
bot->GetSession()->HandleAutoEquipItemSlotOpcode(offhandPacket);
std::ostringstream moveMsg;
moveMsg << "Main hand upgrade found. Moving " << chat->FormatItem(oldMHProto) << " to offhand";
botAI->TellMaster(moveMsg);
}
std::ostringstream out;
out << "Equipping " << chat->FormatItem(itemProto) << " in main hand";
botAI->TellMaster(out);
return;
}
// Priority 2: If not better than main hand, check if better than offhand
else if (canGoOff && newItemScore > offHandScore)
{
// Equip in offhand
WorldPacket eqPacket(CMSG_AUTOEQUIP_ITEM_SLOT, 2);
ObjectGuid newItemGuid = item->GetGUID();
eqPacket << newItemGuid << uint8(EQUIPMENT_SLOT_OFFHAND);
bot->GetSession()->HandleAutoEquipItemSlotOpcode(eqPacket);
std::ostringstream out;
out << "Equipping " << chat->FormatItem(itemProto) << " in offhand";
botAI->TellMaster(out);
return;
}
else
{
// No improvement, do nothing
return;
uint16 src = ((bagIndex << 8) | slot);
uint16 dst = ((INVENTORY_SLOT_BAG_0 << 8) | newBagSlot);
bot->SwapItem(src, dst);
equippedBag = true;
}
}
// If not a special dual-wield/TG scenario or no improvement found, fall back to original logic
if (dstSlot == EQUIPMENT_SLOT_FINGER1 ||
dstSlot == EQUIPMENT_SLOT_TRINKET1 ||
(dstSlot == EQUIPMENT_SLOT_MAINHAND && canDualWield &&
((invType != INVTYPE_2HWEAPON && !have2HWeaponEquipped) || (canTitanGrip && isValidTGWeapon))))
if (!equippedBag)
{
// Handle ring/trinket dual-slot logic
Item* const equippedItems[2] = {
bot->GetItemByPos(INVENTORY_SLOT_BAG_0, dstSlot),
bot->GetItemByPos(INVENTORY_SLOT_BAG_0, dstSlot + 1)
};
if (equippedItems[0])
uint8 dstSlot = botAI->FindEquipSlot(itemProto, NULL_SLOT, true);
bool have2HWeapon = false;
bool isValidTGWeapon = false;
if (dstSlot == EQUIPMENT_SLOT_MAINHAND)
{
if (equippedItems[1])
Item* currentWeapon = bot->GetItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_MAINHAND);
have2HWeapon = currentWeapon && currentWeapon->GetTemplate()->InventoryType == INVTYPE_2HWEAPON;
isValidTGWeapon = itemProto->SubClass == ITEM_SUBCLASS_WEAPON_AXE2 ||
itemProto->SubClass == ITEM_SUBCLASS_WEAPON_MACE2 ||
itemProto->SubClass == ITEM_SUBCLASS_WEAPON_SWORD2;
}
if (dstSlot == EQUIPMENT_SLOT_FINGER1 ||
dstSlot == EQUIPMENT_SLOT_TRINKET1 ||
(dstSlot == EQUIPMENT_SLOT_MAINHAND && bot->CanDualWield() &&
((itemProto->InventoryType != INVTYPE_2HWEAPON && !have2HWeapon) || (bot->CanTitanGrip() && isValidTGWeapon))))
{
Item* const equippedItems[2] = {
bot->GetItemByPos(INVENTORY_SLOT_BAG_0, dstSlot),
bot->GetItemByPos(INVENTORY_SLOT_BAG_0, dstSlot + 1)
};
if (equippedItems[0])
{
// Both slots are full - pick the worst item to replace
StatsWeightCalculator calc(bot);
calc.SetItemSetBonus(false);
calc.SetOverflowPenalty(false);
if (equippedItems[1])
{
// Both slots are full - determine worst item to replace
StatsWeightCalculator calculator(bot);
calculator.SetItemSetBonus(false);
calculator.SetOverflowPenalty(false);
// float newItemScore = calculator.CalculateItem(itemId);
float equippedItemScore[2] = {
equippedItemScore[0] = calculator.CalculateItem(equippedItems[0]->GetTemplate()->ItemId),
equippedItemScore[1] = calculator.CalculateItem(equippedItems[1]->GetTemplate()->ItemId)
};
float firstItemScore = calc.CalculateItem(equippedItems[0]->GetTemplate()->ItemId);
float secondItemScore = calc.CalculateItem(equippedItems[1]->GetTemplate()->ItemId);
// If the second slot is worse, place the new item there
if (firstItemScore > secondItemScore)
// Second item is worse than first, equip candidate item in second slot
if (equippedItemScore[0] > equippedItemScore[1])
{
dstSlot++;
}
}
else // No item equipped in slot 2, equip in that slot instead of replacing first item
{
dstSlot++;
}
}
else
{
// Second slot empty, use it
dstSlot++;
}
}
}
// Equip the item in the chosen slot
{
WorldPacket packet(CMSG_AUTOEQUIP_ITEM_SLOT, 2);
ObjectGuid itemguid = item->GetGUID();
packet << itemguid << dstSlot;
bot->GetSession()->HandleAutoEquipItemSlotOpcode(packet);
// WorldPacket packet(CMSG_AUTOEQUIP_ITEM, 2);
// packet << bagIndex << slot;
// bot->GetSession()->HandleAutoEquipItemOpcode(packet);
}
}
@@ -277,7 +155,6 @@ void EquipAction::EquipItem(Item* item)
botAI->TellMaster(out);
}
bool EquipUpgradesAction::Execute(Event event)
{
if (!sPlayerbotAIConfig->autoEquipUpgradeLoot && !sRandomPlayerbotMgr->IsRandomBot(bot))

View File

@@ -75,7 +75,7 @@ bool InventoryChangeFailureAction::Execute(Event event)
messages[EQUIP_ERR_BAG_FULL4] = messages[EQUIP_ERR_BAG_FULL];
messages[EQUIP_ERR_ITEM_SOLD_OUT] = messages[EQUIP_ERR_ITEM_IS_CURRENTLY_SOLD_OUT];
messages[EQUIP_ERR_OBJECT_IS_BUSY] = "This object is busy";
messages[EQUIP_ERR_NOT_IN_COMBAT] = "I am in combat";
messages[EQUIP_ERR_NOT_IN_COMBAT] = "I am not in combat";
messages[EQUIP_ERR_NOT_WHILE_DISARMED] = "Cannot do while disarmed";
messages[EQUIP_ERR_BAG_FULL6] = messages[EQUIP_ERR_BAG_FULL];
messages[EQUIP_ERR_CANT_EQUIP_RANK] = "Not enough rank";

View File

@@ -177,7 +177,7 @@ bool MovementAction::MoveToLOS(WorldObject* target, bool ranged)
}
bool MovementAction::MoveTo(uint32 mapId, float x, float y, float z, bool idle, bool react, bool normal_only,
bool exact_waypoint, MovementPriority priority, bool lessDelay)
bool exact_waypoint, MovementPriority priority)
{
UpdateMovementState();
if (!IsMovingAllowed(mapId, x, y, z))
@@ -210,10 +210,6 @@ bool MovementAction::MoveTo(uint32 mapId, float x, float y, float z, bool idle,
mm.Clear();
mm.MovePoint(0, x, y, z, generatePath);
float delay = 1000.0f * (distance / vehicleBase->GetSpeed(MOVE_RUN));
if (lessDelay)
{
delay -= botAI->GetReactDelay();
}
delay = std::max(.0f, delay);
delay = std::min((float)sPlayerbotAIConfig->maxWaitForMove, delay);
AI_VALUE(LastMovement&, "last movement").Set(mapId, x, y, z, bot->GetOrientation(), delay, priority);
@@ -237,10 +233,6 @@ bool MovementAction::MoveTo(uint32 mapId, float x, float y, float z, bool idle,
mm.Clear();
mm.MovePoint(0, x, y, z, generatePath);
float delay = 1000.0f * MoveDelay(distance);
if (lessDelay)
{
delay -= botAI->GetReactDelay();
}
delay = std::max(.0f, delay);
delay = std::min((float)sPlayerbotAIConfig->maxWaitForMove, delay);
AI_VALUE(LastMovement&, "last movement").Set(mapId, x, y, z, bot->GetOrientation(), delay, priority);
@@ -272,10 +264,6 @@ bool MovementAction::MoveTo(uint32 mapId, float x, float y, float z, bool idle,
mm.Clear();
mm.MovePoint(0, endP.x, endP.y, endP.z, generatePath);
float delay = 1000.0f * MoveDelay(distance);
if (lessDelay)
{
delay -= botAI->GetReactDelay();
}
delay = std::max(.0f, delay);
delay = std::min((float)sPlayerbotAIConfig->maxWaitForMove, delay);
AI_VALUE(LastMovement&, "last movement").Set(mapId, x, y, z, bot->GetOrientation(), delay, priority);
@@ -834,7 +822,7 @@ bool MovementAction::ReachCombatTo(Unit* target, float distance)
PathGenerator path(bot);
path.CalculatePath(tx, ty, tz, false);
PathType type = path.GetPathType();
int typeOk = PATHFIND_NORMAL | PATHFIND_INCOMPLETE | PATHFIND_SHORTCUT;
int typeOk = PATHFIND_NORMAL | PATHFIND_INCOMPLETE;
if (!(type & typeOk))
return false;
float shortenTo = distance;
@@ -850,7 +838,7 @@ bool MovementAction::ReachCombatTo(Unit* target, float distance)
path.ShortenPathUntilDist(G3D::Vector3(tx, ty, tz), shortenTo);
G3D::Vector3 endPos = path.GetPath().back();
return MoveTo(target->GetMapId(), endPos.x, endPos.y, endPos.z, false, false, false, false,
MovementPriority::MOVEMENT_COMBAT, true);
MovementPriority::MOVEMENT_COMBAT);
}
float MovementAction::GetFollowAngle()
@@ -935,10 +923,10 @@ bool MovementAction::IsMovingAllowed()
if (botAI->IsInVehicle() && !botAI->IsInVehicle(true))
return false;
if (bot->isFrozen() || bot->IsPolymorphed() || (bot->isDead() && !bot->HasPlayerFlag(PLAYER_FLAGS_GHOST)) ||
bot->IsBeingTeleported() || bot->HasRootAura() || bot->HasSpiritOfRedemptionAura() ||
bot->HasConfuseAura() || bot->IsCharmed() || bot->HasStunAura() ||
bot->IsInFlight() || bot->HasUnitState(UNIT_STATE_LOST_CONTROL))
if (bot->isFrozen() || bot->IsPolymorphed() || (bot->isDead() && !bot->HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_GHOST)) ||
bot->IsBeingTeleported() || bot->isInRoots() || bot->HasAuraType(SPELL_AURA_SPIRIT_OF_REDEMPTION) ||
bot->HasAuraType(SPELL_AURA_MOD_CONFUSE) || bot->IsCharmed() || bot->HasAuraType(SPELL_AURA_MOD_STUN) ||
bot->HasUnitState(UNIT_STATE_IN_FLIGHT) || bot->HasUnitState(UNIT_STATE_LOST_CONTROL))
return false;
if (bot->GetMotionMaster()->GetMotionSlotType(MOTION_SLOT_CONTROLLED) != NULL_MOTION_TYPE)
@@ -2025,15 +2013,8 @@ Position MovementAction::BestPositionForMeleeToFlee(Position pos, float radius)
}
bool strict = checkAngle.strict;
float fleeDis = std::min(radius + 1.0f, sPlayerbotAIConfig->fleeDistance);
float dx = bot->GetPositionX() + cos(angle) * fleeDis;
float dy = bot->GetPositionY() + sin(angle) * fleeDis;
float dz = bot->GetPositionZ();
if (!bot->GetMap()->CheckCollisionAndGetValidCoords(bot, bot->GetPositionX(), bot->GetPositionY(),
bot->GetPositionZ(), dx, dy, dz))
{
continue;
}
Position fleePos{dx, dy, dz};
Position fleePos{bot->GetPositionX() + cos(angle) * fleeDis, bot->GetPositionY() + sin(angle) * fleeDis,
bot->GetPositionZ()};
if (strict && currentTarget &&
fleePos.GetExactDist(currentTarget) - currentTarget->GetCombatReach() >
sPlayerbotAIConfig->tooCloseDistance &&
@@ -2088,15 +2069,8 @@ Position MovementAction::BestPositionForRangedToFlee(Position pos, float radius)
}
bool strict = checkAngle.strict;
float fleeDis = std::min(radius + 1.0f, sPlayerbotAIConfig->fleeDistance);
float dx = bot->GetPositionX() + cos(angle) * fleeDis;
float dy = bot->GetPositionY() + sin(angle) * fleeDis;
float dz = bot->GetPositionZ();
if (!bot->GetMap()->CheckCollisionAndGetValidCoords(bot, bot->GetPositionX(), bot->GetPositionY(),
bot->GetPositionZ(), dx, dy, dz))
{
continue;
}
Position fleePos{dx, dy, dz};
Position fleePos{bot->GetPositionX() + cos(angle) * fleeDis, bot->GetPositionY() + sin(angle) * fleeDis,
bot->GetPositionZ()};
if (strict && currentTarget &&
fleePos.GetExactDist(currentTarget) - currentTarget->GetCombatReach() > sPlayerbotAIConfig->spellDistance)
{
@@ -2108,7 +2082,6 @@ Position MovementAction::BestPositionForRangedToFlee(Position pos, float radius)
{
continue;
}
if (pos.GetExactDist(fleePos) > farestDis)
{
farestDis = pos.GetExactDist(fleePos);
@@ -2564,9 +2537,9 @@ bool SetFacingTargetAction::isUseful() { return !AI_VALUE2(bool, "facing", "curr
bool SetFacingTargetAction::isPossible()
{
if (bot->isFrozen() || bot->IsPolymorphed() || (bot->isDead() && !bot->HasPlayerFlag(PLAYER_FLAGS_GHOST)) ||
bot->IsBeingTeleported() || bot->HasConfuseAura() || bot->IsCharmed() ||
bot->HasStunAura() || bot->IsInFlight() ||
if (bot->isFrozen() || bot->IsPolymorphed() || (bot->isDead() && !bot->HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_GHOST)) ||
bot->IsBeingTeleported() || bot->HasAuraType(SPELL_AURA_MOD_CONFUSE) || bot->IsCharmed() ||
bot->HasAuraType(SPELL_AURA_MOD_STUN) || bot->HasUnitState(UNIT_STATE_IN_FLIGHT) ||
bot->HasUnitState(UNIT_STATE_LOST_CONTROL))
return false;

View File

@@ -32,7 +32,7 @@ protected:
bool MoveNear(uint32 mapId, float x, float y, float z, float distance = sPlayerbotAIConfig->contactDistance, MovementPriority priority = MovementPriority::MOVEMENT_NORMAL);
bool MoveToLOS(WorldObject* target, bool ranged = false);
bool MoveTo(uint32 mapId, float x, float y, float z, bool idle = false, bool react = false,
bool normal_only = false, bool exact_waypoint = false, MovementPriority priority = MovementPriority::MOVEMENT_NORMAL, bool lessDelay = false);
bool normal_only = false, bool exact_waypoint = false, MovementPriority priority = MovementPriority::MOVEMENT_NORMAL);
bool MoveTo(WorldObject* target, float distance = 0.0f, MovementPriority priority = MovementPriority::MOVEMENT_NORMAL);
bool MoveNear(WorldObject* target, float distance = sPlayerbotAIConfig->contactDistance, MovementPriority priority = MovementPriority::MOVEMENT_NORMAL);
float GetFollowAngle();

View File

@@ -22,7 +22,7 @@ bool ReleaseSpiritAction::Execute(Event event)
return false;
}
if (bot->GetCorpse() && bot->HasPlayerFlag(PLAYER_FLAGS_GHOST))
if (bot->GetCorpse() && bot->HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_GHOST))
{
botAI->TellMasterNoFacing("I am already a spirit");
return false;
@@ -149,7 +149,7 @@ bool AutoReleaseSpiritAction::isUseful()
if (bot->InBattleground())
return true;
if (bot->HasPlayerFlag(PLAYER_FLAGS_GHOST))
if (bot->HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_GHOST))
return false;
if (!bot->GetGroup())

View File

@@ -188,7 +188,7 @@ bool FindCorpseAction::Execute(Event event)
if (!moved)
{
moved = botAI->DoSpecificAction("spirit healer", Event(), true);
moved = botAI->DoSpecificAction("spirit healer");
}
}
}
@@ -347,17 +347,17 @@ bool SpiritHealerAction::Execute(Event event)
if (moved)
return true;
// if (!botAI->HasActivePlayerMaster())
// {
context->GetValue<uint32>("death count")->Set(dCount + 1);
return bot->TeleportTo(ClosestGrave->Map, ClosestGrave->x, ClosestGrave->y, ClosestGrave->z, 0.f);
// }
if (!botAI->HasActivePlayerMaster())
{
context->GetValue<uint32>("death count")->Set(dCount + 1);
return bot->TeleportTo(ClosestGrave->Map, ClosestGrave->x, ClosestGrave->y, ClosestGrave->z, 0.f);
}
// LOG_INFO("playerbots", "Bot {} {}:{} <{}> can't find a spirit healer", bot->GetGUID().ToString().c_str(),
// bot->GetTeamId() == TEAM_ALLIANCE ? "A" : "H", bot->GetLevel(), bot->GetName().c_str());
LOG_INFO("playerbots", "Bot {} {}:{} <{}> can't find a spirit healer", bot->GetGUID().ToString().c_str(),
bot->GetTeamId() == TEAM_ALLIANCE ? "A" : "H", bot->GetLevel(), bot->GetName().c_str());
// botAI->TellError("Cannot find any spirit healer nearby");
botAI->TellError("Cannot find any spirit healer nearby");
return false;
}
bool SpiritHealerAction::isUseful() { return bot->HasPlayerFlag(PLAYER_FLAGS_GHOST); }
bool SpiritHealerAction::isUseful() { return bot->HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_GHOST); }

View File

@@ -80,7 +80,7 @@ bool MarkRtiAction::Execute(Event event)
for (uint8 i = 0; i < 8; i++)
{
ObjectGuid iconGUID = group->GetTargetIcon(i);
if (iconGUID == unit->GetGUID())
if (guid == unit->GetGUID())
{
marked = true;
break;

View File

@@ -207,7 +207,7 @@ bool TradeStatusAction::CheckTrade()
for (uint32 slot = 0; slot < TRADE_SLOT_TRADED_COUNT; ++slot)
{
Item* item = bot->GetTradeData()->GetItem((TradeSlots)slot);
if (item && !item->GetTemplate()->SellPrice && !item->GetTemplate()->IsConjuredConsumable())
if (item && !item->GetTemplate()->SellPrice)
{
std::ostringstream out;
out << chat->FormatItem(item->GetTemplate()) << " - This is not for sale";

View File

@@ -202,16 +202,6 @@ bool AutoGearAction::Execute(Event event)
botAI->TellError("autogear command is not allowed, please check the configuration.");
return false;
}
if (!sPlayerbotAIConfig->autoGearCommandAltBots)
{
if (!sRandomPlayerbotMgr->IsRandomBot(bot))
{
botAI->TellError("You cannot use autogear on alt bots.");
return false;
}
}
botAI->TellMaster("I'm auto gearing");
uint32 gs = sPlayerbotAIConfig->autoGearScoreLimit == 0
? 0

View File

@@ -5,7 +5,6 @@
#include "WorldBuffAction.h"
#include "AiFactory.h"
#include "Event.h"
#include "Playerbots.h"
@@ -40,11 +39,6 @@ std::vector<uint32> WorldBuffAction::NeedWorldBuffs(Unit* unit)
if (wb.classId != 0 && wb.classId != unit->getClass())
continue;
uint8 tab = AiFactory::GetPlayerSpecTab(unit->ToPlayer());
if (wb.specId != tab)
continue;
if (wb.minLevel != 0 && wb.minLevel > unit->GetLevel())
continue;

View File

@@ -101,7 +101,7 @@ void FeralDruidStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
// triggers.push_back(new TriggerNode("not facing target", NextAction::array(0, new NextAction("set facing",
// ACTION_NORMAL + 7), nullptr)));
triggers.push_back(new TriggerNode(
"enemy out of melee", NextAction::array(0, new NextAction("reach melee", ACTION_HIGH + 1), nullptr)));
"enemy out of melee", NextAction::array(0, new NextAction("reach melee", ACTION_NORMAL + 8), nullptr)));
// triggers.push_back(new TriggerNode("enemy too close for melee", NextAction::array(0, new NextAction("move out of
// enemy contact", ACTION_NORMAL + 8), nullptr)));
triggers.push_back(new TriggerNode(

View File

@@ -21,8 +21,8 @@ class WotlkDungeonOccActionContext : public NamedObjectContext<Action>
static Action* avoid_unstable_sphere(PlayerbotAI* ai) { return new AvoidUnstableSphereAction(ai); }
static Action* mount_drake(PlayerbotAI* ai) { return new MountDrakeAction(ai); }
static Action* dismount_drake(PlayerbotAI* ai) { return new DismountDrakeAction(ai); }
static Action* fly_drake(PlayerbotAI* ai) { return new OccFlyDrakeAction(ai); }
static Action* drake_attack(PlayerbotAI* ai) { return new OccDrakeAttackAction(ai); }
static Action* fly_drake(PlayerbotAI* ai) { return new FlyDrakeAction(ai); }
static Action* drake_attack(PlayerbotAI* ai) { return new DrakeAttackAction(ai); }
static Action* avoid_arcane_explosion(PlayerbotAI* ai) { return new AvoidArcaneExplosionAction(ai); }
static Action* time_bomb_spread(PlayerbotAI* ai) { return new TimeBombSpreadAction(ai); }
};

View File

@@ -111,7 +111,7 @@ bool DismountDrakeAction::Execute(Event event)
return false;
}
bool OccFlyDrakeAction::Execute(Event event)
bool FlyDrakeAction::Execute(Event event)
{
Player* master = botAI->GetMaster();
if (!master) { return false; }
@@ -152,7 +152,7 @@ bool OccFlyDrakeAction::Execute(Event event)
return false;
}
bool OccDrakeAttackAction::Execute(Event event)
bool DrakeAttackAction::Execute(Event event)
{
vehicleBase = bot->GetVehicleBase();
if (!vehicleBase) { return false; }
@@ -188,7 +188,7 @@ bool OccDrakeAttackAction::Execute(Event event)
return false;
}
bool OccDrakeAttackAction::CastDrakeSpellAction(Unit* target, uint32 spellId, uint32 cooldown)
bool DrakeAttackAction::CastDrakeSpellAction(Unit* target, uint32 spellId, uint32 cooldown)
{
if (botAI->CanCastVehicleSpell(spellId, target))
if (botAI->CastVehicleSpell(spellId, target))
@@ -199,7 +199,7 @@ bool OccDrakeAttackAction::CastDrakeSpellAction(Unit* target, uint32 spellId, ui
return false;
}
bool OccDrakeAttackAction::AmberDrakeAction(Unit* target)
bool DrakeAttackAction::AmberDrakeAction(Unit* target)
{
Aura* shockCharges = target->GetAura(SPELL_SHOCK_CHARGE, vehicleBase->GetGUID());
if (shockCharges && shockCharges->GetStackAmount() > 8)
@@ -225,7 +225,7 @@ bool OccDrakeAttackAction::AmberDrakeAction(Unit* target)
return false;
}
bool OccDrakeAttackAction::EmeraldDrakeAction(Unit* target)
bool DrakeAttackAction::EmeraldDrakeAction(Unit* target)
{
Aura* poisonStacks = target->GetAura(SPELL_LEECHING_POISON, vehicleBase->GetGUID());
if (!poisonStacks || (poisonStacks->GetStackAmount() < 3 ||
@@ -286,7 +286,7 @@ bool OccDrakeAttackAction::EmeraldDrakeAction(Unit* target)
return false;
}
bool OccDrakeAttackAction::RubyDrakeAction(Unit* target)
bool DrakeAttackAction::RubyDrakeAction(Unit* target)
{
Aura* evasiveCharges = vehicleBase->GetAura(SPELL_EVASIVE_CHARGES);
Aura* evasiveManeuvers = vehicleBase->GetAura(SPELL_EVASIVE_MANEUVERS);

View File

@@ -38,17 +38,17 @@ public:
bool Execute(Event event) override;
};
class OccFlyDrakeAction : public MovementAction
class FlyDrakeAction : public MovementAction
{
public:
OccFlyDrakeAction(PlayerbotAI* ai) : MovementAction(ai, "occ fly drake") {}
FlyDrakeAction(PlayerbotAI* ai) : MovementAction(ai, "fly drake") {}
bool Execute(Event event) override;
};
class OccDrakeAttackAction : public Action
class DrakeAttackAction : public Action
{
public:
OccDrakeAttackAction(PlayerbotAI* botAI) : Action(botAI, "occ drake attack") {}
DrakeAttackAction(PlayerbotAI* botAI) : Action(botAI, "drake attack") {}
bool Execute(Event event) override;
protected:
@@ -57,6 +57,7 @@ protected:
bool AmberDrakeAction(Unit* target);
bool EmeraldDrakeAction(Unit* target);
bool RubyDrakeAction(Unit* target);
};
class AvoidArcaneExplosionAction : public MovementAction

View File

@@ -26,12 +26,12 @@ float MountingDrakeMultiplier::GetValue(Action* action)
return 1.0f;
}
float OccFlyingMultiplier::GetValue(Action* action)
float FlyingMultiplier::GetValue(Action* action)
{
if (bot->GetMapId() != OCULUS_MAP_ID || !bot->GetVehicleBase()) { return 1.0f; }
// Suppresses FollowAction as well as some attack-based movements
if (dynamic_cast<MovementAction*>(action) && !dynamic_cast<OccFlyDrakeAction*>(action))
if (dynamic_cast<MovementAction*>(action) && !dynamic_cast<FlyDrakeAction*>(action))
{
return 0.0f;
}
@@ -103,7 +103,7 @@ float EregosMultiplier::GetValue(Action* action)
Unit* boss = AI_VALUE2(Unit*, "find target", "ley-guardian eregos");
if (!boss) { return 1.0f; }
if (boss->HasAura(SPELL_PLANAR_SHIFT && dynamic_cast<OccDrakeAttackAction*>(action)))
if (boss->HasAura(SPELL_PLANAR_SHIFT && dynamic_cast<DrakeAttackAction*>(action)))
{
return 0.0f;
}

View File

@@ -21,10 +21,10 @@ class MountingDrakeMultiplier : public Multiplier
virtual float GetValue(Action* action);
};
class OccFlyingMultiplier : public Multiplier
class FlyingMultiplier : public Multiplier
{
public:
OccFlyingMultiplier(PlayerbotAI* ai) : Multiplier(ai, "occ flying drake") {}
FlyingMultiplier(PlayerbotAI* ai) : Multiplier(ai, "flying drake") {}
public:
virtual float GetValue(Action* action);

View File

@@ -36,7 +36,7 @@ void WotlkDungeonOccStrategy::InitTriggers(std::vector<TriggerNode*> &triggers)
void WotlkDungeonOccStrategy::InitMultipliers(std::vector<Multiplier*> &multipliers)
{
multipliers.push_back(new MountingDrakeMultiplier(botAI));
multipliers.push_back(new OccFlyingMultiplier(botAI));
multipliers.push_back(new FlyingMultiplier(botAI));
multipliers.push_back(new UromMultiplier(botAI));
multipliers.push_back(new EregosMultiplier(botAI));
}

View File

@@ -106,7 +106,6 @@ ChatCommandHandlerStrategy::ChatCommandHandlerStrategy(PlayerbotAI* botAI) : Pas
supported.push_back("reputation");
supported.push_back("log");
supported.push_back("los");
supported.push_back("rpg status");
supported.push_back("aura");
supported.push_back("drop");
supported.push_back("share");

View File

@@ -22,7 +22,7 @@ void CombatStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
triggers.push_back(new TriggerNode("not facing target",
NextAction::array(0, new NextAction("set facing", ACTION_MOVE + 7), nullptr)));
triggers.push_back(
new TriggerNode("pet attack", NextAction::array(0, new NextAction("pet attack", 40.0f), nullptr)));
new TriggerNode("pet attack", NextAction::array(0, new NextAction("pet attack", ACTION_NORMAL), nullptr)));
// triggers.push_back(new TriggerNode("combat long stuck", NextAction::array(0, new NextAction("hearthstone", 0.9f),
// new NextAction("repop", 0.8f), nullptr)));
}

View File

@@ -11,10 +11,10 @@ NextAction** GrindingStrategy::getDefaultActions() { return nullptr; }
void GrindingStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
{
triggers.push_back(new TriggerNode("timer", NextAction::array(0, new NextAction("drink", 10.2f), nullptr)));
triggers.push_back(new TriggerNode("timer", NextAction::array(0, new NextAction("food", 10.1f), nullptr)));
triggers.push_back(new TriggerNode("timer", NextAction::array(0, new NextAction("drink", 4.2f), nullptr)));
triggers.push_back(new TriggerNode("timer", NextAction::array(0, new NextAction("food", 4.1f), nullptr)));
triggers.push_back(
new TriggerNode("no target", NextAction::array(0, new NextAction("attack anything", 10.0f), nullptr)));
new TriggerNode("no target", NextAction::array(0, new NextAction("attack anything", 4.0f), nullptr)));
}
void MoveRandomStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)

View File

@@ -14,7 +14,7 @@ void MeleeCombatStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
// triggers.push_back(new TriggerNode("not facing target", NextAction::array(0, new NextAction("set facing",
// ACTION_MOVE + 7), nullptr)));
triggers.push_back(new TriggerNode(
"enemy out of melee", NextAction::array(0, new NextAction("reach melee", ACTION_HIGH + 1), nullptr)));
"enemy out of melee", NextAction::array(0, new NextAction("reach melee", ACTION_NORMAL + 8), nullptr)));
// triggers.push_back(new TriggerNode("enemy too close for melee", NextAction::array(0, new NextAction("move out of
// enemy contact", ACTION_NORMAL + 8), nullptr)));
}

View File

@@ -59,9 +59,8 @@ FrostMageStrategy::FrostMageStrategy(PlayerbotAI* botAI) : GenericMageStrategy(b
NextAction** FrostMageStrategy::getDefaultActions()
{
return NextAction::array(0, new NextAction("frostbolt", ACTION_DEFAULT + 0.2f),
new NextAction("shoot", ACTION_DEFAULT + 0.1f),
new NextAction("fireball", ACTION_DEFAULT), nullptr);
return NextAction::array(0, new NextAction("frostbolt", ACTION_DEFAULT + 0.1f),
new NextAction("shoot", ACTION_DEFAULT), nullptr);
}
void FrostMageStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)

View File

@@ -131,5 +131,5 @@ void DpsPaladinStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
// NextAction::array(0, new NextAction("set facing", ACTION_NORMAL + 7), NULL)));
triggers.push_back(new TriggerNode("enemy out of melee",
NextAction::array(0, new NextAction("reach melee", ACTION_HIGH + 1), NULL)));
NextAction::array(0, new NextAction("reach melee", ACTION_NORMAL + 8), NULL)));
}

View File

@@ -113,5 +113,5 @@ void TankPaladinStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
triggers.push_back(new TriggerNode("not facing target",
NextAction::array(0, new NextAction("set facing", ACTION_NORMAL + 7), nullptr)));
triggers.push_back(new TriggerNode(
"enemy out of melee", NextAction::array(0, new NextAction("reach melee", ACTION_HIGH + 1), nullptr)));
"enemy out of melee", NextAction::array(0, new NextAction("reach melee", ACTION_NORMAL + 8), nullptr)));
}

View File

@@ -5,8 +5,6 @@
#include "Strategy.h"
#include "RaidBwlStrategy.h"
#include "RaidNaxxStrategy.h"
#include "RaidOsStrategy.h"
#include "RaidEoEStrategy.h"
#include "RaidMcStrategy.h"
#include "RaidAq20Strategy.h"
#include "RaidIccStrategy.h"
@@ -23,8 +21,6 @@ public:
creators["bwl"] = &RaidStrategyContext::bwl;
creators["aq20"] = &RaidStrategyContext::aq20;
creators["naxx"] = &RaidStrategyContext::naxx;
creators["wotlk-os"] = &RaidStrategyContext::wotlk_os;
creators["wotlk-eoe"] = &RaidStrategyContext::wotlk_eoe;
creators["uld"] = &RaidStrategyContext::uld;
creators["icc"] = &RaidStrategyContext::icc;
}
@@ -34,8 +30,6 @@ private:
static Strategy* bwl(PlayerbotAI* botAI) { return new RaidBwlStrategy(botAI); }
static Strategy* aq20(PlayerbotAI* botAI) { return new RaidAq20Strategy(botAI); }
static Strategy* naxx(PlayerbotAI* botAI) { return new RaidNaxxStrategy(botAI); }
static Strategy* wotlk_os(PlayerbotAI* botAI) { return new RaidOsStrategy(botAI); }
static Strategy* wotlk_eoe(PlayerbotAI* botAI) { return new RaidEoEStrategy(botAI); }
static Strategy* uld(PlayerbotAI* botAI) { return new RaidUlduarStrategy(botAI); }
static Strategy* icc(PlayerbotAI* botAI) { return new RaidIccStrategy(botAI); }
};

View File

@@ -1,30 +0,0 @@
#ifndef _PLAYERBOT_RAIDEOEACTIONCONTEXT_H
#define _PLAYERBOT_RAIDEOEACTIONCONTEXT_H
#include "Action.h"
#include "NamedObjectContext.h"
#include "RaidEoEActions.h"
class RaidEoEActionContext : public NamedObjectContext<Action>
{
public:
RaidEoEActionContext()
{
creators["malygos position"] = &RaidEoEActionContext::position;
creators["malygos target"] = &RaidEoEActionContext::target;
// creators["pull power spark"] = &RaidEoEActionContext::pull_power_spark;
// creators["kill power spark"] = &RaidEoEActionContext::kill_power_spark;
creators["fly drake"] = &RaidEoEActionContext::fly_drake;
creators["drake attack"] = &RaidEoEActionContext::drake_attack;
}
private:
static Action* position(PlayerbotAI* ai) { return new MalygosPositionAction(ai); }
static Action* target(PlayerbotAI* ai) { return new MalygosTargetAction(ai); }
// static Action* pull_power_spark(PlayerbotAI* ai) { return new PullPowerSparkAction(ai); }
// static Action* kill_power_spark(PlayerbotAI* ai) { return new KillPowerSparkAction(ai); }
static Action* fly_drake(PlayerbotAI* ai) { return new EoEFlyDrakeAction(ai); }
static Action* drake_attack(PlayerbotAI* ai) { return new EoEDrakeAttackAction(ai); }
};
#endif

View File

@@ -1,391 +0,0 @@
#include "RaidEoEActions.h"
#include "RaidEoETriggers.h"
#include "Playerbots.h"
bool MalygosPositionAction::Execute(Event event)
{
Unit* boss = AI_VALUE2(Unit*, "find target", "malygos");
if (!boss) { return false; }
uint8 phase = MalygosTrigger::getPhase(bot, boss);
float distance = 5.0f;
if (phase == 1)
{
Unit* spark = nullptr;
GuidVector targets = AI_VALUE(GuidVector, "possible targets no los");
for (auto& target : targets)
{
Unit* unit = botAI->GetUnit(target);
if (unit && unit->GetEntry() == NPC_POWER_SPARK)
{
spark = unit;
break;
}
}
// Position tank
if (botAI->IsMainTank(bot))
{
if (bot->GetDistance2d(MALYGOS_MAINTANK_POSITION.first, MALYGOS_MAINTANK_POSITION.second) > distance)
{
return MoveTo(EOE_MAP_ID, MALYGOS_MAINTANK_POSITION.first, MALYGOS_MAINTANK_POSITION.second, bot->GetPositionZ(),
false, false, false, false, MovementPriority::MOVEMENT_COMBAT);
}
return false;
}
// Position DK for spark pull
// else if (spark && bot->IsClass(CLASS_DEATH_KNIGHT))
// {
// if (bot->GetDistance2d(MALYGOS_STACK_POSITION.first, MALYGOS_STACK_POSITION.second) > distance)
// {
// bot->Yell("SPARK SPAWNED, MOVING TO STACK", LANG_UNIVERSAL);
// return MoveTo(EOE_MAP_ID, MALYGOS_STACK_POSITION.first, MALYGOS_STACK_POSITION.second, bot->GetPositionZ(),
// false, false, false, false, MovementPriority::MOVEMENT_COMBAT);
// }
// return false;
// }
else if (spark)
{
return false;
}
else if (!bot->IsClass(CLASS_HUNTER))
{
if (bot->GetDistance2d(MALYGOS_STACK_POSITION.first, MALYGOS_STACK_POSITION.second) > (distance * 3.0f))
{
return MoveTo(EOE_MAP_ID, MALYGOS_STACK_POSITION.first, MALYGOS_STACK_POSITION.second, bot->GetPositionZ(),
false, false, false, false, MovementPriority::MOVEMENT_COMBAT);
}
return false;
}
}
return false;
}
bool MalygosTargetAction::Execute(Event event)
{
Unit* boss = AI_VALUE2(Unit*, "find target", "malygos");
if (!boss) { return false; }
uint8 phase = MalygosTrigger::getPhase(bot, boss);
if (phase == 1)
{
if (botAI->IsHeal(bot)) { return false; }
// Init this as boss by default, if no better target is found just fall back to Malygos
Unit* newTarget = boss;
// Unit* spark = nullptr;
// GuidVector targets = AI_VALUE(GuidVector, "possible targets no los");
// for (auto& target : targets)
// {
// Unit* unit = botAI->GetUnit(target);
// if (unit && unit->GetEntry() == NPC_POWER_SPARK)
// {
// spark = unit;
// break;
// }
// }
// if (spark && botAI->IsRangedDps(bot))
// {
// newTarget = spark;
// }
Unit* currentTarget = AI_VALUE(Unit*, "current target");
if (!currentTarget || currentTarget->GetEntry() != newTarget->GetEntry())
{
return Attack(newTarget);
}
}
else if (phase == 2)
{
if (botAI->IsHeal(bot)) { return false; }
Unit* newTarget = nullptr;
Unit* nexusLord = nullptr;
Unit* scionOfEternity = nullptr;
GuidVector targets = AI_VALUE(GuidVector, "possible targets no los");
for (auto& target : targets)
{
Unit* unit = botAI->GetUnit(target);
if (!unit) { continue; }
if (unit->GetEntry() == NPC_NEXUS_LORD)
{
nexusLord = unit;
}
else if (unit->GetEntry() == NPC_SCION_OF_ETERNITY)
{
scionOfEternity = unit;
}
}
if (botAI->IsRangedDps(bot) && scionOfEternity)
{
newTarget = scionOfEternity;
}
else
{
newTarget = nexusLord;
}
if (!newTarget) { return false; }
Unit* currentTarget = AI_VALUE(Unit*, "current target");
if (!currentTarget || currentTarget->GetEntry() != newTarget->GetEntry())
{
return Attack(newTarget);
}
}
// else if (phase == 3)
// {}
return false;
}
// bool PullPowerSparkAction::Execute(Event event)
// {
// Unit* spark = nullptr;
// GuidVector targets = AI_VALUE(GuidVector, "possible targets no los");
// for (auto& target : targets)
// {
// Unit* unit = botAI->GetUnit(target);
// if (unit && unit->GetEntry() == NPC_POWER_SPARK)
// {
// spark = unit;
// break;
// }
// }
// if (!spark) { return false; }
// if (spark->GetDistance2d(MALYGOS_STACK_POSITION.first, MALYGOS_STACK_POSITION.second) > 3.0f)
// {
// bot->Yell("GRIPPING SPARK", LANG_UNIVERSAL);
// return botAI->CastSpell("death grip", spark);
// }
// return false;
// }
// bool PullPowerSparkAction::isPossible()
// {
// Unit* spark = nullptr;
// GuidVector targets = AI_VALUE(GuidVector, "possible targets no los");
// for (auto& target : targets)
// {
// Unit* unit = botAI->GetUnit(target);
// if (unit && unit->GetEntry() == NPC_POWER_SPARK)
// {
// spark = unit;
// break;
// }
// }
// return botAI->CanCastSpell(spell, spark);
// }
// bool PullPowerSparkAction::isUseful()
// {
// Unit* spark = nullptr;
// GuidVector targets = AI_VALUE(GuidVector, "possible targets no los");
// for (auto& target : targets)
// {
// Unit* unit = botAI->GetUnit(target);
// if (unit && unit->GetEntry() == NPC_POWER_SPARK)
// {
// spark = unit;
// break;
// }
// }
// if (!spark)
// return false;
// if (!spark->IsInWorld() || spark->GetMapId() != bot->GetMapId())
// return false;
// return bot->GetDistance2d(MALYGOS_STACK_POSITION.first, MALYGOS_STACK_POSITION.second) < 3.0f;
// }
// bool KillPowerSparkAction::Execute(Event event)
// {
// return false;
// }
bool EoEFlyDrakeAction::isPossible()
{
Unit* vehicleBase = bot->GetVehicleBase();
return (vehicleBase && vehicleBase->GetEntry() == NPC_WYRMREST_SKYTALON);
}
bool EoEFlyDrakeAction::Execute(Event event)
{
Player* master = botAI->GetMaster();
if (!master) { return false; }
Unit* masterVehicle = master->GetVehicleBase();
Unit* vehicleBase = bot->GetVehicleBase();
if (!vehicleBase || !masterVehicle) { return false; }
MotionMaster* mm = vehicleBase->GetMotionMaster();
Unit* boss = AI_VALUE2(Unit*, "find target", "malygos");
if (boss && false)
{
// Handle as boss encounter instead of formation flight
mm->Clear(false);
float distance = vehicleBase->GetExactDist(boss);
float range = 55.0f; // Drake range is 60yd
if (distance > range)
{
mm->MoveForwards(boss, range - distance);
vehicleBase->SendMovementFlagUpdate();
return true;
}
vehicleBase->SetFacingToObject(boss);
mm->MoveIdle();
vehicleBase->SendMovementFlagUpdate();
return false;
}
if (vehicleBase->GetExactDist(masterVehicle) > 5.0f)
{
uint8 numPlayers;
bot->GetRaidDifficulty() == RAID_DIFFICULTY_25MAN_NORMAL ? numPlayers = 25 : numPlayers = 10;
// 3/4 of a circle, with frontal cone 90 deg unobstructed
float angle = botAI->GetGroupSlotIndex(bot) * (2*M_PI - M_PI_2)/numPlayers + M_PI_2;
// float angle = M_PI;
vehicleBase->SetCanFly(true);
mm->MoveFollow(masterVehicle, 3.0f, angle);
vehicleBase->SendMovementFlagUpdate();
return true;
}
return false;
}
bool EoEDrakeAttackAction::isPossible()
{
Unit* vehicleBase = bot->GetVehicleBase();
return (vehicleBase && vehicleBase->GetEntry() == NPC_WYRMREST_SKYTALON);
}
bool EoEDrakeAttackAction::Execute(Event event)
{
vehicleBase = bot->GetVehicleBase();
if (!vehicleBase) { return false; }
// Unit* target = AI_VALUE(Unit*, "current target");
Unit* boss = AI_VALUE2(Unit*, "find target", "malygos");
// if (!boss) { return false; }
if (!boss)
{
GuidVector npcs = AI_VALUE(GuidVector, "possible targets");
for (auto& npc : npcs)
{
Unit* unit = botAI->GetUnit(npc);
if (!unit || unit->GetEntry() != NPC_MALYGOS) { continue; }
boss = unit;
break;
}
}
// Check this again to see if a target was assigned
if (!boss) { return false; }
if (botAI->IsHeal(bot))
{
return DrakeHealAction();
}
else
{
return DrakeDpsAction(boss);
}
return false;
}
bool EoEDrakeAttackAction::CastDrakeSpellAction(Unit* target, uint32 spellId, uint32 cooldown)
{
if (botAI->CanCastVehicleSpell(spellId, target))
if (botAI->CastVehicleSpell(spellId, target))
{
vehicleBase->AddSpellCooldown(spellId, 0, cooldown);
return true;
}
return false;
}
bool EoEDrakeAttackAction::DrakeDpsAction(Unit* target)
{
Unit* vehicleBase = bot->GetVehicleBase();
if (!vehicleBase) { return false; }
Vehicle* veh = bot->GetVehicle();
uint8 comboPoints = vehicleBase->GetComboPoints(target);
if (comboPoints >= 2)
{
return CastDrakeSpellAction(target, SPELL_ENGULF_IN_FLAMES, 0);
}
else
{
return CastDrakeSpellAction(target, SPELL_FLAME_SPIKE, 0);
}
}
bool EoEDrakeAttackAction::DrakeHealAction()
{
Unit* vehicleBase = bot->GetVehicleBase();
if (!vehicleBase) { return false; }
Unit* target = vehicleBase->GetComboTarget();
if (!target)
{
// Unit* newTarget = nullptr;
Unit* newTarget = vehicleBase;
GuidVector members = AI_VALUE(GuidVector, "group members");
for (auto& member : members)
{
Unit* unit = botAI->GetUnit(member);
if (!unit)
{
continue;
}
Unit* drake = unit->GetVehicleBase();
if (!drake || drake->IsFullHealth()) { continue; }
if (!newTarget || drake->GetHealthPct() < newTarget->GetHealthPct() - 5.0f)
{
newTarget = drake;
}
}
target = newTarget;
}
uint8 comboPoints = vehicleBase->GetComboPoints(target);
if (comboPoints >= 5)
{
return CastDrakeSpellAction(target, SPELL_LIFE_BURST, 0);
}
else
{
// "Revivify" may be bugged server-side:
// "botAI->CanCastVehicleSpell()" returns SPELL_FAILED_BAD_TARGETS when targeting drakes.
// Forcing the cast attempt seems to succeed, not sure what's going on here.
// return CastDrakeSpellAction(target, SPELL_REVIVIFY, 0);
return botAI->CastVehicleSpell(SPELL_REVIVIFY, target);
}
}

View File

@@ -1,69 +0,0 @@
#ifndef _PLAYERBOT_RAIDEOEACTIONS_H
#define _PLAYERBOT_RAIDEOEACTIONS_H
#include "MovementActions.h"
#include "AttackAction.h"
#include "GenericSpellActions.h"
#include "PlayerbotAI.h"
#include "Playerbots.h"
const std::pair<float, float> MALYGOS_MAINTANK_POSITION = {757.0f, 1337.0f};
const std::pair<float, float> MALYGOS_STACK_POSITION = {755.0f, 1301.0f};
class MalygosPositionAction : public MovementAction
{
public:
MalygosPositionAction(PlayerbotAI* botAI, std::string const name = "malygos position")
: MovementAction(botAI, name) {}
bool Execute(Event event) override;
};
class MalygosTargetAction : public AttackAction
{
public:
MalygosTargetAction(PlayerbotAI* botAI, std::string const name = "malygos target")
: AttackAction(botAI, name) {}
bool Execute(Event event) override;
};
class PullPowerSparkAction : public CastSpellAction
{
public:
PullPowerSparkAction(PlayerbotAI* botAI, std::string const name = "pull power spark")
: CastSpellAction(botAI, "death grip") {}
bool Execute(Event event) override;
bool isPossible() override;
bool isUseful() override;
};
class KillPowerSparkAction : public AttackAction
{
public:
KillPowerSparkAction(PlayerbotAI* botAI, std::string const name = "kill power spark")
: AttackAction(botAI, name) {}
bool Execute(Event event) override;
};
class EoEFlyDrakeAction : public MovementAction
{
public:
EoEFlyDrakeAction(PlayerbotAI* ai) : MovementAction(ai, "eoe fly drake") {}
bool Execute(Event event) override;
bool isPossible() override;
};
class EoEDrakeAttackAction : public Action
{
public:
EoEDrakeAttackAction(PlayerbotAI* botAI) : Action(botAI, "eoe drake attack") {}
bool Execute(Event event) override;
bool isPossible() override;
protected:
Unit* vehicleBase;
bool CastDrakeSpellAction(Unit* target, uint32 spellId, uint32 cooldown);
bool DrakeDpsAction(Unit* target);
bool DrakeHealAction();
};
#endif

View File

@@ -1,81 +0,0 @@
#include "RaidEoEMultipliers.h"
#include "ChooseTargetActions.h"
#include "DKActions.h"
#include "DruidActions.h"
#include "DruidBearActions.h"
#include "FollowActions.h"
#include "GenericActions.h"
#include "GenericSpellActions.h"
#include "MovementActions.h"
#include "PaladinActions.h"
#include "RaidEoEActions.h"
#include "RaidEoETriggers.h"
#include "ReachTargetActions.h"
#include "ScriptedCreature.h"
#include "WarriorActions.h"
float MalygosMultiplier::GetValue(Action* action)
{
Unit* boss = AI_VALUE2(Unit*, "find target", "malygos");
uint8 phase = MalygosTrigger::getPhase(bot, boss);
if (phase == 0) { return 1.0f; }
if (phase == 1)
{
if (dynamic_cast<FollowAction*>(action))
{
return 0.0f;
}
if (botAI->IsDps(bot) && dynamic_cast<DpsAssistAction*>(action))
{
return 0.0f;
}
if (botAI->IsRangedDps(bot) && dynamic_cast<DropTargetAction*>(action))
{
return 0.0f;
}
if (!botAI->IsMainTank(bot) && dynamic_cast<TankAssistAction*>(action))
{
return 0.0f;
}
// if (dynamic_cast<MovementAction*>(action) && !dynamic_cast<MalygosPositionAction*>(action))
// {
// return 0.0f;
// }
}
else if (phase == 2)
{
if (botAI->IsDps(bot) && dynamic_cast<DpsAssistAction*>(action))
{
return 0.0f;
}
if (dynamic_cast<FleeAction*>(action))
{
return 0.0f;
}
if (dynamic_cast<TankAssistAction*>(action))
{
Unit* target = action->GetTarget();
if (target && target->GetEntry() == NPC_SCION_OF_ETERNITY)
return 0.0f;
}
}
else if (phase == 3)
{
// Suppresses FollowAction as well as some attack-based movements
if (dynamic_cast<MovementAction*>(action) && !dynamic_cast<EoEFlyDrakeAction*>(action))
{
return 0.0f;
}
}
return 1.0f;
}

View File

@@ -1,16 +0,0 @@
#ifndef _PLAYERRBOT_RAIDEOEMULTIPLIERS_H
#define _PLAYERRBOT_RAIDEOEMULTIPLIERS_H
#include "Multiplier.h"
class MalygosMultiplier : public Multiplier
{
public:
MalygosMultiplier(PlayerbotAI* ai) : Multiplier(ai, "malygos") {}
public:
virtual float GetValue(Action* action);
};
#endif

View File

@@ -1,25 +0,0 @@
#include "RaidEoEStrategy.h"
#include "RaidEoEMultipliers.h"
#include "Strategy.h"
void RaidEoEStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
{
triggers.push_back(new TriggerNode("malygos",
NextAction::array(0, new NextAction("malygos position", ACTION_MOVE), nullptr)));
triggers.push_back(new TriggerNode("malygos",
NextAction::array(0, new NextAction("malygos target", ACTION_RAID + 1), nullptr)));
// triggers.push_back(new TriggerNode("power spark",
// NextAction::array(0, new NextAction("pull power spark", ACTION_RAID + 2), nullptr)));
// triggers.push_back(new TriggerNode("power spark",
// NextAction::array(0, new NextAction("kill power spark", ACTION_RAID + 3), nullptr)));
triggers.push_back(new TriggerNode("group flying",
NextAction::array(0, new NextAction("fly drake", ACTION_NORMAL + 1), nullptr)));
triggers.push_back(new TriggerNode("drake combat",
NextAction::array(0, new NextAction("drake attack", ACTION_NORMAL + 5), nullptr)));
}
void RaidEoEStrategy::InitMultipliers(std::vector<Multiplier*> &multipliers)
{
multipliers.push_back(new MalygosMultiplier(botAI));
}

View File

@@ -1,17 +0,0 @@
#ifndef _PLAYERBOT_RAIDEOESTRATEGY_H
#define _PLAYERBOT_RAIDEOESTRATEGY_H
#include "AiObjectContext.h"
#include "Multiplier.h"
#include "Strategy.h"
class RaidEoEStrategy : public Strategy
{
public:
RaidEoEStrategy(PlayerbotAI* ai) : Strategy(ai) {}
virtual std::string const getName() override { return "wotlk-eoe"; }
virtual void InitTriggers(std::vector<TriggerNode*> &triggers) override;
virtual void InitMultipliers(std::vector<Multiplier*> &multipliers) override;
};
#endif

View File

@@ -1,22 +0,0 @@
#ifndef _PLAYERBOT_RAIDEOETRIGGERCONTEXT_H
#define _PLAYERBOT_RAIDEOETRIGGERCONTEXT_H
#include "AiObjectContext.h"
#include "NamedObjectContext.h"
#include "RaidEoETriggers.h"
class RaidEoETriggerContext : public NamedObjectContext<Trigger>
{
public:
RaidEoETriggerContext()
{
creators["malygos"] = &RaidEoETriggerContext::malygos;
creators["power spark"] = &RaidEoETriggerContext::power_spark;
}
private:
static Trigger* power_spark(PlayerbotAI* ai) { return new PowerSparkTrigger(ai); }
static Trigger* malygos(PlayerbotAI* ai) { return new MalygosTrigger(ai); }
};
#endif

View File

@@ -1,53 +0,0 @@
#include "RaidEoETriggers.h"
#include "SharedDefines.h"
uint8 MalygosTrigger::getPhase(Player* bot, Unit* boss)
{
uint8 phase = 0;
Unit* vehicle = bot->GetVehicleBase();
if (bot->GetMapId() != EOE_MAP_ID) { return phase; }
if (vehicle && vehicle->GetEntry() == NPC_WYRMREST_SKYTALON)
{
phase = 3;
}
else if (boss && boss->HealthAbovePct(50))
{
phase = 1;
}
else if (boss)
{
phase = 2;
}
return phase;
}
bool MalygosTrigger::IsActive()
{
return bool(AI_VALUE2(Unit*, "find target", "malygos"));
}
bool PowerSparkTrigger::IsActive()
{
Unit* boss = AI_VALUE2(Unit*, "find target", "malygos");
if (!boss) { return false; }
if (bot->getClass() != CLASS_DEATH_KNIGHT)
{
return false;
}
GuidVector targets = AI_VALUE(GuidVector, "possible targets no los");
for (auto& target : targets)
{
Unit* unit = botAI->GetUnit(target);
if (unit && unit->GetEntry() == NPC_POWER_SPARK)
{
return true;
}
}
return false;
}

View File

@@ -1,58 +0,0 @@
#ifndef _PLAYERBOT_RAIDEOETRIGGERS_H
#define _PLAYERBOT_RAIDEOETRIGGERS_H
#include "PlayerbotAI.h"
#include "Playerbots.h"
#include "Trigger.h"
enum EyeOfEternityIDs
{
NPC_MALYGOS = 28859,
NPC_POWER_SPARK = 30084,
NPC_NEXUS_LORD = 30245,
NPC_SCION_OF_ETERNITY = 30249,
NPC_WYRMREST_SKYTALON = 30161,
SPELL_POWER_SPARK_VISUAL = 55845,
SPELL_POWER_SPARK_GROUND_BUFF = 55852,
SPELL_POWER_SPARK_MALYGOS_BUFF = 56152,
SPELL_TELEPORT_VISUAL = 52096,
SPELL_SCION_ARCANE_BARRAGE = 56397,
SPELL_ARCANE_SHOCK_N = 57058,
SPELL_ARCANE_SHOCK_H = 60073,
SPELL_HASTE = 57060,
SPELL_ALEXSTRASZA_GIFT = 61028,
// Drake Abilities:
// DPS
SPELL_FLAME_SPIKE = 56091,
SPELL_ENGULF_IN_FLAMES = 56092,
// Healing
SPELL_REVIVIFY = 57090,
SPELL_LIFE_BURST = 57143,
// Utility
SPELL_FLAME_SHIELD = 57108,
SPELL_BLAZING_SPEED = 57092,
};
const uint32 EOE_MAP_ID = 616;
class MalygosTrigger : public Trigger
{
public:
MalygosTrigger(PlayerbotAI* botAI) : Trigger(botAI, "malygos") {}
bool IsActive() override;
uint8 static getPhase(Player* bot, Unit* boss);
};
class PowerSparkTrigger : public Trigger
{
public:
PowerSparkTrigger(PlayerbotAI* botAI) : Trigger(botAI, "power spark") {}
bool IsActive() override;
};
#endif

View File

@@ -834,14 +834,17 @@ bool AnubrekhanPositionAction::Execute(Event event)
{
return false;
}
EventMap* eventMap = &boss_ai->events;
uint32 locust = eventMap->GetNextEventTime(2);
uint32 timer = eventMap->GetTimer();
bool inPhase = botAI->HasAura("locust swarm", boss) || boss->GetCurrentSpell(CURRENT_GENERIC_SPELL);
if (inPhase)
if (inPhase || (locust && locust - timer <= 8000))
{
if (botAI->IsMainTank(bot))
{
uint32 nearest = FindNearestWaypoint();
uint32 next_point;
if (inPhase)
if (inPhase || (locust && locust - timer <= 3000))
{
next_point = (nearest + 1) % intervals;
}

View File

@@ -50,6 +50,26 @@ bool BossEventTrigger<T>::IsActive()
return false;
}
template <class T>
bool BossPhaseTrigger<T>::IsActive()
{
Unit* boss = AI_VALUE2(Unit*, "find target", boss_name);
if (!boss)
{
return false;
}
if (this->phase_mask == 0)
{
return true;
}
T* boss_ai = dynamic_cast<T*>(boss->GetAI());
EventMap* eventMap = &boss_ai->events;
uint8 phase_mask = eventMap->GetPhaseMask();
// bot->Yell("phase mask detected: " + to_string(phase_mask) + " compare with " + to_string(this->phase_mask),
// LANG_UNIVERSAL);
return phase_mask == this->phase_mask;
}
bool GrobbulusCloudTrigger::IsActive()
{
Unit* boss = AI_VALUE(Unit*, "boss target");
@@ -142,6 +162,21 @@ bool SapphironFlightTrigger::IsActive()
return helper.IsPhaseFlight();
}
// bool SapphironGroundExceptMainTankTrigger::IsActive()
// {
// return BossPhaseTrigger::IsActive() && !botAI->IsMainTank(bot);
// }
// bool SapphironFlightTrigger::IsActive()
// {
// return BossPhaseTrigger::IsActive();
// }
// bool SapphironGroundChillTrigger::IsActive()
// {
// return BossPhaseTrigger::IsActive() && !botAI->IsMainTank(bot) && botAI->HasAura("chill", bot);
// }
bool GluthTrigger::IsActive() { return helper.UpdateBossAI(); }
bool GluthMainTankMortalWoundTrigger::IsActive()
@@ -169,15 +204,6 @@ bool GluthMainTankMortalWoundTrigger::IsActive()
bool KelthuzadTrigger::IsActive() { return helper.UpdateBossAI(); }
bool AnubrekhanTrigger::IsActive() {
Unit* boss = AI_VALUE2(Unit*, "find target", "anub'rekhan");
if (!boss)
{
return false;
}
return true;
}
bool LoathebTrigger::IsActive() { return helper.UpdateBossAI(); }
bool ThaddiusPhasePetTrigger::IsActive()
@@ -208,3 +234,4 @@ bool ThaddiusPhaseThaddiusTrigger::IsActive()
}
template bool BossEventTrigger<Grobbulus::boss_grobbulus::boss_grobbulusAI>::IsActive();
template bool BossPhaseTrigger<Anubrekhan::boss_anubrekhan::boss_anubrekhanAI>::IsActive();

View File

@@ -49,6 +49,23 @@ protected:
uint32 boss_entry, event_id, last_event_time;
};
template <class T>
class BossPhaseTrigger : public Trigger
{
public:
BossPhaseTrigger(PlayerbotAI* ai, std::string boss_name, uint32 phase_mask, std::string name = "boss event")
: Trigger(ai, name, 1)
{
this->boss_name = boss_name;
this->phase_mask = phase_mask;
}
virtual bool IsActive();
protected:
std::string boss_name;
uint32 phase_mask;
};
class GrobbulusCloudTrigger : public BossEventTrigger<Grobbulus::boss_grobbulus::boss_grobbulusAI>
{
public:
@@ -100,11 +117,10 @@ private:
KelthuzadBossHelper helper;
};
class AnubrekhanTrigger : public Trigger
class AnubrekhanTrigger : public BossPhaseTrigger<Anubrekhan::boss_anubrekhan::boss_anubrekhanAI>
{
public:
AnubrekhanTrigger(PlayerbotAI* ai) : Trigger(ai, "anub'rekhan") {}
bool IsActive() override;
AnubrekhanTrigger(PlayerbotAI* ai) : BossPhaseTrigger(ai, "anub'rekhan", 0, "anub'rekhan trigger") {}
};
class ThaddiusPhasePetTrigger : public Trigger
@@ -178,6 +194,12 @@ private:
SapphironBossHelper helper;
};
// class SapphironGroundExceptMainTankTrigger : public BossPhaseTrigger
// {
// public:
// SapphironGroundExceptMainTankTrigger(PlayerbotAI* ai) : BossPhaseTrigger(ai, "sapphiron", (1 << (2 - 1)),
// "sapphiron ground except main tank") {} virtual bool IsActive();
// };
class SapphironFlightTrigger : public Trigger
{
@@ -189,6 +211,20 @@ private:
SapphironBossHelper helper;
};
// class SapphironGroundChillTrigger : public BossPhaseTrigger
// {
// public:
// SapphironGroundChillTrigger(PlayerbotAI* ai) : BossPhaseTrigger(ai, "sapphiron", 0, "sapphiron chill") {}
// virtual bool IsActive();
// };
// class KelthuzadPhaseTwoTrigger : public BossPhaseTrigger
// {
// public:
// KelthuzadPhaseTwoTrigger(PlayerbotAI* ai) : BossPhaseTrigger(ai, "kel'thuzad", 1 << (2 - 1), "kel'thuzad
// trigger") {}
// };
class GluthTrigger : public Trigger
{
public:
@@ -221,4 +257,5 @@ private:
LoathebBossHelper helper;
};
// template BossEventTrigger<class boss_grobbulus::boss_grobbulusAI>;
#endif

View File

@@ -1,30 +0,0 @@
#ifndef _PLAYERBOT_RAIDOSACTIONCONTEXT_H
#define _PLAYERBOT_RAIDOSACTIONCONTEXT_H
#include "Action.h"
#include "NamedObjectContext.h"
#include "RaidOsActions.h"
class RaidOsActionContext : public NamedObjectContext<Action>
{
public:
RaidOsActionContext()
{
creators["sartharion tank position"] = &RaidOsActionContext::tank_position;
creators["avoid twilight fissure"] = &RaidOsActionContext::avoid_twilight_fissure;
creators["avoid flame tsunami"] = &RaidOsActionContext::avoid_flame_tsunami;
creators["sartharion attack priority"] = &RaidOsActionContext::attack_priority;
creators["enter twilight portal"] = &RaidOsActionContext::enter_twilight_portal;
creators["exit twilight portal"] = &RaidOsActionContext::exit_twilight_portal;
}
private:
static Action* tank_position(PlayerbotAI* ai) { return new SartharionTankPositionAction(ai); }
static Action* avoid_twilight_fissure(PlayerbotAI* ai) { return new AvoidTwilightFissureAction(ai); }
static Action* avoid_flame_tsunami(PlayerbotAI* ai) { return new AvoidFlameTsunamiAction(ai); }
static Action* attack_priority(PlayerbotAI* ai) { return new SartharionAttackPriorityAction(ai); }
static Action* enter_twilight_portal(PlayerbotAI* ai) { return new EnterTwilightPortalAction(ai); }
static Action* exit_twilight_portal(PlayerbotAI* ai) { return new ExitTwilightPortalAction(ai); }
};
#endif

View File

@@ -1,246 +0,0 @@
#include "RaidOsActions.h"
#include "RaidOsTriggers.h"
#include "Playerbots.h"
bool SartharionTankPositionAction::Execute(Event event)
{
Unit* boss = AI_VALUE2(Unit*, "find target", "sartharion");
if (!boss) { return false; }
// Unit* shadron = AI_VALUE2(Unit*, "find target", "shadron");
// Unit* tenebron = AI_VALUE2(Unit*, "find target", "tenebron");
// Unit* vesperon = AI_VALUE2(Unit*, "find target", "vesperon");
Unit* shadron = nullptr;
Unit* tenebron = nullptr;
Unit* vesperon = nullptr;
// Detect incoming drakes before they are on aggro table
GuidVector targets = AI_VALUE(GuidVector, "possible targets no los");
for (auto& target : targets)
{
Unit* unit = botAI->GetUnit(target);
if (!unit) { continue; }
switch (unit->GetEntry())
{
case NPC_SHADRON:
shadron = unit;
continue;
case NPC_TENEBRON:
tenebron = unit;
continue;
case NPC_VESPERON:
vesperon = unit;
continue;
default:
continue;
}
}
Position currentPos = bot->GetPosition();
// Adjustable, this is the acceptable distance to stack point that will be accepted as "safe"
float looseDistance = 12.0f;
if (botAI->IsMainTank(bot))
{
if (bot->GetExactDist2d(SARTHARION_MAINTANK_POSITION.first, SARTHARION_MAINTANK_POSITION.second) > looseDistance)
{
return MoveTo(OS_MAP_ID, SARTHARION_MAINTANK_POSITION.first, SARTHARION_MAINTANK_POSITION.second, currentPos.GetPositionZ(),
false, false, false, false, MovementPriority::MOVEMENT_COMBAT);
}
}
// Offtank grab drakes
else if (shadron || tenebron || vesperon)
{
float triggerDistance = 100.0f;
// Prioritise threat before positioning
if (tenebron && bot->GetExactDist2d(tenebron) < triggerDistance &&
tenebron->GetTarget() != bot->GetGUID() && AI_VALUE(Unit*, "current target") != tenebron)
{
return Attack(tenebron);
}
if (shadron && bot->GetExactDist2d(shadron) < triggerDistance &&
shadron->GetTarget() != bot->GetGUID() && AI_VALUE(Unit*, "current target") != shadron)
{
return Attack(shadron);
}
if (vesperon && bot->GetExactDist2d(vesperon) < triggerDistance &&
vesperon->GetTarget() != bot->GetGUID() && AI_VALUE(Unit*, "current target") != vesperon)
{
return Attack(vesperon);
}
bool drakeInCombat = (tenebron && bot->GetExactDist2d(tenebron) < triggerDistance) ||
(shadron && bot->GetExactDist2d(shadron) < triggerDistance) ||
(vesperon && bot->GetExactDist2d(vesperon) < triggerDistance);
// Offtank has threat on drakes, check positioning
if (drakeInCombat && bot->GetExactDist2d(SARTHARION_OFFTANK_POSITION.first, SARTHARION_OFFTANK_POSITION.second) > looseDistance)
{
return MoveTo(OS_MAP_ID, SARTHARION_OFFTANK_POSITION.first, SARTHARION_OFFTANK_POSITION.second, currentPos.GetPositionZ(),
false, false, false, false, MovementPriority::MOVEMENT_COMBAT);
}
}
return false;
}
bool AvoidTwilightFissureAction::Execute(Event event)
{
const float radius = 5.0f;
GuidVector npcs = AI_VALUE(GuidVector, "nearest hostile npcs");
for (auto& npc : npcs)
{
Unit* unit = botAI->GetUnit(npc);
if (unit && unit->GetEntry() == NPC_TWILIGHT_FISSURE)
{
float currentDistance = bot->GetDistance2d(unit);
if (currentDistance < radius)
{
return MoveAway(unit, radius - currentDistance);
}
}
}
return false;
}
bool AvoidFlameTsunamiAction::Execute(Event event)
{
// Adjustable, this is the acceptable distance to stack point that will be accepted as "safe"
float looseDistance = 4.0f;
GuidVector npcs = AI_VALUE(GuidVector, "nearest hostile npcs");
for (auto& npc : npcs)
{
Unit* unit = botAI->GetUnit(npc);
if (unit && unit->GetEntry() == NPC_FLAME_TSUNAMI)
{
Position currentPos = bot->GetPosition();
// I think these are centrepoints for the wave segments. Either way they uniquely identify the wave
// direction as they have different coords for the left and right waves
// int casting is not a mistake, need to avoid FP errors somehow.
// I always saw these accurate to around 6 decimal places, but if there are issues,
// can switch this to abs comparison of floats which would technically be more robust.
int posY = (int) unit->GetPositionY();
if (posY == 505 || posY == 555) // RIGHT WAVE
{
bool wavePassed = currentPos.GetPositionX() > unit->GetPositionX();
if (wavePassed)
{
return false;
}
if (bot->GetExactDist2d(currentPos.GetPositionX(), TSUNAMI_RIGHT_SAFE_ALL) > looseDistance)
{
return MoveTo(OS_MAP_ID, currentPos.GetPositionX(), TSUNAMI_RIGHT_SAFE_ALL, currentPos.GetPositionZ(),
false, false, false, false, MovementPriority::MOVEMENT_COMBAT);
}
}
else // LEFT WAVE
{
bool wavePassed = currentPos.GetPositionX() < unit->GetPositionX();
if (wavePassed)
{
return false;
}
if (botAI->IsMelee(bot))
{
if (bot->GetExactDist2d(currentPos.GetPositionX(), TSUNAMI_LEFT_SAFE_MELEE) > looseDistance)
{
return MoveTo(OS_MAP_ID, currentPos.GetPositionX(), TSUNAMI_LEFT_SAFE_MELEE, currentPos.GetPositionZ(),
false, false, false, false, MovementPriority::MOVEMENT_COMBAT);
}
}
else // Ranged/healers
{
if (bot->GetExactDist2d(currentPos.GetPositionX(), TSUNAMI_LEFT_SAFE_RANGED) > looseDistance)
{
return MoveTo(OS_MAP_ID, currentPos.GetPositionX(), TSUNAMI_LEFT_SAFE_RANGED, currentPos.GetPositionZ(),
false, false, false, false, MovementPriority::MOVEMENT_COMBAT);
}
}
}
}
}
return false;
}
bool SartharionAttackPriorityAction::Execute(Event event)
{
Unit* sartharion = AI_VALUE2(Unit*, "find target", "sartharion");
Unit* shadron = AI_VALUE2(Unit*, "find target", "shadron");
Unit* tenebron = AI_VALUE2(Unit*, "find target", "tenebron");
Unit* vesperon = AI_VALUE2(Unit*, "find target", "vesperon");
Unit* acolyte = AI_VALUE2(Unit*, "find target", "acolyte of shadron");
Unit* target = nullptr;
if (acolyte)
{
target = acolyte;
}
else if (vesperon)
{
target = vesperon;
}
else if (tenebron)
{
target = tenebron;
}
else if (shadron)
{
target = shadron;
}
else if (sartharion)
{
target = sartharion;
}
if (target && AI_VALUE(Unit*, "current target") != target)
{
return Attack(target);
}
return false;
}
bool EnterTwilightPortalAction::Execute(Event event)
{
Unit* boss = AI_VALUE2(Unit*, "find target", "sartharion");
if (!boss || !boss->HasAura(SPELL_GIFT_OF_TWILIGHT_FIRE)) { return false; }
GameObject* portal = bot->FindNearestGameObject(GO_TWILIGHT_PORTAL, 100.0f);
if (!portal) { return false; }
if (!portal->IsAtInteractDistance(bot))
{
return MoveTo(portal, fmaxf(portal->GetInteractionDistance() - 1.0f, 0.0f));
}
// Go through portal
WorldPacket data1(CMSG_GAMEOBJ_USE);
data1 << portal->GetGUID();
bot->GetSession()->HandleGameObjectUseOpcode(data1);
return true;
}
bool ExitTwilightPortalAction::Execute(Event event)
{
GameObject* portal = bot->FindNearestGameObject(GO_NORMAL_PORTAL, 100.0f);
if (!portal) { return false; }
if (!portal->IsAtInteractDistance(bot))
{
return MoveTo(portal, fmaxf(portal->GetInteractionDistance() - 1.0f, 0.0f));
}
// Go through portal
WorldPacket data1(CMSG_GAMEOBJ_USE);
data1 << portal->GetGUID();
bot->GetSession()->HandleGameObjectUseOpcode(data1);
return true;
}

View File

@@ -1,64 +0,0 @@
#ifndef _PLAYERBOT_RAIDOSACTIONS_H
#define _PLAYERBOT_RAIDOSACTIONS_H
#include "MovementActions.h"
#include "AttackAction.h"
#include "PlayerbotAI.h"
#include "Playerbots.h"
const float TSUNAMI_LEFT_SAFE_MELEE = 552.0f;
const float TSUNAMI_LEFT_SAFE_RANGED = 504.0f;
const float TSUNAMI_RIGHT_SAFE_ALL = 529.0f;
const std::pair<float, float> SARTHARION_MAINTANK_POSITION = {3258.5f, 532.5f};
const std::pair<float, float> SARTHARION_OFFTANK_POSITION = {3230.0f, 526.0f};
const std::pair<float, float> SARTHARION_RANGED_POSITION = {3248.0f, 507.0f};
class SartharionTankPositionAction : public AttackAction
{
public:
SartharionTankPositionAction(PlayerbotAI* botAI, std::string const name = "sartharion tank position")
: AttackAction(botAI, name) {}
bool Execute(Event event) override;
};
class AvoidTwilightFissureAction : public MovementAction
{
public:
AvoidTwilightFissureAction(PlayerbotAI* botAI, std::string const name = "avoid twilight fissure")
: MovementAction(botAI, name) {}
bool Execute(Event event) override;
};
class AvoidFlameTsunamiAction : public MovementAction
{
public:
AvoidFlameTsunamiAction(PlayerbotAI* botAI, std::string const name = "avoid flame tsunami")
: MovementAction(botAI, name) {}
bool Execute(Event event) override;
};
class SartharionAttackPriorityAction : public AttackAction
{
public:
SartharionAttackPriorityAction(PlayerbotAI* botAI, std::string const name = "sartharion attack priority")
: AttackAction(botAI, name) {}
bool Execute(Event event) override;
};
class EnterTwilightPortalAction : public MovementAction
{
public:
EnterTwilightPortalAction(PlayerbotAI* botAI, std::string const name = "enter twilight portal")
: MovementAction(botAI, name) {}
bool Execute(Event event) override;
};
class ExitTwilightPortalAction : public MovementAction
{
public:
ExitTwilightPortalAction(PlayerbotAI* botAI, std::string const name = "exit twilight portal")
: MovementAction(botAI, name) {}
bool Execute(Event event) override;
};
#endif

View File

@@ -1,49 +0,0 @@
#include "RaidOsMultipliers.h"
#include "ChooseTargetActions.h"
#include "DKActions.h"
#include "DruidActions.h"
#include "DruidBearActions.h"
#include "FollowActions.h"
#include "GenericActions.h"
#include "GenericSpellActions.h"
#include "MovementActions.h"
#include "PaladinActions.h"
#include "RaidOsActions.h"
#include "RaidOsTriggers.h"
#include "ReachTargetActions.h"
#include "ScriptedCreature.h"
#include "WarriorActions.h"
float SartharionMultiplier::GetValue(Action* action)
{
Unit* boss = AI_VALUE2(Unit*, "find target", "sartharion");
if (!boss) { return 1.0f; }
Unit* target = action->GetTarget();
if (botAI->IsMainTank(bot) && dynamic_cast<TankFaceAction*>(action))
{
// return 0.0f;
}
if (botAI->IsDps(bot) && dynamic_cast<DpsAssistAction*>(action))
{
return 0.0f;
}
if (botAI->IsMainTank(bot) && target && target != boss &&
(dynamic_cast<TankAssistAction*>(action) || dynamic_cast<CastTauntAction*>(action) || dynamic_cast<CastDarkCommandAction*>(action) ||
dynamic_cast<CastHandOfReckoningAction*>(action) || dynamic_cast<CastGrowlAction*>(action)))
{
return 0.0f;
}
if (botAI->IsAssistTank(bot) && target && target == boss &&
(dynamic_cast<CastTauntAction*>(action) || dynamic_cast<CastDarkCommandAction*>(action) ||
dynamic_cast<CastHandOfReckoningAction*>(action) || dynamic_cast<CastGrowlAction*>(action)))
{
return 0.0f;
}
return 1.0f;
}

View File

@@ -1,16 +0,0 @@
#ifndef _PLAYERRBOT_RAIDOSMULTIPLIERS_H
#define _PLAYERRBOT_RAIDOSMULTIPLIERS_H
#include "Multiplier.h"
class SartharionMultiplier : public Multiplier
{
public:
SartharionMultiplier(PlayerbotAI* ai) : Multiplier(ai, "sartharion") {}
public:
virtual float GetValue(Action* action);
};
#endif

View File

@@ -1,32 +0,0 @@
#include "RaidOsStrategy.h"
#include "RaidOsMultipliers.h"
#include "Strategy.h"
void RaidOsStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
{
triggers.push_back(
new TriggerNode("sartharion tank",
NextAction::array(0, new NextAction("sartharion tank position", ACTION_MOVE), nullptr)));
triggers.push_back(
new TriggerNode("twilight fissure",
NextAction::array(0, new NextAction("avoid twilight fissure", ACTION_RAID + 2), nullptr)));
triggers.push_back(
new TriggerNode("flame tsunami",
NextAction::array(0, new NextAction("avoid flame tsunami", ACTION_RAID + 1), nullptr)));
triggers.push_back(
new TriggerNode("sartharion dps",
NextAction::array(0, new NextAction("sartharion attack priority", ACTION_RAID), nullptr)));
// Flank dragon positioning
triggers.push_back(new TriggerNode("sartharion melee positioning",
NextAction::array(0, new NextAction("rear flank", ACTION_MOVE + 4), nullptr)));
triggers.push_back(new TriggerNode("twilight portal enter",
NextAction::array(0, new NextAction("enter twilight portal", ACTION_RAID + 1), nullptr)));
triggers.push_back(new TriggerNode("twilight portal exit",
NextAction::array(0, new NextAction("exit twilight portal", ACTION_RAID + 1), nullptr)));
}
void RaidOsStrategy::InitMultipliers(std::vector<Multiplier*> &multipliers)
{
multipliers.push_back(new SartharionMultiplier(botAI));
}

View File

@@ -1,17 +0,0 @@
#ifndef _PLAYERBOT_RAIDOSSTRATEGY_H
#define _PLAYERBOT_RAIDOSSTRATEGY_H
#include "AiObjectContext.h"
#include "Multiplier.h"
#include "Strategy.h"
class RaidOsStrategy : public Strategy
{
public:
RaidOsStrategy(PlayerbotAI* ai) : Strategy(ai) {}
virtual std::string const getName() override { return "wotlk-os"; }
virtual void InitTriggers(std::vector<TriggerNode*> &triggers) override;
virtual void InitMultipliers(std::vector<Multiplier*> &multipliers) override;
};
#endif

View File

@@ -1,32 +0,0 @@
#ifndef _PLAYERBOT_RAIDOSTRIGGERCONTEXT_H
#define _PLAYERBOT_RAIDOSTRIGGERCONTEXT_H
#include "AiObjectContext.h"
#include "NamedObjectContext.h"
#include "RaidOsTriggers.h"
class RaidOsTriggerContext : public NamedObjectContext<Trigger>
{
public:
RaidOsTriggerContext()
{
creators["sartharion tank"] = &RaidOsTriggerContext::sartharion_tank;
creators["flame tsunami"] = &RaidOsTriggerContext::flame_tsunami;
creators["twilight fissure"] = &RaidOsTriggerContext::twilight_fissure;
creators["sartharion dps"] = &RaidOsTriggerContext::sartharion_dps;
creators["sartharion melee positioning"] = &RaidOsTriggerContext::sartharion_melee;
creators["twilight portal enter"] = &RaidOsTriggerContext::twilight_portal_enter;
creators["twilight portal exit"] = &RaidOsTriggerContext::twilight_portal_exit;
}
private:
static Trigger* sartharion_tank(PlayerbotAI* ai) { return new SartharionTankTrigger(ai); }
static Trigger* flame_tsunami(PlayerbotAI* ai) { return new FlameTsunamiTrigger(ai); }
static Trigger* twilight_fissure(PlayerbotAI* ai) { return new TwilightFissureTrigger(ai); }
static Trigger* sartharion_dps(PlayerbotAI* ai) { return new SartharionDpsTrigger(ai); }
static Trigger* sartharion_melee(PlayerbotAI* ai) { return new SartharionMeleePositioningTrigger(ai); }
static Trigger* twilight_portal_enter(PlayerbotAI* ai) { return new TwilightPortalEnterTrigger(ai); }
static Trigger* twilight_portal_exit(PlayerbotAI* ai) { return new TwilightPortalExitTrigger(ai); }
};
#endif

View File

@@ -1,128 +0,0 @@
#include "RaidOsTriggers.h"
#include "SharedDefines.h"
bool SartharionTankTrigger::IsActive()
{
Unit* boss = AI_VALUE2(Unit*, "find target", "sartharion");
if (!boss) { return false; }
return botAI->IsTank(bot);
}
bool FlameTsunamiTrigger::IsActive()
{
if (botAI->IsTank(bot)) { return false; }
Unit* boss = AI_VALUE2(Unit*, "find target", "sartharion");
if (!boss) { return false; }
GuidVector npcs = AI_VALUE(GuidVector, "nearest hostile npcs");
for (auto& npc : npcs)
{
Unit* unit = botAI->GetUnit(npc);
if (unit)
{
if (unit->GetEntry() == NPC_FLAME_TSUNAMI)
{
return true;
}
}
}
return false;
}
bool TwilightFissureTrigger::IsActive()
{
Unit* boss = AI_VALUE2(Unit*, "find target", "sartharion");
if (!boss) { return false; }
GuidVector npcs = AI_VALUE(GuidVector, "nearest hostile npcs");
for (auto& npc : npcs)
{
Unit* unit = botAI->GetUnit(npc);
if (unit)
{
if (unit->GetEntry() == NPC_TWILIGHT_FISSURE)
{
return true;
}
}
}
return false;
}
bool SartharionDpsTrigger::IsActive()
{
Unit* boss = AI_VALUE2(Unit*, "find target", "sartharion");
if (!boss) { return false; }
return botAI->IsDps(bot);
}
bool SartharionMeleePositioningTrigger::IsActive()
{
if (!botAI->IsMelee(bot) || !botAI->IsDps(bot)) { return false; }
Unit* boss = AI_VALUE2(Unit*, "find target", "sartharion");
if (!boss) { return false; }
Unit* shadron = AI_VALUE2(Unit*, "find target", "shadron");
Unit* tenebron = AI_VALUE2(Unit*, "find target", "tenebron");
Unit* vesperon = AI_VALUE2(Unit*, "find target", "vesperon");
return !(shadron || tenebron || vesperon);
}
bool TwilightPortalEnterTrigger::IsActive()
{
if (botAI->IsMainTank(bot) || botAI->IsHealAssistantOfIndex(bot, 0)) { return false; }
// In 25-man, take two healers in. Otherwise just take one
// if (bot->GetRaidDifficulty() == RAID_DIFFICULTY_25MAN_NORMAL)
// {
// if (botAI->IsHealAssistantOfIndex(bot, 0) || botAI->IsHealAssistantOfIndex(bot, 1))
// {
// return false;
// }
// }
// else
// {
// if (botAI->IsHealAssistantOfIndex(bot, 0))
// {
// return false;
// }
// }
// Don't enter portal until drakes are dead
if (bot->HasAura(SPELL_POWER_OF_SHADRON) ||
bot->HasAura(SPELL_POWER_OF_TENEBRON) ||
bot->HasAura(SPELL_POWER_OF_VESPERON))
{
return false;
}
Unit* boss = AI_VALUE2(Unit*, "find target", "sartharion");
if (!boss) { return false; }
// GuidVector objects = AI_VALUE(GuidVector, "nearest game objects no los");
// for (auto& object : objects)
// {
// GameObject* go = botAI->GetGameObject(object);
// if (go && go->GetEntry() == GO_TWILIGHT_PORTAL)
// {
// return true;
// }
// }
return bool(bot->FindNearestGameObject(GO_TWILIGHT_PORTAL, 100.0f));
}
bool TwilightPortalExitTrigger::IsActive()
{
return bot->HasAura(SPELL_TWILIGHT_SHIFT) && !AI_VALUE2(Unit*, "find target", "acolyte of shadron");
}

View File

@@ -1,120 +0,0 @@
#ifndef _PLAYERBOT_RAIDOSTRIGGERS_H
#define _PLAYERBOT_RAIDOSTRIGGERS_H
#include "PlayerbotAI.h"
#include "Playerbots.h"
#include "Trigger.h"
enum ObsidianSanctumIDs
{
// Bosses
NPC_SARTHARION = 28860,
NPC_SHADRON = 30451,
NPC_TENEBRON = 30452,
NPC_VESPERON = 30449,
// Mini-boss shared
SPELL_SHADOW_BREATH = 57570,
SPELL_SHADOW_FISSURE = 57579,
SPELL_SUMMON_TWILIGHT_WHELP = 58035,
SPELL_GIFT_OF_TWILIGHT_SHADOW = 57835,
SPELL_TWILIGHT_TORMENT_VESPERON = 57935,
// Sartharion
SPELL_SARTHARION_CLEAVE = 56909,
SPELL_SARTHARION_FLAME_BREATH = 56908,
SPELL_SARTHARION_TAIL_LASH = 56910,
SPELL_CYCLONE_AURA_PERIODIC = 57598,
SPELL_LAVA_STRIKE_DUMMY = 57578,
SPELL_LAVA_STRIKE_DUMMY_TRIGGER = 57697,
SPELL_LAVA_STRIKE_SUMMON = 57572,
SPELL_SARTHARION_PYROBUFFET = 56916,
SPELL_SARTHARION_BERSERK = 61632,
SPELL_SARTHARION_TWILIGHT_REVENGE = 60639,
// Sartharion with drakes
SPELL_WILL_OF_SARTHARION = 61254,
SPELL_POWER_OF_TENEBRON = 61248,
SPELL_POWER_OF_VESPERON = 61251,
SPELL_POWER_OF_SHADRON = 58105,
SPELL_GIFT_OF_TWILIGHT_FIRE = 58766,
// Visuals
SPELL_EGG_MARKER_VISUAL = 58547,
SPELL_FLAME_TSUNAMI_VISUAL = 57494,
// Misc
SPELL_FADE_ARMOR = 60708,
SPELL_FLAME_TSUNAMI_DAMAGE_AURA = 57492,
SPELL_FLAME_TSUNAMI_LEAP = 60241,
SPELL_SARTHARION_PYROBUFFET_TRIGGER = 57557,
NPC_TWILIGHT_EGG = 30882,
NPC_TWILIGHT_WHELP = 30890,
NPC_DISCIPLE_OF_SHADRON = 30688,
NPC_DISCIPLE_OF_VESPERON = 30858,
NPC_ACOLYTE_OF_SHADRON = 31218,
NPC_ACOLYTE_OF_VESPERON = 31219,
// Sartharion fight
NPC_LAVA_BLAZE = 30643,
NPC_FLAME_TSUNAMI = 30616,
NPC_SAFE_AREA_TRIGGER = 30494,
NPC_TWILIGHT_FISSURE = 30641,
GO_TWILIGHT_PORTAL = 193988,
GO_NORMAL_PORTAL = 193989,
SPELL_TWILIGHT_SHIFT = 57874,
};
const uint32 OS_MAP_ID = 615;
class SartharionTankTrigger : public Trigger
{
public:
SartharionTankTrigger(PlayerbotAI* botAI) : Trigger(botAI, "sartharion tank") {}
bool IsActive() override;
};
class FlameTsunamiTrigger : public Trigger
{
public:
FlameTsunamiTrigger(PlayerbotAI* botAI) : Trigger(botAI, "flame tsunami") {}
bool IsActive() override;
};
class TwilightFissureTrigger : public Trigger
{
public:
TwilightFissureTrigger(PlayerbotAI* botAI) : Trigger(botAI, "twilight fissure") {}
bool IsActive() override;
};
class SartharionDpsTrigger : public Trigger
{
public:
SartharionDpsTrigger(PlayerbotAI* botAI) : Trigger(botAI, "sartharion dps") {}
bool IsActive() override;
};
class SartharionMeleePositioningTrigger : public Trigger
{
public:
SartharionMeleePositioningTrigger(PlayerbotAI* botAI) : Trigger(botAI, "sartharion melee positioning") {}
bool IsActive() override;
};
class TwilightPortalEnterTrigger : public Trigger
{
public:
TwilightPortalEnterTrigger(PlayerbotAI* botAI) : Trigger(botAI, "twilight portal enter") {}
bool IsActive() override;
};
class TwilightPortalExitTrigger : public Trigger
{
public:
TwilightPortalExitTrigger(PlayerbotAI* botAI) : Trigger(botAI, "twilight portal exit") {}
bool IsActive() override;
};
#endif

View File

@@ -88,6 +88,6 @@ void AssassinationRogueStrategy::InitTriggers(std::vector<TriggerNode*>& trigger
triggers.push_back(new TriggerNode(
"enemy out of melee",
NextAction::array(0, new NextAction("stealth", ACTION_HIGH + 3), new NextAction("sprint", ACTION_HIGH + 2),
new NextAction("reach melee", ACTION_HIGH + 1), NULL)));
NextAction::array(0, new NextAction("stealth", ACTION_NORMAL + 9), new NextAction("sprint", ACTION_NORMAL + 8),
new NextAction("reach melee", ACTION_NORMAL + 7), NULL)));
}

View File

@@ -134,8 +134,8 @@ void DpsRogueStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
triggers.push_back(new TriggerNode(
"enemy out of melee",
NextAction::array(0, new NextAction("stealth", ACTION_HIGH + 3), new NextAction("sprint", ACTION_HIGH + 2),
new NextAction("reach melee", ACTION_HIGH + 1), nullptr)));
NextAction::array(0, new NextAction("stealth", ACTION_NORMAL + 9), new NextAction("sprint", ACTION_NORMAL + 8),
new NextAction("reach melee", ACTION_NORMAL + 7), nullptr)));
triggers.push_back(new TriggerNode("expose armor",
NextAction::array(0, new NextAction("expose armor", ACTION_HIGH + 3), nullptr)));

View File

@@ -1,380 +0,0 @@
#include "NewRpgAction.h"
#include <cmath>
#include <cstdint>
#include "NewRpgStrategy.h"
#include "ObjectDefines.h"
#include "ObjectGuid.h"
#include "PathGenerator.h"
#include "Player.h"
#include "PlayerbotAI.h"
#include "Playerbots.h"
#include "Random.h"
#include "RandomPlayerbotMgr.h"
#include "Timer.h"
#include "TravelMgr.h"
#include "World.h"
bool TellRpgStatusAction::Execute(Event event)
{
std::string out = botAI->rpgInfo.ToString();
botAI->TellMasterNoFacing(out);
return true;
}
bool NewRpgStatusUpdateAction::Execute(Event event)
{
NewRpgInfo& info = botAI->rpgInfo;
switch (info.status)
{
case NewRpgStatus::IDLE:
{
uint32 roll = urand(1, 100);
// IDLE -> NEAR_NPC
// if ((!info.lastNearNpc || info.lastNearNpc + setNpcInterval < getMSTime()) && roll <= 30)
if (roll <= 30)
{
info.lastNearNpc = getMSTime();
GuidVector possibleTargets = AI_VALUE(GuidVector, "possible rpg targets");
if (!possibleTargets.empty())
{
info.status = NewRpgStatus::NEAR_NPC;
return true;
}
}
// IDLE -> GO_INNKEEPER
else if (bot->GetLevel() >= 6 && roll <= 40)
{
WorldPosition pos = SelectRandomInnKeeperPos();
if (pos != WorldPosition() && bot->GetExactDist(pos) > 50.0f)
{
info.lastGoInnKeeper = getMSTime();
info.status = NewRpgStatus::GO_INNKEEPER;
info.innKeeperPos = pos;
return true;
}
}
// IDLE -> GO_GRIND
else if (roll <= 90)
{
WorldPosition pos = SelectRandomGrindPos();
if (pos != WorldPosition())
{
info.lastGoGrind = getMSTime();
info.status = NewRpgStatus::GO_GRIND;
info.grindPos = pos;
return true;
}
}
// IDLE -> REST
info.status = NewRpgStatus::REST;
info.lastRest = getMSTime();
bot->SetStandState(UNIT_STAND_STATE_SIT);
return true;
}
case NewRpgStatus::GO_GRIND:
{
WorldPosition& originalPos = info.grindPos;
assert(info.grindPos != WorldPosition());
// GO_GRIND -> NEAR_RANDOM
if (bot->GetExactDist(originalPos) < 10.0f)
{
info.status = NewRpgStatus::NEAR_RANDOM;
info.lastNearRandom = getMSTime();
info.grindPos = WorldPosition();
return true;
}
// // just choose another grindPos
// if (!info.lastGoGrind || info.lastGoGrind + setGrindInterval < getMSTime())
// {
// WorldPosition pos = SelectRandomGrindPos();
// if (pos == WorldPosition())
// break;
// info.status = NewRpgStatus::GO_GRIND;
// info.lastGoGrind = getMSTime();
// info.grindPos = pos;
// return true;
// }
break;
}
case NewRpgStatus::GO_INNKEEPER:
{
WorldPosition& originalPos = info.innKeeperPos;
assert(info.innKeeperPos != WorldPosition());
// GO_INNKEEPER -> NEAR_NPC
if (bot->GetExactDist(originalPos) < 10.0f)
{
info.lastNearNpc = getMSTime();
info.status = NewRpgStatus::NEAR_NPC;
info.innKeeperPos = WorldPosition();
return true;
}
break;
}
case NewRpgStatus::NEAR_RANDOM:
{
// NEAR_RANDOM -> IDLE
if (info.lastNearRandom + statusNearRandomDuration < getMSTime())
{
info.status = NewRpgStatus::IDLE;
return true;
}
break;
}
case NewRpgStatus::NEAR_NPC:
{
if (info.lastNearNpc + statusNearNpcDuration < getMSTime())
{
info.status = NewRpgStatus::IDLE;
return true;
}
break;
}
case NewRpgStatus::REST:
{
// REST -> IDLE
if (info.lastRest + statusRestDuration < getMSTime())
{
info.status = NewRpgStatus::IDLE;
return true;
}
break;
}
default:
break;
}
return false;
}
WorldPosition NewRpgStatusUpdateAction::SelectRandomGrindPos()
{
const std::vector<WorldLocation>& locs = sRandomPlayerbotMgr->locsPerLevelCache[bot->GetLevel()];
std::vector<WorldLocation> lo_prepared_locs, hi_prepared_locs;
for (auto& loc : locs)
{
if (bot->GetMapId() != loc.GetMapId())
continue;
if (bot->GetMap()->GetZoneId(bot->GetPhaseMask(), loc.GetPositionX(), loc.GetPositionY(), loc.GetPositionZ()) !=
bot->GetZoneId())
continue;
if (bot->GetExactDist(loc) < 500.0f)
{
hi_prepared_locs.push_back(loc);
}
if (bot->GetExactDist(loc) < 2500.0f)
{
lo_prepared_locs.push_back(loc);
}
}
WorldPosition dest;
if (urand(1, 100) <= 50 && !hi_prepared_locs.empty())
{
uint32 idx = urand(0, hi_prepared_locs.size() - 1);
dest = hi_prepared_locs[idx];
}
else if (!lo_prepared_locs.empty())
{
uint32 idx = urand(0, lo_prepared_locs.size() - 1);
dest = lo_prepared_locs[idx];
}
LOG_DEBUG("playerbots", "[New Rpg] Bot {} select random grind pos Map:{} X:{} Y:{} Z:{} ({}+{} available in {})",
bot->GetName(), dest.GetMapId(), dest.GetPositionX(), dest.GetPositionY(), dest.GetPositionZ(),
hi_prepared_locs.size(), lo_prepared_locs.size() - hi_prepared_locs.size(), locs.size());
return dest;
}
WorldPosition NewRpgStatusUpdateAction::SelectRandomInnKeeperPos()
{
const std::vector<WorldLocation>& locs = IsAlliance(bot->getRace())
? sRandomPlayerbotMgr->allianceStarterPerLevelCache[bot->GetLevel()]
: sRandomPlayerbotMgr->hordeStarterPerLevelCache[bot->GetLevel()];
std::vector<WorldLocation> prepared_locs;
for (auto& loc : locs)
{
if (bot->GetMapId() != loc.GetMapId())
continue;
float range = bot->GetLevel() <= 5 ? 500.0f : 2500.0f;
if (bot->GetExactDist(loc) < range)
{
prepared_locs.push_back(loc);
}
}
WorldPosition dest;
if (!prepared_locs.empty())
{
uint32 idx = urand(0, prepared_locs.size() - 1);
dest = prepared_locs[idx];
}
LOG_DEBUG("playerbots", "[New Rpg] Bot {} select random inn keeper pos Map:{} X:{} Y:{} Z:{} ({} available in {})",
bot->GetName(), dest.GetMapId(), dest.GetPositionX(), dest.GetPositionY(), dest.GetPositionZ(),
prepared_locs.size(), locs.size());
return dest;
}
bool NewRpgGoFarAwayPosAction::MoveFarTo(WorldPosition dest)
{
float dis = bot->GetExactDist(dest);
if (dis < pathFinderDis)
{
return MoveTo(dest.getMapId(), dest.GetPositionX(), dest.GetPositionY(), dest.GetPositionZ(), false, false,
false, true);
}
// performance optimization
if (IsWaitingForLastMove(MovementPriority::MOVEMENT_NORMAL))
{
return false;
}
float minDelta = M_PI;
const float x = bot->GetPositionX();
const float y = bot->GetPositionY();
const float z = bot->GetPositionZ();
float rx, ry, rz;
bool found = false;
int attempt = 10;
while (--attempt)
{
float angle = bot->GetAngle(&dest);
float delta = (rand_norm() - 0.5) * M_PI * 2;
angle += delta;
float dis = rand_norm() * pathFinderDis;
float dx = x + cos(angle) * dis;
float dy = y + sin(angle) * dis;
float dz = z + 5.0f;
bot->UpdateAllowedPositionZ(dx, dy, dz);
PathGenerator path(bot);
path.CalculatePath(dx, dy, dz);
PathType type = path.GetPathType();
bool canReach = type == PATHFIND_INCOMPLETE || type == PATHFIND_NORMAL;
if (canReach && fabs(delta) <= minDelta)
{
found = true;
const G3D::Vector3& endPos = path.GetActualEndPosition();
rx = endPos.x;
ry = endPos.y;
rz = endPos.z;
minDelta = fabs(delta);
}
}
if (found)
{
return MoveTo(bot->GetMapId(), rx, ry, rz, false, false, false, true);
}
// don't fallback to direct move
// float angle = bot->GetAngle(&dest);
// return MoveTo(bot->GetMapId(), x + cos(angle) * pathFinderDis, y + sin(angle) * pathFinderDis, z);
return false;
}
bool NewRpgGoGrindAction::Execute(Event event) { return MoveFarTo(botAI->rpgInfo.grindPos); }
bool NewRpgGoInnKeeperAction::Execute(Event event) { return MoveFarTo(botAI->rpgInfo.innKeeperPos); }
bool NewRpgMoveRandomAction::Execute(Event event)
{
float distance = rand_norm() * moveStep;
Map* map = bot->GetMap();
const float x = bot->GetPositionX();
const float y = bot->GetPositionY();
const float z = bot->GetPositionZ();
int attempts = 5;
while (--attempts)
{
float angle = (float)rand_norm() * 2 * static_cast<float>(M_PI);
float dx = x + distance * cos(angle);
float dy = y + distance * sin(angle);
float dz = z;
if (!map->CheckCollisionAndGetValidCoords(bot, bot->GetPositionX(), bot->GetPositionY(), bot->GetPositionZ(),
dx, dy, dz))
continue;
if (map->IsInWater(bot->GetPhaseMask(), dx, dy, dz, bot->GetCollisionHeight()))
continue;
bool moved = MoveTo(bot->GetMapId(), dx, dy, dz, false, false, false, true);
if (moved)
return true;
}
return false;
}
bool NewRpgMoveNpcAction::Execute(Event event)
{
NewRpgInfo& info = botAI->rpgInfo;
if (!info.npcPos)
{
GuidVector possibleTargets = AI_VALUE(GuidVector, "possible rpg targets");
if (possibleTargets.empty())
return false;
int idx = urand(0, possibleTargets.size() - 1);
ObjectGuid guid = possibleTargets[idx];
Unit* unit = botAI->GetUnit(guid);
if (unit)
{
info.npcPos = GuidPosition(unit);
info.lastReachNpc = 0;
}
else
return false;
}
if (bot->GetDistance(info.npcPos) <= INTERACTION_DISTANCE)
{
if (!info.lastReachNpc)
{
info.lastReachNpc = getMSTime();
return true;
}
if (info.lastReachNpc && info.lastReachNpc + stayTime > getMSTime())
return false;
info.npcPos = GuidPosition();
info.lastReachNpc = 0;
}
else
{
assert(info.npcPos);
Unit* unit = botAI->GetUnit(info.npcPos);
if (!unit)
return false;
float x = unit->GetPositionX();
float y = unit->GetPositionY();
float z = unit->GetPositionZ();
float mapId = unit->GetMapId();
float angle = 0.f;
if (bot->IsWithinLOS(x, y, z))
{
if (!unit->isMoving())
angle = unit->GetAngle(bot) + (M_PI * irand(-25, 25) / 100.0); // Closest 45 degrees towards the target
else
angle = unit->GetOrientation() +
(M_PI * irand(-25, 25) / 100.0); // 45 degrees infront of target (leading it's movement)
}
else
angle = 2 * M_PI * rand_norm(); // A circle around the target.
float rnd = rand_norm();
x += cos(angle) * INTERACTION_DISTANCE * rnd;
y += sin(angle) * INTERACTION_DISTANCE * rnd;
// bool exact = true;
if (!unit->GetMap()->CheckCollisionAndGetValidCoords(unit, unit->GetPositionX(), unit->GetPositionY(),
unit->GetPositionZ(), x, y, z))
{
x = unit->GetPositionX();
y = unit->GetPositionY();
z = unit->GetPositionZ();
// exact = false;
}
return MoveTo(mapId, x, y, z, false, false, false, true);
}
return true;
}

View File

@@ -1,78 +0,0 @@
#ifndef _PLAYERBOT_NEWRPGACTION_H
#define _PLAYERBOT_NEWRPGACTION_H
#include "Duration.h"
#include "MovementActions.h"
#include "NewRpgStrategy.h"
#include "TravelMgr.h"
#include "PlayerbotAI.h"
class TellRpgStatusAction : public Action
{
public:
TellRpgStatusAction(PlayerbotAI* botAI) : Action(botAI, "rpg status") {}
bool Execute(Event event) override;
};
class NewRpgStatusUpdateAction : public Action
{
public:
NewRpgStatusUpdateAction(PlayerbotAI* botAI) : Action(botAI, "new rpg status update") {}
bool Execute(Event event) override;
protected:
// const int32 setGrindInterval = 5 * 60 * 1000;
// const int32 setNpcInterval = 1 * 60 * 1000;
const int32 statusNearNpcDuration = 2 * 60 * 1000;
const int32 statusNearRandomDuration = 2 * 60 * 1000;
const int32 statusRestDuration = 30 * 1000;
WorldPosition SelectRandomGrindPos();
WorldPosition SelectRandomInnKeeperPos();
};
class NewRpgGoFarAwayPosAction : public MovementAction
{
public:
NewRpgGoFarAwayPosAction(PlayerbotAI* botAI, std::string name) : MovementAction(botAI, name) {}
// bool Execute(Event event) override;
bool MoveFarTo(WorldPosition dest);
protected:
// WorldPosition dest;
float pathFinderDis = 70.0f; // path finder
};
class NewRpgGoGrindAction : public NewRpgGoFarAwayPosAction
{
public:
NewRpgGoGrindAction(PlayerbotAI* botAI) : NewRpgGoFarAwayPosAction(botAI, "new rpg go grind") {}
bool Execute(Event event) override;
};
class NewRpgGoInnKeeperAction : public NewRpgGoFarAwayPosAction
{
public:
NewRpgGoInnKeeperAction(PlayerbotAI* botAI) : NewRpgGoFarAwayPosAction(botAI, "new rpg go innkeeper") {}
bool Execute(Event event) override;
};
class NewRpgMoveRandomAction : public MovementAction
{
public:
NewRpgMoveRandomAction(PlayerbotAI* botAI) : MovementAction(botAI, "new rpg move random") {}
bool Execute(Event event) override;
protected:
const float moveStep = 50.0f;
};
class NewRpgMoveNpcAction : public MovementAction
{
public:
NewRpgMoveNpcAction(PlayerbotAI* botAI) : MovementAction(botAI, "new rpg move npcs") {}
bool Execute(Event event) override;
protected:
const uint32 stayTime = 8 * 1000;
};
#endif

View File

@@ -1,35 +0,0 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it
* and/or modify it under version 2 of the License, or (at your option), any later version.
*/
#include "NewRpgStrategy.h"
#include "Playerbots.h"
NewRpgStrategy::NewRpgStrategy(PlayerbotAI* botAI) : Strategy(botAI) {}
NextAction** NewRpgStrategy::getDefaultActions()
{
return NextAction::array(0, new NextAction("new rpg status update", 5.0f), nullptr);
}
void NewRpgStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
{
triggers.push_back(
new TriggerNode("go grind status", NextAction::array(0, new NextAction("new rpg go grind", 1.0f), nullptr)));
triggers.push_back(
new TriggerNode("go innkeeper status", NextAction::array(0, new NextAction("new rpg go innkeeper", 1.0f), nullptr)));
triggers.push_back(
new TriggerNode("near random status", NextAction::array(0, new NextAction("new rpg move random", 1.0f), nullptr)));
triggers.push_back(
new TriggerNode("near npc status", NextAction::array(0, new NextAction("new rpg move npc", 1.0f), nullptr)));
}
void NewRpgStrategy::InitMultipliers(std::vector<Multiplier*>& multipliers)
{
// multipliers.push_back(new RpgActionMultiplier(botAI));
}

View File

@@ -1,98 +0,0 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it
* and/or modify it under version 2 of the License, or (at your option), any later version.
*/
#ifndef _PLAYERBOT_NEWRPGSTRATEGY_H
#define _PLAYERBOT_NEWRPGSTRATEGY_H
#include <cstdint>
#include "Strategy.h"
#include "TravelMgr.h"
class PlayerbotAI;
enum class NewRpgStatus
{
// Going to far away place
GO_GRIND,
GO_INNKEEPER,
// Exploring nearby
NEAR_RANDOM,
NEAR_NPC,
// Taking a break
REST,
// Initial status
IDLE
};
struct NewRpgInfo
{
NewRpgStatus status{NewRpgStatus::IDLE};
// NewRpgStatus::GO_GRIND
WorldPosition grindPos{};
uint32 lastGoGrind{0};
// NewRpgStatus::GO_INNKEEPER
WorldPosition innKeeperPos{};
uint32 lastGoInnKeeper{0};
// NewRpgStatus::NEAR_NPC
GuidPosition npcPos{};
uint32 lastNearNpc{0};
uint32 lastReachNpc{0};
// NewRpgStatus::NEAR_RANDOM
uint32 lastNearRandom{0};
// NewRpgStatus::REST
uint32 lastRest{0};
std::string ToString()
{
std::stringstream out;
out << "Status: ";
switch (status)
{
case NewRpgStatus::GO_GRIND:
out << "GO_GRIND";
out << "\nGrindPos: " << grindPos.GetMapId() << " " << grindPos.GetPositionX() << " " << grindPos.GetPositionY() << " " << grindPos.GetPositionZ();
out << "\nlastGoGrind: " << lastGoGrind;
break;
case NewRpgStatus::GO_INNKEEPER:
out << "GO_INNKEEPER";
out << "\nInnKeeperPos: " << innKeeperPos.GetMapId() << " " << innKeeperPos.GetPositionX() << " " << innKeeperPos.GetPositionY() << " " << innKeeperPos.GetPositionZ();
out << "\nlastGoInnKeeper: " << lastGoInnKeeper;
break;
case NewRpgStatus::NEAR_NPC:
out << "NEAR_NPC";
out << "\nNpcPos: " << npcPos.GetMapId() << " " << npcPos.GetPositionX() << " " << npcPos.GetPositionY() << " " << npcPos.GetPositionZ();
out << "\nlastNearNpc: " << lastNearNpc;
out << "\nlastReachNpc: " << lastReachNpc;
break;
case NewRpgStatus::NEAR_RANDOM:
out << "NEAR_RANDOM";
out << "\nlastNearRandom: " << lastNearRandom;
break;
case NewRpgStatus::IDLE:
out << "IDLE";
break;
case NewRpgStatus::REST:
out << "REST";
out << "\nlastRest: " << lastRest;
break;
default:
out << "UNKNOWN";
}
return out.str();
}
};
class NewRpgStrategy : public Strategy
{
public:
NewRpgStrategy(PlayerbotAI* botAI);
std::string const getName() override { return "new rpg"; }
NextAction** getDefaultActions() override;
void InitTriggers(std::vector<TriggerNode*>& triggers) override;
void InitMultipliers(std::vector<Multiplier*>& multipliers) override;
};
#endif

View File

@@ -1,4 +0,0 @@
#include "NewRpgTriggers.h"
#include "PlayerbotAI.h"
bool NewRpgStatusTrigger::IsActive() { return status == botAI->rpgInfo.status; }

View File

@@ -1,20 +0,0 @@
#ifndef _PLAYERBOT_NEWRPGTRIGGERS_H
#define _PLAYERBOT_NEWRPGTRIGGERS_H
#include "NewRpgStrategy.h"
#include "Trigger.h"
class NewRpgStatusTrigger : public Trigger
{
public:
NewRpgStatusTrigger(PlayerbotAI* botAI, NewRpgStatus status = NewRpgStatus::IDLE)
: Trigger(botAI, "new rpg status"), status(status)
{
}
bool IsActive() override;
protected:
NewRpgStatus status;
};
#endif

View File

@@ -50,7 +50,7 @@ void HealShamanStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
NextAction::array(0, new NextAction("earthliving weapon", 22.0f), nullptr)));
triggers.push_back(new TriggerNode(
"group heal setting",
NextAction::array(0, new NextAction("riptide on party", 27.0f), new NextAction("chain heal on party", 26.0f), NULL)));
NextAction::array(0, new NextAction("riptide on party", 23.0f), new NextAction("chain heal on party", 22.0f), NULL)));
triggers.push_back(new TriggerNode(
"party member critical health",

View File

@@ -77,7 +77,7 @@ void MeleeShamanStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
triggers.push_back(new TriggerNode(
"medium aoe", NextAction::array(0, new NextAction("strength of earth totem", ACTION_LIGHT_HEAL), nullptr)));
triggers.push_back(new TriggerNode(
"enemy out of melee", NextAction::array(0, new NextAction("reach melee", ACTION_HIGH + 1), nullptr)));
"enemy out of melee", NextAction::array(0, new NextAction("reach melee", ACTION_NORMAL + 8), nullptr)));
triggers.push_back(new TriggerNode(
"no fire totem",

View File

@@ -24,7 +24,6 @@ public:
creators["reputation"] = &ChatTriggerContext::reputation;
creators["log"] = &ChatTriggerContext::log;
creators["los"] = &ChatTriggerContext::los;
creators["rpg status"] = &ChatTriggerContext::rpg_status;
creators["aura"] = &ChatTriggerContext::aura;
creators["drop"] = &ChatTriggerContext::drop;
creators["share"] = &ChatTriggerContext::share;
@@ -212,7 +211,6 @@ private:
static Trigger* reputation(PlayerbotAI* botAI) { return new ChatCommandTrigger(botAI, "reputation"); }
static Trigger* log(PlayerbotAI* botAI) { return new ChatCommandTrigger(botAI, "log"); }
static Trigger* los(PlayerbotAI* botAI) { return new ChatCommandTrigger(botAI, "los"); }
static Trigger* rpg_status(PlayerbotAI* botAI) { return new ChatCommandTrigger(botAI, "rpg status"); }
static Trigger* aura(PlayerbotAI* ai) { return new ChatCommandTrigger(ai, "aura"); }
static Trigger* loot_all(PlayerbotAI* botAI) { return new ChatCommandTrigger(botAI, "add all loot"); }
static Trigger* release(PlayerbotAI* botAI) { return new ChatCommandTrigger(botAI, "release"); }

View File

@@ -25,8 +25,4 @@ bool PartyMemberNeedCureTrigger::IsActive()
return target && target->IsInWorld();
}
bool NeedWorldBuffTrigger::IsActive()
{
std::any_of(WorldBuffAction::NeedWorldBuffs(bot).begin(), WorldBuffAction::NeedWorldBuffs(bot).end(),
[](const auto& wb) { return true; });
}
bool NeedWorldBuffTrigger::IsActive() { return !WorldBuffAction::NeedWorldBuffs(bot).empty(); }

View File

@@ -12,8 +12,6 @@
#include "LfgTriggers.h"
#include "LootTriggers.h"
#include "NamedObjectContext.h"
#include "NewRpgStrategy.h"
#include "NewRpgTriggers.h"
#include "PvpTriggers.h"
#include "RaidNaxxTriggers.h"
#include "RpgTriggers.h"
@@ -215,10 +213,6 @@ public:
creators["rpg craft"] = &TriggerContext::rpg_craft;
creators["rpg trade useful"] = &TriggerContext::rpg_trade_useful;
creators["rpg duel"] = &TriggerContext::rpg_duel;
creators["go grind status"] = &TriggerContext::go_grind_status;
creators["go innkeeper status"] = &TriggerContext::go_innkeeper_status;
creators["near random status"] = &TriggerContext::near_random_status;
creators["near npc status"] = &TriggerContext::near_npc_status;
}
private:
@@ -408,10 +402,6 @@ private:
static Trigger* rpg_craft(PlayerbotAI* botAI) { return new RpgCraftTrigger(botAI); }
static Trigger* rpg_trade_useful(PlayerbotAI* botAI) { return new RpgTradeUsefulTrigger(botAI); }
static Trigger* rpg_duel(PlayerbotAI* botAI) { return new RpgDuelTrigger(botAI); }
static Trigger* go_grind_status(PlayerbotAI* botAI) { return new NewRpgStatusTrigger(botAI, NewRpgStatus::GO_GRIND); }
static Trigger* go_innkeeper_status(PlayerbotAI* botAI) { return new NewRpgStatusTrigger(botAI, NewRpgStatus::GO_INNKEEPER); }
static Trigger* near_random_status(PlayerbotAI* botAI) { return new NewRpgStatusTrigger(botAI, NewRpgStatus::NEAR_RANDOM); }
static Trigger* near_npc_status(PlayerbotAI* botAI) { return new NewRpgStatusTrigger(botAI, NewRpgStatus::NEAR_NPC); }
};
#endif

View File

@@ -175,7 +175,7 @@ bool AttackersValue::IsPossibleTarget(Unit* attacker, Player* bot, float range)
// !((attacker->IsPolymorphed() || botAI->HasAura("sap", attacker) || /*attacker->IsCharmed() ||*/
// attacker->isFeared()) && !rti) &&
/*!sServerFacade->IsInRoots(attacker) &&*/
!attacker->IsFriendlyTo(bot) && !attacker->HasSpiritOfRedemptionAura() &&
!attacker->IsFriendlyTo(bot) && !attacker->HasAuraType(SPELL_AURA_SPIRIT_OF_REDEMPTION) &&
// !(attacker->GetGUID().IsPet() && enemy) &&
!(attacker->GetCreatureType() == CREATURE_TYPE_CRITTER && !attacker->IsInCombat()) &&
!attacker->HasUnitFlag(UNIT_FLAG_IMMUNE_TO_PC) && !attacker->HasUnitFlag(UNIT_FLAG_NOT_SELECTABLE) &&

View File

@@ -298,9 +298,8 @@ Unit* DpsTargetValue::Calculate()
return rti;
// FindLeastHpTargetStrategy strategy(botAI);
Group* group = bot->GetGroup();
float dps = AI_VALUE(float, "estimated group dps");
if (group && botAI->IsCaster(bot))
if (botAI->IsCaster(bot))
{
CasterFindTargetSmartStrategy strategy(botAI, dps);
return TargetValue::FindTarget(&strategy);

View File

@@ -18,7 +18,7 @@ bool NearestEnemyPlayersValue::AcceptUnit(Unit* unit)
!enemy->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NON_ATTACKABLE_2) &&
((inCannon || !enemy->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE))) &&
/*!enemy->HasStealthAura() && !enemy->HasInvisibilityAura()*/ enemy->CanSeeOrDetect(bot) &&
!(enemy->HasSpiritOfRedemptionAura()))
!(enemy->HasAuraType(SPELL_AURA_SPIRIT_OF_REDEMPTION)))
return true;
return false;

View File

@@ -52,7 +52,7 @@ Unit* GrindTargetValue::FindTargetForGrinding(uint32 assistCount)
float distance = 0;
Unit* result = nullptr;
// std::unordered_map<uint32, bool> needForQuestMap;
std::unordered_map<uint32, bool> needForQuestMap;
for (ObjectGuid const guid : targets)
{
@@ -99,18 +99,18 @@ Unit* GrindTargetValue::FindTargetForGrinding(uint32 assistCount)
if (!bot->InBattleground() && (int)unit->GetLevel() - (int)bot->GetLevel() > 4 && !unit->GetGUID().IsPlayer())
continue;
// if (needForQuestMap.find(unit->GetEntry()) == needForQuestMap.end())
// needForQuestMap[unit->GetEntry()] = needForQuest(unit);
if (needForQuestMap.find(unit->GetEntry()) == needForQuestMap.end())
needForQuestMap[unit->GetEntry()] = needForQuest(unit);
// if (!needForQuestMap[unit->GetEntry()])
// {
// Creature* creature = dynamic_cast<Creature*>(unit);
// if ((urand(0, 100) < 60 || (context->GetValue<TravelTarget*>("travel target")->Get()->isWorking() &&
// context->GetValue<TravelTarget*>("travel target")->Get()->getDestination()->getName() != "GrindTravelDestination")))
// {
// continue;
// }
// }
if (!needForQuestMap[unit->GetEntry()])
{
Creature* creature = dynamic_cast<Creature*>(unit);
if ((urand(0, 100) < 60 || (context->GetValue<TravelTarget*>("travel target")->Get()->isWorking() &&
context->GetValue<TravelTarget*>("travel target")->Get()->getDestination()->getName() != "GrindTravelDestination")))
{
continue;
}
}
if (Creature* creature = unit->ToCreature())
if (CreatureTemplate const* CreatureTemplate = creature->GetCreatureTemplate())
@@ -142,7 +142,8 @@ Unit* GrindTargetValue::FindTargetForGrinding(uint32 assistCount)
else
{
float newdistance = bot->GetDistance(unit);
if (!result || (newdistance < distance))
if (!result || (newdistance < distance &&
urand(0, abs(distance - newdistance)) > sPlayerbotAIConfig->sightDistance * 0.1))
{
distance = newdistance;
result = unit;

View File

@@ -21,7 +21,7 @@ bool InvalidTargetValue::Calculate()
return target->GetMapId() != bot->GetMapId() || target->HasUnitFlag(UNIT_FLAG_NOT_SELECTABLE) ||
target->HasUnitFlag(UNIT_FLAG_NON_ATTACKABLE) || target->HasUnitFlag(UNIT_FLAG_NON_ATTACKABLE_2) ||
!target->IsVisible() || !target->IsAlive() || target->IsPolymorphed() || target->IsCharmed() ||
target->HasFearAura() || target->HasUnitState(UNIT_STATE_ISOLATED) || target->IsFriendlyTo(bot) ||
target->isFeared() || target->HasUnitState(UNIT_STATE_ISOLATED) || target->IsFriendlyTo(bot) ||
!AttackersValue::IsValidTarget(target, bot);
}

View File

@@ -256,42 +256,20 @@ ItemUsage ItemUsageValue::QueryItemUsageForEquip(ItemTemplate const* itemProto)
// Check weapon case separately to keep things a bit cleaner
bool have2HWeapon = false;
bool isValidTGWeapon = false;
if (dstSlot == EQUIPMENT_SLOT_MAINHAND)
{
Item* currentWeapon = bot->GetItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_MAINHAND);
have2HWeapon = currentWeapon && currentWeapon->GetTemplate()->InventoryType == INVTYPE_2HWEAPON;
// Determine if the new weapon is a valid Titan Grip weapon
isValidTGWeapon = (itemProto->SubClass == ITEM_SUBCLASS_WEAPON_AXE2 ||
itemProto->SubClass == ITEM_SUBCLASS_WEAPON_MACE2 ||
itemProto->SubClass == ITEM_SUBCLASS_WEAPON_SWORD2);
// If the bot can Titan Grip, ignore any 2H weapon that isn't a 2H sword, mace, or axe.
if (bot->CanTitanGrip())
{
// If this weapon is 2H but not one of the valid TG weapon types, do not equip it at all.
if (itemProto->InventoryType == INVTYPE_2HWEAPON && !isValidTGWeapon)
{
return ITEM_USAGE_NONE;
}
}
// Now handle the logic for equipping and possible offhand slots
// If the bot can Dual Wield and:
// - The weapon is not 2H and we currently don't have a 2H weapon equipped
// OR
// - The bot can Titan Grip and it is a valid TG weapon
// Then we can consider the offhand slot as well.
if (bot->CanDualWield() &&
((itemProto->InventoryType != INVTYPE_2HWEAPON && !have2HWeapon) ||
(bot->CanTitanGrip() && isValidTGWeapon)))
isValidTGWeapon = itemProto->SubClass == ITEM_SUBCLASS_WEAPON_AXE2 ||
itemProto->SubClass == ITEM_SUBCLASS_WEAPON_MACE2 ||
itemProto->SubClass == ITEM_SUBCLASS_WEAPON_SWORD2;
if (bot->CanDualWield() && ((itemProto->InventoryType != INVTYPE_2HWEAPON && !have2HWeapon) || (bot->CanTitanGrip() && isValidTGWeapon)))
{
possibleSlots = 2;
}
}
for (uint8 i = 0; i < possibleSlots; i++)
{
bool shouldEquipInSlot = shouldEquip;

View File

@@ -14,7 +14,7 @@ class PlayerbotAI;
class PossibleRpgTargetsValue : public NearestUnitsValue
{
public:
PossibleRpgTargetsValue(PlayerbotAI* botAI, float range = 70.0f);
PossibleRpgTargetsValue(PlayerbotAI* botAI, float range = sPlayerbotAIConfig->rpgDistance);
static std::vector<uint32> allowedNpcFlags;

View File

@@ -48,6 +48,9 @@ Unit* RtiTargetValue::Calculate()
if (!guid)
return nullptr;
if (!bot->IsInCombat())
return nullptr;
//////////////////////////////////////////////////////begin: delete below check
// Some units that need to be killed in battle are not on the list of attackers,
// such as the Kor'kron Battle-Mage in Icecrown Citadel.
@@ -59,7 +62,7 @@ Unit* RtiTargetValue::Calculate()
//////////////////////////////////////////////////////end: delete below check
Unit* unit = botAI->GetUnit(guid);
if (!unit || unit->isDead() || !bot->IsWithinLOSInMap(unit) || !AttackersValue::IsValidTarget(unit, bot) ||
if (!unit || unit->isDead() || !bot->IsWithinLOSInMap(unit) || !AttackersValue::IsValidTarget(unit, bot) ||
sServerFacade->IsDistanceGreaterThan(sServerFacade->GetDistance2d(bot, unit),
sPlayerbotAIConfig->sightDistance))
return nullptr;

View File

@@ -48,7 +48,7 @@ private:
{
return new ActionNode("summon succubus",
/*P*/ nullptr,
/*A*/ NextAction::array(0, new NextAction("summon voidwalker"), nullptr),
/*A*/ NextAction::array(0, new NextAction("summon imp"), nullptr),
/*C*/ nullptr);
}
static ActionNode* summon_felhunter([[maybe_unused]] PlayerbotAI* botAI)

View File

@@ -23,5 +23,3 @@ bool CastFearOnCcAction::isPossible() { return botAI->CanCastSpell("fear", GetTa
bool CastFearOnCcAction::isUseful() { return true; }
bool CastLifeTapAction::isUseful() { return AI_VALUE2(uint8, "health", "self target") > sPlayerbotAIConfig->lowHealth; }
Unit* UseSoulstoneAction::GetTarget() { return botAI->GetMaster(); }

View File

@@ -7,7 +7,6 @@
#define _PLAYERBOT_WARLOCKACTIONS_H
#include "GenericSpellActions.h"
#include "UseItemAction.h"
class PlayerbotAI;
class Unit;
@@ -303,12 +302,4 @@ class CastIncinerateAction : public CastSpellAction
public:
CastIncinerateAction(PlayerbotAI* ai) : CastSpellAction(ai, "incinerate") {}
};
class UseSoulstoneAction : public UseSpellItemAction
{
public:
UseSoulstoneAction(PlayerbotAI* ai) : UseSpellItemAction(ai, "soulstone") {}
Unit* GetTarget() override;
};
#endif

View File

@@ -184,7 +184,6 @@ public:
creators["metamorphosis"] = &WarlockAiObjectContextInternal::metamorphosis;
creators["soul fire"] = &WarlockAiObjectContextInternal::soul_fire;
creators["incinerate"] = &WarlockAiObjectContextInternal::incinerate;
creators["soulstone"] = &WarlockAiObjectContextInternal::soulstone;
}
private:
@@ -240,7 +239,6 @@ private:
static Action* metamorphosis(PlayerbotAI* ai) { return new CastMetamorphosisAction(ai); }
static Action* soul_fire(PlayerbotAI* ai) { return new CastSoulFireAction(ai); }
static Action* incinerate(PlayerbotAI* ai) { return new CastIncinerateAction(ai); }
static Action* soulstone(PlayerbotAI* ai) { return new UseSoulstoneAction(ai); }
};
WarlockAiObjectContext::WarlockAiObjectContext(PlayerbotAI* botAI) : AiObjectContext(botAI)

View File

@@ -16,7 +16,7 @@ void GenericWarriorStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
{
CombatStrategy::InitTriggers(triggers);
triggers.push_back(new TriggerNode(
"enemy out of melee", NextAction::array(0, new NextAction("reach melee", ACTION_HIGH + 1), nullptr)));
"enemy out of melee", NextAction::array(0, new NextAction("reach melee", ACTION_NORMAL + 8), nullptr)));
/*triggers.push_back(new TriggerNode("bloodrage", NextAction::array(0, new NextAction("bloodrage", ACTION_HIGH + 1),
nullptr))); triggers.push_back(new TriggerNode("shield bash", NextAction::array(0, new NextAction("shield bash",
ACTION_INTERRUPT + 4), nullptr))); triggers.push_back(new TriggerNode("shield bash on enemy healer",