diff --git a/conf/playerbots.conf.dist b/conf/playerbots.conf.dist index 22ed33cf..f8d1b8ec 100644 --- a/conf/playerbots.conf.dist +++ b/conf/playerbots.conf.dist @@ -82,8 +82,8 @@ AiPlayerbot.Enabled = 1 AiPlayerbot.RandomBotAutologin = 1 # Random bot count -AiPlayerbot.MinRandomBots = 50 -AiPlayerbot.MaxRandomBots = 50 +AiPlayerbot.MinRandomBots = 500 +AiPlayerbot.MaxRandomBots = 500 # Random bot accounts # If you are not using any expansion at all, you may have to set this manually, then @@ -561,10 +561,13 @@ AiPlayerbot.EnableRandomBotTrading = 1 AiPlayerbot.DisableRandomLevels = 0 # Set randombots starting level here if "AiPlayerbot.DisableRandomLevels" enabled -AiPlayerbot.RandombotStartingLevel = 5 +AiPlayerbot.RandombotStartingLevel = 1 -# Chance random bot has max level on first randomize (default 0.15) -AiPlayerbot.RandomBotMaxLevelChance = 0.15 +# Chance random bot has min level on first randomize (default 0.1) +AiPlayerbot.RandomBotMinLevelChance = 0.1 + +# Chance random bot has max level on first randomize (default 0.1) +AiPlayerbot.RandomBotMaxLevelChance = 0.1 # Fix the level of random bot (won't level up by grinding) # Default: 0 (disable) @@ -689,8 +692,8 @@ AiPlayerbot.AutoDoQuests = 1 # Random Bots will behave more like real players (exprimental) # This option will override AutoDoQuests -# Default: 0 (disabled) -AiPlayerbot.EnableNewRpgStrategy = 0 +# Default: 1 (enabled) +AiPlayerbot.EnableNewRpgStrategy = 1 # Quest items to leave (do not destroy) AiPlayerbot.RandomBotQuestItems = "6948,5175,5176,5177,5178,16309,12382,13704,11000" diff --git a/src/AiFactory.cpp b/src/AiFactory.cpp index ce2ca5e9..47822353 100644 --- a/src/AiFactory.cpp +++ b/src/AiFactory.cpp @@ -617,7 +617,7 @@ void AiFactory::AddDefaultNonCombatStrategies(Player* player, PlayerbotAI* const "gather", "duel", "pvp", "buff", "mount", "emote", nullptr); } - if (sPlayerbotAIConfig->autoSaveMana) + if (sPlayerbotAIConfig->autoSaveMana && PlayerbotAI::IsHeal(player, true)) { nonCombatEngine->addStrategy("save mana", false); } diff --git a/src/PlayerbotAIConfig.cpp b/src/PlayerbotAIConfig.cpp index 14b0da87..98a5560d 100644 --- a/src/PlayerbotAIConfig.cpp +++ b/src/PlayerbotAIConfig.cpp @@ -120,7 +120,8 @@ bool PlayerbotAIConfig::Initialize() randomGearQualityLimit = sConfigMgr->GetOption("AiPlayerbot.RandomGearQualityLimit", 3); randomGearScoreLimit = sConfigMgr->GetOption("AiPlayerbot.RandomGearScoreLimit", 0); - randomBotMaxLevelChance = sConfigMgr->GetOption("AiPlayerbot.RandomBotMaxLevelChance", 0.15f); + randomBotMinLevelChance = sConfigMgr->GetOption("AiPlayerbot.RandomBotMinLevelChance", 0.1f); + randomBotMaxLevelChance = sConfigMgr->GetOption("AiPlayerbot.RandomBotMaxLevelChance", 0.1f); randomBotRpgChance = sConfigMgr->GetOption("AiPlayerbot.RandomBotRpgChance", 0.20f); iterationsPerTick = sConfigMgr->GetOption("AiPlayerbot.IterationsPerTick", 100); diff --git a/src/PlayerbotAIConfig.h b/src/PlayerbotAIConfig.h index 9de6ceee..1eca0295 100644 --- a/src/PlayerbotAIConfig.h +++ b/src/PlayerbotAIConfig.h @@ -86,7 +86,7 @@ public: float randomGearLoweringChance; int32 randomGearQualityLimit; int32 randomGearScoreLimit; - float randomBotMaxLevelChance; + float randomBotMinLevelChance, randomBotMaxLevelChance; float randomBotRpgChance; uint32 minRandomBots, maxRandomBots; uint32 randomBotUpdateInterval, randomBotCountChangeMinInterval, randomBotCountChangeMaxInterval; diff --git a/src/PlayerbotMgr.cpp b/src/PlayerbotMgr.cpp index 59fad35d..71ed04c5 100644 --- a/src/PlayerbotMgr.cpp +++ b/src/PlayerbotMgr.cpp @@ -275,7 +275,7 @@ void PlayerbotHolder::LogoutPlayerBot(ObjectGuid guid) sPlayerbotDbStore->Save(botAI); } - LOG_INFO("playerbots", "Bot {} logging out", bot->GetName().c_str()); + LOG_DEBUG("playerbots", "Bot {} logging out", bot->GetName().c_str()); bot->SaveToDB(false, false); WorldSession* botWorldSessionPtr = bot->GetSession(); @@ -543,12 +543,15 @@ void PlayerbotHolder::OnBotLogin(Player* const bot) bot->SaveToDB(false, false); bool addClassBot = sRandomPlayerbotMgr->IsAddclassBot(bot->GetGUID().GetCounter()); - if (addClassBot && master && isRandomAccount && master->GetLevel() < bot->GetLevel()) + if (addClassBot && master && isRandomAccount && abs((int)master->GetLevel() - (int)bot->GetLevel()) > 3) { // PlayerbotFactory factory(bot, master->GetLevel()); // factory.Randomize(false); uint32 mixedGearScore = PlayerbotAI::GetMixedGearScore(master, true, false, 12) * sPlayerbotAIConfig->autoInitEquipLevelLimitRatio; + // work around: distinguish from 0 if no gear + if (mixedGearScore == 0) + mixedGearScore = 1; PlayerbotFactory factory(bot, master->GetLevel(), ITEM_QUALITY_LEGENDARY, mixedGearScore); factory.Randomize(false); } @@ -728,6 +731,9 @@ std::string const PlayerbotHolder::ProcessBotCommand(std::string const cmd, Obje { uint32 mixedGearScore = PlayerbotAI::GetMixedGearScore(master, true, false, 12) * sPlayerbotAIConfig->autoInitEquipLevelLimitRatio; + // work around: distinguish from 0 if no gear + if (mixedGearScore == 0) + mixedGearScore = 1; PlayerbotFactory factory(bot, master->GetLevel(), ITEM_QUALITY_LEGENDARY, mixedGearScore); factory.Randomize(false); return "ok, gear score limit: " + std::to_string(mixedGearScore / PlayerbotAI::GetItemScoreMultiplier(ItemQualities(ITEM_QUALITY_EPIC))) + @@ -1051,6 +1057,11 @@ std::vector PlayerbotHolder::HandlePlayerbotCommand(char const* arg messages.push_back("Error: Invalid Class. Try again."); return messages; } + if (claz == 6 && master->GetLevel() < sWorld->getIntConfig(CONFIG_START_HEROIC_PLAYER_LEVEL)) + { + messages.push_back("Your level is too low to summon Deathknight"); + return messages; + } uint8 teamId = master->GetTeamId(true); const std::unordered_set &guidCache = sRandomPlayerbotMgr->addclassCache[RandomPlayerbotMgr::GetTeamClassIdx(teamId == TEAM_ALLIANCE, claz)]; for (const ObjectGuid &guid: guidCache) diff --git a/src/Playerbots.cpp b/src/Playerbots.cpp index a9c58d3a..c1bf439e 100644 --- a/src/Playerbots.cpp +++ b/src/Playerbots.cpp @@ -379,7 +379,11 @@ public: sRandomPlayerbotMgr->OnPlayerLogout(player); } - void OnPlayerbotLogoutBots() override { sRandomPlayerbotMgr->LogoutAllBots(); } + void OnPlayerbotLogoutBots() override + { + LOG_INFO("playerbots", "Logging out all bots..."); + sRandomPlayerbotMgr->LogoutAllBots(); + } }; void AddPlayerbotsScripts() diff --git a/src/RandomItemMgr.cpp b/src/RandomItemMgr.cpp index 9a7ad711..799c1cb3 100644 --- a/src/RandomItemMgr.cpp +++ b/src/RandomItemMgr.cpp @@ -2362,7 +2362,7 @@ void RandomItemMgr::BuildPotionCache() continue; uint32 requiredLevel = proto->RequiredLevel; - if (requiredLevel > level || (level > 15 && requiredLevel < level - 15)) + if (requiredLevel > level || (level > 13 && requiredLevel < level - 13)) continue; if (proto->RequiredSkill) diff --git a/src/RandomPlayerbotFactory.cpp b/src/RandomPlayerbotFactory.cpp index 7142d93b..41e02906 100644 --- a/src/RandomPlayerbotFactory.cpp +++ b/src/RandomPlayerbotFactory.cpp @@ -981,7 +981,7 @@ void RandomPlayerbotFactory::CreateRandomArenaTeams(ArenaType type, uint32 count sPlayerbotAIConfig->randomBotArenaTeams.push_back(arenateam->GetId()); } - LOG_INFO("playerbots", "{} random bot {}vs{} arena teams available", arenaTeamNumber, type, type); + LOG_DEBUG("playerbots", "{} random bot {}vs{} arena teams available", arenaTeamNumber, type, type); } std::string const RandomPlayerbotFactory::CreateRandomArenaTeamName() diff --git a/src/RandomPlayerbotMgr.cpp b/src/RandomPlayerbotMgr.cpp index ca160a6d..b6947dda 100644 --- a/src/RandomPlayerbotMgr.cpp +++ b/src/RandomPlayerbotMgr.cpp @@ -386,7 +386,8 @@ void RandomPlayerbotMgr::UpdateAIInternal(uint32 elapsed, bool /*minimal*/) } else { - activatePrintStatsThread(); + sRandomPlayerbotMgr->PrintStats(); + // activatePrintStatsThread(); } } uint32 updateBots = sPlayerbotAIConfig->randomBotsPerInterval * onlineBotFocus / 100; @@ -415,7 +416,7 @@ void RandomPlayerbotMgr::UpdateAIInternal(uint32 elapsed, bool /*minimal*/) loginBots += updateBots; loginBots = std::min(loginBots, maxNewBots); - LOG_INFO("playerbots", "{} new bots", loginBots); + LOG_DEBUG("playerbots", "{} new bots prepared to login", loginBots); // Log in bots for (auto bot : availableBots) @@ -1103,10 +1104,10 @@ bool RandomPlayerbotMgr::ProcessBot(uint32 bot) if (!player || !player->GetGroup()) { if (player) - LOG_INFO("playerbots", "Bot #{} {}:{} <{}>: log out", bot, IsAlliance(player->getRace()) ? "A" : "H", + LOG_DEBUG("playerbots", "Bot #{} {}:{} <{}>: log out", bot, IsAlliance(player->getRace()) ? "A" : "H", player->GetLevel(), player->GetName().c_str()); else - LOG_INFO("playerbots", "Bot #{}: log out", bot); + LOG_DEBUG("playerbots", "Bot #{}: log out", bot); SetEventValue(bot, "add", 0, 0); currentBots.erase(std::remove(currentBots.begin(), currentBots.end(), bot), currentBots.end()); @@ -1189,7 +1190,7 @@ bool RandomPlayerbotMgr::ProcessBot(uint32 bot) uint32 logout = GetEventValue(bot, "logout"); if (player && !logout && !isValid) { - LOG_INFO("playerbots", "Bot #{} {}:{} <{}>: log out", bot, IsAlliance(player->getRace()) ? "A" : "H", + LOG_DEBUG("playerbots", "Bot #{} {}:{} <{}>: log out", bot, IsAlliance(player->getRace()) ? "A" : "H", player->GetLevel(), player->GetName().c_str()); LogoutPlayerBot(botGUID); currentBots.remove(bot); @@ -1434,6 +1435,7 @@ void RandomPlayerbotMgr::RandomTeleport(Player* bot, std::vector& z = 0.05f + ground; PlayerInfo const* pInfo = sObjectMgr->GetPlayerInfo(bot->getRace(true), bot->getClass()); float dis = loc.GetExactDist(pInfo->positionX, pInfo->positionY, pInfo->positionZ); + // yunfan: distance check for low level if (bot->GetLevel() <= 4 && (loc.GetMapId() != pInfo->mapId || dis > 500.0f)) { @@ -1462,6 +1464,12 @@ void RandomPlayerbotMgr::RandomTeleport(Player* bot, std::vector& bot->SetHomebind(loc, zone->ID); } + // Prevent blink to be detected by visible real players + if (dis < 150.0f && botAI->HasPlayerNearby(150.0f)) + { + break; + } + bot->GetMotionMaster()->Clear(); PlayerbotAI* botAI = GET_PLAYERBOT_AI(bot); if (botAI) @@ -1478,8 +1486,8 @@ void RandomPlayerbotMgr::RandomTeleport(Player* bot, std::vector& if (pmo) pmo->finish(); - LOG_ERROR("playerbots", "Cannot teleport bot {} - no locations available ({} locations)", bot->GetName().c_str(), - tlocs.size()); + // LOG_ERROR("playerbots", "Cannot teleport bot {} - no locations available ({} locations)", bot->GetName().c_str(), + // tlocs.size()); } void RandomPlayerbotMgr::PrepareZone2LevelBracket() @@ -1969,6 +1977,13 @@ void RandomPlayerbotMgr::RandomizeFirst(Player* bot) if (sPlayerbotAIConfig->syncLevelWithPlayers) maxLevel = std::max(sPlayerbotAIConfig->randomBotMinLevel, std::min(playersLevel, sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL))); + + uint32 minLevel = sPlayerbotAIConfig->randomBotMinLevel; + if (bot->getClass() == CLASS_DEATH_KNIGHT) + { + maxLevel = std::max(maxLevel, sWorld->getIntConfig(CONFIG_START_HEROIC_PLAYER_LEVEL)); + minLevel = std::max(minLevel, sWorld->getIntConfig(CONFIG_START_HEROIC_PLAYER_LEVEL)); + } PerformanceMonitorOperation* pmo = sPerformanceMonitor->start(PERF_MON_RNDBOT, "RandomizeFirst"); @@ -1987,14 +2002,19 @@ void RandomPlayerbotMgr::RandomizeFirst(Player* bot) } else { - level = urand(sPlayerbotAIConfig->randomBotMinLevel, maxLevel); - if (urand(1, 100) < 100 * sPlayerbotAIConfig->randomBotMaxLevelChance) + uint32 roll = urand(1, 100); + if (roll <= 100 * sPlayerbotAIConfig->randomBotMaxLevelChance) + { level = maxLevel; - - if (bot->getClass() == CLASS_DEATH_KNIGHT) - level = urand( - std::max(sPlayerbotAIConfig->randomBotMinLevel, sWorld->getIntConfig(CONFIG_START_HEROIC_PLAYER_LEVEL)), - std::max(sWorld->getIntConfig(CONFIG_START_HEROIC_PLAYER_LEVEL), maxLevel)); + } + else if (roll <= (100 * (sPlayerbotAIConfig->randomBotMaxLevelChance + sPlayerbotAIConfig->randomBotMinLevelChance))) + { + level = minLevel; + } + else + { + level = urand(minLevel, maxLevel); + } } if (sPlayerbotAIConfig->disableRandomLevels) @@ -2394,7 +2414,8 @@ bool RandomPlayerbotMgr::HandlePlayerbotConsoleCommand(ChatHandler* handler, cha if (cmd == "stats") { - activatePrintStatsThread(); + sRandomPlayerbotMgr->PrintStats(); + // activatePrintStatsThread(); return true; } @@ -2536,8 +2557,16 @@ void RandomPlayerbotMgr::OnPlayerLogout(Player* player) void RandomPlayerbotMgr::OnBotLoginInternal(Player* const bot) { - LOG_INFO("playerbots", "{}/{} Bot {} logged in", playerBots.size(), sRandomPlayerbotMgr->GetMaxAllowedBotCount(), - bot->GetName().c_str()); + if (_isBotLogging) + { + LOG_INFO("playerbots", "{}/{} Bot {} logged in", playerBots.size(), sRandomPlayerbotMgr->GetMaxAllowedBotCount(), + bot->GetName().c_str()); + + if (playerBots.size() == sRandomPlayerbotMgr->GetMaxAllowedBotCount()) + { + _isBotLogging = false; + } + } if (sPlayerbotAIConfig->randomBotFixedLevel) { @@ -2697,7 +2726,7 @@ void RandomPlayerbotMgr::PrintStats() uint32 engine_combat = 0; uint32 engine_dead = 0; std::unordered_map rpgStatusCount; - NewRpgStatistic rpgStasticTotal; + // static NewRpgStatistic rpgStasticTotal; std::unordered_map zoneCount; uint8 maxBotLevel = 0; for (PlayerBotMap::iterator i = playerBots.begin(); i != playerBots.end(); ++i) @@ -2775,6 +2804,7 @@ void RandomPlayerbotMgr::PrintStats() { rpgStatusCount[botAI->rpgInfo.status]++; rpgStasticTotal += botAI->rpgStatistic; + botAI->rpgStatistic = NewRpgStatistic(); } } @@ -2792,7 +2822,8 @@ void RandomPlayerbotMgr::PrintStats() if (((i + 1) % step == 0) || i == maxBotLevel) { - LOG_INFO("playerbots", " {}..{}: {} alliance, {} horde", from, i, currentAlliance, currentHorde); + if (currentAlliance || currentHorde) + LOG_INFO("playerbots", " {}..{}: {} alliance, {} horde", from, i, currentAlliance, currentHorde); currentAlliance = 0; currentHorde = 0; from = i + 1; diff --git a/src/RandomPlayerbotMgr.h b/src/RandomPlayerbotMgr.h index fc9ab95a..56f63b63 100644 --- a/src/RandomPlayerbotMgr.h +++ b/src/RandomPlayerbotMgr.h @@ -6,6 +6,7 @@ #ifndef _PLAYERBOT_RANDOMPLAYERBOTMGR_H #define _PLAYERBOT_RANDOMPLAYERBOTMGR_H +#include "NewRpgInfo.h" #include "ObjectGuid.h" #include "PlayerbotMgr.h" @@ -193,6 +194,8 @@ private: botPID pid = botPID(1, 50, -50, 0, 0, 0); float activityMod = 0.25; bool _isBotInitializing = true; + bool _isBotLogging = true; + NewRpgStatistic rpgStasticTotal; uint32 GetEventValue(uint32 bot, std::string const event); std::string const GetEventData(uint32 bot, std::string const event); uint32 SetEventValue(uint32 bot, std::string const event, uint32 value, uint32 validIn, diff --git a/src/factory/PlayerbotFactory.cpp b/src/factory/PlayerbotFactory.cpp index ff493666..58506566 100644 --- a/src/factory/PlayerbotFactory.cpp +++ b/src/factory/PlayerbotFactory.cpp @@ -42,6 +42,12 @@ const uint64 diveMask = (1LL << 7) | (1LL << 44) | (1LL << 37) | (1LL << 38) | (1LL << 26) | (1LL << 30) | (1LL << 27) | (1LL << 33) | (1LL << 24) | (1LL << 34); + +static std::vector initSlotsOrder = {EQUIPMENT_SLOT_TRINKET1, EQUIPMENT_SLOT_TRINKET2, EQUIPMENT_SLOT_MAINHAND, + EQUIPMENT_SLOT_OFFHAND, EQUIPMENT_SLOT_RANGED, EQUIPMENT_SLOT_HEAD, EQUIPMENT_SLOT_SHOULDERS, EQUIPMENT_SLOT_CHEST, + EQUIPMENT_SLOT_LEGS, EQUIPMENT_SLOT_HANDS, EQUIPMENT_SLOT_NECK, EQUIPMENT_SLOT_BODY, EQUIPMENT_SLOT_WAIST, + EQUIPMENT_SLOT_FEET, EQUIPMENT_SLOT_WRISTS, EQUIPMENT_SLOT_FINGER1, EQUIPMENT_SLOT_FINGER2, EQUIPMENT_SLOT_BACK}; + uint32 PlayerbotFactory::tradeSkills[] = {SKILL_ALCHEMY, SKILL_ENCHANTING, SKILL_SKINNING, SKILL_TAILORING, SKILL_LEATHERWORKING, SKILL_ENGINEERING, SKILL_HERBALISM, SKILL_MINING, SKILL_BLACKSMITHING, SKILL_COOKING, SKILL_FIRST_AID, SKILL_FISHING, @@ -208,7 +214,7 @@ void PlayerbotFactory::Randomize(bool incremental) // { // return; // } - LOG_INFO("playerbots", "{} randomizing {} (level {} class = {})...", (incremental ? "Incremental" : "Full"), + LOG_DEBUG("playerbots", "{} randomizing {} (level {} class = {})...", (incremental ? "Incremental" : "Full"), bot->GetName().c_str(), level, bot->getClass()); // LOG_DEBUG("playerbots", "Preparing to {} randomize...", (incremental ? "incremental" : "full")); Prepare(); @@ -1595,8 +1601,7 @@ void PlayerbotFactory::InitEquipment(bool incremental, bool second_chance) int32 delta = std::min(blevel, 10u); StatsWeightCalculator calculator(bot); - // Reverse order may work better - for (int32 slot = (int32)EQUIPMENT_SLOT_TABARD; slot >= (int32)EQUIPMENT_SLOT_START; slot--) + for (int32 slot : initSlotsOrder) { if (slot == EQUIPMENT_SLOT_TABARD || slot == EQUIPMENT_SLOT_BODY) continue; @@ -1604,10 +1609,7 @@ void PlayerbotFactory::InitEquipment(bool incremental, bool second_chance) if (level < 50 && (slot == EQUIPMENT_SLOT_TRINKET1 || slot == EQUIPMENT_SLOT_TRINKET2)) continue; - if (level < 30 && slot == EQUIPMENT_SLOT_NECK) - continue; - - if (level < 25 && slot == EQUIPMENT_SLOT_HEAD) + if (level < 30 && (slot == EQUIPMENT_SLOT_NECK || slot == EQUIPMENT_SLOT_HEAD)) continue; if (level < 20 && (slot == EQUIPMENT_SLOT_FINGER1 || slot == EQUIPMENT_SLOT_FINGER2)) @@ -1627,7 +1629,7 @@ void PlayerbotFactory::InitEquipment(bool incremental, bool second_chance) oldItem = bot->GetItemByPos(INVENTORY_SLOT_BAG_0, slot); - uint32 desiredQuality = itemQuality; + int32 desiredQuality = itemQuality; if (urand(0, 100) < 100 * sPlayerbotAIConfig->randomGearLoweringChance && desiredQuality > ITEM_QUALITY_NORMAL) { desiredQuality--; @@ -1662,8 +1664,10 @@ void PlayerbotFactory::InitEquipment(bool incremental, bool second_chance) ItemTemplate const* proto = sObjectMgr->GetItemTemplate(itemId); if (!proto) continue; + + bool shouldCheckGS = desiredQuality > ITEM_QUALITY_NORMAL; - if (gearScoreLimit != 0 && + if (shouldCheckGS && gearScoreLimit != 0 && CalcMixedGearScore(proto->ItemLevel, proto->Quality) > gearScoreLimit) { continue; @@ -1692,7 +1696,7 @@ void PlayerbotFactory::InitEquipment(bool incremental, bool second_chance) } } } - } while (items[slot].size() < 25 && desiredQuality-- > ITEM_QUALITY_NORMAL); + } while (items[slot].size() < 25 && desiredQuality-- > ITEM_QUALITY_POOR); std::vector& ids = items[slot]; if (ids.empty()) @@ -1766,7 +1770,7 @@ void PlayerbotFactory::InitEquipment(bool incremental, bool second_chance) /// @todo: clean up duplicate code if (second_chance) { - for (int32 slot = (int32)EQUIPMENT_SLOT_TABARD; slot >= (int32)EQUIPMENT_SLOT_START; slot--) + for (int32 slot : initSlotsOrder) { if (slot == EQUIPMENT_SLOT_TABARD || slot == EQUIPMENT_SLOT_BODY) continue; @@ -1774,17 +1778,15 @@ void PlayerbotFactory::InitEquipment(bool incremental, bool second_chance) if (level < 50 && (slot == EQUIPMENT_SLOT_TRINKET1 || slot == EQUIPMENT_SLOT_TRINKET2)) continue; - if (level < 30 && slot == EQUIPMENT_SLOT_NECK) - continue; - - if (level < 25 && slot == EQUIPMENT_SLOT_HEAD) + if (level < 30 && (slot == EQUIPMENT_SLOT_NECK || slot == EQUIPMENT_SLOT_HEAD)) continue; if (level < 20 && (slot == EQUIPMENT_SLOT_FINGER1 || slot == EQUIPMENT_SLOT_FINGER2)) continue; - + if (level < 5 && (slot != EQUIPMENT_SLOT_MAINHAND) && (slot != EQUIPMENT_SLOT_OFFHAND) && - (slot != EQUIPMENT_SLOT_FEET) && (slot != EQUIPMENT_SLOT_LEGS) && (slot != EQUIPMENT_SLOT_CHEST)) + (slot != EQUIPMENT_SLOT_FEET) && (slot != EQUIPMENT_SLOT_LEGS) && (slot != EQUIPMENT_SLOT_CHEST) && + (slot != EQUIPMENT_SLOT_RANGED)) continue; if (Item* oldItem = bot->GetItemByPos(INVENTORY_SLOT_BAG_0, slot)) @@ -3967,6 +3969,9 @@ void PlayerbotFactory::ApplyEnchantAndGemsNew(bool destoryOld) { continue; } + + if (item->GetTemplate() && item->GetTemplate()->Quality < ITEM_QUALITY_UNCOMMON) + continue; int32 bestEnchantId = -1; float bestScore = 0; for (const uint32& enchantSpell : enchantSpellIdCache) diff --git a/src/factory/StatsWeightCalculator.cpp b/src/factory/StatsWeightCalculator.cpp index 00a2f3d3..7a747e6a 100644 --- a/src/factory/StatsWeightCalculator.cpp +++ b/src/factory/StatsWeightCalculator.cpp @@ -129,6 +129,7 @@ void StatsWeightCalculator::GenerateBasicWeights(Player* player) stats_weights_[STATS_TYPE_STAMINA] += 0.1f; stats_weights_[STATS_TYPE_ARMOR] += 0.001f; stats_weights_[STATS_TYPE_BONUS] += 1.0f; + stats_weights_[STATS_TYPE_MELEE_DPS] += 0.01f; if (cls == CLASS_HUNTER && (tab == HUNTER_TAB_BEASTMASTER || tab == HUNTER_TAB_SURVIVAL)) { diff --git a/src/strategy/actions/GenericActions.cpp b/src/strategy/actions/GenericActions.cpp index cfbce10c..f0ce6fb0 100644 --- a/src/strategy/actions/GenericActions.cpp +++ b/src/strategy/actions/GenericActions.cpp @@ -8,6 +8,17 @@ #include "CreatureAI.h" #include "Playerbots.h" +enum PetSpells +{ + PET_PROWL_1 = 24450, + PET_PROWL_2 = 24452, + PET_PROWL_3 = 24453, + PET_COWER = 1742, + PET_LEAP = 47482 +}; + +static std::vector disabledPetSpells = {PET_PROWL_1, PET_PROWL_2, PET_PROWL_3, PET_COWER, PET_LEAP}; + bool MeleeAction::isUseful() { // do not allow if can't attack from vehicle @@ -44,18 +55,20 @@ bool TogglePetSpellAutoCastAction::Execute(Event event) { if (itr->second.state == PETSPELL_REMOVED) continue; - + uint32 spellId = itr->first; const SpellInfo* spellInfo = sSpellMgr->GetSpellInfo(spellId); if (!spellInfo->IsAutocastable()) continue; bool shouldApply = true; - if (spellId == 1742 /*cower*/ || spellId == 24450 /*Prowl*/ || - spellId == 47482 /*Leap*/ /* || spellId == 47481 Gnaw*/) + for (uint32 disabledSpell : disabledPetSpells) { - - shouldApply = false; + if (spellId == disabledSpell) + { + shouldApply = false; + break; + } } bool isAutoCast = false; for (unsigned int& m_autospell : pet->m_autospells) diff --git a/src/strategy/actions/MovementActions.cpp b/src/strategy/actions/MovementActions.cpp index f7baab1c..bd866f72 100644 --- a/src/strategy/actions/MovementActions.cpp +++ b/src/strategy/actions/MovementActions.cpp @@ -847,6 +847,26 @@ bool MovementAction::ReachCombatTo(Unit* target, float distance) float tx = target->GetPositionX(); float ty = target->GetPositionY(); float tz = target->GetPositionZ(); + + float targetOrientation = target->GetOrientation(); + + float deltaAngle = Position::NormalizeOrientation(targetOrientation - target->GetAngle(bot)); + if (deltaAngle > M_PI) + deltaAngle -= 2.0f * M_PI; // -PI..PI + // if target is moving forward and moving far away, predict the position + bool behind = fabs(deltaAngle) > M_PI_2; + if (target->HasUnitMovementFlag(MOVEMENTFLAG_FORWARD) && behind) { + float predictDis = std::min(3.0f, target->GetObjectSize() * 2); + tx += cos(target->GetOrientation()) * predictDis; + ty += sin(target->GetOrientation()) * predictDis; + if (!target->GetMap()->CheckCollisionAndGetValidCoords(target, target->GetPositionX(), target->GetPositionY(), target->GetPositionZ(), + tx, ty, tz)) + { + tx = target->GetPositionX(); + ty = target->GetPositionY(); + tz = target->GetPositionZ(); + } + } float combatDistance = bot->GetCombatReach() + target->GetCombatReach(); distance += combatDistance; @@ -863,7 +883,7 @@ bool MovementAction::ReachCombatTo(Unit* target, float distance) // Avoid walking too far when moving towards each other float disToGo = bot->GetExactDist(tx, ty, tz) - distance; - if (disToGo >= 10.0f) + if (disToGo >= 6.0f) shortenTo = disToGo / 2 + distance; // if (bot->GetExactDist(tx, ty, tz) <= shortenTo) diff --git a/src/strategy/mage/FrostMageStrategy.cpp b/src/strategy/mage/FrostMageStrategy.cpp index 79214108..55d84ad0 100644 --- a/src/strategy/mage/FrostMageStrategy.cpp +++ b/src/strategy/mage/FrostMageStrategy.cpp @@ -79,7 +79,8 @@ void FrostMageStrategy::InitTriggers(std::vector& triggers) new TriggerNode("has pet", NextAction::array(0, new NextAction("toggle pet spell", ACTION_HIGH + 1), nullptr))); triggers.push_back( new TriggerNode("medium health", NextAction::array(0, new NextAction("ice barrier", ACTION_NORMAL), nullptr))); - + triggers.push_back( + new TriggerNode("being attacked", NextAction::array(0, new NextAction("ice barrier", ACTION_HIGH + 1), nullptr))); triggers.push_back(new TriggerNode( "brain freeze", NextAction::array(0, new NextAction("frostfire bolt", ACTION_NORMAL + 3), nullptr))); // Combo cast the last charge of fingers of frost for double crits. diff --git a/src/strategy/priest/GenericPriestStrategy.cpp b/src/strategy/priest/GenericPriestStrategy.cpp index 82ca65e9..ce924b5a 100644 --- a/src/strategy/priest/GenericPriestStrategy.cpp +++ b/src/strategy/priest/GenericPriestStrategy.cpp @@ -56,6 +56,8 @@ void GenericPriestStrategy::InitTriggers(std::vector& triggers) triggers.push_back(new TriggerNode("enemy too close for spell", NextAction::array(0, new NextAction("flee", ACTION_MOVE + 9), nullptr))); triggers.push_back(new TriggerNode("often", NextAction::array(0, new NextAction("apply oil", 1.0f), nullptr))); + triggers.push_back(new TriggerNode("being attacked", + NextAction::array(0, new NextAction("power word: shield", ACTION_HIGH + 1), nullptr))); } PriestCureStrategy::PriestCureStrategy(PlayerbotAI* botAI) : Strategy(botAI) diff --git a/src/strategy/values/DpsTargetValue.cpp b/src/strategy/values/DpsTargetValue.cpp index 526f2e42..c8555c08 100644 --- a/src/strategy/values/DpsTargetValue.cpp +++ b/src/strategy/values/DpsTargetValue.cpp @@ -135,11 +135,11 @@ protected: float targetExpectedLifeTime; }; -// non caster -class NonCasterFindTargetSmartStrategy : public FindTargetStrategy +// General +class GeneralFindTargetSmartStrategy : public FindTargetStrategy { public: - NonCasterFindTargetSmartStrategy(PlayerbotAI* botAI, float dps) + GeneralFindTargetSmartStrategy(PlayerbotAI* botAI, float dps) : FindTargetStrategy(botAI), dps_(dps), targetExpectedLifeTime(1000000) { } @@ -178,7 +178,6 @@ public: { float new_time = new_unit->GetHealth() / dps_; float old_time = old_unit->GetHealth() / dps_; - // [5-20] > (5-0] > (20-inf) int new_level = GetIntervalLevel(new_unit); int old_level = GetIntervalLevel(old_unit); if (new_level != old_level) @@ -297,20 +296,24 @@ Unit* DpsTargetValue::Calculate() if (rti) return rti; - // FindLeastHpTargetStrategy strategy(botAI); - Group* group = bot->GetGroup(); float dps = AI_VALUE(float, "estimated group dps"); - if (group && botAI->IsCaster(bot)) + + if (botAI->GetNearGroupMemberCount() > 3) { - CasterFindTargetSmartStrategy strategy(botAI, dps); - return TargetValue::FindTarget(&strategy); + if (botAI->IsCaster(bot)) + { + // Caster find target strategy avoids casting spells on enemies + // with too low health to ensure the effectiveness of casting + CasterFindTargetSmartStrategy strategy(botAI, dps); + return TargetValue::FindTarget(&strategy); + } + else if (botAI->IsCombo(bot)) + { + ComboFindTargetSmartStrategy strategy(botAI, dps); + return TargetValue::FindTarget(&strategy); + } } - else if (botAI->IsCombo(bot)) - { - ComboFindTargetSmartStrategy strategy(botAI, dps); - return TargetValue::FindTarget(&strategy); - } - NonCasterFindTargetSmartStrategy strategy(botAI, dps); + GeneralFindTargetSmartStrategy strategy(botAI, dps); return TargetValue::FindTarget(&strategy); } diff --git a/src/strategy/values/EstimatedLifetimeValue.cpp b/src/strategy/values/EstimatedLifetimeValue.cpp index 1c19e310..dd792071 100644 --- a/src/strategy/values/EstimatedLifetimeValue.cpp +++ b/src/strategy/values/EstimatedLifetimeValue.cpp @@ -36,6 +36,10 @@ float EstimatedGroupDpsValue::Calculate() if (member == bot) // calculated continue; + // ignore real player as they may not help with damage + if (!GET_PLAYERBOT_AI(member) || GET_PLAYERBOT_AI(member)->IsRealPlayer()) + continue; + if (!member || !member->IsInWorld() || !member->IsAlive()) continue;