This commit is contained in:
Bobblybook
2024-10-20 16:18:27 +11:00
14 changed files with 411 additions and 194 deletions

View File

@@ -1263,11 +1263,6 @@ Playerbots.Updates.EnableDatabases = 1
# Command server port, 0 - disabled
AiPlayerbot.CommandServerPort = 8888
# Diff with/without player in server. The server will tune bot activity to reach the desired server tick speed (in ms).# PLAYERBOT SYSTEM SETTINGS #
AiPlayerbot.EnablePrototypePerformanceDiff = 0
AiPlayerbot.DiffWithPlayer = 100
AiPlayerbot.DiffEmpty = 200
#
#
#
@@ -1462,9 +1457,18 @@ AiPlayerbot.RandombotsWalkingRPG.InDoors = 0
# The default is 10. With 10% of all bots going active or inactive each minute.
AiPlayerbot.BotActiveAlone = 100
# Specify 1 for enabled, 0 for disabled.
# The default is 1. Automatically adjusts 'BotActiveAlone' percentage based on server latency.
AiPlayerbot.botActiveAloneAutoScale = 1
# Specify smart scaling is enabled or not.
# The default is 1. When enabled (smart) scales the 'BotActiveAlone' value.
AiPlayerbot.botActiveAloneSmartScale = 1
# Only when botLevel is between WhenMinLevel and WhenMaxLevel.
AiPlayerbot.botActiveAloneSmartScaleWhenMinLevel = 1
AiPlayerbot.botActiveAloneSmartScaleWhenMaxLevel = 80
# The server will tune bot activity to reach the desired server tick speed (in ms)
# bots will only join battleground when there is no lag based on latency diffs below
AiPlayerbot.botActiveAloneSmartScaleDiffWithPlayer = 100
AiPlayerbot.botActiveAloneSmartScaleDiffEmpty = 200
# Premade spell to avoid (undetected spells)
# spellid-radius, ...

View File

