Compare commits

..

1 Commits

Author SHA1 Message Date
Yunfan Li
6003dbc1b5 Directory reorganization 2025-05-07 00:06:31 +08:00
993 changed files with 4566 additions and 8846 deletions

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
DROP TABLE IF EXISTS `playerbots_account_keys`;
DROP TABLE IF EXISTS `playerbot_account_keys`;
CREATE TABLE `playerbots_account_keys` (
CREATE TABLE `playerbot_account_keys` (
`account_id` INT PRIMARY KEY,
`security_key` VARCHAR(255) NOT NULL,
`created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP

View File

@@ -1,6 +1,6 @@
DROP TABLE IF EXISTS `playerbots_account_links`;
DROP TABLE IF EXISTS `playerbot_account_links`;
CREATE TABLE `playerbots_account_links` (
CREATE TABLE `playerbot_account_links` (
`id` INT AUTO_INCREMENT PRIMARY KEY,
`account_id` INT NOT NULL,
`linked_account_id` INT NOT NULL,

File diff suppressed because it is too large Load Diff

View File

@@ -1,15 +0,0 @@
DROP TABLE IF EXISTS `playerbot_account_keys`;
CREATE TABLE IF NOT EXISTS `playerbots_account_keys` (
`account_id` INT PRIMARY KEY,
`security_key` VARCHAR(255) NOT NULL,
`created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP
) ENGINE=INNODB DEFAULT CHARSET=latin1;
DROP TABLE IF EXISTS `playerbot_account_links`;
CREATE TABLE IF NOT EXISTS `playerbots_account_links` (
`id` INT AUTO_INCREMENT PRIMARY KEY,
`account_id` INT NOT NULL,
`linked_account_id` INT NOT NULL,
`created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
UNIQUE KEY `account_link` (`account_id`, `linked_account_id`)
) ENGINE=INNODB DEFAULT CHARSET=latin1;

View File

@@ -19,7 +19,6 @@
#include "CreatureData.h"
#include "EmoteAction.h"
#include "Engine.h"
#include "EventProcessor.h"
#include "ExternalEventHelper.h"
#include "GameObjectData.h"
#include "GameTime.h"
@@ -157,7 +156,7 @@ PlayerbotAI::PlayerbotAI(Player* bot)
masterIncomingPacketHandlers.AddHandler(CMSG_GAMEOBJ_USE, "use game object");
masterIncomingPacketHandlers.AddHandler(CMSG_AREATRIGGER, "area trigger");
// masterIncomingPacketHandlers.AddHandler(CMSG_GAMEOBJ_USE, "use game object");
// masterIncomingPacketHandlers.AddHandler(CMSG_LOOT_ROLL, "loot roll");
masterIncomingPacketHandlers.AddHandler(CMSG_LOOT_ROLL, "loot roll");
masterIncomingPacketHandlers.AddHandler(CMSG_GOSSIP_HELLO, "gossip hello");
masterIncomingPacketHandlers.AddHandler(CMSG_QUESTGIVER_HELLO, "gossip hello");
masterIncomingPacketHandlers.AddHandler(CMSG_ACTIVATETAXI, "activate taxi");
@@ -797,7 +796,6 @@ bool PlayerbotAI::IsAllowedCommand(std::string const text)
unsecuredCommands.insert("sendmail");
unsecuredCommands.insert("invite");
unsecuredCommands.insert("leave");
unsecuredCommands.insert("lfg");
unsecuredCommands.insert("rpg status");
}
@@ -1425,21 +1423,12 @@ void PlayerbotAI::DoNextAction(bool min)
master = newMaster;
botAI->SetMaster(newMaster);
botAI->ResetStrategies();
if (!bot->InBattleground())
{
botAI->ChangeStrategy("+follow", BOT_STATE_NON_COMBAT);
botAI->ChangeStrategy("+follow", BOT_STATE_NON_COMBAT);
if (botAI->GetMaster() == botAI->GetGroupMaster())
botAI->TellMaster("Hello, I follow you!");
else
botAI->TellMaster(!urand(0, 2) ? "Hello!" : "Hi!");
}
if (botAI->GetMaster() == botAI->GetGroupMaster())
botAI->TellMaster("Hello, I follow you!");
else
{
// we're in a battleground, stay with the pack and focus on objective
botAI->ChangeStrategy("-follow", BOT_STATE_NON_COMBAT);
}
botAI->TellMaster(!urand(0, 2) ? "Hello!" : "Hi!");
}
}
@@ -6281,23 +6270,3 @@ SpellFamilyNames PlayerbotAI::Class2SpellFamilyName(uint8 cls)
}
return SPELLFAMILY_GENERIC;
}
void PlayerbotAI::AddTimedEvent(std::function<void()> callback, uint32 delayMs)
{
class LambdaEvent final : public BasicEvent
{
std::function<void()> _cb;
public:
explicit LambdaEvent(std::function<void()> cb) : _cb(std::move(cb)) {}
bool Execute(uint64 /*execTime*/, uint32 /*diff*/) override
{
_cb();
return true; // remove after execution
}
};
// Every Player already owns an EventMap called m_Events
bot->m_Events.AddEvent(
new LambdaEvent(std::move(callback)),
bot->m_Events.CalculateTime(delayMs));
}

View File

@@ -590,9 +590,6 @@ public:
NewRpgStatistic rpgStatistic;
std::unordered_set<uint32> lowPriorityQuest;
// Schedules a callback to run once after <delayMs> milliseconds.
void AddTimedEvent(std::function<void()> callback, uint32 delayMs);
private:
static void _fillGearScoreData(Player* player, Item* item, std::vector<uint32>* gearScore, uint32& twoHandScore,
bool mixed = false);

View File

@@ -82,8 +82,6 @@ bool PlayerbotAIConfig::Initialize()
sitDelay = sConfigMgr->GetOption<int32>("AiPlayerbot.SitDelay", 30000);
returnDelay = sConfigMgr->GetOption<int32>("AiPlayerbot.ReturnDelay", 7000);
lootDelay = sConfigMgr->GetOption<int32>("AiPlayerbot.LootDelay", 1000);
disabledWithoutRealPlayerLoginDelay = sConfigMgr->GetOption<int32>("AiPlayerbot.DisabledWithoutRealPlayerLoginDelay", 30);
disabledWithoutRealPlayerLogoutDelay = sConfigMgr->GetOption<int32>("AiPlayerbot.DisabledWithoutRealPlayerLogoutDelay", 300);
farDistance = sConfigMgr->GetOption<float>("AiPlayerbot.FarDistance", 20.0f);
sightDistance = sConfigMgr->GetOption<float>("AiPlayerbot.SightDistance", 75.0f);
@@ -131,7 +129,6 @@ bool PlayerbotAIConfig::Initialize()
allowAccountBots = sConfigMgr->GetOption<bool>("AiPlayerbot.AllowAccountBots", true);
allowGuildBots = sConfigMgr->GetOption<bool>("AiPlayerbot.AllowGuildBots", true);
allowTrustedAccountBots = sConfigMgr->GetOption<bool>("AiPlayerbot.AllowTrustedAccountBots", true);
disabledWithoutRealPlayer = sConfigMgr->GetOption<bool>("AiPlayerbot.DisabledWithoutRealPlayer", false);
randomBotGuildNearby = sConfigMgr->GetOption<bool>("AiPlayerbot.RandomBotGuildNearby", false);
randomBotInvitePlayer = sConfigMgr->GetOption<bool>("AiPlayerbot.RandomBotInvitePlayer", false);
inviteChat = sConfigMgr->GetOption<bool>("AiPlayerbot.InviteChat", false);
@@ -156,9 +153,7 @@ bool PlayerbotAIConfig::Initialize()
LoadList<std::vector<uint32>>(
sConfigMgr->GetOption<std::string>("AiPlayerbot.RandomBotQuestIds", "7848,3802,5505,6502,7761"),
randomBotQuestIds);
LoadSet<std::set<uint32>>(sConfigMgr->GetOption<std::string>("AiPlayerbot.DisallowedGameObjects", "176213,17155"),
disallowedGameObjects);
botAutologin = sConfigMgr->GetOption<bool>("AiPlayerbot.BotAutologin", false);
randomBotAutologin = sConfigMgr->GetOption<bool>("AiPlayerbot.RandomBotAutologin", true);
minRandomBots = sConfigMgr->GetOption<int32>("AiPlayerbot.MinRandomBots", 50);
@@ -312,48 +307,11 @@ bool PlayerbotAIConfig::Initialize()
summonAtInnkeepersEnabled = sConfigMgr->GetOption<bool>("AiPlayerbot.SummonAtInnkeepersEnabled", true);
randomBotMinLevel = sConfigMgr->GetOption<int32>("AiPlayerbot.RandomBotMinLevel", 1);
randomBotMaxLevel = sConfigMgr->GetOption<int32>("AiPlayerbot.RandomBotMaxLevel", 80);
if (randomBotMaxLevel > sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL))
randomBotMaxLevel = sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL);
randomBotLoginAtStartup = sConfigMgr->GetOption<bool>("AiPlayerbot.RandomBotLoginAtStartup", true);
randomBotTeleLowerLevel = sConfigMgr->GetOption<int32>("AiPlayerbot.RandomBotTeleLowerLevel", 1);
randomBotTeleHigherLevel = sConfigMgr->GetOption<int32>("AiPlayerbot.RandomBotTeleHigherLevel", 3);
randomBotTeleLowerLevel = sConfigMgr->GetOption<int32>("AiPlayerbot.RandomBotTeleLowerLevel", 3);
randomBotTeleHigherLevel = sConfigMgr->GetOption<int32>("AiPlayerbot.RandomBotTeleHigherLevel", 1);
openGoSpell = sConfigMgr->GetOption<int32>("AiPlayerbot.OpenGoSpell", 6477);
// Zones for NewRpgStrategy teleportation brackets
std::vector<uint32> zoneIds = {
// Classic WoW - Low-level zones
1, 12, 14, 85, 141, 215, 3430, 3524,
// Classic WoW - Mid-level zones
17, 38, 40, 130, 148, 3433, 3525,
// Classic WoW - High-level zones
10, 11, 44, 267, 331, 400, 406,
// Classic WoW - Higher-level zones
3, 8, 15, 16, 33, 45, 47, 51, 357, 405, 440,
// Classic WoW - Top-level zones
4, 28, 46, 139, 361, 490, 618, 1377,
// The Burning Crusade - Zones
3483, 3518, 3519, 3520, 3521, 3522, 3523, 4080,
// Wrath of the Lich King - Zones
65, 66, 67, 210, 394, 495, 2817, 3537, 3711, 4197
};
for (uint32 zoneId : zoneIds)
{
std::string setting = "AiPlayerbot.ZoneBracket." + std::to_string(zoneId);
std::string value = sConfigMgr->GetOption<std::string>(setting, "");
if (!value.empty())
{
size_t commaPos = value.find(',');
if (commaPos != std::string::npos)
{
uint32 minLevel = atoi(value.substr(0, commaPos).c_str());
uint32 maxLevel = atoi(value.substr(commaPos + 1).c_str());
zoneBrackets[zoneId] = std::make_pair(minLevel, maxLevel);
}
}
}
randomChangeMultiplier = sConfigMgr->GetOption<float>("AiPlayerbot.RandomChangeMultiplier", 1.0);
randomBotCombatStrategies = sConfigMgr->GetOption<std::string>("AiPlayerbot.RandomBotCombatStrategies", "-threat");
@@ -373,12 +331,6 @@ bool PlayerbotAIConfig::Initialize()
useFlyMountAtMinLevel = sConfigMgr->GetOption<int32>("AiPlayerbot.UseFlyMountAtMinLevel", 60);
useFastFlyMountAtMinLevel = sConfigMgr->GetOption<int32>("AiPlayerbot.UseFastFlyMountAtMinLevel", 70);
// stagger bot flightpath takeoff
delayMin = sConfigMgr->GetOption<uint32>("AiPlayerbot.BotTaxiDelayMinMs", 350u);
delayMax = sConfigMgr->GetOption<uint32>("AiPlayerbot.BotTaxiDelayMaxMs", 5000u);
gapMs = sConfigMgr->GetOption<uint32>("AiPlayerbot.BotTaxiGapMs", 200u);
gapJitterMs = sConfigMgr->GetOption<uint32>("AiPlayerbot.BotTaxiGapJitterMs", 100u);
LOG_INFO("server.loading", "Loading TalentSpecs...");
for (uint32 cls = 1; cls < MAX_CLASSES; ++cls)
@@ -514,7 +466,6 @@ bool PlayerbotAIConfig::Initialize()
equipmentPersistence = sConfigMgr->GetOption<bool>("AiPlayerbot.EquipmentPersistence", false);
equipmentPersistenceLevel = sConfigMgr->GetOption<int32>("AiPlayerbot.EquipmentPersistenceLevel", 80);
groupInvitationPermission = sConfigMgr->GetOption<int32>("AiPlayerbot.GroupInvitationPermission", 1);
keepAltsInGroup = sConfigMgr->GetOption<bool>("AiPlayerbot.KeepAltsInGroup", false);
allowSummonInCombat = sConfigMgr->GetOption<bool>("AiPlayerbot.AllowSummonInCombat", true);
allowSummonWhenMasterIsDead = sConfigMgr->GetOption<bool>("AiPlayerbot.AllowSummonWhenMasterIsDead", true);
allowSummonWhenBotIsDead = sConfigMgr->GetOption<bool>("AiPlayerbot.AllowSummonWhenBotIsDead", true);
@@ -532,7 +483,7 @@ bool PlayerbotAIConfig::Initialize()
autoGearQualityLimit = sConfigMgr->GetOption<int32>("AiPlayerbot.AutoGearQualityLimit", 3);
autoGearScoreLimit = sConfigMgr->GetOption<int32>("AiPlayerbot.AutoGearScoreLimit", 0);
randomBotXPRate = sConfigMgr->GetOption<float>("AiPlayerbot.RandomBotXPRate", 1.0);
playerbotsXPrate = sConfigMgr->GetOption<float>("AiPlayerbot.PlayerbotsXPRate", 1.0);
randomBotAllianceRatio = sConfigMgr->GetOption<int32>("AiPlayerbot.RandomBotAllianceRatio", 50);
randomBotHordeRatio = sConfigMgr->GetOption<int32>("AiPlayerbot.RandomBotHordeRatio", 50);
disableDeathKnightLogin = sConfigMgr->GetOption<bool>("AiPlayerbot.DisableDeathKnightLogin", 0);

View File

@@ -55,7 +55,6 @@ public:
bool IsInPvpProhibitedArea(uint32 id);
bool enabled;
bool disabledWithoutRealPlayer;
bool allowAccountBots, allowGuildBots, allowTrustedAccountBots;
bool randomBotGuildNearby, randomBotInvitePlayer, inviteChat;
uint32 globalCoolDown, reactDelay, maxWaitForMove, disableMoveSplinePath, maxMovementSearchTime, expireActionTime,
@@ -72,7 +71,6 @@ public:
float maxAoeAvoidRadius;
std::set<uint32> aoeAvoidSpellWhitelist;
bool tellWhenAvoidAoe;
std::set<uint32> disallowedGameObjects;
uint32 openGoSpell;
bool randomBotAutologin;
@@ -101,7 +99,6 @@ public:
uint32 minRandomBotPvpTime, maxRandomBotPvpTime;
uint32 randomBotsPerInterval;
uint32 minRandomBotsPriceChangeInterval, maxRandomBotsPriceChangeInterval;
uint32 disabledWithoutRealPlayerLoginDelay, disabledWithoutRealPlayerLogoutDelay;
bool randomBotJoinLfg;
// chat
@@ -198,7 +195,6 @@ public:
bool randomBotLoginAtStartup;
uint32 randomBotTeleLowerLevel, randomBotTeleHigherLevel;
std::map<uint32, std::pair<uint32, uint32>> zoneBrackets;
bool logInGroupOnly, logValuesPerTick;
bool fleeingEnabled;
bool summonAtInnkeepersEnabled;
@@ -279,7 +275,7 @@ public:
bool randomBotShowCloak;
bool randomBotFixedLevel;
bool disableRandomLevels;
float randomBotXPRate;
float playerbotsXPrate;
uint32 randomBotAllianceRatio;
uint32 randomBotHordeRatio;
bool disableDeathKnightLogin;
@@ -334,8 +330,6 @@ public:
bool equipmentPersistence;
int32 equipmentPersistenceLevel;
int32 groupInvitationPermission;
bool keepAltsInGroup = false;
bool KeepAltsInGroup() const { return keepAltsInGroup; }
bool allowSummonInCombat;
bool allowSummonWhenMasterIsDead;
bool allowSummonWhenBotIsDead;
@@ -354,12 +348,6 @@ public:
uint32 useFlyMountAtMinLevel;
uint32 useFastFlyMountAtMinLevel;
// stagger flightpath takeoff
uint32 delayMin;
uint32 delayMax;
uint32 gapMs;
uint32 gapJitterMs;
std::string const GetTimestampStr();
bool hasLog(std::string const fileName)
{

View File

@@ -157,7 +157,7 @@ void PlayerbotHolder::AddPlayerBot(ObjectGuid playerGuid, uint32 masterAccountId
bool PlayerbotHolder::IsAccountLinked(uint32 accountId, uint32 linkedAccountId)
{
QueryResult result = PlayerbotsDatabase.Query(
"SELECT 1 FROM playerbots_account_links WHERE account_id = {} AND linked_account_id = {}", accountId, linkedAccountId);
"SELECT 1 FROM playerbot_account_links WHERE account_id = {} AND linked_account_id = {}", accountId, linkedAccountId);
return result != nullptr;
}
@@ -473,11 +473,11 @@ void PlayerbotHolder::OnBotLogin(Player* const bot)
}
Player* master = botAI->GetMaster();
if (master)
if (!master)
{
ObjectGuid masterGuid = master->GetGUID();
if (master->GetGroup() && !master->GetGroup()->IsLeader(masterGuid))
master->GetGroup()->ChangeLeader(masterGuid);
// Log a warning to indicate that the master is null
LOG_DEBUG("mod-playerbots", "Master is null for bot with GUID: {}", bot->GetGUID().GetRawValue());
return;
}
Group* group = bot->GetGroup();
@@ -496,10 +496,7 @@ void PlayerbotHolder::OnBotLogin(Player* const bot)
break;
}
}
// Don't disband alt groups when master goes away
// Controlled by config
if (sPlayerbotAIConfig->KeepAltsInGroup())
else
{
uint32 account = sCharacterCache->GetCharacterAccountIdByGuid(member);
if (!sPlayerbotAIConfig->IsInRandomAccountList(account))
@@ -1737,7 +1734,7 @@ void PlayerbotMgr::HandleSetSecurityKeyCommand(Player* player, const std::string
// Store the hashed key in the database
PlayerbotsDatabase.Execute(
"REPLACE INTO playerbots_account_keys (account_id, security_key) VALUES ({}, '{}')",
"REPLACE INTO playerbot_account_keys (account_id, security_key) VALUES ({}, '{}')",
accountId, hashedKey.str());
ChatHandler(player->GetSession()).PSendSysMessage("Security key set successfully.");
@@ -1755,7 +1752,7 @@ void PlayerbotMgr::HandleLinkAccountCommand(Player* player, const std::string& a
Field* fields = result->Fetch();
uint32 linkedAccountId = fields[0].Get<uint32>();
result = PlayerbotsDatabase.Query("SELECT security_key FROM playerbots_account_keys WHERE account_id = {}", linkedAccountId);
result = PlayerbotsDatabase.Query("SELECT security_key FROM playerbot_account_keys WHERE account_id = {}", linkedAccountId);
if (!result)
{
ChatHandler(player->GetSession()).PSendSysMessage("Invalid security key.");
@@ -1781,10 +1778,10 @@ void PlayerbotMgr::HandleLinkAccountCommand(Player* player, const std::string& a
uint32 accountId = player->GetSession()->GetAccountId();
PlayerbotsDatabase.Execute(
"INSERT IGNORE INTO playerbots_account_links (account_id, linked_account_id) VALUES ({}, {})",
"INSERT IGNORE INTO playerbot_account_links (account_id, linked_account_id) VALUES ({}, {})",
accountId, linkedAccountId);
PlayerbotsDatabase.Execute(
"INSERT IGNORE INTO playerbots_account_links (account_id, linked_account_id) VALUES ({}, {})",
"INSERT IGNORE INTO playerbot_account_links (account_id, linked_account_id) VALUES ({}, {})",
linkedAccountId, accountId);
ChatHandler(player->GetSession()).PSendSysMessage("Account linked successfully.");
@@ -1793,7 +1790,7 @@ void PlayerbotMgr::HandleLinkAccountCommand(Player* player, const std::string& a
void PlayerbotMgr::HandleViewLinkedAccountsCommand(Player* player)
{
uint32 accountId = player->GetSession()->GetAccountId();
QueryResult result = PlayerbotsDatabase.Query("SELECT linked_account_id FROM playerbots_account_links WHERE account_id = {}", accountId);
QueryResult result = PlayerbotsDatabase.Query("SELECT linked_account_id FROM playerbot_account_links WHERE account_id = {}", accountId);
if (!result)
{
@@ -1834,7 +1831,7 @@ void PlayerbotMgr::HandleUnlinkAccountCommand(Player* player, const std::string&
uint32 linkedAccountId = fields[0].Get<uint32>();
uint32 accountId = player->GetSession()->GetAccountId();
PlayerbotsDatabase.Execute("DELETE FROM playerbots_account_links WHERE (account_id = {} AND linked_account_id = {}) OR (account_id = {} AND linked_account_id = {})",
PlayerbotsDatabase.Execute("DELETE FROM playerbot_account_links WHERE (account_id = {} AND linked_account_id = {}) OR (account_id = {} AND linked_account_id = {})",
accountId, linkedAccountId, linkedAccountId, accountId);
ChatHandler(player->GetSession()).PSendSysMessage("Account unlinked successfully.");

View File

@@ -219,12 +219,9 @@ public:
if (!player->GetSession()->IsBot())
return;
if (!sRandomPlayerbotMgr->IsRandomBot(player))
return;
if (sPlayerbotAIConfig->randomBotXPRate != 1.0)
if (sPlayerbotAIConfig->playerbotsXPrate != 1.0)
{
amount = static_cast<uint32>(std::round(static_cast<float>(amount) * sPlayerbotAIConfig->randomBotXPRate));
amount = static_cast<uint32>(std::round(static_cast<float>(amount) * sPlayerbotAIConfig->playerbotsXPrate));
}
}
};

View File

@@ -49,7 +49,6 @@
#include "UpdateTime.h"
#include "World.h"
#include "RandomPlayerbotFactory.h"
#include <WorldSessionMgr.h>
struct GuidClassRaceInfo {
ObjectGuid::LowType guid;
@@ -356,43 +355,7 @@ void RandomPlayerbotMgr::UpdateAIInternal(uint32 elapsed, bool /*minimal*/)
PERF_MON_TOTAL,
onlineBotCount < maxAllowedBotCount ? "RandomPlayerbotMgr::Login" : "RandomPlayerbotMgr::UpdateAIInternal");
bool realPlayerIsLogged = false;
if (sPlayerbotAIConfig->disabledWithoutRealPlayer)
{
if (sWorldSessionMgr->GetActiveAndQueuedSessionCount() > 0)
{
RealPlayerLastTimeSeen = time(nullptr);
realPlayerIsLogged = true;
if (DelayLoginBotsTimer == 0)
{
DelayLoginBotsTimer = time(nullptr) + sPlayerbotAIConfig->disabledWithoutRealPlayerLoginDelay;
}
}
else
{
if (DelayLoginBotsTimer)
{
DelayLoginBotsTimer = 0;
}
if (RealPlayerLastTimeSeen != 0 && onlineBotCount > 0 &&
time(nullptr) > RealPlayerLastTimeSeen + sPlayerbotAIConfig->disabledWithoutRealPlayerLogoutDelay)
{
LogoutAllBots();
LOG_INFO("playerbots",
"Logout all bots due no real player session.");
}
}
if (availableBotCount < maxAllowedBotCount &&
(sPlayerbotAIConfig->disabledWithoutRealPlayer == false ||
(realPlayerIsLogged && DelayLoginBotsTimer != 0 && time(nullptr) >= DelayLoginBotsTimer)))
{
AddRandomBots();
}
}
else if (availableBotCount < maxAllowedBotCount)
if (availableBotCount < maxAllowedBotCount)
{
AddRandomBots();
}
@@ -428,11 +391,7 @@ void RandomPlayerbotMgr::UpdateAIInternal(uint32 elapsed, bool /*minimal*/)
}
}
uint32 updateBots = sPlayerbotAIConfig->randomBotsPerInterval * onlineBotFocus / 100;
uint32 maxNewBots = onlineBotCount < maxAllowedBotCount &&
(sPlayerbotAIConfig->disabledWithoutRealPlayer == false ||
(realPlayerIsLogged && DelayLoginBotsTimer != 0 && time(nullptr) >= DelayLoginBotsTimer))
? maxAllowedBotCount - onlineBotCount
: 0;
uint32 maxNewBots = onlineBotCount < maxAllowedBotCount ? maxAllowedBotCount - onlineBotCount : 0;
uint32 loginBots = std::min(sPlayerbotAIConfig->randomBotsPerInterval - updateBots, maxNewBots);
if (!availableBots.empty())
@@ -473,8 +432,6 @@ void RandomPlayerbotMgr::UpdateAIInternal(uint32 elapsed, bool /*minimal*/)
if (!loginBots)
break;
}
DelayLoginBotsTimer = 0;
}
}
@@ -1606,12 +1563,7 @@ void RandomPlayerbotMgr::PrepareZone2LevelBracket()
zone2LevelBracket[2817] = {77, 80}; // Crystalsong Forest
zone2LevelBracket[3537] = {68, 75}; // Borean Tundra
zone2LevelBracket[3711] = {75, 80}; // Sholazar Basin
zone2LevelBracket[4197] = {79, 80}; // Wintergrasp
// Override with values from config
for (auto const& [zoneId, bracketPair] : sPlayerbotAIConfig->zoneBrackets) {
zone2LevelBracket[zoneId] = {bracketPair.first, bracketPair.second};
}
zone2LevelBracket[4197] = {79, 80}; // Wintergrasp
}
void RandomPlayerbotMgr::PrepareTeleportCache()
@@ -1675,8 +1627,8 @@ void RandomPlayerbotMgr::PrepareTeleportCache()
uint32 level = (min_level + max_level + 1) / 2;
WorldLocation loc(mapId, x, y, z, 0);
collected_locs++;
for (int32 l = (int32)level - (int32)sPlayerbotAIConfig->randomBotTeleLowerLevel;
l <= (int32)level + (int32)sPlayerbotAIConfig->randomBotTeleHigherLevel; l++)
for (int32 l = (int32)level - (int32)sPlayerbotAIConfig->randomBotTeleHigherLevel;
l <= (int32)level + (int32)sPlayerbotAIConfig->randomBotTeleLowerLevel; l++)
{
if (l < 1 || l > maxLevel)
{

View File

@@ -206,8 +206,6 @@ private:
time_t BgCheckTimer;
time_t LfgCheckTimer;
time_t PlayersCheckTimer;
time_t RealPlayerLastTimeSeen = 0;
time_t DelayLoginBotsTimer;
time_t printStatsTimer;
uint32 AddRandomBots();
bool ProcessBot(uint32 bot);

View File

@@ -531,7 +531,7 @@ void AiFactory::AddDefaultNonCombatStrategies(Player* player, PlayerbotAI* const
nonCombatEngine->addStrategiesNoInit("bthreat", "tank assist", "barmor", nullptr);
if (player->GetLevel() >= 20)
{
nonCombatEngine->addStrategy("bhealth", false);
nonCombatEngine->addStrategy("bstats", false);
}
else
{

View File

@@ -813,10 +813,6 @@ void PlayerbotFactory::InitPetTalents()
void PlayerbotFactory::InitPet()
{
Pet* pet = bot->GetPet();
if (!pet && bot->GetPetStable() && bot->GetPetStable()->CurrentPet)
return;
if (!pet)
{
if (bot->getClass() != CLASS_HUNTER || bot->GetLevel() < 10)
@@ -865,8 +861,7 @@ void PlayerbotFactory::InitPet()
uint32 pet_number = sObjectMgr->GeneratePetNumber();
if (bot->GetPetStable() && bot->GetPetStable()->CurrentPet)
{
auto petGuid = bot->GetPetStable()->CurrentPet.value(); // To correct the build warnin in VS
// bot->GetPetStable()->CurrentPet.value();
bot->GetPetStable()->CurrentPet.value();
// bot->GetPetStable()->CurrentPet.reset();
bot->RemovePet(nullptr, PET_SAVE_AS_CURRENT);
bot->RemovePet(nullptr, PET_SAVE_NOT_IN_SLOT);
@@ -1743,7 +1738,7 @@ void PlayerbotFactory::InitEquipment(bool incremental, bool second_chance)
if (incremental && oldItem)
{
float old_score = calculator.CalculateItem(oldItem->GetEntry(), oldItem->GetItemRandomPropertyId());
float old_score = calculator.CalculateItem(oldItem->GetEntry());
if (bestScoreForSlot < 1.2f * old_score)
continue;
}
@@ -2205,15 +2200,33 @@ void PlayerbotFactory::InitSkills()
//uint32 maxValue = level * 5; //not used, line marked for removal.
bot->UpdateSkillsForLevel();
bot->SetSkill(SKILL_RIDING, 0, 0, 0);
auto SafeLearn = [this](uint32 spellId)
{
if (!bot->HasSpell(spellId))
bot->learnSpell(spellId, false, true); // Avoid duplicate attempts in DB
};
// Define Riding skill according to level
if (bot->GetLevel() >= 70)
bot->SetSkill(SKILL_RIDING, 300, 300, 300);
else if (bot->GetLevel() >= 60)
bot->SetSkill(SKILL_RIDING, 225, 225, 225);
else if (bot->GetLevel() >= 40)
bot->SetSkill(SKILL_RIDING, 150, 150, 150);
else if (bot->GetLevel() >= 20)
bot->SetSkill(SKILL_RIDING, 75, 75, 75);
else
bot->SetSkill(SKILL_RIDING, 0, 0, 0);
// Safe learning of mount spells
if (bot->GetLevel() >= sPlayerbotAIConfig->useGroundMountAtMinLevel)
bot->learnSpell(33388);
SafeLearn(33388); // Apprentice
if (bot->GetLevel() >= sPlayerbotAIConfig->useFastGroundMountAtMinLevel)
bot->learnSpell(33391);
SafeLearn(33391); // Journeyman
if (bot->GetLevel() >= sPlayerbotAIConfig->useFlyMountAtMinLevel)
bot->learnSpell(34090);
SafeLearn(34090); // Expert
if (bot->GetLevel() >= sPlayerbotAIConfig->useFastFlyMountAtMinLevel)
bot->learnSpell(34091);
SafeLearn(34091); // Artisan
uint32 skillLevel = bot->GetLevel() < 40 ? 0 : 1;
uint32 dualWieldLevel = bot->GetLevel() < 20 ? 0 : 1;

View File

@@ -3,7 +3,6 @@
#include <cstdint>
#include "DBCStores.h"
#include "ItemEnchantmentMgr.h"
#include "ItemTemplate.h"
#include "ObjectMgr.h"
#include "PlayerbotAI.h"
@@ -206,7 +205,7 @@ void StatsCollector::CollectSpellStats(uint32 spellId, float multiplier, int32 s
}
}
void StatsCollector::CollectEnchantStats(SpellItemEnchantmentEntry const* enchant, uint32 default_enchant_amount)
void StatsCollector::CollectEnchantStats(SpellItemEnchantmentEntry const* enchant)
{
for (int s = 0; s < MAX_SPELL_ITEM_ENCHANTMENT_EFFECTS; ++s)
{
@@ -232,10 +231,6 @@ void StatsCollector::CollectEnchantStats(SpellItemEnchantmentEntry const* enchan
}
case ITEM_ENCHANTMENT_TYPE_STAT:
{
// for item random suffix
if (!enchant_amount)
enchant_amount = default_enchant_amount;
if (!enchant_amount)
{
break;
@@ -255,7 +250,7 @@ bool StatsCollector::SpecialSpellFilter(uint32 spellId)
// trinket
switch (spellId)
{
case 60764: // Totem of Splintering
case 60764: // Totem of Splintering
if (type_ & (CollectorType::SPELL))
return true;
break;
@@ -749,7 +744,7 @@ void StatsCollector::HandleApplyAura(const SpellEffectInfo& effectInfo, float mu
int32 StatsCollector::AverageValue(const SpellEffectInfo& effectInfo)
{
// float basePointsPerLevel = effectInfo.RealPointsPerLevel; //not used, line marked for removal.
//float basePointsPerLevel = effectInfo.RealPointsPerLevel; //not used, line marked for removal.
int32 basePoints = effectInfo.BasePoints;
int32 randomPoints = int32(effectInfo.DieSides);
@@ -772,7 +767,7 @@ bool StatsCollector::CheckSpellValidation(uint32 spellFamilyName, flag96 spelFal
{
if (PlayerbotAI::Class2SpellFamilyName(cls_) != spellFamilyName)
return false;
bool isHealingSpell = PlayerbotAI::IsHealingSpell(spellFamilyName, spelFalimyFlags);
// strict to healer
if (strict && (type_ & CollectorType::SPELL_HEAL))

View File

@@ -66,7 +66,7 @@ public:
void Reset();
void CollectItemStats(ItemTemplate const* proto);
void CollectSpellStats(uint32 spellId, float multiplier = 1.0f, int32 spellCooldown = -1);
void CollectEnchantStats(SpellItemEnchantmentEntry const* enchant, uint32 default_enchant_amount = 0);
void CollectEnchantStats(SpellItemEnchantmentEntry const* enchant);
bool CanBeTriggeredByType(SpellInfo const* spellInfo, uint32 procFlags, bool strict = true);
bool CheckSpellValidation(uint32 spellFamilyName, flag96 spelFalimyFlags, bool strict = true);

View File

@@ -9,7 +9,6 @@
#include "AiFactory.h"
#include "DBCStores.h"
#include "ItemEnchantmentMgr.h"
#include "ItemTemplate.h"
#include "ObjectMgr.h"
#include "PlayerbotAI.h"
@@ -60,7 +59,7 @@ void StatsWeightCalculator::Reset()
}
}
float StatsWeightCalculator::CalculateItem(uint32 itemId, int32 randomPropertyIds)
float StatsWeightCalculator::CalculateItem(uint32 itemId)
{
ItemTemplate const* proto = &sObjectMgr->GetItemTemplateStore()->at(itemId);
@@ -70,9 +69,6 @@ float StatsWeightCalculator::CalculateItem(uint32 itemId, int32 randomPropertyId
Reset();
collector_->CollectItemStats(proto);
if (randomPropertyIds != 0)
CalculateRandomProperty(randomPropertyIds, itemId);
if (enable_overflow_penalty_)
ApplyOverflowPenalty(player_);
@@ -120,53 +116,6 @@ float StatsWeightCalculator::CalculateEnchant(uint32 enchantId)
return weight_;
}
void StatsWeightCalculator::CalculateRandomProperty(int32 randomPropertyId, uint32 itemId)
{
if (randomPropertyId > 0)
{
ItemRandomPropertiesEntry const* item_rand = sItemRandomPropertiesStore.LookupEntry(randomPropertyId);
if (!item_rand)
{
return;
}
for (uint32 i = PROP_ENCHANTMENT_SLOT_0; i < MAX_ENCHANTMENT_SLOT; ++i)
{
uint32 enchantId = item_rand->Enchantment[i - PROP_ENCHANTMENT_SLOT_0];
SpellItemEnchantmentEntry const* enchant = sSpellItemEnchantmentStore.LookupEntry(enchantId);
if (enchant)
collector_->CollectEnchantStats(enchant);
}
}
else
{
ItemRandomSuffixEntry const* item_rand = sItemRandomSuffixStore.LookupEntry(-randomPropertyId);
if (!item_rand)
{
return;
}
for (uint32 i = PROP_ENCHANTMENT_SLOT_0; i < MAX_ENCHANTMENT_SLOT; ++i)
{
uint32 enchantId = item_rand->Enchantment[i - PROP_ENCHANTMENT_SLOT_0];
SpellItemEnchantmentEntry const* enchant = sSpellItemEnchantmentStore.LookupEntry(enchantId);
uint32 enchant_amount = 0;
for (int k = 0; k < MAX_ITEM_ENCHANTMENT_EFFECTS; ++k)
{
if (item_rand->Enchantment[k] == enchantId)
{
enchant_amount = uint32((item_rand->AllocationPct[k] * GenerateEnchSuffixFactor(itemId)) / 10000);
break;
}
}
if (enchant)
collector_->CollectEnchantStats(enchant, enchant_amount);
}
}
}
void StatsWeightCalculator::GenerateWeights(Player* player)
{
GenerateBasicWeights(player);
@@ -344,8 +293,8 @@ void StatsWeightCalculator::GenerateBasicWeights(Player* player)
stats_weights_[STATS_TYPE_CRIT] += 0.8f;
stats_weights_[STATS_TYPE_HASTE] += 1.0f;
}
else if ((cls == CLASS_PALADIN && tab == PALADIN_TAB_HOLY) || // holy
(cls == CLASS_SHAMAN && tab == SHAMAN_TAB_RESTORATION)) // heal
else if ((cls == CLASS_PALADIN && tab == PALADIN_TAB_HOLY) || // holy
(cls == CLASS_SHAMAN && tab == SHAMAN_TAB_RESTORATION)) // heal
{
stats_weights_[STATS_TYPE_INTELLECT] += 0.9f;
stats_weights_[STATS_TYPE_SPIRIT] += 0.15f;
@@ -354,7 +303,7 @@ void StatsWeightCalculator::GenerateBasicWeights(Player* player)
stats_weights_[STATS_TYPE_CRIT] += 0.6f;
stats_weights_[STATS_TYPE_HASTE] += 0.8f;
}
else if ((cls == CLASS_PRIEST && tab != PRIEST_TAB_SHADOW) || // discipline / holy
else if ((cls == CLASS_PRIEST && tab != PRIEST_TAB_SHADOW) || // discipline / holy
(cls == CLASS_DRUID && tab == DRUID_TAB_RESTORATION))
{
stats_weights_[STATS_TYPE_INTELLECT] += 0.8f;
@@ -515,9 +464,9 @@ void StatsWeightCalculator::CalculateItemTypePenalty(ItemTemplate const* proto)
// {
// weight_ *= 1.0;
// }
// double hand
if (proto->Class == ITEM_CLASS_WEAPON)
{
// double hand
bool isDoubleHand = proto->Class == ITEM_CLASS_WEAPON &&
!(ITEM_SUBCLASS_MASK_SINGLE_HAND & (1 << proto->SubClass)) &&
!(ITEM_SUBCLASS_MASK_WEAPON_RANGED & (1 << proto->SubClass));
@@ -525,41 +474,29 @@ void StatsWeightCalculator::CalculateItemTypePenalty(ItemTemplate const* proto)
if (isDoubleHand)
{
weight_ *= 0.5;
// spec without double hand
// enhancement, rogue, ice dk, unholy dk, shield tank, fury warrior without titan's grip but with duel wield
if (((cls == CLASS_SHAMAN && tab == SHAMAN_TAB_ENHANCEMENT && player_->CanDualWield()) ||
(cls == CLASS_ROGUE) || (cls == CLASS_DEATH_KNIGHT && tab == DEATHKNIGHT_TAB_FROST) ||
(cls == CLASS_WARRIOR && tab == WARRIOR_TAB_FURY && !player_->CanTitanGrip() && player_->CanDualWield()) ||
(cls == CLASS_WARRIOR && tab == WARRIOR_TAB_PROTECTION) ||
(cls == CLASS_PALADIN && tab == PALADIN_TAB_PROTECTION)))
{
weight_ *= 0.1;
}
}
// spec without double hand
// enhancement, rogue, ice dk, unholy dk, shield tank, fury warrior without titan's grip but with duel wield
if (isDoubleHand &&
((cls == CLASS_SHAMAN && tab == SHAMAN_TAB_ENHANCEMENT && player_->CanDualWield()) ||
(cls == CLASS_ROGUE) || (cls == CLASS_DEATH_KNIGHT && tab == DEATHKNIGHT_TAB_FROST) ||
(cls == CLASS_WARRIOR && tab == WARRIOR_TAB_FURY && !player_->CanTitanGrip() && player_->CanDualWield()) ||
(cls == CLASS_WARRIOR && tab == WARRIOR_TAB_PROTECTION) ||
(cls == CLASS_PALADIN && tab == PALADIN_TAB_PROTECTION)))
{
weight_ *= 0.1;
}
// spec with double hand
// fury without duel wield, arms, bear, retribution, blood dk
if (!isDoubleHand)
if (!isDoubleHand &&
((cls == CLASS_HUNTER && !player_->CanDualWield()) ||
(cls == CLASS_WARRIOR && tab == WARRIOR_TAB_FURY && !player_->CanDualWield()) ||
(cls == CLASS_WARRIOR && tab == WARRIOR_TAB_ARMS) || (cls == CLASS_DRUID && tab == DRUID_TAB_FERAL) ||
(cls == CLASS_PALADIN && tab == PALADIN_TAB_RETRIBUTION) ||
(cls == CLASS_DEATH_KNIGHT && tab == DEATHKNIGHT_TAB_BLOOD) ||
(cls == CLASS_SHAMAN && tab == SHAMAN_TAB_ENHANCEMENT && !player_->CanDualWield())))
{
if ((cls == CLASS_HUNTER && !player_->CanDualWield()) ||
(cls == CLASS_WARRIOR && tab == WARRIOR_TAB_FURY && !player_->CanDualWield()) ||
(cls == CLASS_WARRIOR && tab == WARRIOR_TAB_ARMS) || (cls == CLASS_DRUID && tab == DRUID_TAB_FERAL) ||
(cls == CLASS_PALADIN && tab == PALADIN_TAB_RETRIBUTION) ||
(cls == CLASS_DEATH_KNIGHT && tab == DEATHKNIGHT_TAB_BLOOD) ||
(cls == CLASS_SHAMAN && tab == SHAMAN_TAB_ENHANCEMENT && !player_->CanDualWield()))
{
weight_ *= 0.1;
}
// caster's main hand (cannot duel weapon but can equip two-hands stuff)
if (cls == CLASS_MAGE ||
cls == CLASS_PRIEST ||
cls == CLASS_WARLOCK ||
cls == CLASS_DRUID ||
(cls == CLASS_SHAMAN && !player_->CanDualWield()))
{
weight_ *= 0.65;
}
weight_ *= 0.1;
}
// fury with titan's grip
if ((!isDoubleHand || proto->SubClass == ITEM_SUBCLASS_WEAPON_POLEARM ||
@@ -568,18 +505,15 @@ void StatsWeightCalculator::CalculateItemTypePenalty(ItemTemplate const* proto)
{
weight_ *= 0.1;
}
if (cls == CLASS_HUNTER && proto->SubClass == ITEM_SUBCLASS_WEAPON_THROWN)
{
weight_ *= 0.1;
}
if (cls == CLASS_ROGUE && (tab == ROGUE_TAB_ASSASSINATION || tab == ROGUE_TAB_SUBTLETY) &&
proto->SubClass != ITEM_SUBCLASS_WEAPON_DAGGER)
{
weight_ *= 0.5;
}
if (cls == CLASS_ROGUE && player_->HasAura(13964) &&
(proto->SubClass == ITEM_SUBCLASS_WEAPON_SWORD || proto->SubClass == ITEM_SUBCLASS_WEAPON_AXE))
{
@@ -625,13 +559,12 @@ void StatsWeightCalculator::ApplyOverflowPenalty(Player* player)
if (hitOverflowType_ & CollectorType::SPELL)
{
hit_current = player->GetTotalAuraModifier(SPELL_AURA_MOD_SPELL_HIT_CHANCE);
hit_current +=
player->GetTotalAuraModifier(SPELL_AURA_MOD_INCREASES_SPELL_PCT_TO_HIT); // suppression (18176)
hit_current += player->GetTotalAuraModifier(SPELL_AURA_MOD_INCREASES_SPELL_PCT_TO_HIT); // suppression (18176)
hit_current += player->GetRatingBonusValue(CR_HIT_SPELL);
if (cls == CLASS_PRIEST && tab == PRIEST_TAB_SHADOW && player->HasAura(15835)) // Shadow Focus
if (cls == CLASS_PRIEST && tab == PRIEST_TAB_SHADOW && player->HasAura(15835)) // Shadow Focus
hit_current += 3;
if (cls == CLASS_MAGE && tab == MAGE_TAB_ARCANE && player->HasAura(12840)) // Arcane Focus
if (cls == CLASS_MAGE && tab == MAGE_TAB_ARCANE && player->HasAura(12840)) // Arcane Focus
hit_current += 3;
hit_overflow = SPELL_HIT_OVERFLOW;
@@ -724,7 +657,7 @@ void StatsWeightCalculator::ApplyWeightFinetune(Player* player)
{
if (type_ & (CollectorType::MELEE | CollectorType::RANGED))
{
float armor_penetration_current /*, armor_penetration_overflow*/; // not used, line marked for removal.
float armor_penetration_current/*, armor_penetration_overflow*/; //not used, line marked for removal.
armor_penetration_current = player->GetRatingBonusValue(CR_ARMOR_PENETRATION);
if (armor_penetration_current > 50)
stats_weights_[STATS_TYPE_ARMOR_PENETRATION] *= 1.2f;

View File

@@ -28,19 +28,18 @@ class StatsWeightCalculator
public:
StatsWeightCalculator(Player* player);
void Reset();
float CalculateItem(uint32 itemId, int32 randomPropertyId = 0);
float CalculateItem(uint32 itemId);
float CalculateEnchant(uint32 enchantId);
void SetOverflowPenalty(bool apply) { enable_overflow_penalty_ = apply; }
void SetItemSetBonus(bool apply) { enable_item_set_bonus_ = apply; }
void SetQualityBlend(bool apply) { enable_quality_blend_ = apply; }
private:
private:
void GenerateWeights(Player* player);
void GenerateBasicWeights(Player* player);
void GenerateAdditionalWeights(Player* player);
void CalculateRandomProperty(int32 randomPropertyId, uint32 itemId);
void CalculateItemSetMod(Player* player, ItemTemplate const* proto);
void CalculateSocketBonus(Player* player, ItemTemplate const* proto);

View File

@@ -305,49 +305,6 @@ ItemIds ChatHelper::parseItems(std::string const text)
return itemIds;
}
ItemWithRandomProperty ChatHelper::parseItemWithRandomProperty(std::string const text)
{
ItemWithRandomProperty res;
size_t itemStart = text.find("Hitem:");
if (itemStart == std::string::npos)
return res;
itemStart += 6;
if (itemStart >= text.length())
return res;
size_t colonPos = text.find(':', itemStart);
if (colonPos == std::string::npos)
return res;
std::string itemIdStr = text.substr(itemStart, colonPos - itemStart);
res.itemId = atoi(itemIdStr.c_str());
std::vector<std::string> params;
size_t currentPos = colonPos + 1;
while (currentPos < text.length()) {
size_t nextColon = text.find(':', currentPos);
if (nextColon == std::string::npos) {
size_t hTag = text.find("|h", currentPos);
if (hTag != std::string::npos) {
params.push_back(text.substr(currentPos, hTag - currentPos));
}
break;
}
params.push_back(text.substr(currentPos, nextColon - currentPos));
currentPos = nextColon + 1;
}
if (params.size() >= 6) {
res.randomPropertyId = atoi(params[5].c_str());
}
return res;
}
std::string const ChatHelper::FormatQuest(Quest const* quest)
{
if (!quest)
@@ -425,7 +382,7 @@ std::string const ChatHelper::FormatSpell(SpellInfo const* spellInfo)
std::string const ChatHelper::FormatItem(ItemTemplate const* proto, uint32 count, uint32 total)
{
char color[32];
snprintf(color, sizeof(color), "%x", ItemQualityColors[proto->Quality]);
sprintf(color, "%x", ItemQualityColors[proto->Quality]);
std::string itemName;
const ItemLocale* locale = sObjectMgr->GetItemLocale(proto->ItemId);
@@ -452,7 +409,7 @@ std::string const ChatHelper::FormatItem(ItemTemplate const* proto, uint32 count
std::string const ChatHelper::FormatQItem(uint32 itemId)
{
char color[32];
snprintf(color, sizeof(color), "%x", ItemQualityColors[0]);
sprintf(color, "%x", ItemQualityColors[0]);
std::ostringstream out;
out << "|c" << color << "|Hitem:" << itemId << ":0:0:0:0:0:0:0"

View File

@@ -25,11 +25,6 @@ struct ItemTemplate;
typedef std::set<uint32> ItemIds;
typedef std::set<uint32> SpellIds;
struct ItemWithRandomProperty {
uint32 itemId{0};
int32 randomPropertyId{0};
};
class ChatHelper : public PlayerbotAIAware
{
public:
@@ -38,7 +33,6 @@ public:
static std::string const formatMoney(uint32 copper);
static uint32 parseMoney(std::string const text);
static ItemIds parseItems(std::string const text);
static ItemWithRandomProperty parseItemWithRandomProperty(std::string const text);
uint32 parseSpell(std::string const text);
static std::string parseValue(const std::string& type, const std::string& text);

View File

@@ -232,20 +232,6 @@ public:
}
};
class CollectItemsVisitor : public IterateItemsVisitor
{
public:
CollectItemsVisitor() : IterateItemsVisitor() {}
std::vector<Item*> items;
bool Visit(Item* item) override
{
items.push_back(item);
return true;
}
};
class ItemCountByQuality : public IterateItemsVisitor
{
public:

View File

@@ -85,7 +85,7 @@ void LootObject::Refresh(Player* bot, ObjectGuid lootGUID)
bool hasAnyQuestItems = false;
GameObjectQuestItemList const* items = sObjectMgr->GetGameObjectQuestItemList(go->GetEntry());
for (size_t i = 0; i < MAX_GAMEOBJECT_QUEST_ITEMS; i++)
for (int i = 0; i < MAX_GAMEOBJECT_QUEST_ITEMS; i++)
{
if (!items || i >= items->size())
break;
@@ -297,11 +297,6 @@ bool LootObject::IsLootPossible(Player* bot)
return false;
}
// Prevent bot from running to chests that are unlootable (e.g. Gunship Armory before completing the event)
GameObject* go = botAI->GetGameObject(guid);
if (go && go->HasFlag(GAMEOBJECT_FLAGS, GO_FLAG_INTERACT_COND | GO_FLAG_NOT_SELECTABLE))
return false;
if (skillId == SKILL_NONE)
return true;

View File

@@ -29,7 +29,6 @@ public:
LootObject() : skillId(0), reqSkillValue(0), reqItem(0) {}
LootObject(Player* bot, ObjectGuid guid);
LootObject(LootObject const& other);
LootObject& operator=(LootObject const& other) = default;
bool IsEmpty() { return !guid; }
bool IsLootPossible(Player* bot);

View File

@@ -17,28 +17,28 @@
#include "ValueContext.h"
#include "WorldPacketActionContext.h"
#include "WorldPacketTriggerContext.h"
#include "raids/RaidStrategyContext.h"
#include "raids/blackwinglair/RaidBwlActionContext.h"
#include "raids/blackwinglair/RaidBwlTriggerContext.h"
#include "raids/naxxramas/RaidNaxxActionContext.h"
#include "raids/naxxramas/RaidNaxxTriggerContext.h"
#include "raids/icecrown/RaidIccActionContext.h"
#include "raids/icecrown/RaidIccTriggerContext.h"
#include "raids/obsidiansanctum/RaidOsActionContext.h"
#include "raids/obsidiansanctum/RaidOsTriggerContext.h"
#include "raids/eyeofeternity/RaidEoEActionContext.h"
#include "raids/vaultofarchavon/RaidVoATriggerContext.h"
#include "raids/onyxia/RaidOnyxiaActionContext.h"
#include "raids/onyxia/RaidOnyxiaTriggerContext.h"
#include "raids/vaultofarchavon/RaidVoAActionContext.h"
#include "raids/eyeofeternity/RaidEoETriggerContext.h"
#include "raids/moltencore/RaidMcActionContext.h"
#include "raids/moltencore/RaidMcTriggerContext.h"
#include "raids/aq20/RaidAq20ActionContext.h"
#include "raids/aq20/RaidAq20TriggerContext.h"
#include "dungeons/DungeonStrategyContext.h"
#include "dungeons/wotlk/WotlkDungeonActionContext.h"
#include "dungeons/wotlk/WotlkDungeonTriggerContext.h"
#include "RaidStrategyContext.h"
#include "RaidBwlActionContext.h"
#include "RaidBwlTriggerContext.h"
#include "RaidNaxxActionContext.h"
#include "RaidNaxxTriggerContext.h"
#include "RaidIccActionContext.h"
#include "RaidIccTriggerContext.h"
#include "RaidOsActionContext.h"
#include "RaidOsTriggerContext.h"
#include "RaidEoEActionContext.h"
#include "RaidVoATriggerContext.h"
#include "RaidOnyxiaActionContext.h"
#include "RaidOnyxiaTriggerContext.h"
#include "RaidVoAActionContext.h"
#include "RaidEoETriggerContext.h"
#include "RaidMcActionContext.h"
#include "RaidMcTriggerContext.h"
#include "RaidAq20ActionContext.h"
#include "RaidAq20TriggerContext.h"
#include "DungeonStrategyContext.h"
#include "WotlkDungeonActionContext.h"
#include "WotlkDungeonTriggerContext.h"
AiObjectContext::AiObjectContext(PlayerbotAI* botAI) : PlayerbotAIAware(botAI)
{
@@ -75,7 +75,6 @@ AiObjectContext::AiObjectContext(PlayerbotAI* botAI) : PlayerbotAIAware(botAI)
actionContexts.Add(new WotlkDungeonUPActionContext());
actionContexts.Add(new WotlkDungeonCoSActionContext());
actionContexts.Add(new WotlkDungeonFoSActionContext());
actionContexts.Add(new WotlkDungeonPoSActionContext());
actionContexts.Add(new WotlkDungeonToCActionContext());
triggerContexts.Add(new TriggerContext());
@@ -103,8 +102,7 @@ AiObjectContext::AiObjectContext(PlayerbotAI* botAI) : PlayerbotAIAware(botAI)
triggerContexts.Add(new WotlkDungeonOccTriggerContext());
triggerContexts.Add(new WotlkDungeonUPTriggerContext());
triggerContexts.Add(new WotlkDungeonCoSTriggerContext());
triggerContexts.Add(new WotlkDungeonFoSTriggerContext());
triggerContexts.Add(new WotlkDungeonPoSTriggerContext());
triggerContexts.Add(new WotlkDungeonFosTriggerContext());
triggerContexts.Add(new WotlkDungeonToCTriggerContext());
valueContexts.Add(new ValueContext());

View File

@@ -1,450 +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 "InviteToGroupAction.h"
#include "BroadcastHelper.h"
#include "Event.h"
#include "GuildMgr.h"
#include "Log.h"
#include "Playerbots.h"
#include "ServerFacade.h"
bool InviteToGroupAction::Invite(Player* inviter, Player* player)
{
if (!player)
return false;
if (inviter == player)
return false;
if (!GET_PLAYERBOT_AI(player) && !botAI->GetSecurity()->CheckLevelFor(PLAYERBOT_SECURITY_INVITE, true, player))
return false;
if (Group* group = inviter->GetGroup())
{
if (GET_PLAYERBOT_AI(player) && !GET_PLAYERBOT_AI(player)->IsRealPlayer())
if (!group->isRaidGroup() && group->GetMembersCount() > 4)
group->ConvertToRaid();
}
WorldPacket p;
uint32 roles_mask = 0;
p << player->GetName();
p << roles_mask;
inviter->GetSession()->HandleGroupInviteOpcode(p);
return true;
}
bool InviteNearbyToGroupAction::Execute(Event event)
{
GuidVector nearGuids = botAI->GetAiObjectContext()->GetValue<GuidVector>("nearest friendly players")->Get();
for (auto& i : nearGuids)
{
Player* player = ObjectAccessor::FindPlayer(i);
if (!player)
continue;
if (player == bot)
continue;
if (player->GetMapId() != bot->GetMapId())
continue;
if (player->GetGroup())
continue;
if (!sPlayerbotAIConfig->randomBotInvitePlayer && GET_PLAYERBOT_AI(player)->IsRealPlayer())
continue;
Group* group = bot->GetGroup();
if (player->isDND())
continue;
if (player->IsBeingTeleported())
continue;
PlayerbotAI* botAI = GET_PLAYERBOT_AI(bot);
if (botAI)
{
if (botAI->GetGrouperType() == GrouperType::SOLO &&
!botAI->HasRealPlayerMaster()) // Do not invite solo players.
continue;
if (botAI->HasActivePlayerMaster()) // Do not invite alts of active players.
continue;
}
if (abs(int32(player->GetLevel() - bot->GetLevel())) > 2)
continue;
if (sServerFacade->GetDistance2d(bot, player) > sPlayerbotAIConfig->sightDistance)
continue;
// When inviting the 5th member of the group convert to raid for future invites.
if (group && botAI->GetGrouperType() > GrouperType::LEADER_5 && !group->isRaidGroup() &&
bot->GetGroup()->GetMembersCount() > 3)
group->ConvertToRaid();
if (sPlayerbotAIConfig->inviteChat && sRandomPlayerbotMgr->IsRandomBot(bot))
{
std::map<std::string, std::string> placeholders;
placeholders["%player"] = player->GetName();
if (group && group->isRaidGroup())
bot->Say(BOT_TEXT2("join_raid", placeholders),
(bot->GetTeamId() == ALLIANCE ? LANG_COMMON : LANG_ORCISH));
else
bot->Say(BOT_TEXT2("join_group", placeholders),
(bot->GetTeamId() == ALLIANCE ? LANG_COMMON : LANG_ORCISH));
}
return Invite(bot, player);
}
return false;
}
bool InviteNearbyToGroupAction::isUseful()
{
if (!sPlayerbotAIConfig->randomBotGroupNearby)
return false;
if (bot->InBattleground())
return false;
if (bot->InBattlegroundQueue())
return false;
GrouperType grouperType = botAI->GetGrouperType();
if (grouperType == GrouperType::SOLO || grouperType == GrouperType::MEMBER)
return false;
Group* group = bot->GetGroup();
if (group)
{
if (group->isRaidGroup() && group->IsFull())
return false;
if (botAI->GetGroupMaster() != bot)
return false;
uint32 memberCount = group->GetMembersCount();
if (memberCount >= uint8(grouperType))
return false;
}
if (botAI->HasActivePlayerMaster()) // Alts do not invite randomly
return false;
return true;
}
std::vector<Player*> InviteGuildToGroupAction::getGuildMembers()
{
Guild* guild = sGuildMgr->GetGuildById(bot->GetGuildId());
FindGuildMembers worker;
guild->BroadcastWorker(worker);
return worker.GetResult();
}
bool InviteGuildToGroupAction::Execute(Event event)
{
Guild* guild = sGuildMgr->GetGuildById(bot->GetGuildId());
for (auto& member : getGuildMembers())
{
Player* player = member;
if (!player)
continue;
if (player == bot)
continue;
if (player->GetGroup())
continue;
if (player->isDND())
continue;
if (!sPlayerbotAIConfig->randomBotInvitePlayer && GET_PLAYERBOT_AI(player)->IsRealPlayer())
continue;
if (player->IsBeingTeleported())
continue;
if (player->GetMapId() != bot->GetMapId() && player->GetLevel() < 30)
continue;
if (WorldPosition(player).distance(bot) > 1000 && player->GetLevel() < 15)
continue;
PlayerbotAI* playerAi = GET_PLAYERBOT_AI(player);
if (playerAi)
{
if (playerAi->GetGrouperType() == GrouperType::SOLO &&
!playerAi->HasRealPlayerMaster()) // Do not invite solo players.
continue;
if (playerAi->HasActivePlayerMaster()) // Do not invite alts of active players.
continue;
if (player->GetLevel() >
bot->GetLevel() + 5) // Invite higher levels that need money so they can grind money and help out.
{
if (!PAI_VALUE(bool, "should get money"))
continue;
}
}
if (bot->GetLevel() >
player->GetLevel() + 5) // Do not invite members that too low level or risk dragging them to deadly places.
continue;
if (!playerAi && sServerFacade->GetDistance2d(bot, player) > sPlayerbotAIConfig->sightDistance)
continue;
Group* group = bot->GetGroup();
// When inviting the 5th member of the group convert to raid for future invites.
if (group && botAI->GetGrouperType() > GrouperType::LEADER_5 && !group->isRaidGroup() &&
bot->GetGroup()->GetMembersCount() > 3)
{
group->ConvertToRaid();
}
if (sPlayerbotAIConfig->inviteChat &&
(sRandomPlayerbotMgr->IsRandomBot(bot) || !botAI->HasActivePlayerMaster()))
{
BroadcastHelper::BroadcastGuildGroupOrRaidInvite(botAI, bot, player, group);
}
return Invite(bot, player);
}
return false;
}
bool JoinGroupAction::Execute(Event event)
{
if (bot->InBattleground())
return false;
if (bot->InBattlegroundQueue())
return false;
Player* master = event.getOwner();
Group* group = master->GetGroup();
if (group)
{
if (group->IsFull())
return false;
if (bot->GetGroup() == group)
return false;
}
if (bot->GetGroup())
{
if (botAI->HasRealPlayerMaster())
return false;
if (!botAI->DoSpecificAction("leave", event, true))
return false;
}
return Invite(master, bot);
}
bool LfgAction::Execute(Event event)
{
Player* requester = event.getOwner() ? event.getOwner() : GetMaster();
if (bot->InBattleground())
return false;
if (bot->InBattlegroundQueue())
return false;
if (!botAI->IsSafe(requester))
return false;
if (requester->GetLevel() == DEFAULT_MAX_LEVEL && bot->GetLevel() != DEFAULT_MAX_LEVEL)
return false;
if (requester->GetLevel() > bot->GetLevel() + 4 || bot->GetLevel() > requester->GetLevel() + 4)
return false;
std::string param = event.getParam();
if (!param.empty() && param != "40" && param != "25" && param != "20" && param != "10" && param != "5")
return false;
Group* group = requester->GetGroup();
std::unordered_map<Classes, std::unordered_map<BotRoles, uint32>> allowedClassNr;
std::unordered_map<BotRoles, uint32> allowedRoles;
allowedRoles[BOT_ROLE_TANK] = 1;
allowedRoles[BOT_ROLE_HEALER] = 1;
allowedRoles[BOT_ROLE_DPS] = 3;
BotRoles role = botAI->IsTank(requester, false)
? BOT_ROLE_TANK
: (botAI->IsHeal(requester, false) ? BOT_ROLE_HEALER : BOT_ROLE_DPS);
Classes cls = (Classes)requester->getClass();
if (group)
{
if (param.empty() && group->isRaidGroup())
// Default to WotLK Raiding. Max size 25
param = "25";
// Select optimal group layout.
if (param == "40")
{
allowedRoles[BOT_ROLE_TANK] = 4;
allowedRoles[BOT_ROLE_HEALER] = 16;
allowedRoles[BOT_ROLE_DPS] = 20;
/*
allowedClassNr[CLASS_PALADIN][BOT_ROLE_TANK] = 0;
allowedClassNr[CLASS_DRUID][BOT_ROLE_TANK] = 1;
allowedClassNr[CLASS_DRUID][BOT_ROLE_HEALER] = 3;
allowedClassNr[CLASS_PALADIN][BOT_ROLE_HEALER] = 4;
allowedClassNr[CLASS_SHAMAN][BOT_ROLE_HEALER] = 4;
allowedClassNr[CLASS_PRIEST][BOT_ROLE_HEALER] = 11;
allowedClassNr[CLASS_WARRIOR][BOT_ROLE_DPS] = 8;
allowedClassNr[CLASS_PALADIN][BOT_ROLE_DPS] = 4;
allowedClassNr[CLASS_HUNTER][BOT_ROLE_DPS] = 4;
allowedClassNr[CLASS_ROGUE][BOT_ROLE_DPS] = 6;
allowedClassNr[CLASS_PRIEST][BOT_ROLE_DPS] = 1;
allowedClassNr[CLASS_SHAMAN][BOT_ROLE_DPS] = 4;
allowedClassNr[CLASS_MAGE][BOT_ROLE_DPS] = 15;
allowedClassNr[CLASS_WARLOCK][BOT_ROLE_DPS] = 4;
allowedClassNr[CLASS_DRUID][BOT_ROLE_DPS] = 1;
*/
}
else if (param == "25")
{
allowedRoles[BOT_ROLE_TANK] = 3;
allowedRoles[BOT_ROLE_HEALER] = 7;
allowedRoles[BOT_ROLE_DPS] = 15;
}
else if (param == "20")
{
allowedRoles[BOT_ROLE_TANK] = 2;
allowedRoles[BOT_ROLE_HEALER] = 5;
allowedRoles[BOT_ROLE_DPS] = 13;
}
else if (param == "10")
{
allowedRoles[BOT_ROLE_TANK] = 2;
allowedRoles[BOT_ROLE_HEALER] = 3;
allowedRoles[BOT_ROLE_DPS] = 5;
}
if (group->IsFull())
{
if (param.empty() || param == "5" || group->isRaidGroup())
return false; // Group or raid is full so stop trying.
else
group->ConvertToRaid(); // We want a raid but are in a group so convert and continue.
}
Group::MemberSlotList const& groupSlot = group->GetMemberSlots();
for (Group::member_citerator itr = groupSlot.begin(); itr != groupSlot.end(); itr++)
{
// Only add group member targets that are alive and near the player
Player* player = ObjectAccessor::FindPlayer(itr->guid);
if (!botAI->IsSafe(player))
return false;
role = botAI->IsTank(player, false) ? BOT_ROLE_TANK
: (botAI->IsHeal(player, false) ? BOT_ROLE_HEALER : BOT_ROLE_DPS);
cls = (Classes)player->getClass();
if (allowedRoles[role] > 0)
allowedRoles[role]--;
if (allowedClassNr[cls].find(role) != allowedClassNr[cls].end() && allowedClassNr[cls][role] > 0)
allowedClassNr[cls][role]--;
}
}
else
{
if (allowedRoles[role] > 0)
allowedRoles[role]--;
if (allowedClassNr[cls].find(role) != allowedClassNr[cls].end() && allowedClassNr[cls][role] > 0)
allowedClassNr[cls][role]--;
}
role = botAI->IsTank(bot, false) ? BOT_ROLE_TANK : (botAI->IsHeal(bot, false) ? BOT_ROLE_HEALER : BOT_ROLE_DPS);
cls = (Classes)bot->getClass();
if (allowedRoles[role] == 0)
return false;
if (allowedClassNr[cls].find(role) != allowedClassNr[cls].end() && allowedClassNr[cls][role] == 0)
return false;
if (bot->GetGroup())
{
if (botAI->HasRealPlayerMaster())
return false;
if (!botAI->DoSpecificAction("leave", event, true))
return false;
}
bool invite = Invite(requester, bot);
if (invite)
{
Event acceptEvent("accept invitation", requester ? requester->GetGUID() : ObjectGuid::Empty);
if (!botAI->DoSpecificAction("accept invitation", acceptEvent, true))
return false;
std::map<std::string, std::string> placeholders;
placeholders["%role"] = (role & BOT_ROLE_TANK ? "tank" : (role & BOT_ROLE_HEALER ? "healer" : "dps"));
placeholders["%spotsleft"] = std::to_string(allowedRoles[role] - 1);
std::ostringstream out;
if (allowedRoles[role] > 1)
{
out << "Joining as " << placeholders["%role"] << ", " << placeholders["%spotsleft"] << " "
<< placeholders["%role"] << " spots left.";
botAI->TellMasterNoFacing(out.str());
//botAI->DoSpecificAction("autogear");
//botAI->DoSpecificAction("maintenance");
}
else
{
out << "Joining as " << placeholders["%role"] << ".";
botAI->TellMasterNoFacing(out.str());
//botAI->DoSpecificAction("autogear");
//botAI->DoSpecificAction("maintenance");
}
return true;
}
return false;
}

Some files were not shown because too many files have changed in this diff Show More