@@ -464,7 +464,7 @@ INSERT INTO `ai_playerbot_texts` (`name`, `text`, `say_type`, `reply_type`, `tex
-- %my_level
('suggest_something', 'Wanna party in %zone_name.', 0, 0, '', 'Je veux faire la fête dans %zone_name.', '', '', '', '¡Vamos a perrear a %zone_name!', '', 'Ищу группу в %zone_name.'),
('suggest_something', 'Anyone is looking for %my_role?', 0, 0, '', 'Quelqu\'un cherche un %my_role ?', '', '', '', '¿Alguien está buscando %my_role?', '', 'Кто-нибудь ищет %my_role?'),
('suggest_something', '%my_role is looking for quild.', 0, 0, '', '%my_role recherche une guilde.', '', '', '', '%my_role está buscando hermandad.', '', '%my_role ищу гильдию.'),
('suggest_something', '%my_role is looking for guild.', 0, 0, '', '%my_role recherche une guilde.', '', '', '', '%my_role está buscando hermandad.', '', '%my_role ищу гильдию.'),
('suggest_something', 'Looking for gold.', 0, 0, '', 'A la recherche de l\'or.', '', '', '', 'Buscando oro.', '', 'Дайте голды'),
('suggest_something', '%my_role wants to join a good guild.', 0, 0, '', '%my_role veut rejoindre une bonne guilde.', '', '', '', '%my_role quiere unirse a una buen hermandad.', '', '%my_role хочу в хорошую гильдию.'),
('suggest_something', 'Need a friend.', 0, 0, '', 'Besoin d\'un ami.', '', '', '', 'Necesito un amigo...', '', 'Ищу друга.'),
@@ -1457,19 +1457,3 @@ INSERT INTO `ai_playerbot_texts` (`name`, `text`, `say_type`, `reply_type`, `tex
('dummy_end', 'dummy', 0, 0, '', '', '', '', '', '', '', '');
DROP TABLE IF EXISTS `ai_playerbot_texts_chance`;
CREATE TABLE IF NOT EXISTS `ai_playerbot_texts_chance` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`name` varchar(255) NOT NULL,
`probability` bigint(20) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=UTF8;
/*!40000 ALTER TABLE `ai_playerbot_texts_chance` DISABLE KEYS */;
INSERT INTO `ai_playerbot_texts_chance` (`id`, `name`, `probability`) VALUES
(1, 'taunt', 30),
(2, 'aoe', 75),
(3, 'loot', 20);
/*!40000 ALTER TABLE `ai_playerbot_texts_chance` ENABLE KEYS */;

View File

@@ -0,0 +1,14 @@
DROP TABLE IF EXISTS `ai_playerbot_texts_chance`;
CREATE TABLE IF NOT EXISTS `ai_playerbot_texts_chance` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`name` varchar(255) NOT NULL,
`probability` bigint(20) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=UTF8;
/*!40000 ALTER TABLE `ai_playerbot_texts_chance` DISABLE KEYS */;
INSERT INTO `ai_playerbot_texts_chance` (`id`, `name`, `probability`) VALUES
(1, 'taunt', 30),
(2, 'aoe', 75),
(3, 'loot', 20);
/*!40000 ALTER TABLE `ai_playerbot_texts_chance` ENABLE KEYS */;

View File

@@ -304,9 +304,13 @@ ItemIds ChatHelper::parseItems(std::string const text)
std::string const ChatHelper::FormatQuest(Quest const* quest)
{
if (!quest)
{
return "Invalid quest";
}
std::ostringstream out;
out << "|cFFFFFF00|Hquest:" << quest->GetQuestId() << ':' << quest->GetQuestLevel() << "|h[" << quest->GetTitle()
<< "]|h|r";
out << "|cFFFFFF00|Hquest:" << quest->GetQuestId() << ':' << quest->GetQuestLevel() << "|h[" << quest->GetTitle() << "]|h|r";
return out.str();
}

View File

@@ -239,6 +239,7 @@ void PlayerbotAI::UpdateAI(uint32 elapsed, bool minimal)
{
return;
}
// if (!GetMaster() || !GetMaster()->IsInWorld() || !GetMaster()->GetSession() ||
// GetMaster()->GetSession()->isLogingOut()) {
// return;
@@ -301,6 +302,7 @@ void PlayerbotAI::UpdateAI(uint32 elapsed, bool minimal)
// bot->GetMotionMaster()->Clear();
// bot->GetMotionMaster()->MoveIdle();
// }
// cheat options
if (bot->IsAlive() && ((uint32)GetCheat() > 0 || (uint32)sPlayerbotAIConfig->botCheatMask > 0))
{
@@ -3926,7 +3928,10 @@ Player* PlayerbotAI::GetGroupMaster()
uint32 PlayerbotAI::GetFixedBotNumer(BotTypeNumber typeNumber, uint32 maxNum, float cyclePerMin)
{
uint32 randseed = rand32(); // Seed random number
//deterministic seed
uint8 seedNumber = uint8(typeNumber);
std::mt19937 rng(seedNumber);
uint32 randseed = rng(); // Seed random number
uint32 randnum = bot->GetGUID().GetCounter() + randseed; // Semi-random but fixed number for each bot.
if (cyclePerMin > 0)
@@ -3936,8 +3941,7 @@ uint32 PlayerbotAI::GetFixedBotNumer(BotTypeNumber typeNumber, uint32 maxNum, fl
randnum += cycle; // Make the random number cylce.
}
randnum =
(randnum % (maxNum + 1)); // Loops the randomnumber at maxNum. Bassically removes all the numbers above 99.
randnum = (randnum % (maxNum + 1)); // Loops the randomnumber at maxNum. Bassically removes all the numbers above 99.
return randnum; // Now we have a number unique for each bot between 0 and maxNum that increases by cyclePerMin.
}
@@ -4075,18 +4079,15 @@ inline bool HasRealPlayers(Map* map)
return false;
}
bool PlayerbotAI::AllowActive(ActivityType activityType)
ActivePiorityType PlayerbotAI::GetPriorityType(ActivityType activityType)
{
// General exceptions
if (activityType == PACKET_ACTIVITY)
return true;
// First priority - priorities disabled or has player master. Always active.
if (HasRealPlayerMaster())
return ActivePiorityType::HAS_REAL_PLAYER_MASTER;
if (GetMaster()) // Has player master. Always active.
{
PlayerbotAI* masterBotAI = GET_PLAYERBOT_AI(GetMaster());
if (!masterBotAI || masterBotAI->IsRealPlayer())
return true;
}
// Self bot in a group with a bot master.
if (IsRealPlayer())
return ActivePiorityType::IS_REAL_PLAYER;
Group* group = bot->GetGroup();
if (group)
@@ -4100,21 +4101,49 @@ bool PlayerbotAI::AllowActive(ActivityType activityType)
if (member == bot)
continue;
//IN_GROUP_WITH_REAL_PLAYER
PlayerbotAI* memberBotAI = GET_PLAYERBOT_AI(member);
if (!memberBotAI || memberBotAI->HasRealPlayerMaster())
return true;
return ActivePiorityType::IN_GROUP_WITH_REAL_PLAYER;
//IN_GROUP_WITH_REAL_PLAYER
if (group->IsLeader(member->GetGUID()))
{
if (!memberBotAI->AllowActivity(PARTY_ACTIVITY))
return false;
return ActivePiorityType::IN_GROUP_WITH_REAL_PLAYER;
}
}
}
if (!WorldPosition(bot).isOverworld()) // bg, raid, dungeon
return true;
//IN_INSTANCE
if (bot->IsBeingTeleported()) // Allow activity while teleportation.
return ActivePiorityType::IN_INSTANCE;
if (bot->InBattlegroundQueue()) // In bg queue. Speed up bg queue/join.
return true;
//IN_INSTANCE
if (!WorldPosition(bot).isOverworld())
return ActivePiorityType::IN_INSTANCE;
//VISIBLE_FOR_PLAYER
if (HasPlayerNearby(sPlayerbotAIConfig->reactDistance))
return ActivePiorityType::VISIBLE_FOR_PLAYER;
//IN_COMBAT
if (activityType != OUT_OF_PARTY_ACTIVITY && activityType != PACKET_ACTIVITY)
{
// Is in combat, defend yourself.
if (bot->IsInCombat())
return ActivePiorityType::IN_COMBAT;
}
//NEARBY_PLAYER
if (HasPlayerNearby(300.f))
return ActivePiorityType::NEARBY_PLAYER;
//if (sPlayerbotAIConfig->IsFreeAltBot(bot) || HasStrategy("travel once", BotState::BOT_STATE_NON_COMBAT))
// return ActivePiorityType::IS_ALWAYS_ACTIVE;
if (bot->InBattlegroundQueue())
return ActivePiorityType::IN_BG_QUEUE;
bool isLFG = false;
if (group)
@@ -4124,62 +4153,172 @@ bool PlayerbotAI::AllowActive(ActivityType activityType)
isLFG = true;
}
}
if (sLFGMgr->GetState(bot->GetGUID()) != lfg::LFG_STATE_NONE)
{
isLFG = true;
}
if (isLFG)
return true;
return ActivePiorityType::IN_LFG;
if (activityType != OUT_OF_PARTY_ACTIVITY && activityType != PACKET_ACTIVITY) // Is in combat. Defend yourself.
if (bot->IsInCombat())
return true;
//IN_EMPTY_SERVER
if (sRandomPlayerbotMgr->GetPlayers().empty())
return ActivePiorityType::IN_EMPTY_SERVER;
if (HasPlayerNearby(300.f)) // Player is near. Always active.
return true;
// friends always active
// HasFriend sometimes cause crash, disable
// for (auto& player : sRandomPlayerbotMgr->GetPlayers())
// {
// if (!player || !player->IsInWorld())
// continue;
// if (player->GetSocial()->HasFriend(bot->GetGUID()))
// return true;
// }
if (activityType == OUT_OF_PARTY_ACTIVITY ||
activityType == GRIND_ACTIVITY) // Many bots nearby. Do not do heavy area checks.
if (HasManyPlayersNearby())
return false;
// Bots don't need to move using PathGenerator.
if (activityType == DETAILED_MOVE_ACTIVITY)
return false;
// All exceptions are now done.
// Below is code to have a specified % of bots active at all times.
// The default is 10%. With 0.1% of all bots going active or inactive each minute.
if (sPlayerbotAIConfig->botActiveAlone <= 0)
return false;
uint32 mod = sPlayerbotAIConfig->botActiveAlone > 100 ? 100 : sPlayerbotAIConfig->botActiveAlone;
if (sPlayerbotAIConfig->botActiveAloneAutoScale)
//PLAYER_FRIEND (on friendlist of real player) (needs to be tested for stability)
/*for (auto& player : sRandomPlayerbotMgr->GetPlayers())
{
mod = AutoScaleActivity(mod);
if (!player || !player->IsInWorld())
continue;
if (player->GetSocial() &&
bot->GetGUID() &&
player->GetSocial()->HasFriend(bot->GetGUID()))
return ActivePiorityType::PLAYER_FRIEND;
}*/
//PLAYER_GUILD (contains real player)
if (IsInRealGuild())
return ActivePiorityType::PLAYER_GUILD;
//IN_INACTIVE_MAP
if (bot->IsBeingTeleported() || !bot->IsInWorld() || !HasRealPlayers(bot->GetMap()))
return ActivePiorityType::IN_INACTIVE_MAP;
//IN_ACTIVE_MAP
if (!bot->IsBeingTeleported() && bot->IsInWorld() && HasRealPlayers(bot->GetMap()))
return ActivePiorityType::IN_ACTIVE_MAP;
// IN_ACTIVE_AREA
if (activityType == OUT_OF_PARTY_ACTIVITY || activityType == GRIND_ACTIVITY)
{
if (HasManyPlayersNearby(20, sPlayerbotAIConfig->sightDistance))
return ActivePiorityType::IN_ACTIVE_AREA;
}
uint32 ActivityNumber =
GetFixedBotNumer(BotTypeNumber::ACTIVITY_TYPE_NUMBER, 100,
sPlayerbotAIConfig->botActiveAlone * static_cast<float>(mod) / 100 * 0.01f);
return ActivePiorityType::IN_ACTIVE_AREA;
}
return ActivityNumber <=
(sPlayerbotAIConfig->botActiveAlone * mod) /
100; // The given percentage of bots should be active and rotate 1% of those active bots each minute.
// Returns the lower and upper bracket for bots to be active.
// Ie. { 10, 20 } means all bots in this bracket will be inactive below 10% activityMod,
// and will be active above 20% activityMod and scale between those values.
std::pair<uint32, uint32> PlayerbotAI::GetPriorityBracket(ActivePiorityType type)
{
switch (type)
{
case ActivePiorityType::HAS_REAL_PLAYER_MASTER:
case ActivePiorityType::IS_REAL_PLAYER:
case ActivePiorityType::IN_GROUP_WITH_REAL_PLAYER:
case ActivePiorityType::IN_INSTANCE:
case ActivePiorityType::VISIBLE_FOR_PLAYER:
return {0, 0};
case ActivePiorityType::IS_ALWAYS_ACTIVE:
case ActivePiorityType::IN_COMBAT:
return {0, 10};
case ActivePiorityType::IN_BG_QUEUE:
return {0, 20};
case ActivePiorityType::IN_LFG:
return {0, 30};
case ActivePiorityType::NEARBY_PLAYER:
return {0, 40};
case ActivePiorityType::PLAYER_FRIEND:
case ActivePiorityType::PLAYER_GUILD:
return {0, 50};
case ActivePiorityType::IN_ACTIVE_AREA:
case ActivePiorityType::IN_EMPTY_SERVER:
return {50, 100};
case ActivePiorityType::IN_ACTIVE_MAP:
return {70, 100};
case ActivePiorityType::IN_INACTIVE_MAP:
return {80, 100};
default:
return {90, 100};
}
return {90, 100};
}
bool PlayerbotAI::AllowActive(ActivityType activityType)
{
// General exceptions
if (activityType == PACKET_ACTIVITY)
return true;
ActivePiorityType type = GetPriorityType(activityType);
if (activityType == DETAILED_MOVE_ACTIVITY)
{
switch (type)
{
case ActivePiorityType::HAS_REAL_PLAYER_MASTER:
case ActivePiorityType::IS_REAL_PLAYER:
case ActivePiorityType::IN_GROUP_WITH_REAL_PLAYER:
case ActivePiorityType::IN_INSTANCE:
case ActivePiorityType::VISIBLE_FOR_PLAYER:
case ActivePiorityType::IN_COMBAT:
case ActivePiorityType::NEARBY_PLAYER:
return true;
break;
case ActivePiorityType::IS_ALWAYS_ACTIVE:
case ActivePiorityType::IN_BG_QUEUE:
case ActivePiorityType::IN_LFG:
case ActivePiorityType::PLAYER_FRIEND:
case ActivePiorityType::PLAYER_GUILD:
case ActivePiorityType::IN_ACTIVE_AREA:
case ActivePiorityType::IN_EMPTY_SERVER:
case ActivePiorityType::IN_ACTIVE_MAP:
case ActivePiorityType::IN_INACTIVE_MAP:
default:
break;
}
}
else if (activityType == REACT_ACTIVITY)
{
switch (type)
{
case ActivePiorityType::HAS_REAL_PLAYER_MASTER:
case ActivePiorityType::IS_REAL_PLAYER:
case ActivePiorityType::IN_GROUP_WITH_REAL_PLAYER:
case ActivePiorityType::IN_INSTANCE:
case ActivePiorityType::VISIBLE_FOR_PLAYER:
case ActivePiorityType::IS_ALWAYS_ACTIVE:
case ActivePiorityType::IN_COMBAT:
return true;
break;
case ActivePiorityType::NEARBY_PLAYER:
case ActivePiorityType::IN_BG_QUEUE:
case ActivePiorityType::IN_LFG:
case ActivePiorityType::PLAYER_FRIEND:
case ActivePiorityType::PLAYER_GUILD:
case ActivePiorityType::IN_ACTIVE_AREA:
case ActivePiorityType::IN_EMPTY_SERVER:
case ActivePiorityType::IN_ACTIVE_MAP:
case ActivePiorityType::IN_INACTIVE_MAP:
default:
return false;
break;
}
}
// GetPriorityBracket acitivity
float normalizedBotActiveAlone = sPlayerbotAIConfig->botActiveAlone > 100 ? 100 : sPlayerbotAIConfig->botActiveAlone;
float activePerc = normalizedBotActiveAlone;
if (sPlayerbotAIConfig->botActiveAloneSmartScale &&
bot->GetLevel() >= sPlayerbotAIConfig->botActiveAloneSmartScaleWhenMinLevel &&
bot->GetLevel() <= sPlayerbotAIConfig->botActiveAloneSmartScaleWhenMaxLevel)
{
std::pair<uint8, uint8> priorityBracket = GetPriorityBracket(type);
if (!priorityBracket.second) return true;
float activityPercentage = sRandomPlayerbotMgr->getActivityPercentage();
if (priorityBracket.first >= activityPercentage) return false;
if (priorityBracket.second <= activityPercentage && priorityBracket.second < 100) return true;
activePerc = (activityPercentage - priorityBracket.first) / (priorityBracket.second - priorityBracket.first);
activePerc *= (priorityBracket.second == 100) ? normalizedBotActiveAlone : 100;
}
// The last number if the amount it cycles per min. Currently set to 1% of the active bots.
uint32 ActivityNumber = GetFixedBotNumer(BotTypeNumber::ACTIVITY_TYPE_NUMBER, 100, activePerc * 0.01f);
// The given percentage of bots should be active and rotate 1% of those active bots each minute.
return ActivityNumber <= (activePerc);
}
bool PlayerbotAI::AllowActivity(ActivityType activityType, bool checkNow)
@@ -4196,31 +4335,6 @@ bool PlayerbotAI::AllowActivity(ActivityType activityType, bool checkNow)
return allowed;
}
uint32 PlayerbotAI::AutoScaleActivity(uint32 mod)
{
uint32 maxDiff = sWorldUpdateTime.GetAverageUpdateTime();
if (maxDiff > 500) return 0;
if (maxDiff > 250)
{
if (Map* map = bot->GetMap())
{
if (map->GetEntry()->IsWorldMap() &&
(!HasRealPlayers(map) ||
!map->IsGridLoaded(bot->GetPositionX(), bot->GetPositionY())))
return 0;
}
return (mod * 1) / 10;
}
if (maxDiff > 200) return (mod * 3) / 10;
if (maxDiff > 150) return (mod * 5) / 10;
if (maxDiff > 100) return (mod * 6) / 10;
if (maxDiff > 80) return (mod * 9) / 10;
return mod;
}
bool PlayerbotAI::IsOpposing(Player* player) { return IsOpposing(player->getRace(), bot->getRace()); }
bool PlayerbotAI::IsOpposing(uint8 race1, uint8 race2)
@@ -5328,15 +5442,29 @@ bool PlayerbotAI::CanMove()
if (IsInVehicle() && !IsInVehicle(true))
return false;
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))
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;
}
bool PlayerbotAI::IsTaxiFlying()
{
return bot->HasUnitMovementFlag(MOVEMENTFLAG_ONTRANSPORT) &&
bot->HasUnitState(UNIT_STATE_IGNORE_PATHFINDING);
}
bool PlayerbotAI::IsInRealGuild()
{
if (!bot->GetGuildId())

View File

@@ -241,6 +241,27 @@ enum class GuilderType : uint8
VERY_LARGE = 250
};
enum class ActivePiorityType : uint8
{
IS_REAL_PLAYER = 0,
HAS_REAL_PLAYER_MASTER = 1,
IN_GROUP_WITH_REAL_PLAYER = 2,
IN_INSTANCE = 3,
VISIBLE_FOR_PLAYER = 4,
IS_ALWAYS_ACTIVE = 5,
IN_COMBAT = 6,
IN_BG_QUEUE = 7,
IN_LFG = 8,
NEARBY_PLAYER = 9,
PLAYER_FRIEND = 10,
PLAYER_GUILD = 11,
IN_ACTIVE_AREA = 12,
IN_ACTIVE_MAP = 13,
IN_INACTIVE_MAP = 14,
IN_EMPTY_SERVER = 15,
MAX_TYPE
};
enum ActivityType
{
GRIND_ACTIVITY = 1,
@@ -250,8 +271,8 @@ enum ActivityType
PACKET_ACTIVITY = 5,
DETAILED_MOVE_ACTIVITY = 6,
PARTY_ACTIVITY = 7,
ALL_ACTIVITY = 8,
REACT_ACTIVITY = 8,
ALL_ACTIVITY = 9,
MAX_ACTIVITY_TYPE
};
@@ -525,9 +546,10 @@ public:
bool HasPlayerNearby(WorldPosition* pos, float range = sPlayerbotAIConfig->reactDistance);
bool HasPlayerNearby(float range = sPlayerbotAIConfig->reactDistance);
bool HasManyPlayersNearby(uint32 trigerrValue = 20, float range = sPlayerbotAIConfig->sightDistance);
ActivePiorityType GetPriorityType(ActivityType activityType);
std::pair<uint32, uint32> GetPriorityBracket(ActivePiorityType type);
bool AllowActive(ActivityType activityType);
bool AllowActivity(ActivityType activityType = ALL_ACTIVITY, bool checkNow = false);
uint32 AutoScaleActivity(uint32 mod);
// Check if player is safe to use.
bool IsSafe(Player* player);
@@ -554,6 +576,7 @@ public:
void ResetJumpDestination() { jumpDestination = Position(); }
bool CanMove();
bool IsTaxiFlying();
bool IsInRealGuild();
static std::vector<std::string> dispel_whitelist;
bool EqualLowercaseName(std::string s1, std::string s2);

View File

@@ -465,11 +465,15 @@ bool PlayerbotAIConfig::Initialize()
playerbotsXPrate = sConfigMgr->GetOption<int32>("AiPlayerbot.KillXPRate", 1);
disableDeathKnightLogin = sConfigMgr->GetOption<bool>("AiPlayerbot.DisableDeathKnightLogin", 0);
botActiveAlone = sConfigMgr->GetOption<int32>("AiPlayerbot.BotActiveAlone", 100);
botActiveAloneAutoScale = sConfigMgr->GetOption<bool>("AiPlayerbot.botActiveAloneAutoScale", true);
enablePrototypePerformanceDiff = sConfigMgr->GetOption<bool>("AiPlayerbot.EnablePrototypePerformanceDiff", false);
diffWithPlayer = sConfigMgr->GetOption<int32>("AiPlayerbot.DiffWithPlayer", 100);
diffEmpty = sConfigMgr->GetOption<int32>("AiPlayerbot.DiffEmpty", 200);
botActiveAloneSmartScale = sConfigMgr->GetOption<bool>("AiPlayerbot.botActiveAloneSmartScale", 1);
botActiveAloneSmartScaleWhenMinLevel =
sConfigMgr->GetOption<uint32>("AiPlayerbot.botActiveAloneSmartScaleWhenMinLevel", 1);
botActiveAloneSmartScaleWhenMaxLevel =
sConfigMgr->GetOption<uint32>("AiPlayerbot.botActiveAloneSmartScaleWhenMaxLevel", 80);
botActiveAloneSmartScaleDiffWithPlayer =
sConfigMgr->GetOption<uint32>("AiPlayerbot.botActiveAloneSmartScaleDiffWithPlayer", 100);
botActiveAloneSmartScaleDiffEmpty =
sConfigMgr->GetOption<uint32>("AiPlayerbot.botActiveAloneSmartScaleDiffEmpty", 200);
randombotsWalkingRPG = sConfigMgr->GetOption<bool>("AiPlayerbot.RandombotsWalkingRPG", false);
randombotsWalkingRPGInDoors = sConfigMgr->GetOption<bool>("AiPlayerbot.RandombotsWalkingRPG.InDoors", false);

View File

@@ -263,11 +263,11 @@ public:
uint32 playerbotsXPrate;
bool disableDeathKnightLogin;
uint32 botActiveAlone;
bool botActiveAloneAutoScale;
uint32 enablePrototypePerformanceDiff;
uint32 diffWithPlayer;
uint32 diffEmpty;
bool botActiveAloneSmartScale;
uint32 botActiveAloneSmartScaleWhenMinLevel;
uint32 botActiveAloneSmartScaleWhenMaxLevel;
uint32 botActiveAloneSmartScaleDiffWithPlayer;
uint32 botActiveAloneSmartScaleDiffEmpty;
bool freeMethodLoot;
int32 lootRollLevel;

View File

@@ -100,6 +100,8 @@ void PlayerbotHolder::HandlePlayerBotLoginCallback(PlayerbotLoginQueryHolder con
Player* bot = botSession->GetPlayer();
if (!bot)
{
// Log para debug
LOG_ERROR("mod-playerbots", "Bot player could not be loaded for account ID: {}", botAccountId);
botSession->LogoutPlayer(true);
delete botSession;
botLoading.erase(holder.GetGuid());
@@ -108,6 +110,14 @@ void PlayerbotHolder::HandlePlayerBotLoginCallback(PlayerbotLoginQueryHolder con
uint32 masterAccount = holder.GetMasterAccountId();
WorldSession* masterSession = masterAccount ? sWorld->FindSession(masterAccount) : nullptr;
// Check if masterSession->GetPlayer() is valid
Player* masterPlayer = masterSession ? masterSession->GetPlayer() : nullptr;
if (masterSession && !masterPlayer)
{
LOG_ERROR("mod-playerbots", "Master session found but no player is associated for master account ID: {}", masterAccount);
}
std::ostringstream out;
bool allowed = false;
if (botAccountId == masterAccount)
@@ -115,7 +125,7 @@ void PlayerbotHolder::HandlePlayerBotLoginCallback(PlayerbotLoginQueryHolder con
allowed = true;
}
else if (masterSession && sPlayerbotAIConfig->allowGuildBots && bot->GetGuildId() != 0 &&
bot->GetGuildId() == masterSession->GetPlayer()->GetGuildId())
bot->GetGuildId() == masterPlayer->GetGuildId())
{
allowed = true;
}
@@ -129,10 +139,14 @@ void PlayerbotHolder::HandlePlayerBotLoginCallback(PlayerbotLoginQueryHolder con
out << "Failure: You are not allowed to control bot " << bot->GetName().c_str();
}
if (allowed && masterSession)
if (allowed && masterSession && masterPlayer)
{
Player* player = masterSession->GetPlayer();
PlayerbotMgr* mgr = GET_PLAYERBOT_MGR(player);
PlayerbotMgr* mgr = GET_PLAYERBOT_MGR(masterPlayer);
if (!mgr)
{
LOG_ERROR("mod-playerbots", "PlayerbotMgr not found for master player with GUID: {}", masterPlayer->GetGUID().GetRawValue());
}
uint32 count = mgr->GetPlayerbotsCount();
uint32 cls_count = mgr->GetPlayerbotsCountByClass(bot->getClass());
if (count >= sPlayerbotAIConfig->maxAddedBots)
@@ -428,14 +442,17 @@ void PlayerbotHolder::OnBotLogin(Player* const bot)
PlayerbotAI* botAI = GET_PLAYERBOT_AI(bot);
if (!botAI)
{
// Log a warning here to indicate that the botAI is null
LOG_ERROR("mod-playerbots", "PlayerbotAI is null for bot with GUID: {}", bot->GetGUID().GetRawValue());
return;
}
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_ERROR("mod-playerbots", "Master is null for bot with GUID: {}", bot->GetGUID().GetRawValue());
return;
}
Group* group = bot->GetGroup();

View File

@@ -292,12 +292,8 @@ void RandomPlayerbotMgr::UpdateAIInternal(uint32 elapsed, bool /*minimal*/)
if (!sPlayerbotAIConfig->randomBotAutologin || !sPlayerbotAIConfig->enabled)
return;
if (sPlayerbotAIConfig->enablePrototypePerformanceDiff)
if (sPlayerbotAIConfig->botActiveAloneSmartScale)
{
LOG_INFO("playerbots", "---------------------------------------");
LOG_INFO("playerbots",
"PROTOTYPE: Playerbot performance enhancements are active. Issues and instability may occur.");
LOG_INFO("playerbots", "---------------------------------------");
ScaleBotActivity();
}
@@ -414,8 +410,9 @@ void RandomPlayerbotMgr::ScaleBotActivity()
// max/min activity
// % increase/decrease wanted diff , avg diff
float activityPercentageMod = pid.calculate(
sRandomPlayerbotMgr->GetPlayers().empty() ? sPlayerbotAIConfig->diffEmpty : sPlayerbotAIConfig->diffWithPlayer,
float activityPercentageMod = pid.calculate(sRandomPlayerbotMgr->GetPlayers().empty() ?
sPlayerbotAIConfig->botActiveAloneSmartScaleDiffEmpty :
sPlayerbotAIConfig->botActiveAloneSmartScaleDiffWithPlayer,
sWorldUpdateTime.GetAverageUpdateTime());
activityPercentage = activityPercentageMod + 50;
@@ -1108,6 +1105,9 @@ bool RandomPlayerbotMgr::ProcessBot(uint32 bot)
bool RandomPlayerbotMgr::ProcessBot(Player* player)
{
if (!player || !player->IsInWorld() || player->IsBeingTeleported() || player->GetSession()->isLogingOut())
return false;
uint32 bot = player->GetGUID().GetCounter();
if (player->InBattleground())
@@ -1262,9 +1262,7 @@ void RandomPlayerbotMgr::RandomTeleport(Player* bot, std::vector<WorldLocation>&
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 (botAI->IsTaxiFlying() && botAI->HasPlayerNearby())
return;
}

View File

@@ -234,16 +234,15 @@ bool BGJoinAction::shouldJoinBg(BattlegroundQueueTypeId queueTypeId, Battlegroun
return false;
TeamId teamId = bot->GetTeamId();
bool noLag = sWorldUpdateTime.GetAverageUpdateTime() < (sRandomPlayerbotMgr->GetPlayers().empty()
? sPlayerbotAIConfig->diffEmpty
: sPlayerbotAIConfig->diffWithPlayer) *
1.1;
bool noLag = sWorldUpdateTime.GetAverageUpdateTime() < (sRandomPlayerbotMgr->GetPlayers().empty() ?
sPlayerbotAIConfig->botActiveAloneSmartScaleDiffEmpty :
sPlayerbotAIConfig->botActiveAloneSmartScaleDiffWithPlayer) * 1.1;
uint32 BracketSize = bg->GetMaxPlayersPerTeam() * 2;
uint32 TeamSize = bg->GetMaxPlayersPerTeam();
// If performance diff is enabled, only queue if there is no lag
if (sPlayerbotAIConfig->enablePrototypePerformanceDiff && !noLag)
if (sPlayerbotAIConfig->botActiveAloneSmartScale && !noLag)
return false;
// If the bot is in a group, only the leader can queue
@@ -578,16 +577,15 @@ bool FreeBGJoinAction::shouldJoinBg(BattlegroundQueueTypeId queueTypeId, Battleg
return false;
TeamId teamId = bot->GetTeamId();
bool noLag = sWorldUpdateTime.GetAverageUpdateTime() < (sRandomPlayerbotMgr->GetPlayers().empty()
? sPlayerbotAIConfig->diffEmpty
: sPlayerbotAIConfig->diffWithPlayer) *
1.1;
bool noLag = sWorldUpdateTime.GetAverageUpdateTime() < (sRandomPlayerbotMgr->GetPlayers().empty() ?
sPlayerbotAIConfig->botActiveAloneSmartScaleDiffEmpty :
sPlayerbotAIConfig->botActiveAloneSmartScaleDiffWithPlayer) * 1.1;
uint32 BracketSize = bg->GetMaxPlayersPerTeam() * 2;
uint32 TeamSize = bg->GetMaxPlayersPerTeam();
// If performance diff is enabled, only queue if there is no lag
if (sPlayerbotAIConfig->enablePrototypePerformanceDiff && !noLag)
if (sPlayerbotAIConfig->botActiveAloneSmartScale && !noLag)
return false;
// If the bot is in a group, only the leader can queue

View File

@@ -58,55 +58,92 @@ bool DropQuestAction::Execute(Event event)
return true;
}
bool CleanQuestLogAction::Execute(Event event)
{
Player* requester = event.getOwner() ? event.getOwner() : GetMaster();
std::string link = event.getParam();
if (botAI->HasActivePlayerMaster() || !sRandomPlayerbotMgr->IsRandomBot(bot))
if (!requester)
{
botAI->TellMaster("No event owner detected");
return false;
uint8 totalQuests = 0;
// Count the total quests
DropQuestType(totalQuests);
if (MAX_QUEST_LOG_SIZE - totalQuests > 6)
{
// Drop failed quests
DropQuestType(totalQuests, MAX_QUEST_LOG_SIZE, true, true);
return true;
}
// Only drop gray quests when able to fight proper lvl quests.
if (AI_VALUE(bool, "can fight equal"))
// Only output this message if "debug rpg" strategy is enabled
if (botAI->HasStrategy("debug rpg", BotState::BOT_STATE_COMBAT))
{
// Drop gray/red quests.
DropQuestType(totalQuests, MAX_QUEST_LOG_SIZE - 6);
// Drop gray/red quests with progress.
DropQuestType(totalQuests, MAX_QUEST_LOG_SIZE - 6, false, true);
// Drop gray/red completed quests.
DropQuestType(totalQuests, MAX_QUEST_LOG_SIZE - 6, false, true, true);
botAI->TellMaster("Clean Quest Log command received, removing grey/trivial quests...");
}
if (MAX_QUEST_LOG_SIZE - totalQuests > 4)
return true;
uint8 botLevel = bot->GetLevel(); // Get bot's level
uint8 numQuest = 0;
for (uint8 slot = 0; slot < MAX_QUEST_LOG_SIZE; ++slot)
{
if (bot->GetQuestSlotQuestId(slot))
{
numQuest++;
}
}
DropQuestType(totalQuests, MAX_QUEST_LOG_SIZE - 4, true); // Drop quests without progress.
for (uint8 slot = 0; slot < MAX_QUEST_LOG_SIZE; ++slot)
{
uint32 questId = bot->GetQuestSlotQuestId(slot);
if (!questId)
continue;
if (MAX_QUEST_LOG_SIZE - totalQuests > 2)
return true;
const Quest* quest = sObjectMgr->GetQuestTemplate(questId);
if (!quest)
continue;
DropQuestType(totalQuests, MAX_QUEST_LOG_SIZE - 2, true, true); // Drop quests with progress.
// Determine if quest is trivial by comparing levels
int32 questLevel = quest->GetQuestLevel();
if (questLevel == -1) // For scaling quests, default to bot level
{
questLevel = botLevel;
}
if (MAX_QUEST_LOG_SIZE - totalQuests > 0)
return true;
// Check if the quest is trivial (grey) for the bot
if ((botLevel - questLevel) >= 5)
{
// Output only if "debug rpg" strategy is enabled
if (botAI->HasStrategy("debug rpg", BotState::BOT_STATE_COMBAT))
{
botAI->TellMaster("Quest [ " + quest->GetTitle() + " ] will be removed because it is trivial (grey).");
}
DropQuestType(totalQuests, MAX_QUEST_LOG_SIZE - 1, true, true, true); // Drop completed quests.
// Remove quest
bot->SetQuestSlot(slot, 0);
bot->TakeQuestSourceItem(questId, false);
bot->SetQuestStatus(questId, QUEST_STATUS_NONE);
bot->RemoveRewardedQuest(questId);
if (MAX_QUEST_LOG_SIZE - totalQuests > 0)
return true;
numQuest--;
return false;
if (botAI->HasStrategy("debug rpg", BotState::BOT_STATE_COMBAT))
{
const std::string text_quest = ChatHelper::FormatQuest(quest);
LOG_INFO("playerbots", "{} => Quest [ {} ] removed", bot->GetName(), quest->GetTitle());
bot->Say("Quest [ " + text_quest + " ] removed", LANG_UNIVERSAL);
}
if (botAI->HasStrategy("debug rpg", BotState::BOT_STATE_COMBAT))
{
botAI->TellMaster("Quest [ " + quest->GetTitle() + " ] has been removed.");
}
}
else
{
// Only output if "debug rpg" strategy is enabled
if (botAI->HasStrategy("debug rpg", BotState::BOT_STATE_COMBAT))
{
botAI->TellMaster("Quest [ " + quest->GetTitle() + " ] is not trivial and will be kept.");
}
}
}
return true;
}
void CleanQuestLogAction::DropQuestType(uint8& numQuest, uint8 wantNum, bool isGreen, bool hasProgress, bool isComplete)
{
std::vector<uint8> slots;

View File

@@ -31,6 +31,8 @@ bool PartyCommandAction::Execute(Event event)
if (master && member == master->GetName())
return Leave(bot);
botAI->Reset();
return false;
}
@@ -63,6 +65,8 @@ bool UninviteAction::Execute(Event event)
return Leave(bot);
}
botAI->Reset();
return false;
}
@@ -161,5 +165,7 @@ bool LeaveFarAwayAction::isUseful()
return true;
}
botAI->Reset();
return false;
}

View File

@@ -79,7 +79,7 @@ bool RpgAction::SetNextRpgAction()
{
NextAction* nextAction = nextActions[i];
if (nextAction->getRelevance() > 2.0f)
if (nextAction->getRelevance() > 5.0f)
continue;
if (!isChecked && !trigger->IsActive())
@@ -92,7 +92,7 @@ bool RpgAction::SetNextRpgAction()
continue;
actions.push_back(action);
relevances.push_back((nextAction->getRelevance() - 1) * 1000);
relevances.push_back((nextAction->getRelevance() - 1) * 500);
}
NextAction::destroy(nextActions);
}