From 9f61db5d69f586ff7da4209b4ba8532b700f3dfe Mon Sep 17 00:00:00 2001 From: antony Date: Thu, 29 Aug 2024 11:40:38 +0200 Subject: [PATCH 01/47] Fix double squar --- src/strategy/actions/AcceptQuestAction.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/strategy/actions/AcceptQuestAction.cpp b/src/strategy/actions/AcceptQuestAction.cpp index fcbd2438..259e31db 100644 --- a/src/strategy/actions/AcceptQuestAction.cpp +++ b/src/strategy/actions/AcceptQuestAction.cpp @@ -51,7 +51,7 @@ bool AcceptQuestAction::Execute(Event event) guid = unit->GetGUID().GetRawValue(); break; } - if (unit && text == "*" && sqrt(bot->GetDistance(unit)) <= INTERACTION_DISTANCE) + if (unit && text == "*" && bot->GetDistance(unit) <= INTERACTION_DISTANCE) hasAccept |= QuestAction::ProcessQuests(unit); } GuidVector gos = AI_VALUE(GuidVector, "nearest game objects no los"); @@ -63,7 +63,7 @@ bool AcceptQuestAction::Execute(Event event) guid = go->GetGUID().GetRawValue(); break; } - if (go && text == "*" && sqrt(bot->GetDistance(go)) <= INTERACTION_DISTANCE) + if (go && text == "*" && bot->GetDistance(go) <= INTERACTION_DISTANCE) hasAccept |= QuestAction::ProcessQuests(go); } } From 9dde64003bb55e614787de5cfa524a0822ce1d45 Mon Sep 17 00:00:00 2001 From: Yunfan Li Date: Sat, 31 Aug 2024 00:05:38 +0800 Subject: [PATCH 02/47] Enable macos CI --- .github/workflows/macos_build.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/macos_build.yml b/.github/workflows/macos_build.yml index a0827729..5ea55963 100644 --- a/.github/workflows/macos_build.yml +++ b/.github/workflows/macos_build.yml @@ -1,9 +1,9 @@ name: macos-build on: push: - branches: [never-match-this-branch] + branches: [ "master" ] pull_request: - branches: [never-match-this-branch] + branches: [ "master" ] # concurrency: # group: ${{ github.head_ref }} || concat(${{ github.ref }}, ${{ github.workflow }}) From f48bf2351a889766064fe293f524bf6f38f2d618 Mon Sep 17 00:00:00 2001 From: Yunfan Li Date: Sat, 31 Aug 2024 17:23:13 +0800 Subject: [PATCH 03/47] Test items filter --- src/RandomItemMgr.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/RandomItemMgr.cpp b/src/RandomItemMgr.cpp index 2d9f3d5b..fdde9c95 100644 --- a/src/RandomItemMgr.cpp +++ b/src/RandomItemMgr.cpp @@ -972,7 +972,8 @@ void RandomItemMgr::BuildItemInfoCache() strstr(proto->Name1.c_str(), " TEST") || strstr(proto->Name1.c_str(), "2200 ") || strstr(proto->Name1.c_str(), "Deprecated ") || strstr(proto->Name1.c_str(), "Unused ") || strstr(proto->Name1.c_str(), "Monster ") || strstr(proto->Name1.c_str(), "[PH]") || - strstr(proto->Name1.c_str(), "(OLD)") || strstr(proto->Name1.c_str(), "QR")) + strstr(proto->Name1.c_str(), "(OLD)") || strstr(proto->Name1.c_str(), "QR") || + strstr(proto->Name1.c_str(), "zzOLD")) { itemForTest.insert(proto->ItemId); continue; From f804ba2d65ed5828e1dd99bfe291ac936eee17e4 Mon Sep 17 00:00:00 2001 From: Yunfan Li Date: Sat, 31 Aug 2024 19:58:28 +0800 Subject: [PATCH 04/47] Speed up addclass with pre calculation --- conf/playerbots.conf.dist | 7 +++- src/PlayerbotMgr.cpp | 44 ++++++---------------- src/RandomPlayerbotMgr.cpp | 75 ++++++++++++++++++++------------------ src/RandomPlayerbotMgr.h | 4 ++ 4 files changed, 61 insertions(+), 69 deletions(-) diff --git a/conf/playerbots.conf.dist b/conf/playerbots.conf.dist index 57f5548d..c6c0dfe1 100644 --- a/conf/playerbots.conf.dist +++ b/conf/playerbots.conf.dist @@ -80,6 +80,7 @@ AiPlayerbot.Enabled = 1 AiPlayerbot.RandomBotAutologin = 1 # Random bot account +# Please ensure that RandomBotAccountCount is greater than (MaxRandomBots / 10 + AddClassAccountPoolSize) AiPlayerbot.RandomBotAccountCount = 200 # Random bot count @@ -108,12 +109,16 @@ AiPlayerbot.DeleteRandomBotAccounts = 0 AiPlayerbot.MaxAddedBots = 40 # Maximum number of bots per class added by one account -AiPlayerbot.MaxAddedBotsPerClass = 10 +AiPlayerbot.MaxAddedBotsPerClass = 40 # Enable/Disable create bot by addclass command (0 = GM only, 1 = enable) # default: 1 (enable) AiPlayerbot.AddClassCommand = 1 +# Set the addclass command account pool size +# Addclass command uses a subset of accounts from RandomBotAccountCount +AiPlayerbot.AddClassAccountPoolSize = 50 + # Bot group invitation permission level (0 = GM only, 1 = accept based on level, 2 = always accept) # default: 1 (accept based on level) AiPlayerbot.GroupInvitationPermission = 1 diff --git a/src/PlayerbotMgr.cpp b/src/PlayerbotMgr.cpp index 46f884f8..1958f9aa 100644 --- a/src/PlayerbotMgr.cpp +++ b/src/PlayerbotMgr.cpp @@ -18,12 +18,14 @@ #include "Group.h" #include "GroupMgr.h" #include "ObjectAccessor.h" +#include "ObjectGuid.h" #include "ObjectMgr.h" #include "PlayerbotAIConfig.h" #include "PlayerbotDbStore.h" #include "PlayerbotFactory.h" #include "PlayerbotSecurity.h" #include "Playerbots.h" +#include "RandomPlayerbotMgr.h" #include "SharedDefines.h" #include "WorldSession.h" #include "ChannelMgr.h" @@ -1003,44 +1005,20 @@ std::vector PlayerbotHolder::HandlePlayerbotCommand(char const* arg messages.push_back("Error: Invalid Class. Try again."); return messages; } - uint8 master_race = master->getRace(); - std::string race_limit; - switch (master_race) + uint8 teamId = master->GetTeamId(true); + std::vector &guidCache = sRandomPlayerbotMgr->addclassCache[RandomPlayerbotMgr::GetTeamClassIdx(teamId == TEAM_ALLIANCE, claz)]; + for (size_t i = 0; i < guidCache.size(); i++) { - case 1: - case 3: - case 4: - case 7: - case 11: - race_limit = "1, 3, 4, 7, 11"; - break; - case 2: - case 5: - case 6: - case 8: - case 10: - race_limit = "2, 5, 6, 8, 10"; - break; - } - uint32 maxAccountId = sPlayerbotAIConfig->randomBotAccounts.back(); - // find a bot fit conditions and not in any guild - QueryResult results = CharacterDatabase.Query( - "SELECT guid FROM characters " - "WHERE name IN (SELECT name FROM playerbots_names) AND class = '{}' AND online = 0 AND race IN ({}) AND " - "guid NOT IN ( SELECT guid FROM guild_member ) " - "AND account <= {} " - "ORDER BY account DESC LIMIT 1", - claz, race_limit, maxAccountId); - if (results) - { - Field* fields = results->Fetch(); - ObjectGuid guid = ObjectGuid(HighGuid::Player, fields[0].Get()); + ObjectGuid guid = guidCache[i]; + if (botLoading.find(guid) != botLoading.end()) + continue; + if (ObjectAccessor::FindConnectedPlayer(guid)) + continue; AddPlayerBot(guid, master->GetSession()->GetAccountId()); - messages.push_back("Add class " + std::string(charname)); return messages; } - messages.push_back("Add class failed."); + messages.push_back("Add class failed, no available characters!"); return messages; } diff --git a/src/RandomPlayerbotMgr.cpp b/src/RandomPlayerbotMgr.cpp index 80f090ce..10f2bfe5 100644 --- a/src/RandomPlayerbotMgr.cpp +++ b/src/RandomPlayerbotMgr.cpp @@ -162,6 +162,8 @@ RandomPlayerbotMgr::RandomPlayerbotMgr() : PlayerbotHolder(), processTicks(0) { sPlayerbotCommandServer->Start(); PrepareTeleportCache(); + if (sPlayerbotAIConfig->addClassCommand) + PrepareAddclassCache(); } BattlegroundData.clear(); @@ -1473,40 +1475,44 @@ void RandomPlayerbotMgr::PrepareTeleportCache() } while (results->NextRow()); } LOG_INFO("playerbots", "{} banker locations for level collected.", collected_locs); +} - // temporary only use locsPerLevelCache, so disable rpgLocsCacheLevel cache - - // LOG_INFO("playerbots", "Preparing RPG teleport caches for {} factions...", sFactionTemplateStore.GetNumRows()); - // results = WorldDatabase.Query("SELECT map, position_x, position_y, position_z, r.race, r.minl, r.maxl FROM - // creature c INNER JOIN playerbots_rpg_races r ON c.id1 = r.entry " - // "WHERE r.race < 15"); - // if (results) - // { - // do - // { - // Field* fields = results->Fetch(); - // uint16 mapId = fields[0].Get(); - // float x = fields[1].Get(); - // float y = fields[2].Get(); - // float z = fields[3].Get(); - // uint32 race = fields[4].Get(); - // uint32 minl = fields[5].Get(); - // uint32 maxl = fields[6].Get(); - - // for (uint32 level = 1; level < sPlayerbotAIConfig->randomBotMaxLevel + 1; level++) - // { - // if (level > maxl || level < minl) - // continue; - - // WorldLocation loc(mapId, x, y, z, 0); - // for (uint32 r = 1; r < MAX_RACES; r++) - // { - // if (race == r || race == 0) - // rpgLocsCacheLevel[r][level].push_back(loc); - // } - // } - // } while (results->NextRow()); - // } +void RandomPlayerbotMgr::PrepareAddclassCache() +{ + int32 maxAccountId = sPlayerbotAIConfig->randomBotAccounts.back(); + int32 minIdx = + sPlayerbotAIConfig->randomBotAccounts.size() - 1 >= sPlayerbotAIConfig->addClassAccountPoolSize + ? sPlayerbotAIConfig->randomBotAccounts.size() - sPlayerbotAIConfig->addClassAccountPoolSize : 0; + int32 minAccountId = sPlayerbotAIConfig->randomBotAccounts[minIdx]; + if (minAccountId < 0) + { + LOG_ERROR("playerbots", "No available account for add class!"); + } + int32 collected = 0; + for (uint8 claz = CLASS_WARRIOR; claz <= CLASS_DRUID; claz++) + { + if (claz == 10) + continue; + QueryResult results = CharacterDatabase.Query( + "SELECT guid, race FROM characters " + "WHERE account >= {} AND account <= {} AND class = '{}' AND online = 0 AND " + "guid NOT IN ( SELECT guid FROM guild_member ) " + "ORDER BY account DESC", + minAccountId, maxAccountId, claz); + if (results) + { + do + { + Field* fields = results->Fetch(); + ObjectGuid guid = ObjectGuid(HighGuid::Player, fields[0].Get()); + uint32 race = fields[1].Get(); + bool isAlliance = race == 1 || race == 3 || race == 4 || race == 7 || race == 11; + addclassCache[GetTeamClassIdx(isAlliance, claz)].push_back(guid); + collected++; + } while (results->NextRow()); + } + } + LOG_INFO("playerbots", "{} characters collected for addclass command.", collected); } void RandomPlayerbotMgr::RandomTeleportForLevel(Player* bot) @@ -2395,7 +2401,7 @@ void RandomPlayerbotMgr::PrintStats() ++engine_combat; else ++engine_dead; - + uint8 spec = AiFactory::GetPlayerSpecTab(bot); switch (bot->getClass()) { @@ -2779,4 +2785,3 @@ ObjectGuid const RandomPlayerbotMgr::GetBattleMasterGUID(Player* bot, Battlegrou return battleMasterGUID; } - diff --git a/src/RandomPlayerbotMgr.h b/src/RandomPlayerbotMgr.h index f1e80ab6..2105477c 100644 --- a/src/RandomPlayerbotMgr.h +++ b/src/RandomPlayerbotMgr.h @@ -6,6 +6,7 @@ #ifndef _PLAYERBOT_RANDOMPLAYERBOTMGR_H #define _PLAYERBOT_RANDOMPLAYERBOTMGR_H +#include "ObjectGuid.h" #include "PlayerbotMgr.h" struct BattlegroundInfo @@ -166,7 +167,9 @@ public: float getActivityMod() { return activityMod; } float getActivityPercentage() { return activityMod * 100.0f; } void setActivityPercentage(float percentage) { activityMod = percentage / 100.0f; } + static uint8 GetTeamClassIdx(bool isAlliance, uint8 claz) { return isAlliance * 20 + claz; } + std::map> addclassCache; protected: void OnBotLoginInternal(Player* const bot) override; @@ -190,6 +193,7 @@ private: void RandomTeleport(Player* bot, std::vector& locs, bool hearth = false); uint32 GetZoneLevel(uint16 mapId, float teleX, float teleY, float teleZ); void PrepareTeleportCache(); + void PrepareAddclassCache(); typedef void (RandomPlayerbotMgr::*ConsoleCommandHandler)(Player*); std::vector players; From e3e0bdde97fc3f03943412d868bb876678f4441a Mon Sep 17 00:00:00 2001 From: Yunfan Li Date: Sat, 31 Aug 2024 20:06:26 +0800 Subject: [PATCH 05/47] Add PlayerbotAIConfig --- src/PlayerbotAIConfig.cpp | 1 + src/PlayerbotAIConfig.h | 1 + 2 files changed, 2 insertions(+) diff --git a/src/PlayerbotAIConfig.cpp b/src/PlayerbotAIConfig.cpp index 41c0360c..1267591e 100644 --- a/src/PlayerbotAIConfig.cpp +++ b/src/PlayerbotAIConfig.cpp @@ -420,6 +420,7 @@ bool PlayerbotAIConfig::Initialize() maxAddedBots = sConfigMgr->GetOption("AiPlayerbot.MaxAddedBots", 40); maxAddedBotsPerClass = sConfigMgr->GetOption("AiPlayerbot.MaxAddedBotsPerClass", 10); addClassCommand = sConfigMgr->GetOption("AiPlayerbot.AddClassCommand", 1); + addClassAccountPoolSize = sConfigMgr->GetOption("AiPlayerbot.AddClassAccountPoolSize", 50); maintenanceCommand = sConfigMgr->GetOption("AiPlayerbot.MaintenanceCommand", 1); autoGearCommand = sConfigMgr->GetOption("AiPlayerbot.AutoGearCommand", 1); autoGearQualityLimit = sConfigMgr->GetOption("AiPlayerbot.AutoGearQualityLimit", 3); diff --git a/src/PlayerbotAIConfig.h b/src/PlayerbotAIConfig.h index 21dbee67..70536935 100644 --- a/src/PlayerbotAIConfig.h +++ b/src/PlayerbotAIConfig.h @@ -306,6 +306,7 @@ public: float autoInitEquipLevelLimitRatio; int32 maxAddedBots, maxAddedBotsPerClass; int32 addClassCommand; + int32 addClassAccountPoolSize; int32 maintenanceCommand; int32 autoGearCommand, autoGearQualityLimit, autoGearScoreLimit; From a63fbb3b5f7c86348c7201e277af6860e5cf30b5 Mon Sep 17 00:00:00 2001 From: Yunfan Li Date: Sat, 31 Aug 2024 23:16:14 +0800 Subject: [PATCH 06/47] Improve druid cat spec --- conf/playerbots.conf.dist | 9 ++-- src/AiFactory.cpp | 1 - src/PlayerbotAI.cpp | 8 ++- src/PlayerbotAIConfig.cpp | 13 +++-- src/factory/PlayerbotFactory.cpp | 45 +++++++++++----- src/strategy/druid/CatDpsDruidStrategy.cpp | 53 +++++++++++-------- src/strategy/druid/DruidAiObjectContext.cpp | 8 +++ src/strategy/druid/DruidCatActions.cpp | 6 +++ src/strategy/druid/DruidCatActions.h | 24 +++++++-- src/strategy/druid/DruidTriggers.h | 16 +++++- src/strategy/druid/FeralDruidStrategy.cpp | 2 + .../values/AttackerWithoutAuraTargetValue.cpp | 33 ++++++++++++ .../values/AttackerWithoutAuraTargetValue.h | 4 +- src/strategy/values/ExpectedLifetimeValue.cpp | 3 ++ 14 files changed, 174 insertions(+), 51 deletions(-) create mode 100644 src/strategy/druid/DruidCatActions.cpp diff --git a/conf/playerbots.conf.dist b/conf/playerbots.conf.dist index c6c0dfe1..9954da5e 100644 --- a/conf/playerbots.conf.dist +++ b/conf/playerbots.conf.dist @@ -996,7 +996,7 @@ AiPlayerbot.PremadeSpecLink.11.2.60 = --230033312031501531050013051 AiPlayerbot.PremadeSpecLink.11.2.80 = 05320001--230033312031512531153313051 AiPlayerbot.PremadeSpecName.11.3 = cat pve AiPlayerbot.PremadeSpecGlyph.11.3 = 40902,43331,40901,43335,44922,45604 -AiPlayerbot.PremadeSpecLink.11.3.60 = -553202032322010052100030310501 +AiPlayerbot.PremadeSpecLink.11.3.60 = -552202032322010053100030310501 AiPlayerbot.PremadeSpecLink.11.3.80 = -553202032322010053100030310511-205503012 # @@ -1183,11 +1183,12 @@ AiPlayerbot.RandomClassSpecIndex.9.2 = 2 AiPlayerbot.RandomClassSpecProb.11.0 = 20 AiPlayerbot.RandomClassSpecIndex.11.0 = 0 -AiPlayerbot.RandomClassSpecProb.11.1 = 40 +AiPlayerbot.RandomClassSpecProb.11.1 = 30 AiPlayerbot.RandomClassSpecIndex.11.1 = 1 -AiPlayerbot.RandomClassSpecProb.11.2 = 40 +AiPlayerbot.RandomClassSpecProb.11.2 = 30 AiPlayerbot.RandomClassSpecIndex.11.2 = 2 - +AiPlayerbot.RandomClassSpecProb.11.3 = 20 +AiPlayerbot.RandomClassSpecIndex.11.3 = 3 # # # diff --git a/src/AiFactory.cpp b/src/AiFactory.cpp index 05f2daec..75787511 100644 --- a/src/AiFactory.cpp +++ b/src/AiFactory.cpp @@ -71,7 +71,6 @@ uint8 AiFactory::GetPlayerSpecTab(Player* bot) max = tabs[i]; } } - return tab; } else diff --git a/src/PlayerbotAI.cpp b/src/PlayerbotAI.cpp index 07b07293..27214fc5 100644 --- a/src/PlayerbotAI.cpp +++ b/src/PlayerbotAI.cpp @@ -1998,6 +1998,10 @@ bool PlayerbotAI::IsDps(Player* player) { return true; } + if (tab == DRUID_TAB_FERAL && !IsTank(player)) + { + return true; + } break; case CLASS_SHAMAN: if (tab != SHAMAN_TAB_RESTORATION) @@ -2792,8 +2796,8 @@ bool PlayerbotAI::CanCastSpell(uint32 spellid, Unit* target, bool checkHasSpell, } uint32 CastingTime = !spellInfo->IsChanneled() ? spellInfo->CalcCastTime(bot) : spellInfo->GetDuration(); - bool interruptOnMove = spellInfo->InterruptFlags & SPELL_INTERRUPT_FLAG_MOVEMENT; - if ((CastingTime || interruptOnMove) && bot->isMoving()) + // bool interruptOnMove = spellInfo->InterruptFlags & SPELL_INTERRUPT_FLAG_MOVEMENT; + if ((CastingTime || spellInfo->IsAutoRepeatRangedSpell()) && bot->isMoving()) { if (!sPlayerbotAIConfig->logInGroupOnly || (bot->GetGroup() && HasRealPlayerMaster())) { diff --git a/src/PlayerbotAIConfig.cpp b/src/PlayerbotAIConfig.cpp index 1267591e..d6844aaf 100644 --- a/src/PlayerbotAIConfig.cpp +++ b/src/PlayerbotAIConfig.cpp @@ -329,15 +329,22 @@ bool PlayerbotAIConfig::Initialize() parsedSpecLinkOrder[cls][spec][level] = ParseTempTalentsOrder(cls, premadeSpecLink[cls][spec][level]); } } - for (uint32 spec = 0; spec < 3; ++spec) + for (uint32 spec = 0; spec < MAX_SPECNO; ++spec) { std::ostringstream os; os << "AiPlayerbot.RandomClassSpecProb." << cls << "." << spec; - randomClassSpecProb[cls][spec] = sConfigMgr->GetOption(os.str().c_str(), 33); + uint32 def; + if (spec <= 1) + def = 33; + else if (spec == 2) + def = 34; + else + def = 0; + randomClassSpecProb[cls][spec] = sConfigMgr->GetOption(os.str().c_str(), def, false); os.str(""); os.clear(); os << "AiPlayerbot.RandomClassSpecIndex." << cls << "." << spec; - randomClassSpecIndex[cls][spec] = sConfigMgr->GetOption(os.str().c_str(), spec + 1); + randomClassSpecIndex[cls][spec] = sConfigMgr->GetOption(os.str().c_str(), spec, false); } } diff --git a/src/factory/PlayerbotFactory.cpp b/src/factory/PlayerbotFactory.cpp index 511d92a0..be60bf1b 100644 --- a/src/factory/PlayerbotFactory.cpp +++ b/src/factory/PlayerbotFactory.cpp @@ -878,14 +878,31 @@ void PlayerbotFactory::InitTalentsTree(bool increment /*false*/, bool use_templa uint32 total_tabs = tabs[0] + tabs[1] + tabs[2]; if (increment && total_tabs != 0) { + /// @todo: match current talent with template specTab = AiFactory::GetPlayerSpecTab(bot); - } + /// @todo: fix cat druid hardcode + if (bot->getClass() == CLASS_DRUID && specTab == DRUID_TAB_FERAL && bot->GetLevel() >= 20 && PlayerbotAI::IsDps(bot)) + specTab = 3; + } else { - uint32 point = urand(0, 100); - uint32 p1 = sPlayerbotAIConfig->randomClassSpecProb[cls][0]; - uint32 p2 = p1 + sPlayerbotAIConfig->randomClassSpecProb[cls][1]; - specTab = point < p1 ? 0 : (point < p2 ? 1 : 2); + uint32 point = urand(1, 100); + uint32 currentP = 0; + int i; + for (i = 0; i < MAX_SPECNO; i++) + { + currentP += sPlayerbotAIConfig->randomClassSpecProb[cls][i]; + if (point <= currentP) + { + specTab = i; + break; + } + } + if (i == MAX_SPECNO) + { + specTab = 0; + LOG_ERROR("playerbots", "Fail to select spec num for bot {}! Set to 0.", bot->GetName()); + } } if (reset) { @@ -896,12 +913,13 @@ void PlayerbotFactory::InitTalentsTree(bool increment /*false*/, bool use_templa { InitTalentsByTemplate(specTab); } - else - { - InitTalents(specTab); - if (bot->GetFreeTalentPoints()) - InitTalents((specTab + 1) % 3); - } + // always use template now + // else + // { + // InitTalents(specTab); + // if (bot->GetFreeTalentPoints()) + // InitTalents((specTab + 1) % 3); + // } bot->SendTalentsInfoData(false); } @@ -1433,7 +1451,7 @@ void Shuffle(std::vector& items) void PlayerbotFactory::InitEquipment(bool incremental) { std::unordered_map> items; - int tab = AiFactory::GetPlayerSpecTab(bot); + // int tab = AiFactory::GetPlayerSpecTab(bot); uint32 blevel = bot->GetLevel(); int32 delta = 2; @@ -3062,6 +3080,9 @@ void PlayerbotFactory::InitGlyphs(bool increment) uint8 cls = bot->getClass(); uint8 tab = AiFactory::GetPlayerSpecTab(bot); + /// @todo: fix cat druid hardcode + if (bot->getClass() == CLASS_DRUID && tab == DRUID_TAB_FERAL && PlayerbotAI::IsDps(bot)) + tab = 3; std::list glyphs; ItemTemplateContainer const* itemTemplates = sObjectMgr->GetItemTemplateStore(); for (ItemTemplateContainer::const_iterator i = itemTemplates->begin(); i != itemTemplates->end(); ++i) diff --git a/src/strategy/druid/CatDpsDruidStrategy.cpp b/src/strategy/druid/CatDpsDruidStrategy.cpp index 80120330..bb860b02 100644 --- a/src/strategy/druid/CatDpsDruidStrategy.cpp +++ b/src/strategy/druid/CatDpsDruidStrategy.cpp @@ -70,7 +70,7 @@ private: { return new ActionNode("mangle (cat)", /*P*/ nullptr, - /*A*/ NextAction::array(0, new NextAction("claw"), nullptr), + /*A*/ nullptr, /*C*/ nullptr); } @@ -122,38 +122,49 @@ CatDpsDruidStrategy::CatDpsDruidStrategy(PlayerbotAI* botAI) : FeralDruidStrateg NextAction** CatDpsDruidStrategy::getDefaultActions() { - return NextAction::array(0, new NextAction("mangle (cat)", ACTION_DEFAULT + 0.1f), nullptr); + return NextAction::array(0, new NextAction("mangle (cat)", ACTION_DEFAULT + 0.3f), + new NextAction("shred", ACTION_DEFAULT + 0.2f), nullptr); } void CatDpsDruidStrategy::InitTriggers(std::vector& triggers) { FeralDruidStrategy::InitTriggers(triggers); + // Default priority + triggers.push_back(new TriggerNode("high energy available", + NextAction::array(0, new NextAction("claw", ACTION_DEFAULT + 0.1f), nullptr))); triggers.push_back( - new TriggerNode("cat form", NextAction::array(0, new NextAction("cat form", ACTION_HIGH + 2), nullptr))); + new TriggerNode("faerie fire (feral)", + NextAction::array(0, new NextAction("faerie fire (feral)", ACTION_DEFAULT + 0.0f), nullptr))); + + // Main spell triggers.push_back( - new TriggerNode("rake", NextAction::array(0, new NextAction("rake", ACTION_NORMAL + 5), nullptr))); + new TriggerNode("cat form", NextAction::array(0, new NextAction("cat form", ACTION_HIGH + 8), nullptr))); + + triggers.push_back(new TriggerNode("tiger's fury", + NextAction::array(0, new NextAction("tiger's fury", ACTION_HIGH + 7), nullptr))); + triggers.push_back( + new TriggerNode("savage roar", NextAction::array(0, new NextAction("savage roar", ACTION_HIGH + 5), nullptr))); + triggers.push_back(new TriggerNode("combo points available", + NextAction::array(0, new NextAction("rip", ACTION_HIGH + 4), nullptr))); triggers.push_back(new TriggerNode( - "combo points available", NextAction::array(0, new NextAction("ferocious bite", ACTION_NORMAL + 9), nullptr))); + "combo points available", NextAction::array(0, new NextAction("ferocious bite", ACTION_HIGH + 3), nullptr))); + triggers.push_back(new TriggerNode("target with combo points almost dead", + NextAction::array(0, new NextAction("ferocious bite", ACTION_HIGH + 2), nullptr))); + triggers.push_back(new TriggerNode("rake", NextAction::array(0, new NextAction("rake", ACTION_HIGH + 2), nullptr))); triggers.push_back( - new TriggerNode("medium threat", NextAction::array(0, new NextAction("cower", ACTION_EMERGENCY + 1), nullptr))); + new TriggerNode("medium threat", NextAction::array(0, new NextAction("cower", ACTION_HIGH + 1), nullptr))); + + // AOE + triggers.push_back( + new TriggerNode("medium aoe", NextAction::array(0, new NextAction("swipe (cat)", ACTION_HIGH + 3), nullptr))); triggers.push_back(new TriggerNode( - "faerie fire (feral)", NextAction::array(0, new NextAction("faerie fire (feral)", ACTION_HIGH), nullptr))); + "light aoe", NextAction::array(0, new NextAction("rake on attacker", ACTION_HIGH + 2), nullptr))); + // Reach target triggers.push_back(new TriggerNode( - "tiger's fury", NextAction::array(0, new NextAction("tiger's fury", ACTION_EMERGENCY + 1), nullptr))); + "enemy out of melee", NextAction::array(0, new NextAction("feral charge - cat", ACTION_HIGH + 9), nullptr))); triggers.push_back( - new TriggerNode("behind target", NextAction::array(0, new NextAction("pounce", ACTION_HIGH + 1), nullptr))); - // triggers.push_back(new TriggerNode("player has no flag", NextAction::array(0, new NextAction("prowl", - // ACTION_HIGH), nullptr))); triggers.push_back(new TriggerNode("enemy out of melee", NextAction::array(0, new - // NextAction("prowl", ACTION_INTERRUPT + 1), nullptr))); - triggers.push_back( - new TriggerNode("player has flag", NextAction::array(0, new NextAction("dash", ACTION_EMERGENCY), nullptr))); - triggers.push_back(new TriggerNode("enemy flagcarrier near", - NextAction::array(0, new NextAction("dash", ACTION_EMERGENCY), nullptr))); + new TriggerNode("enemy out of melee", NextAction::array(0, new NextAction("dash", ACTION_HIGH + 8), nullptr))); } -void CatAoeDruidStrategy::InitTriggers(std::vector& triggers) -{ - triggers.push_back( - new TriggerNode("medium aoe", NextAction::array(0, new NextAction("swipe (cat)", ACTION_HIGH + 2), nullptr))); -} +void CatAoeDruidStrategy::InitTriggers(std::vector& triggers) {} diff --git a/src/strategy/druid/DruidAiObjectContext.cpp b/src/strategy/druid/DruidAiObjectContext.cpp index b1181ae2..61e66e95 100644 --- a/src/strategy/druid/DruidAiObjectContext.cpp +++ b/src/strategy/druid/DruidAiObjectContext.cpp @@ -83,6 +83,8 @@ public: creators["moonfire"] = &DruidTriggerFactoryInternal::moonfire; creators["nature's grasp"] = &DruidTriggerFactoryInternal::natures_grasp; creators["tiger's fury"] = &DruidTriggerFactoryInternal::tigers_fury; + creators["berserk"] = &DruidTriggerFactoryInternal::berserk; + creators["savage roar"] = &DruidTriggerFactoryInternal::savage_roar; creators["rake"] = &DruidTriggerFactoryInternal::rake; creators["mark of the wild"] = &DruidTriggerFactoryInternal::mark_of_the_wild; creators["mark of the wild on party"] = &DruidTriggerFactoryInternal::mark_of_the_wild_on_party; @@ -117,6 +119,8 @@ private: static Trigger* faerie_fire(PlayerbotAI* botAI) { return new FaerieFireTrigger(botAI); } static Trigger* natures_grasp(PlayerbotAI* botAI) { return new NaturesGraspTrigger(botAI); } static Trigger* tigers_fury(PlayerbotAI* botAI) { return new TigersFuryTrigger(botAI); } + static Trigger* berserk(PlayerbotAI* botAI) { return new BerserkTrigger(botAI); } + static Trigger* savage_roar(PlayerbotAI* botAI) { return new SavageRoarTrigger(botAI); } static Trigger* rake(PlayerbotAI* botAI) { return new RakeTrigger(botAI); } static Trigger* mark_of_the_wild(PlayerbotAI* botAI) { return new MarkOfTheWildTrigger(botAI); } static Trigger* mark_of_the_wild_on_party(PlayerbotAI* botAI) { return new MarkOfTheWildOnPartyTrigger(botAI); } @@ -174,6 +178,7 @@ public: creators["mangle (cat)"] = &DruidAiObjectContextInternal::mangle_cat; creators["swipe (cat)"] = &DruidAiObjectContextInternal::swipe_cat; creators["rake"] = &DruidAiObjectContextInternal::rake; + creators["rake on attacker"] = &DruidAiObjectContextInternal::rake_on_attacker; creators["ferocious bite"] = &DruidAiObjectContextInternal::ferocious_bite; creators["rip"] = &DruidAiObjectContextInternal::rip; creators["cower"] = &DruidAiObjectContextInternal::cower; @@ -188,6 +193,7 @@ public: creators["abolish poison on party"] = &DruidAiObjectContextInternal::abolish_poison_on_party; creators["berserk"] = &DruidAiObjectContextInternal::berserk; creators["tiger's fury"] = &DruidAiObjectContextInternal::tigers_fury; + creators["savage roar"] = &DruidAiObjectContextInternal::savage_roar; creators["mark of the wild"] = &DruidAiObjectContextInternal::mark_of_the_wild; creators["mark of the wild on party"] = &DruidAiObjectContextInternal::mark_of_the_wild_on_party; creators["regrowth"] = &DruidAiObjectContextInternal::regrowth; @@ -257,6 +263,7 @@ private: static Action* mangle_cat(PlayerbotAI* botAI) { return new CastMangleCatAction(botAI); } static Action* swipe_cat(PlayerbotAI* botAI) { return new CastSwipeCatAction(botAI); } static Action* rake(PlayerbotAI* botAI) { return new CastRakeAction(botAI); } + static Action* rake_on_attacker(PlayerbotAI* botAI) { return new CastRakeOnMeleeAttackersAction(botAI); } static Action* ferocious_bite(PlayerbotAI* botAI) { return new CastFerociousBiteAction(botAI); } static Action* rip(PlayerbotAI* botAI) { return new CastRipAction(botAI); } static Action* cower(PlayerbotAI* botAI) { return new CastCowerAction(botAI); } @@ -271,6 +278,7 @@ private: static Action* abolish_poison_on_party(PlayerbotAI* botAI) { return new CastAbolishPoisonOnPartyAction(botAI); } static Action* berserk(PlayerbotAI* botAI) { return new CastBerserkAction(botAI); } static Action* tigers_fury(PlayerbotAI* botAI) { return new CastTigersFuryAction(botAI); } + static Action* savage_roar(PlayerbotAI* botAI) { return new CastSavageRoarAction(botAI); } static Action* mark_of_the_wild(PlayerbotAI* botAI) { return new CastMarkOfTheWildAction(botAI); } static Action* mark_of_the_wild_on_party(PlayerbotAI* botAI) { return new CastMarkOfTheWildOnPartyAction(botAI); } static Action* regrowth(PlayerbotAI* botAI) { return new CastRegrowthAction(botAI); } diff --git a/src/strategy/druid/DruidCatActions.cpp b/src/strategy/druid/DruidCatActions.cpp new file mode 100644 index 00000000..fb3a0e88 --- /dev/null +++ b/src/strategy/druid/DruidCatActions.cpp @@ -0,0 +1,6 @@ +#include "DruidCatActions.h" + +bool CastMangleCatAction::isUseful() +{ + return CastMeleeDebuffSpellAction::isUseful() && !botAI->HasAura("mangle (bear)", GetTarget(), false, isOwner); +} \ No newline at end of file diff --git a/src/strategy/druid/DruidCatActions.h b/src/strategy/druid/DruidCatActions.h index 579bdd00..8adddf98 100644 --- a/src/strategy/druid/DruidCatActions.h +++ b/src/strategy/druid/DruidCatActions.h @@ -35,10 +35,23 @@ public: CastTigersFuryAction(PlayerbotAI* botAI) : CastBuffSpellAction(botAI, "tiger's fury") {} }; +class CastSavageRoarAction : public CastBuffSpellAction +{ +public: + CastSavageRoarAction(PlayerbotAI* botAI) : CastBuffSpellAction(botAI, "savage roar") {} + std::string const GetTargetName() override { return "current target"; } +}; + class CastRakeAction : public CastDebuffSpellAction { public: - CastRakeAction(PlayerbotAI* botAI) : CastDebuffSpellAction(botAI, "rake") {} + CastRakeAction(PlayerbotAI* botAI) : CastDebuffSpellAction(botAI, "rake", true, 6.0f) {} +}; + +class CastRakeOnMeleeAttackersAction : public CastDebuffSpellOnMeleeAttackerAction +{ +public: + CastRakeOnMeleeAttackersAction(PlayerbotAI* botAI) : CastDebuffSpellOnMeleeAttackerAction(botAI, "rake", true, 6.0f) {} }; class CastClawAction : public CastMeleeSpellAction @@ -47,10 +60,11 @@ public: CastClawAction(PlayerbotAI* botAI) : CastMeleeSpellAction(botAI, "claw") {} }; -class CastMangleCatAction : public CastMeleeSpellAction +class CastMangleCatAction : public CastMeleeDebuffSpellAction { public: - CastMangleCatAction(PlayerbotAI* botAI) : CastMeleeSpellAction(botAI, "mangle (cat)") {} + CastMangleCatAction(PlayerbotAI* botAI) : CastMeleeDebuffSpellAction(botAI, "mangle (cat)", false, 0.0f) {} + bool isUseful() override; }; class CastSwipeCatAction : public CastMeleeSpellAction @@ -65,10 +79,10 @@ public: CastFerociousBiteAction(PlayerbotAI* botAI) : CastMeleeSpellAction(botAI, "ferocious bite") {} }; -class CastRipAction : public CastMeleeSpellAction +class CastRipAction : public CastMeleeDebuffSpellAction { public: - CastRipAction(PlayerbotAI* botAI) : CastMeleeSpellAction(botAI, "rip") {} + CastRipAction(PlayerbotAI* botAI) : CastMeleeDebuffSpellAction(botAI, "rip", true, 12.0f) {} }; class CastShredAction : public CastMeleeSpellAction diff --git a/src/strategy/druid/DruidTriggers.h b/src/strategy/druid/DruidTriggers.h index 392b487e..14b210fe 100644 --- a/src/strategy/druid/DruidTriggers.h +++ b/src/strategy/druid/DruidTriggers.h @@ -95,10 +95,22 @@ public: BashInterruptSpellTrigger(PlayerbotAI* botAI) : InterruptSpellTrigger(botAI, "bash") {} }; -class TigersFuryTrigger : public BoostTrigger +class TigersFuryTrigger : public BuffTrigger { public: - TigersFuryTrigger(PlayerbotAI* botAI) : BoostTrigger(botAI, "tiger's fury") {} + TigersFuryTrigger(PlayerbotAI* botAI) : BuffTrigger(botAI, "tiger's fury") {} +}; + +class BerserkTrigger : public BoostTrigger +{ +public: + BerserkTrigger(PlayerbotAI* botAI) : BoostTrigger(botAI, "berserk") {} +}; + +class SavageRoarTrigger : public BuffTrigger +{ +public: + SavageRoarTrigger(PlayerbotAI* botAI) : BuffTrigger(botAI, "savage roar") {} }; class NaturesGraspTrigger : public BoostTrigger diff --git a/src/strategy/druid/FeralDruidStrategy.cpp b/src/strategy/druid/FeralDruidStrategy.cpp index ed4b030b..91e315b2 100644 --- a/src/strategy/druid/FeralDruidStrategy.cpp +++ b/src/strategy/druid/FeralDruidStrategy.cpp @@ -112,4 +112,6 @@ void FeralDruidStrategy::InitTriggers(std::vector& triggers) NextAction::array(0, new NextAction("dash", ACTION_EMERGENCY + 2), nullptr))); triggers.push_back(new TriggerNode("enemy flagcarrier near", NextAction::array(0, new NextAction("dash", ACTION_EMERGENCY + 2), nullptr))); + triggers.push_back( + new TriggerNode("berserk", NextAction::array(0, new NextAction("berserk", ACTION_HIGH + 6), nullptr))); } diff --git a/src/strategy/values/AttackerWithoutAuraTargetValue.cpp b/src/strategy/values/AttackerWithoutAuraTargetValue.cpp index a3945e64..99489af4 100644 --- a/src/strategy/values/AttackerWithoutAuraTargetValue.cpp +++ b/src/strategy/values/AttackerWithoutAuraTargetValue.cpp @@ -36,3 +36,36 @@ Unit* AttackerWithoutAuraTargetValue::Calculate() return result; } + +Unit* MeleeAttackerWithoutAuraTargetValue::Calculate() +{ + GuidVector attackers = botAI->GetAiObjectContext()->GetValue("attackers")->Get(); + // Unit* target = botAI->GetAiObjectContext()->GetValue("current target")->Get(); + uint32 max_health = 0; + Unit* result = nullptr; + for (ObjectGuid const guid : attackers) + { + Unit* unit = botAI->GetUnit(guid); + if (!unit || !unit->IsAlive()) + continue; + + if (!bot->IsWithinMeleeRange(unit)) + continue; + + if (checkArc && !bot->HasInArc(CAST_ANGLE_IN_FRONT, unit)) + continue; + + if (unit->GetHealth() < max_health) + { + continue; + } + + if (!botAI->HasAura(qualifier, unit, false, true)) + { + max_health = unit->GetHealth(); + result = unit; + } + } + + return result; +} diff --git a/src/strategy/values/AttackerWithoutAuraTargetValue.h b/src/strategy/values/AttackerWithoutAuraTargetValue.h index 73867f9d..7d7d0666 100644 --- a/src/strategy/values/AttackerWithoutAuraTargetValue.h +++ b/src/strategy/values/AttackerWithoutAuraTargetValue.h @@ -28,7 +28,9 @@ protected: class MeleeAttackerWithoutAuraTargetValue : public AttackerWithoutAuraTargetValue { public: - MeleeAttackerWithoutAuraTargetValue(PlayerbotAI* botAI) : AttackerWithoutAuraTargetValue(botAI, "melee") {} + MeleeAttackerWithoutAuraTargetValue(PlayerbotAI* botAI, bool checkArc = true) : AttackerWithoutAuraTargetValue(botAI, "melee"), checkArc(checkArc) {} + Unit* Calculate() override; + bool checkArc; }; #endif diff --git a/src/strategy/values/ExpectedLifetimeValue.cpp b/src/strategy/values/ExpectedLifetimeValue.cpp index a8a554b6..0f3f4b83 100644 --- a/src/strategy/values/ExpectedLifetimeValue.cpp +++ b/src/strategy/values/ExpectedLifetimeValue.cpp @@ -13,6 +13,9 @@ float ExpectedLifetimeValue::Calculate() return 0.0f; } float dps = AI_VALUE(float, "expected group dps"); + bool aoePenalty = AI_VALUE(uint8, "attacker count") >= 3; + if (aoePenalty) + dps *= 0.75; float res = target->GetHealth() / dps; // bot->Say(target->GetName() + " lifetime: " + std::to_string(res), LANG_UNIVERSAL); return res; From 3d1920675b568fb2addc8e11681bf9759f1958e5 Mon Sep 17 00:00:00 2001 From: Yunfan Li Date: Sat, 31 Aug 2024 23:45:24 +0800 Subject: [PATCH 07/47] Keep bear strategy for bear druid in bg --- conf/playerbots.conf.dist | 4 ++-- src/AiFactory.cpp | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/conf/playerbots.conf.dist b/conf/playerbots.conf.dist index 9954da5e..31fcf6eb 100644 --- a/conf/playerbots.conf.dist +++ b/conf/playerbots.conf.dist @@ -1183,9 +1183,9 @@ AiPlayerbot.RandomClassSpecIndex.9.2 = 2 AiPlayerbot.RandomClassSpecProb.11.0 = 20 AiPlayerbot.RandomClassSpecIndex.11.0 = 0 -AiPlayerbot.RandomClassSpecProb.11.1 = 30 +AiPlayerbot.RandomClassSpecProb.11.1 = 25 AiPlayerbot.RandomClassSpecIndex.11.1 = 1 -AiPlayerbot.RandomClassSpecProb.11.2 = 30 +AiPlayerbot.RandomClassSpecProb.11.2 = 35 AiPlayerbot.RandomClassSpecIndex.11.2 = 2 AiPlayerbot.RandomClassSpecProb.11.3 = 20 AiPlayerbot.RandomClassSpecIndex.11.3 = 3 diff --git a/src/AiFactory.cpp b/src/AiFactory.cpp index 75787511..d107b0dc 100644 --- a/src/AiFactory.cpp +++ b/src/AiFactory.cpp @@ -480,11 +480,11 @@ void AiFactory::AddDefaultCombatStrategies(Player* player, PlayerbotAI* const fa if ((player->getClass() == CLASS_DRUID && tab == 2) || (player->getClass() == CLASS_SHAMAN && tab == 2)) engine->addStrategiesNoInit("caster", "caster aoe", nullptr); - if (player->getClass() == CLASS_DRUID && tab == 1) - engine->addStrategiesNoInit(/*"behind",*/ "dps", nullptr); + // if (player->getClass() == CLASS_DRUID && tab == 1) + // engine->addStrategiesNoInit(/*"behind",*/ "dps", nullptr); - if (player->getClass() == CLASS_ROGUE) - engine->addStrategiesNoInit(/*"behind",*/ "stealth", nullptr); + // if (player->getClass() == CLASS_ROGUE) + // engine->addStrategiesNoInit(/*"behind",*/ "stealth", nullptr); } } From 0281fe73ad68273011b37bf0ea00514998806f3d Mon Sep 17 00:00:00 2001 From: Yunfan Li Date: Sun, 1 Sep 2024 00:05:31 +0800 Subject: [PATCH 08/47] Estimated group dps calculation --- src/PlayerbotAI.cpp | 26 +++++++++++++++++++ src/PlayerbotAI.h | 1 + src/strategy/values/ExpectedLifetimeValue.cpp | 2 +- 3 files changed, 28 insertions(+), 1 deletion(-) diff --git a/src/PlayerbotAI.cpp b/src/PlayerbotAI.cpp index 27214fc5..c8842cf4 100644 --- a/src/PlayerbotAI.cpp +++ b/src/PlayerbotAI.cpp @@ -5178,6 +5178,32 @@ uint32 PlayerbotAI::GetBuffedCount(Player* player, std::string const spellname) return bcount; } +int32 PlayerbotAI::GetNearGroupMemberCount(float dis) +{ + int count = 1; // yourself + if (Group* group = bot->GetGroup()) + { + for (GroupReference* gref = group->GetFirstMember(); gref; gref = gref->next()) + { + Player* member = gref->GetSource(); + if (member == bot) // calculated + continue; + + if (!member || !member->IsInWorld()) + continue; + + if (member->GetMapId() != bot->GetMapId()) + continue; + + if (member->GetExactDist(bot) > dis) + continue; + + count++; + } + } + return count; +} + bool PlayerbotAI::CanMove() { // do not allow if not vehicle driver diff --git a/src/PlayerbotAI.h b/src/PlayerbotAI.h index 61242629..73bc61a0 100644 --- a/src/PlayerbotAI.h +++ b/src/PlayerbotAI.h @@ -469,6 +469,7 @@ public: void ImbueItem(Item* item); void EnchantItemT(uint32 spellid, uint8 slot); uint32 GetBuffedCount(Player* player, std::string const spellname); + int32 GetNearGroupMemberCount(float dis = sPlayerbotAIConfig->sightDistance); virtual bool CanCastSpell(std::string const name, Unit* target, Item* itemTarget = nullptr); virtual bool CastSpell(std::string const name, Unit* target, Item* itemTarget = nullptr); diff --git a/src/strategy/values/ExpectedLifetimeValue.cpp b/src/strategy/values/ExpectedLifetimeValue.cpp index 0f3f4b83..892ee349 100644 --- a/src/strategy/values/ExpectedLifetimeValue.cpp +++ b/src/strategy/values/ExpectedLifetimeValue.cpp @@ -31,7 +31,7 @@ float ExpectedGroupDpsValue::Calculate() } else { - dps_num = group->GetMembersCount() * 0.7; + dps_num = botAI->GetNearGroupMemberCount() * 0.7; } uint32 mixedGearScore = PlayerbotAI::GetMixedGearScore(bot, true, false, 12); // efficiency record based on rare gear level, is there better calculation method? From 5c33aca6d33de9decf3527adac67976310460726 Mon Sep 17 00:00:00 2001 From: Atidot3 Date: Sat, 31 Aug 2024 22:53:57 +0200 Subject: [PATCH 09/47] fix double chat --- src/strategy/actions/SayAction.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/strategy/actions/SayAction.cpp b/src/strategy/actions/SayAction.cpp index 6bf973ab..81a7387b 100644 --- a/src/strategy/actions/SayAction.cpp +++ b/src/strategy/actions/SayAction.cpp @@ -510,9 +510,11 @@ bool ChatReplyAction::SendGeneralResponse(Player* bot, ChatChannelSource chatCha } case ChatChannelSource::SRC_GENERAL: { - //may reply to the same channel or whisper - GET_PLAYERBOT_AI(bot)->SayToChannel(responseMessage, ChatChannelId::GENERAL); - GET_PLAYERBOT_AI(bot)->Whisper(responseMessage, name); + //may reply to the same channel 80% or whisper + if (urand(0, 100) < 80) + GET_PLAYERBOT_AI(bot)->SayToChannel(responseMessage, ChatChannelId::GENERAL); + else + GET_PLAYERBOT_AI(bot)->Whisper(responseMessage, name); break; } case ChatChannelSource::SRC_TRADE: From 3f9165a71451d608dbb2013695de6ac0380f9d11 Mon Sep 17 00:00:00 2001 From: Yunfan Li Date: Sun, 1 Sep 2024 14:09:35 +0800 Subject: [PATCH 10/47] Fix addclass --- src/PlayerbotAIConfig.cpp | 4 ++++ src/RandomPlayerbotMgr.cpp | 2 -- src/RandomPlayerbotMgr.h | 2 +- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/PlayerbotAIConfig.cpp b/src/PlayerbotAIConfig.cpp index d6844aaf..a87135f1 100644 --- a/src/PlayerbotAIConfig.cpp +++ b/src/PlayerbotAIConfig.cpp @@ -13,6 +13,7 @@ #include "Playerbots.h" #include "RandomItemMgr.h" #include "RandomPlayerbotFactory.h" +#include "RandomPlayerbotMgr.h" #include "Talentspec.h" template @@ -482,6 +483,9 @@ bool PlayerbotAIConfig::Initialize() selfBotLevel = sConfigMgr->GetOption("AiPlayerbot.SelfBotLevel", 1); RandomPlayerbotFactory::CreateRandomBots(); + if (sPlayerbotAIConfig->addClassCommand) + sRandomPlayerbotMgr->PrepareAddclassCache(); + if (World::IsStopped()) { return true; diff --git a/src/RandomPlayerbotMgr.cpp b/src/RandomPlayerbotMgr.cpp index 10f2bfe5..21bd012a 100644 --- a/src/RandomPlayerbotMgr.cpp +++ b/src/RandomPlayerbotMgr.cpp @@ -162,8 +162,6 @@ RandomPlayerbotMgr::RandomPlayerbotMgr() : PlayerbotHolder(), processTicks(0) { sPlayerbotCommandServer->Start(); PrepareTeleportCache(); - if (sPlayerbotAIConfig->addClassCommand) - PrepareAddclassCache(); } BattlegroundData.clear(); diff --git a/src/RandomPlayerbotMgr.h b/src/RandomPlayerbotMgr.h index 2105477c..5c35aefa 100644 --- a/src/RandomPlayerbotMgr.h +++ b/src/RandomPlayerbotMgr.h @@ -169,6 +169,7 @@ public: void setActivityPercentage(float percentage) { activityMod = percentage / 100.0f; } static uint8 GetTeamClassIdx(bool isAlliance, uint8 claz) { return isAlliance * 20 + claz; } + void PrepareAddclassCache(); std::map> addclassCache; protected: void OnBotLoginInternal(Player* const bot) override; @@ -193,7 +194,6 @@ private: void RandomTeleport(Player* bot, std::vector& locs, bool hearth = false); uint32 GetZoneLevel(uint16 mapId, float teleX, float teleY, float teleZ); void PrepareTeleportCache(); - void PrepareAddclassCache(); typedef void (RandomPlayerbotMgr::*ConsoleCommandHandler)(Player*); std::vector players; From 0c71ae5c612b9c1fc2b6477a5ed7ccbf6ba7fa0f Mon Sep 17 00:00:00 2001 From: Yunfan Li Date: Sun, 1 Sep 2024 14:37:35 +0800 Subject: [PATCH 11/47] Shaman maelstrom weapon --- src/strategy/shaman/MeleeShamanStrategy.cpp | 7 ++----- src/strategy/shaman/ShamanActions.cpp | 5 ++++- src/strategy/shaman/ShamanAiObjectContext.cpp | 8 ++++++-- src/strategy/shaman/ShamanTriggers.h | 2 +- 4 files changed, 13 insertions(+), 9 deletions(-) diff --git a/src/strategy/shaman/MeleeShamanStrategy.cpp b/src/strategy/shaman/MeleeShamanStrategy.cpp index 0764f8a6..bf51ba9b 100644 --- a/src/strategy/shaman/MeleeShamanStrategy.cpp +++ b/src/strategy/shaman/MeleeShamanStrategy.cpp @@ -72,11 +72,8 @@ void MeleeShamanStrategy::InitTriggers(std::vector& triggers) triggers.push_back( new TriggerNode("flame shock", NextAction::array(0, new NextAction("flame shock", 20.0f), nullptr))); triggers.push_back( - new TriggerNode("maelstrom weapon", NextAction::array(0, new NextAction("lightning bolt", 25.0f), nullptr))); - triggers.push_back(new TriggerNode("not facing target", - NextAction::array(0, new NextAction("set facing", ACTION_NORMAL + 7), nullptr))); - // triggers.push_back(new TriggerNode("enemy too close for melee", NextAction::array(0, new NextAction("move out of - // enemy contact", ACTION_NORMAL + 8), nullptr))); + new TriggerNode("maelstrom weapon 4", NextAction::array(0, new NextAction("lightning bolt", 25.0f), nullptr))); + triggers.push_back(new TriggerNode( "medium aoe", NextAction::array(0, new NextAction("strength of earth totem", ACTION_LIGHT_HEAL), nullptr))); triggers.push_back(new TriggerNode( diff --git a/src/strategy/shaman/ShamanActions.cpp b/src/strategy/shaman/ShamanActions.cpp index 7394c320..3815b2b6 100644 --- a/src/strategy/shaman/ShamanActions.cpp +++ b/src/strategy/shaman/ShamanActions.cpp @@ -51,11 +51,14 @@ bool CastMagmaTotemAction::isUseful() { } bool CastFireNovaAction::isUseful() { + Unit* target = AI_VALUE(Unit*, "current target"); + if (!target) + return false; Creature* fireTotem = bot->GetMap()->GetCreature(bot->m_SummonSlot[1]); if (!fireTotem) return false; - if (bot->GetDistance(fireTotem) > 8.0f) + if (target->GetDistance(fireTotem) > 8.0f) return false; return CastMeleeSpellAction::isUseful(); diff --git a/src/strategy/shaman/ShamanAiObjectContext.cpp b/src/strategy/shaman/ShamanAiObjectContext.cpp index f4a821ce..f6dd9400 100644 --- a/src/strategy/shaman/ShamanAiObjectContext.cpp +++ b/src/strategy/shaman/ShamanAiObjectContext.cpp @@ -110,13 +110,17 @@ public: creators["no water totem"] = &ShamanATriggerFactoryInternal::no_water_totem; creators["no air totem"] = &ShamanATriggerFactoryInternal::no_air_totem; creators["earth shield on main tank"] = &ShamanATriggerFactoryInternal::earth_shield_on_main_tank; - creators["maelstrom weapon"] = &ShamanATriggerFactoryInternal::maelstrom_weapon; + creators["maelstrom weapon 3"] = &ShamanATriggerFactoryInternal::maelstrom_weapon_3; + creators["maelstrom weapon 4"] = &ShamanATriggerFactoryInternal::maelstrom_weapon_4; + creators["maelstrom weapon 5"] = &ShamanATriggerFactoryInternal::maelstrom_weapon_5; creators["flame shock"] = &ShamanATriggerFactoryInternal::flame_shock; creators["wrath of air totem"] = &ShamanATriggerFactoryInternal::wrath_of_air_totem; } private: - static Trigger* maelstrom_weapon(PlayerbotAI* botAI) { return new MaelstromWeaponTrigger(botAI); } + static Trigger* maelstrom_weapon_3(PlayerbotAI* botAI) { return new MaelstromWeaponTrigger(botAI, 3); } + static Trigger* maelstrom_weapon_4(PlayerbotAI* botAI) { return new MaelstromWeaponTrigger(botAI, 4); } + static Trigger* maelstrom_weapon_5(PlayerbotAI* botAI) { return new MaelstromWeaponTrigger(botAI, 5); } static Trigger* heroism(PlayerbotAI* botAI) { return new HeroismTrigger(botAI); } static Trigger* bloodlust(PlayerbotAI* botAI) { return new BloodlustTrigger(botAI); } static Trigger* elemental_mastery(PlayerbotAI* botAI) { return new ElementalMasteryTrigger(botAI); } diff --git a/src/strategy/shaman/ShamanTriggers.h b/src/strategy/shaman/ShamanTriggers.h index 97e9ec03..2ab651a1 100644 --- a/src/strategy/shaman/ShamanTriggers.h +++ b/src/strategy/shaman/ShamanTriggers.h @@ -241,7 +241,7 @@ public: class MaelstromWeaponTrigger : public HasAuraStackTrigger { public: - MaelstromWeaponTrigger(PlayerbotAI* botAI) : HasAuraStackTrigger(botAI, "maelstrom weapon", 5) {} + MaelstromWeaponTrigger(PlayerbotAI* botAI, int stack = 5) : HasAuraStackTrigger(botAI, "maelstrom weapon", stack) {} }; class WindShearInterruptEnemyHealerSpellTrigger : public InterruptEnemyHealerTrigger From 62ba364872c0457fb3648e8e3dfe7890ac63948d Mon Sep 17 00:00:00 2001 From: Yunfan Li Date: Sun, 1 Sep 2024 14:45:38 +0800 Subject: [PATCH 12/47] Cat druid optimization --- src/factory/PlayerbotFactory.cpp | 4 +- src/strategy/druid/CatDpsDruidStrategy.cpp | 24 ++++++------ src/strategy/druid/DruidAiObjectContext.cpp | 4 ++ src/strategy/druid/DruidCatActions.cpp | 5 --- src/strategy/druid/DruidCatActions.h | 5 +-- src/strategy/druid/DruidTriggers.h | 41 +++++++++++++++++++++ 6 files changed, 62 insertions(+), 21 deletions(-) diff --git a/src/factory/PlayerbotFactory.cpp b/src/factory/PlayerbotFactory.cpp index be60bf1b..e03447cb 100644 --- a/src/factory/PlayerbotFactory.cpp +++ b/src/factory/PlayerbotFactory.cpp @@ -881,7 +881,7 @@ void PlayerbotFactory::InitTalentsTree(bool increment /*false*/, bool use_templa /// @todo: match current talent with template specTab = AiFactory::GetPlayerSpecTab(bot); /// @todo: fix cat druid hardcode - if (bot->getClass() == CLASS_DRUID && specTab == DRUID_TAB_FERAL && bot->GetLevel() >= 20 && PlayerbotAI::IsDps(bot)) + if (bot->getClass() == CLASS_DRUID && specTab == DRUID_TAB_FERAL && bot->GetLevel() >= 20 && !bot->HasAura(16931)) specTab = 3; } else @@ -3081,7 +3081,7 @@ void PlayerbotFactory::InitGlyphs(bool increment) uint8 cls = bot->getClass(); uint8 tab = AiFactory::GetPlayerSpecTab(bot); /// @todo: fix cat druid hardcode - if (bot->getClass() == CLASS_DRUID && tab == DRUID_TAB_FERAL && PlayerbotAI::IsDps(bot)) + if (bot->getClass() == CLASS_DRUID && tab == DRUID_TAB_FERAL && bot->GetLevel() >= 20 && !bot->HasAura(16931)) tab = 3; std::list glyphs; ItemTemplateContainer const* itemTemplates = sObjectMgr->GetItemTemplateStore(); diff --git a/src/strategy/druid/CatDpsDruidStrategy.cpp b/src/strategy/druid/CatDpsDruidStrategy.cpp index bb860b02..0ae00e28 100644 --- a/src/strategy/druid/CatDpsDruidStrategy.cpp +++ b/src/strategy/druid/CatDpsDruidStrategy.cpp @@ -122,8 +122,8 @@ CatDpsDruidStrategy::CatDpsDruidStrategy(PlayerbotAI* botAI) : FeralDruidStrateg NextAction** CatDpsDruidStrategy::getDefaultActions() { - return NextAction::array(0, new NextAction("mangle (cat)", ACTION_DEFAULT + 0.3f), - new NextAction("shred", ACTION_DEFAULT + 0.2f), nullptr); + return NextAction::array(0, new NextAction("shred", ACTION_DEFAULT + 0.4f), + new NextAction("tiger's fury", ACTION_DEFAULT + 0.1f), nullptr); } void CatDpsDruidStrategy::InitTriggers(std::vector& triggers) @@ -132,7 +132,9 @@ void CatDpsDruidStrategy::InitTriggers(std::vector& triggers) // Default priority triggers.push_back(new TriggerNode("high energy available", - NextAction::array(0, new NextAction("claw", ACTION_DEFAULT + 0.1f), nullptr))); + NextAction::array(0, new NextAction("mangle (cat)", ACTION_DEFAULT + 0.3f), nullptr))); + triggers.push_back(new TriggerNode("high energy available", + NextAction::array(0, new NextAction("claw", ACTION_DEFAULT + 0.2f), nullptr))); triggers.push_back( new TriggerNode("faerie fire (feral)", NextAction::array(0, new NextAction("faerie fire (feral)", ACTION_DEFAULT + 0.0f), nullptr))); @@ -140,17 +142,17 @@ void CatDpsDruidStrategy::InitTriggers(std::vector& triggers) // Main spell triggers.push_back( new TriggerNode("cat form", NextAction::array(0, new NextAction("cat form", ACTION_HIGH + 8), nullptr))); - - triggers.push_back(new TriggerNode("tiger's fury", - NextAction::array(0, new NextAction("tiger's fury", ACTION_HIGH + 7), nullptr))); triggers.push_back( - new TriggerNode("savage roar", NextAction::array(0, new NextAction("savage roar", ACTION_HIGH + 5), nullptr))); + new TriggerNode("savage roar", NextAction::array(0, new NextAction("savage roar", ACTION_HIGH + 7), nullptr))); triggers.push_back(new TriggerNode("combo points available", - NextAction::array(0, new NextAction("rip", ACTION_HIGH + 4), nullptr))); + NextAction::array(0, new NextAction("rip", ACTION_HIGH + 6), nullptr))); triggers.push_back(new TriggerNode( - "combo points available", NextAction::array(0, new NextAction("ferocious bite", ACTION_HIGH + 3), nullptr))); - triggers.push_back(new TriggerNode("target with combo points almost dead", - NextAction::array(0, new NextAction("ferocious bite", ACTION_HIGH + 2), nullptr))); + "ferocious bite time", NextAction::array(0, new NextAction("ferocious bite", ACTION_HIGH + 5), nullptr))); + triggers.push_back( + new TriggerNode("target with combo points almost dead", + NextAction::array(0, new NextAction("ferocious bite", ACTION_HIGH + 4), nullptr))); + triggers.push_back(new TriggerNode("mangle (cat)", + NextAction::array(0, new NextAction("mangle (cat)", ACTION_HIGH + 3), nullptr))); triggers.push_back(new TriggerNode("rake", NextAction::array(0, new NextAction("rake", ACTION_HIGH + 2), nullptr))); triggers.push_back( new TriggerNode("medium threat", NextAction::array(0, new NextAction("cower", ACTION_HIGH + 1), nullptr))); diff --git a/src/strategy/druid/DruidAiObjectContext.cpp b/src/strategy/druid/DruidAiObjectContext.cpp index 61e66e95..04652487 100644 --- a/src/strategy/druid/DruidAiObjectContext.cpp +++ b/src/strategy/druid/DruidAiObjectContext.cpp @@ -103,6 +103,8 @@ public: creators["party member remove curse"] = &DruidTriggerFactoryInternal::party_member_remove_curse; creators["eclipse (solar) cooldown"] = &DruidTriggerFactoryInternal::eclipse_solar_cooldown; creators["eclipse (lunar) cooldown"] = &DruidTriggerFactoryInternal::eclipse_lunar_cooldown; + creators["mangle (cat)"] = &DruidTriggerFactoryInternal::mangle_cat; + creators["ferocious bite time"] = &DruidTriggerFactoryInternal::ferocious_bite_time; } private: @@ -137,6 +139,8 @@ private: static Trigger* party_member_remove_curse(PlayerbotAI* ai) { return new DruidPartyMemberRemoveCurseTrigger(ai); } static Trigger* eclipse_solar_cooldown(PlayerbotAI* ai) { return new EclipseSolarCooldownTrigger(ai); } static Trigger* eclipse_lunar_cooldown(PlayerbotAI* ai) { return new EclipseLunarCooldownTrigger(ai); } + static Trigger* mangle_cat(PlayerbotAI* ai) { return new MangleCatTrigger(ai); } + static Trigger* ferocious_bite_time(PlayerbotAI* ai) { return new FerociousBiteTimeTrigger(ai); } }; class DruidAiObjectContextInternal : public NamedObjectContext diff --git a/src/strategy/druid/DruidCatActions.cpp b/src/strategy/druid/DruidCatActions.cpp index fb3a0e88..03571a8e 100644 --- a/src/strategy/druid/DruidCatActions.cpp +++ b/src/strategy/druid/DruidCatActions.cpp @@ -1,6 +1 @@ #include "DruidCatActions.h" - -bool CastMangleCatAction::isUseful() -{ - return CastMeleeDebuffSpellAction::isUseful() && !botAI->HasAura("mangle (bear)", GetTarget(), false, isOwner); -} \ No newline at end of file diff --git a/src/strategy/druid/DruidCatActions.h b/src/strategy/druid/DruidCatActions.h index 8adddf98..cf4c6c35 100644 --- a/src/strategy/druid/DruidCatActions.h +++ b/src/strategy/druid/DruidCatActions.h @@ -60,11 +60,10 @@ public: CastClawAction(PlayerbotAI* botAI) : CastMeleeSpellAction(botAI, "claw") {} }; -class CastMangleCatAction : public CastMeleeDebuffSpellAction +class CastMangleCatAction : public CastMeleeSpellAction { public: - CastMangleCatAction(PlayerbotAI* botAI) : CastMeleeDebuffSpellAction(botAI, "mangle (cat)", false, 0.0f) {} - bool isUseful() override; + CastMangleCatAction(PlayerbotAI* botAI) : CastMeleeSpellAction(botAI, "mangle (cat)") {} }; class CastSwipeCatAction : public CastMeleeSpellAction diff --git a/src/strategy/druid/DruidTriggers.h b/src/strategy/druid/DruidTriggers.h index 14b210fe..b0f13205 100644 --- a/src/strategy/druid/DruidTriggers.h +++ b/src/strategy/druid/DruidTriggers.h @@ -9,6 +9,8 @@ #include "CureTriggers.h" #include "GenericTriggers.h" #include "Player.h" +#include "PlayerbotAI.h" +#include "Playerbots.h" #include "SharedDefines.h" class PlayerbotAI; @@ -224,4 +226,43 @@ public: bool IsActive() override { return bot->HasSpellCooldown(48518); } }; +class MangleCatTrigger : public DebuffTrigger +{ +public: + MangleCatTrigger(PlayerbotAI* ai) : DebuffTrigger(ai, "mangle (cat)", 1, false, 0.0f) {} + bool IsActive() override + { + return DebuffTrigger::IsActive() && !botAI->HasAura("mangle (bear)", GetTarget(), false, false, -1, true) + && !botAI->HasAura("trauma", GetTarget(), false, false, -1, true); + } +}; + +class FerociousBiteTimeTrigger : public Trigger +{ +public: + FerociousBiteTimeTrigger(PlayerbotAI* ai) : Trigger(ai, "ferocious bite time") {} + bool IsActive() override + { + Unit* target = AI_VALUE(Unit*, "current target"); + if (!target) + return false; + + uint8 cp = AI_VALUE2(uint8, "combo", "current target"); + if (cp < 5) + return false; + + Aura* roar = botAI->GetAura("savage roar", bot); + bool roarCheck = roar && roar->GetDuration() > 8000; + if (!roarCheck) + return false; + + Aura* rip = botAI->GetAura("rip", target, true); + bool ripCheck = rip && rip->GetDuration() > 8000; + if (!ripCheck) + return false; + + return true; + } +}; + #endif From ae37876848590a4bd336d38793f6a4da1ee2becb Mon Sep 17 00:00:00 2001 From: Yunfan Li Date: Sun, 1 Sep 2024 15:46:39 +0800 Subject: [PATCH 13/47] Shaman stats weight and totem --- conf/playerbots.conf.dist | 2 +- src/factory/StatsWeightCalculator.cpp | 20 +++++++++++++++---- src/strategy/shaman/CasterShamanStrategy.cpp | 4 +++- src/strategy/shaman/HealShamanStrategy.cpp | 5 +++++ src/strategy/shaman/MeleeShamanStrategy.cpp | 5 ++--- src/strategy/shaman/ShamanActions.h | 15 ++++++++++++++ src/strategy/shaman/ShamanAiObjectContext.cpp | 2 ++ src/strategy/shaman/TotemsShamanStrategy.cpp | 11 ++-------- 8 files changed, 46 insertions(+), 18 deletions(-) diff --git a/conf/playerbots.conf.dist b/conf/playerbots.conf.dist index 31fcf6eb..675c322b 100644 --- a/conf/playerbots.conf.dist +++ b/conf/playerbots.conf.dist @@ -916,7 +916,7 @@ AiPlayerbot.PremadeSpecGlyph.7.0 = 41536,43385,41532,43386,44923,45776 AiPlayerbot.PremadeSpecLink.7.0.60 = 4530001520213351102301351 AiPlayerbot.PremadeSpecLink.7.0.80 = 3530001523213351322301351-005050031 AiPlayerbot.PremadeSpecName.7.1 = enh pve -AiPlayerbot.PremadeSpecGlyph.7.1 = 41530,43385,41539,43386,44923,41540 +AiPlayerbot.PremadeSpecGlyph.7.1 = 41542,43385,41539,43386,44923,45771 AiPlayerbot.PremadeSpecLink.7.1.60 = -30205033005001333031131131051 AiPlayerbot.PremadeSpecLink.7.1.80 = 053030052-30205033005021333031131131051 AiPlayerbot.PremadeSpecName.7.2 = resto pve diff --git a/src/factory/StatsWeightCalculator.cpp b/src/factory/StatsWeightCalculator.cpp index 543de321..b0055272 100644 --- a/src/factory/StatsWeightCalculator.cpp +++ b/src/factory/StatsWeightCalculator.cpp @@ -132,7 +132,7 @@ void StatsWeightCalculator::GenerateBasicWeights(Player* player) stats_weights_[STATS_TYPE_HASTE] += 1.8f; stats_weights_[STATS_TYPE_RANGED_DPS] += 5.0f; } - else if ((cls == CLASS_ROGUE && tab == ROGUE_TAB_COMBAT) || (cls == CLASS_DRUID && tab == DRUID_TAB_FERAL && !PlayerbotAI::IsTank(player))) + else if (cls == CLASS_ROGUE && tab == ROGUE_TAB_COMBAT) { stats_weights_[STATS_TYPE_AGILITY] += 1.8f; stats_weights_[STATS_TYPE_STRENGTH] += 1.1f; @@ -143,7 +143,19 @@ void StatsWeightCalculator::GenerateBasicWeights(Player* player) stats_weights_[STATS_TYPE_HASTE] += 1.4f; stats_weights_[STATS_TYPE_EXPERTISE] += 2.0f; stats_weights_[STATS_TYPE_MELEE_DPS] += 5.0f; - } + } + else if (cls == CLASS_DRUID && tab == DRUID_TAB_FERAL && !PlayerbotAI::IsTank(player)) + { + stats_weights_[STATS_TYPE_AGILITY] += 2.4f; + stats_weights_[STATS_TYPE_STRENGTH] += 2.3f; + stats_weights_[STATS_TYPE_ATTACK_POWER] += 1.0f; + stats_weights_[STATS_TYPE_ARMOR_PENETRATION] += 2.1f; + stats_weights_[STATS_TYPE_HIT] += 1.9f; + stats_weights_[STATS_TYPE_CRIT] += 1.8f; + stats_weights_[STATS_TYPE_HASTE] += 1.4f; + stats_weights_[STATS_TYPE_EXPERTISE] += 2.0f; + stats_weights_[STATS_TYPE_MELEE_DPS] += 5.0f; + } else if (cls == CLASS_ROGUE && (tab == ROGUE_TAB_ASSASSINATION || tab == ROGUE_TAB_SUBTLETY)) { stats_weights_[STATS_TYPE_AGILITY] += 1.7f; @@ -224,13 +236,13 @@ void StatsWeightCalculator::GenerateBasicWeights(Player* player) stats_weights_[STATS_TYPE_STRENGTH] += 1.1f; stats_weights_[STATS_TYPE_INTELLECT] += 0.5f; stats_weights_[STATS_TYPE_ATTACK_POWER] += 1.0f; - stats_weights_[STATS_TYPE_SPELL_POWER] += 1.0f; + stats_weights_[STATS_TYPE_SPELL_POWER] += 0.9f; stats_weights_[STATS_TYPE_ARMOR_PENETRATION] += 1.2f; stats_weights_[STATS_TYPE_HIT] += 1.7f; stats_weights_[STATS_TYPE_CRIT] += 1.4f; stats_weights_[STATS_TYPE_HASTE] += 1.8f; stats_weights_[STATS_TYPE_EXPERTISE] += 2.0f; - stats_weights_[STATS_TYPE_MELEE_DPS] += 5.2f; + stats_weights_[STATS_TYPE_MELEE_DPS] += 8.5f; } else if (cls == CLASS_WARLOCK || cls == CLASS_MAGE || diff --git a/src/strategy/shaman/CasterShamanStrategy.cpp b/src/strategy/shaman/CasterShamanStrategy.cpp index 88e3ca06..c11c523d 100644 --- a/src/strategy/shaman/CasterShamanStrategy.cpp +++ b/src/strategy/shaman/CasterShamanStrategy.cpp @@ -66,7 +66,9 @@ void CasterShamanStrategy::InitTriggers(std::vector& triggers) // shock", 21.0f), nullptr))); triggers.push_back( new TriggerNode("no fire totem", NextAction::array(0, new NextAction("totem of wrath", 15.0f), NULL))); - + triggers.push_back(new TriggerNode("fire elemental totem", + NextAction::array(0, new NextAction("fire elemental totem", 32.0f), nullptr))); + triggers.push_back( new TriggerNode("medium mana", NextAction::array(0, new NextAction("thunderstorm", ACTION_HIGH + 1), nullptr))); diff --git a/src/strategy/shaman/HealShamanStrategy.cpp b/src/strategy/shaman/HealShamanStrategy.cpp index 7fda4651..ff305559 100644 --- a/src/strategy/shaman/HealShamanStrategy.cpp +++ b/src/strategy/shaman/HealShamanStrategy.cpp @@ -104,6 +104,11 @@ void HealShamanStrategy::InitTriggers(std::vector& triggers) triggers.push_back( new TriggerNode("medium mana", NextAction::array(0, new NextAction("mana tide totem", ACTION_HIGH + 5), NULL))); + triggers.push_back( + new TriggerNode("no fire totem", NextAction::array(0, new NextAction("flametongue totem", 7.0f), + new NextAction("searing totem", 6.0f), nullptr))); + triggers.push_back(new TriggerNode("fire elemental totem", + NextAction::array(0, new NextAction("fire elemental totem", 32.0f), nullptr))); triggers.push_back(new TriggerNode( "party member to heal out of spell range", NextAction::array(0, new NextAction("reach party member to heal", ACTION_CRITICAL_HEAL + 1), nullptr))); diff --git a/src/strategy/shaman/MeleeShamanStrategy.cpp b/src/strategy/shaman/MeleeShamanStrategy.cpp index bf51ba9b..d38c163a 100644 --- a/src/strategy/shaman/MeleeShamanStrategy.cpp +++ b/src/strategy/shaman/MeleeShamanStrategy.cpp @@ -83,9 +83,8 @@ void MeleeShamanStrategy::InitTriggers(std::vector& triggers) "no fire totem", NextAction::array(0, new NextAction("reach melee", 23.0f), new NextAction("magma totem", 22.0f), nullptr))); - triggers.push_back(new TriggerNode("fire elemental totem", - NextAction::array(0, new NextAction("reach melee", 33.0f), - new NextAction("fire elemental totem", 32.0f), nullptr))); + triggers.push_back(new TriggerNode( + "fire elemental totem", NextAction::array(0, new NextAction("fire elemental totem melee", 32.0f), nullptr))); triggers.push_back( new TriggerNode("no air totem", NextAction::array(0, new NextAction("windfury totem", 20.0f), nullptr))); diff --git a/src/strategy/shaman/ShamanActions.h b/src/strategy/shaman/ShamanActions.h index 2568cd11..c4f6ce38 100644 --- a/src/strategy/shaman/ShamanActions.h +++ b/src/strategy/shaman/ShamanActions.h @@ -7,6 +7,7 @@ #define _PLAYERBOT_SHAMANACTIONS_H #include "GenericSpellActions.h" +#include "Playerbots.h" #include "SharedDefines.h" class PlayerbotAI; @@ -426,6 +427,20 @@ public: virtual bool isUseful() override { return CastTotemAction::isUseful(); } }; +class CastFireElementalTotemMeleeAction : public CastTotemAction +{ +public: + CastFireElementalTotemMeleeAction(PlayerbotAI* ai) : CastTotemAction(ai, "fire elemental totem", "", 0.0f) {} + virtual std::string const GetTargetName() override { return "self target"; } + virtual bool isUseful() override + { + Unit* target = AI_VALUE(Unit*, "current target"); + if (!target || !bot->IsWithinMeleeRange(target)) + return false; + return CastTotemAction::isUseful(); + } +}; + class CastWrathOfAirTotemAction : public CastTotemAction { public: diff --git a/src/strategy/shaman/ShamanAiObjectContext.cpp b/src/strategy/shaman/ShamanAiObjectContext.cpp index f6dd9400..e67f4230 100644 --- a/src/strategy/shaman/ShamanAiObjectContext.cpp +++ b/src/strategy/shaman/ShamanAiObjectContext.cpp @@ -238,6 +238,7 @@ public: creators["lava burst"] = &ShamanAiObjectContextInternal::lava_burst; creators["earth shield on main tank"] = &ShamanAiObjectContextInternal::earth_shield_on_main_tank; creators["fire elemental totem"] = &ShamanAiObjectContextInternal::fire_elemental_totem; + creators["fire elemental totem melee"] = &ShamanAiObjectContextInternal::fire_elemental_totem_melee; creators["totem of wrath"] = &ShamanAiObjectContextInternal::totem_of_wrath; creators["wrath of air totem"] = &ShamanAiObjectContextInternal::wrath_of_air_totem; creators["shamanistic rage"] = &ShamanAiObjectContextInternal::shamanistic_rage; @@ -318,6 +319,7 @@ private: static Action* earth_shield_on_main_tank(PlayerbotAI* ai) { return new CastEarthShieldOnMainTankAction(ai); } static Action* totem_of_wrath(PlayerbotAI* ai) { return new CastTotemOfWrathAction(ai); } static Action* fire_elemental_totem(PlayerbotAI* ai) { return new CastFireElementalTotemAction(ai); } + static Action* fire_elemental_totem_melee(PlayerbotAI* ai) { return new CastFireElementalTotemMeleeAction(ai); } static Action* wrath_of_air_totem(PlayerbotAI* ai) { return new CastWrathOfAirTotemAction(ai); } static Action* shamanistic_rage(PlayerbotAI* ai) { return new CastShamanisticRageAction(ai); } static Action* feral_spirit(PlayerbotAI* ai) { return new CastFeralSpiritAction(ai); } diff --git a/src/strategy/shaman/TotemsShamanStrategy.cpp b/src/strategy/shaman/TotemsShamanStrategy.cpp index cfa0c3be..f3346a5c 100644 --- a/src/strategy/shaman/TotemsShamanStrategy.cpp +++ b/src/strategy/shaman/TotemsShamanStrategy.cpp @@ -13,20 +13,13 @@ void TotemsShamanStrategy::InitTriggers(std::vector& triggers) { GenericShamanStrategy::InitTriggers(triggers); - triggers.push_back(new TriggerNode("fire elemental totem", - NextAction::array(0, new NextAction("fire elemental totem", 32.0f), nullptr))); - triggers.push_back( - new TriggerNode("no air totem", NextAction::array(0, new NextAction("wrath of air totem", 8.0f), NULL))); + new TriggerNode("no air totem", NextAction::array(0, new NextAction("wrath of air totem", 8.0f), nullptr))); triggers.push_back( new TriggerNode("no water totem", NextAction::array(0, new NextAction("mana spring totem", 7.0f), new NextAction("healing stream totem", 6.0f), nullptr))); - triggers.push_back( - new TriggerNode("no fire totem", NextAction::array(0, new NextAction("flametongue totem", 7.0f), - new NextAction("searing totem", 6.0f), nullptr))); - triggers.push_back(new TriggerNode("strength of earth totem", - NextAction::array(0, new NextAction("strength of earth totem", 6.0f), NULL))); + NextAction::array(0, new NextAction("strength of earth totem", 6.0f), nullptr))); } From 360a025b341cb80c88e887bd1dc5e4ea4ea21cdf Mon Sep 17 00:00:00 2001 From: Yunfan Li Date: Sun, 1 Sep 2024 17:11:46 +0800 Subject: [PATCH 14/47] Estimated dps calculation --- src/strategy/actions/ChatActionContext.h | 4 +- src/strategy/actions/GenericSpellActions.cpp | 2 +- src/strategy/actions/TellLosAction.cpp | 6 +- src/strategy/actions/TellLosAction.h | 4 +- src/strategy/generic/CastTimeStrategy.cpp | 2 +- .../generic/ChatCommandHandlerStrategy.cpp | 2 +- src/strategy/rogue/RogueTriggers.cpp | 2 +- src/strategy/shaman/ShamanActions.cpp | 2 +- src/strategy/triggers/GenericTriggers.cpp | 2 +- .../values/AttackerWithoutAuraTargetValue.cpp | 2 +- src/strategy/values/DpsTargetValue.cpp | 2 +- .../values/EstimatedLifetimeValue.cpp | 135 ++++++++++++++++++ ...fetimeValue.h => EstimatedLifetimeValue.h} | 16 ++- src/strategy/values/ExpectedLifetimeValue.cpp | 102 ------------- src/strategy/values/ValueContext.h | 10 +- 15 files changed, 165 insertions(+), 128 deletions(-) create mode 100644 src/strategy/values/EstimatedLifetimeValue.cpp rename src/strategy/values/{ExpectedLifetimeValue.h => EstimatedLifetimeValue.h} (51%) delete mode 100644 src/strategy/values/ExpectedLifetimeValue.cpp diff --git a/src/strategy/actions/ChatActionContext.h b/src/strategy/actions/ChatActionContext.h index 76c1bc54..d56c5e6f 100644 --- a/src/strategy/actions/ChatActionContext.h +++ b/src/strategy/actions/ChatActionContext.h @@ -172,7 +172,7 @@ public: creators["rtsc"] = &ChatActionContext::rtsc; creators["naxx chat shortcut"] = &ChatActionContext::naxx_chat_shortcut; creators["bwl chat shortcut"] = &ChatActionContext::bwl_chat_shortcut; - creators["tell expected dps"] = &ChatActionContext::tell_expected_dps; + creators["tell estimated dps"] = &ChatActionContext::tell_estimated_dps; creators["join"] = &ChatActionContext::join; creators["calc"] = &ChatActionContext::calc; } @@ -271,7 +271,7 @@ private: static Action* rtsc(PlayerbotAI* botAI) { return new RTSCAction(botAI); } static Action* naxx_chat_shortcut(PlayerbotAI* ai) { return new NaxxChatShortcutAction(ai); } static Action* bwl_chat_shortcut(PlayerbotAI* ai) { return new BwlChatShortcutAction(ai); } - static Action* tell_expected_dps(PlayerbotAI* ai) { return new TellExpectedDpsAction(ai); } + static Action* tell_estimated_dps(PlayerbotAI* ai) { return new TellEstimatedDpsAction(ai); } static Action* join(PlayerbotAI* ai) { return new JoinGroupAction(ai); } static Action* calc(PlayerbotAI* ai) { return new TellCalculateItemAction(ai); } }; diff --git a/src/strategy/actions/GenericSpellActions.cpp b/src/strategy/actions/GenericSpellActions.cpp index 05938358..2425a223 100644 --- a/src/strategy/actions/GenericSpellActions.cpp +++ b/src/strategy/actions/GenericSpellActions.cpp @@ -364,5 +364,5 @@ bool CastDebuffSpellAction::isUseful() return false; } return CastAuraSpellAction::isUseful() && - (target->GetHealth() / AI_VALUE(float, "expected group dps")) >= needLifeTime; + (target->GetHealth() / AI_VALUE(float, "estimated group dps")) >= needLifeTime; } \ No newline at end of file diff --git a/src/strategy/actions/TellLosAction.cpp b/src/strategy/actions/TellLosAction.cpp index 159fb845..03dd3569 100644 --- a/src/strategy/actions/TellLosAction.cpp +++ b/src/strategy/actions/TellLosAction.cpp @@ -130,10 +130,10 @@ bool TellAuraAction::Execute(Event event) return true; } -bool TellExpectedDpsAction::Execute(Event event) +bool TellEstimatedDpsAction::Execute(Event event) { - float dps = AI_VALUE(float, "expected group dps"); - botAI->TellMaster("Expected Group DPS: " + std::to_string(dps)); + float dps = AI_VALUE(float, "estimated group dps"); + botAI->TellMaster("Estimated Group DPS: " + std::to_string(dps)); return true; } diff --git a/src/strategy/actions/TellLosAction.h b/src/strategy/actions/TellLosAction.h index 1adc56d0..988564cf 100644 --- a/src/strategy/actions/TellLosAction.h +++ b/src/strategy/actions/TellLosAction.h @@ -30,10 +30,10 @@ public: virtual bool Execute(Event event); }; -class TellExpectedDpsAction : public Action +class TellEstimatedDpsAction : public Action { public: - TellExpectedDpsAction(PlayerbotAI* ai) : Action(ai, "tell expected dps") {} + TellEstimatedDpsAction(PlayerbotAI* ai) : Action(ai, "tell estimated dps") {} virtual bool Execute(Event event); }; diff --git a/src/strategy/generic/CastTimeStrategy.cpp b/src/strategy/generic/CastTimeStrategy.cpp index 7d1b4545..483e2b8c 100644 --- a/src/strategy/generic/CastTimeStrategy.cpp +++ b/src/strategy/generic/CastTimeStrategy.cpp @@ -47,7 +47,7 @@ float CastTimeMultiplier::GetValue(Action* action) return 1.0f; } - if (castTime > (1000 * target->GetHealth() / AI_VALUE(float, "expected group dps"))) + if (castTime > (1000 * target->GetHealth() / AI_VALUE(float, "estimated group dps"))) { return 0.1f; } diff --git a/src/strategy/generic/ChatCommandHandlerStrategy.cpp b/src/strategy/generic/ChatCommandHandlerStrategy.cpp index 78c5aef7..58db48f5 100644 --- a/src/strategy/generic/ChatCommandHandlerStrategy.cpp +++ b/src/strategy/generic/ChatCommandHandlerStrategy.cpp @@ -89,7 +89,7 @@ void ChatCommandHandlerStrategy::InitTriggers(std::vector& trigger triggers.push_back( new TriggerNode("bwl", NextAction::array(0, new NextAction("bwl chat shortcut", relevance), NULL))); triggers.push_back( - new TriggerNode("dps", NextAction::array(0, new NextAction("tell expected dps", relevance), NULL))); + new TriggerNode("dps", NextAction::array(0, new NextAction("tell estimated dps", relevance), NULL))); triggers.push_back( new TriggerNode("disperse", NextAction::array(0, new NextAction("disperse set", relevance), NULL))); } diff --git a/src/strategy/rogue/RogueTriggers.cpp b/src/strategy/rogue/RogueTriggers.cpp index 31e1a390..ed12016e 100644 --- a/src/strategy/rogue/RogueTriggers.cpp +++ b/src/strategy/rogue/RogueTriggers.cpp @@ -133,5 +133,5 @@ bool TargetWithComboPointsLowerHealTrigger::IsActive() return false; } return ComboPointsAvailableTrigger::IsActive() && - (target->GetHealth() / AI_VALUE(float, "expected group dps")) <= lifeTime; + (target->GetHealth() / AI_VALUE(float, "estimated group dps")) <= lifeTime; } \ No newline at end of file diff --git a/src/strategy/shaman/ShamanActions.cpp b/src/strategy/shaman/ShamanActions.cpp index 3815b2b6..9ce29240 100644 --- a/src/strategy/shaman/ShamanActions.cpp +++ b/src/strategy/shaman/ShamanActions.cpp @@ -17,7 +17,7 @@ bool CastTotemAction::isUseful() { return false; } - float dps = AI_VALUE(float, "expected group dps"); + float dps = AI_VALUE(float, "estimated group dps"); if (target->GetHealth() / dps < needLifeTime) { return false; diff --git a/src/strategy/triggers/GenericTriggers.cpp b/src/strategy/triggers/GenericTriggers.cpp index c1f80eb7..7c40ef37 100644 --- a/src/strategy/triggers/GenericTriggers.cpp +++ b/src/strategy/triggers/GenericTriggers.cpp @@ -221,7 +221,7 @@ bool DebuffTrigger::IsActive() { return false; } - return BuffTrigger::IsActive() && (target->GetHealth() / AI_VALUE(float, "expected group dps")) >= needLifeTime; + return BuffTrigger::IsActive() && (target->GetHealth() / AI_VALUE(float, "estimated group dps")) >= needLifeTime; } bool DebuffOnBossTrigger::IsActive() diff --git a/src/strategy/values/AttackerWithoutAuraTargetValue.cpp b/src/strategy/values/AttackerWithoutAuraTargetValue.cpp index 99489af4..e99d0f3e 100644 --- a/src/strategy/values/AttackerWithoutAuraTargetValue.cpp +++ b/src/strategy/values/AttackerWithoutAuraTargetValue.cpp @@ -52,7 +52,7 @@ Unit* MeleeAttackerWithoutAuraTargetValue::Calculate() if (!bot->IsWithinMeleeRange(unit)) continue; - if (checkArc && !bot->HasInArc(CAST_ANGLE_IN_FRONT, unit)) + if (checkArc && !bot->HasInArc(M_PI, unit)) continue; if (unit->GetHealth() < max_health) diff --git a/src/strategy/values/DpsTargetValue.cpp b/src/strategy/values/DpsTargetValue.cpp index e2e12cb3..5fc276df 100644 --- a/src/strategy/values/DpsTargetValue.cpp +++ b/src/strategy/values/DpsTargetValue.cpp @@ -291,7 +291,7 @@ Unit* DpsTargetValue::Calculate() return rti; // FindLeastHpTargetStrategy strategy(botAI); - float dps = AI_VALUE(float, "expected group dps"); + float dps = AI_VALUE(float, "estimated group dps"); if (botAI->IsCaster(bot)) { CasterFindTargetSmartStrategy strategy(botAI, dps); diff --git a/src/strategy/values/EstimatedLifetimeValue.cpp b/src/strategy/values/EstimatedLifetimeValue.cpp new file mode 100644 index 00000000..4352934f --- /dev/null +++ b/src/strategy/values/EstimatedLifetimeValue.cpp @@ -0,0 +1,135 @@ +#include "EstimatedLifetimeValue.h" + +#include "AiFactory.h" +#include "PlayerbotAI.h" +#include "PlayerbotAIConfig.h" +#include "Playerbots.h" +#include "SharedDefines.h" + +float EstimatedLifetimeValue::Calculate() +{ + Unit* target = AI_VALUE(Unit*, qualifier); + if (!target || !target->IsAlive()) + { + return 0.0f; + } + float dps = AI_VALUE(float, "estimated group dps"); + bool aoePenalty = AI_VALUE(uint8, "attacker count") >= 3; + if (aoePenalty) + dps *= 0.75; + float res = target->GetHealth() / dps; + // bot->Say(target->GetName() + " lifetime: " + std::to_string(res), LANG_UNIVERSAL); + return res; +} + +float EstimatedGroupDpsValue::Calculate() +{ + float totalDps; + + std::vector groupPlayer={bot}; + if (Group* group = bot->GetGroup()) + { + for (GroupReference* gref = group->GetFirstMember(); gref; gref = gref->next()) + { + Player* member = gref->GetSource(); + if (member == bot) // calculated + continue; + + if (!member || !member->IsInWorld()) + continue; + + if (member->GetMapId() != bot->GetMapId()) + continue; + + if (member->GetExactDist(bot) > sPlayerbotAIConfig->sightDistance) + continue; + + groupPlayer.push_back(member); + } + } + for (Player* player : groupPlayer) + { + float roleMultiplier; + if (botAI->IsTank(player)) + roleMultiplier = 0.3f; + else if (botAI->IsHeal(player)) + roleMultiplier = 0.1f; + else + roleMultiplier = 1.0f; + float basicDps = GetBasicDps(player->GetLevel()); + float basicGs = GetBasicGs(player->GetLevel()); + uint32 mixedGearScore = PlayerbotAI::GetMixedGearScore(player, true, false, 12); + + float gap = (float)mixedGearScore / basicGs - 1; + float gs_modifier = gap * 3 + 1; + if (gs_modifier < 0.75) + gs_modifier = 0.75; + if (gs_modifier > 4) + gs_modifier = 4; + totalDps += basicDps * roleMultiplier * gs_modifier; + } + + return totalDps; +} + +float EstimatedGroupDpsValue::GetBasicDps(uint32 level) +{ + float basic_dps; + + if (level <= 15) + { + basic_dps = 5 + level * 1; + } + else if (level <= 25) + { + basic_dps = 20 + (level - 15) * 2; + } + else if (level <= 45) + { + basic_dps = 40 + (level - 25) * 3; + } + else if (level <= 55) + { + basic_dps = 100 + (level - 45) * 20; + } + else if (level <= 60) + { + basic_dps = 300 + (level - 55) * 50; + } + else if (level <= 70) + { + basic_dps = 450 + (level - 60) * 40; + } + else + { + basic_dps = 750 + (level - 70) * 175; + } + return basic_dps; +} + +float EstimatedGroupDpsValue::GetBasicGs(uint32 level) +{ + float basic_gs; + + if (level <= 8) + { + basic_gs = (level + 5) * 2; + } + else if (level <= 15) + { + basic_gs = (level + 5) * 3; + } + else if (level <= 60) + { + basic_gs = (level + 5) * 4; + } + else if (level <= 70) + { + basic_gs = (85 + (level - 60) * 3) * 4; + } + else + { + basic_gs = (155 + (level - 70) * 4) * 4; + } + return basic_gs; +} \ No newline at end of file diff --git a/src/strategy/values/ExpectedLifetimeValue.h b/src/strategy/values/EstimatedLifetimeValue.h similarity index 51% rename from src/strategy/values/ExpectedLifetimeValue.h rename to src/strategy/values/EstimatedLifetimeValue.h index 2afcece8..f050ac71 100644 --- a/src/strategy/values/ExpectedLifetimeValue.h +++ b/src/strategy/values/EstimatedLifetimeValue.h @@ -3,8 +3,8 @@ * and/or modify it under version 2 of the License, or (at your option), any later version. */ -#ifndef _PLAYERBOT_EXPECTEDLIFETIMEVALUE_H -#define _PLAYERBOT_EXPECTEDLIFETIMEVALUE_H +#ifndef _PLAYERBOT_EstimatedLifetimeValue_H +#define _PLAYERBOT_EstimatedLifetimeValue_H #include "NamedObjectContext.h" #include "PossibleTargetsValue.h" @@ -15,22 +15,26 @@ class PlayerbotAI; class Unit; // [target health] / [expected group single target dps] = [expected lifetime] -class ExpectedLifetimeValue : public FloatCalculatedValue, public Qualified +class EstimatedLifetimeValue : public FloatCalculatedValue, public Qualified { public: - ExpectedLifetimeValue(PlayerbotAI* botAI) : FloatCalculatedValue(botAI, "expected lifetime") {} + EstimatedLifetimeValue(PlayerbotAI* botAI) : FloatCalculatedValue(botAI, "estimated lifetime") {} public: float Calculate() override; }; -class ExpectedGroupDpsValue : public FloatCalculatedValue +class EstimatedGroupDpsValue : public FloatCalculatedValue { public: - ExpectedGroupDpsValue(PlayerbotAI* botAI) : FloatCalculatedValue(botAI, "expected group dps", 20 * 1000) {} + EstimatedGroupDpsValue(PlayerbotAI* botAI) : FloatCalculatedValue(botAI, "estimated group dps", 20 * 1000) {} public: float Calculate() override; + +protected: + float GetBasicDps(uint32 level); + float GetBasicGs(uint32 level); }; #endif diff --git a/src/strategy/values/ExpectedLifetimeValue.cpp b/src/strategy/values/ExpectedLifetimeValue.cpp deleted file mode 100644 index 892ee349..00000000 --- a/src/strategy/values/ExpectedLifetimeValue.cpp +++ /dev/null @@ -1,102 +0,0 @@ -#include "ExpectedLifetimeValue.h" - -#include "AiFactory.h" -#include "PlayerbotAI.h" -#include "Playerbots.h" -#include "SharedDefines.h" - -float ExpectedLifetimeValue::Calculate() -{ - Unit* target = AI_VALUE(Unit*, qualifier); - if (!target || !target->IsAlive()) - { - return 0.0f; - } - float dps = AI_VALUE(float, "expected group dps"); - bool aoePenalty = AI_VALUE(uint8, "attacker count") >= 3; - if (aoePenalty) - dps *= 0.75; - float res = target->GetHealth() / dps; - // bot->Say(target->GetName() + " lifetime: " + std::to_string(res), LANG_UNIVERSAL); - return res; -} - -float ExpectedGroupDpsValue::Calculate() -{ - float dps_num; - Group* group = bot->GetGroup(); - if (!group) - { - dps_num = 0.7; - } - else - { - dps_num = botAI->GetNearGroupMemberCount() * 0.7; - } - uint32 mixedGearScore = PlayerbotAI::GetMixedGearScore(bot, true, false, 12); - // efficiency record based on rare gear level, is there better calculation method? - // float dps_efficiency = 1; - float basic_dps; - int32 basic_gs; - int32 level = bot->GetLevel(); - - if (level <= 15) - { - basic_dps = 5 + level * 1; - } - else if (level <= 25) - { - basic_dps = 20 + (level - 15) * 2; - } - else if (level <= 40) - { - basic_dps = 40 + (level - 30) * 4; - } - else if (level <= 55) - { - basic_dps = 100 + (level - 45) * 20; - } - else if (level <= 60) - { - basic_dps = 300 + (level - 55) * 50; - } - else if (level <= 70) - { - basic_dps = 450 + (level - 60) * 40; - } - else - { - basic_dps = 750 + (level - 70) * 175; - } - - if (level <= 8) - { - basic_gs = (level + 5) * 2; - } - else if (level <= 15) - { - basic_gs = (level + 5) * 3; - } - else if (level <= 60) - { - basic_gs = (level + 5) * 4; - } - else if (level <= 70) - { - basic_gs = (85 + (level - 60) * 3) * 4; - } - else - { - basic_gs = (155 + (level - 70) * 4) * 4; - } - float gap = mixedGearScore - basic_gs; - float gs_modifier = (float)mixedGearScore / basic_gs - 1; - gs_modifier = gs_modifier * 3 + 1; - - if (gs_modifier < 0.75) - gs_modifier = 0.75; - if (gs_modifier > 4) - gs_modifier = 4; - - return dps_num * basic_dps * gs_modifier; -} \ No newline at end of file diff --git a/src/strategy/values/ValueContext.h b/src/strategy/values/ValueContext.h index 6fb93fdf..f8f4a8fe 100644 --- a/src/strategy/values/ValueContext.h +++ b/src/strategy/values/ValueContext.h @@ -26,7 +26,7 @@ #include "DuelTargetValue.h" #include "EnemyHealerTargetValue.h" #include "EnemyPlayerValue.h" -#include "ExpectedLifetimeValue.h" +#include "EstimatedLifetimeValue.h" #include "Formations.h" #include "GrindTargetValue.h" #include "GroupValues.h" @@ -299,8 +299,8 @@ public: creators["boss target"] = &ValueContext::boss_target; creators["nearest triggers"] = &ValueContext::nearest_triggers; creators["neglect threat"] = &ValueContext::neglect_threat; - creators["expected lifetime"] = &ValueContext::expected_lifetime; - creators["expected group dps"] = &ValueContext::expected_group_dps; + creators["estimated lifetime"] = &ValueContext::expected_lifetime; + creators["estimated group dps"] = &ValueContext::expected_group_dps; creators["area debuff"] = &ValueContext::area_debuff; creators["nearest trap with damage"] = &ValueContext::nearest_trap_with_damange; creators["disperse distance"] = &ValueContext::disperse_distance; @@ -538,8 +538,8 @@ private: static UntypedValue* boss_target(PlayerbotAI* ai) { return new BossTargetValue(ai); } static UntypedValue* nearest_triggers(PlayerbotAI* ai) { return new NearestTriggersValue(ai); } static UntypedValue* neglect_threat(PlayerbotAI* ai) { return new NeglectThreatResetValue(ai); } - static UntypedValue* expected_lifetime(PlayerbotAI* ai) { return new ExpectedLifetimeValue(ai); } - static UntypedValue* expected_group_dps(PlayerbotAI* ai) { return new ExpectedGroupDpsValue(ai); } + static UntypedValue* expected_lifetime(PlayerbotAI* ai) { return new EstimatedLifetimeValue(ai); } + static UntypedValue* expected_group_dps(PlayerbotAI* ai) { return new EstimatedGroupDpsValue(ai); } static UntypedValue* area_debuff(PlayerbotAI* ai) { return new AreaDebuffValue(ai); } static UntypedValue* nearest_trap_with_damange(PlayerbotAI* ai) { return new NearestTrapWithDamageValue(ai); } static UntypedValue* disperse_distance(PlayerbotAI* ai) { return new DisperseDistanceValue(ai); } From 52bb8b9770051814a9f5eb2d65cadce665af0c3e Mon Sep 17 00:00:00 2001 From: Yunfan Li Date: Sun, 1 Sep 2024 17:19:12 +0800 Subject: [PATCH 15/47] General trigger fix --- src/strategy/rogue/RogueAiObjectContext.cpp | 6 ------ src/strategy/rogue/RogueTriggers.cpp | 11 ----------- src/strategy/rogue/RogueTriggers.h | 11 ----------- src/strategy/triggers/GenericTriggers.cpp | 11 +++++++++++ src/strategy/triggers/GenericTriggers.h | 13 +++++++++++++ src/strategy/triggers/TriggerContext.h | 5 +++++ 6 files changed, 29 insertions(+), 28 deletions(-) diff --git a/src/strategy/rogue/RogueAiObjectContext.cpp b/src/strategy/rogue/RogueAiObjectContext.cpp index 355c8953..351c7c12 100644 --- a/src/strategy/rogue/RogueAiObjectContext.cpp +++ b/src/strategy/rogue/RogueAiObjectContext.cpp @@ -77,8 +77,6 @@ public: creators["tricks of the trade on main tank"] = &RogueTriggerFactoryInternal::tricks_of_the_trade_on_main_tank; creators["adrenaline rush"] = &RogueTriggerFactoryInternal::adrenaline_rush; creators["blade fury"] = &RogueTriggerFactoryInternal::blade_fury; - creators["target with combo points almost dead"] = - &RogueTriggerFactoryInternal::target_with_combo_points_almost_dead; } private: @@ -102,10 +100,6 @@ private: { return new TricksOfTheTradeOnMainTankTrigger(ai); } - static Trigger* target_with_combo_points_almost_dead(PlayerbotAI* ai) - { - return new TargetWithComboPointsLowerHealTrigger(ai, 3, 3.0f); - } }; class RogueAiObjectContextInternal : public NamedObjectContext diff --git a/src/strategy/rogue/RogueTriggers.cpp b/src/strategy/rogue/RogueTriggers.cpp index ed12016e..d4b89443 100644 --- a/src/strategy/rogue/RogueTriggers.cpp +++ b/src/strategy/rogue/RogueTriggers.cpp @@ -124,14 +124,3 @@ bool OffHandWeaponNoEnchantTrigger::IsActive() return false; return true; } - -bool TargetWithComboPointsLowerHealTrigger::IsActive() -{ - Unit* target = AI_VALUE(Unit*, "current target"); - if (!target || !target->IsAlive() || !target->IsInWorld()) - { - return false; - } - return ComboPointsAvailableTrigger::IsActive() && - (target->GetHealth() / AI_VALUE(float, "estimated group dps")) <= lifeTime; -} \ No newline at end of file diff --git a/src/strategy/rogue/RogueTriggers.h b/src/strategy/rogue/RogueTriggers.h index 467bd9fc..48ce772e 100644 --- a/src/strategy/rogue/RogueTriggers.h +++ b/src/strategy/rogue/RogueTriggers.h @@ -127,17 +127,6 @@ public: TricksOfTheTradeOnMainTankTrigger(PlayerbotAI* ai) : BuffOnMainTankTrigger(ai, "tricks of the trade", true) {} }; -class TargetWithComboPointsLowerHealTrigger : public ComboPointsAvailableTrigger -{ -public: - TargetWithComboPointsLowerHealTrigger(PlayerbotAI* ai, int32 combo_point = 5, float lifeTime = 8.0f) - : ComboPointsAvailableTrigger(ai, combo_point), lifeTime(lifeTime) - { - } - bool IsActive() override; -private: - float lifeTime; -}; #endif diff --git a/src/strategy/triggers/GenericTriggers.cpp b/src/strategy/triggers/GenericTriggers.cpp index 7c40ef37..32253186 100644 --- a/src/strategy/triggers/GenericTriggers.cpp +++ b/src/strategy/triggers/GenericTriggers.cpp @@ -83,6 +83,17 @@ bool EnergyAvailable::IsActive() { return AI_VALUE2(uint8, "energy", "self targe bool ComboPointsAvailableTrigger::IsActive() { return AI_VALUE2(uint8, "combo", "current target") >= amount; } +bool TargetWithComboPointsLowerHealTrigger::IsActive() +{ + Unit* target = AI_VALUE(Unit*, "current target"); + if (!target || !target->IsAlive() || !target->IsInWorld()) + { + return false; + } + return ComboPointsAvailableTrigger::IsActive() && + (target->GetHealth() / AI_VALUE(float, "estimated group dps")) <= lifeTime; +} + bool LoseAggroTrigger::IsActive() { return !AI_VALUE2(bool, "has aggro", "current target"); } bool HasAggroTrigger::IsActive() { return AI_VALUE2(bool, "has aggro", "current target"); } diff --git a/src/strategy/triggers/GenericTriggers.h b/src/strategy/triggers/GenericTriggers.h index 35a49e3f..674b514d 100644 --- a/src/strategy/triggers/GenericTriggers.h +++ b/src/strategy/triggers/GenericTriggers.h @@ -113,6 +113,19 @@ public: bool IsActive() override; }; +class TargetWithComboPointsLowerHealTrigger : public ComboPointsAvailableTrigger +{ +public: + TargetWithComboPointsLowerHealTrigger(PlayerbotAI* ai, int32 combo_point = 5, float lifeTime = 8.0f) + : ComboPointsAvailableTrigger(ai, combo_point), lifeTime(lifeTime) + { + } + bool IsActive() override; + +private: + float lifeTime; +}; + class LoseAggroTrigger : public Trigger { public: diff --git a/src/strategy/triggers/TriggerContext.h b/src/strategy/triggers/TriggerContext.h index 788b7743..acad38f0 100644 --- a/src/strategy/triggers/TriggerContext.h +++ b/src/strategy/triggers/TriggerContext.h @@ -96,6 +96,7 @@ public: creators["combo points available"] = &TriggerContext::ComboPointsAvailable; creators["combo points 3 available"] = &TriggerContext::ComboPoints3Available; + creators["target with combo points almost dead"] = &TriggerContext::target_with_combo_points_almost_dead; creators["medium threat"] = &TriggerContext::MediumThreat; @@ -309,6 +310,10 @@ private: } static Trigger* ComboPointsAvailable(PlayerbotAI* botAI) { return new ComboPointsAvailableTrigger(botAI); } static Trigger* ComboPoints3Available(PlayerbotAI* botAI) { return new ComboPointsAvailableTrigger(botAI, 3); } + static Trigger* target_with_combo_points_almost_dead(PlayerbotAI* ai) + { + return new TargetWithComboPointsLowerHealTrigger(ai, 3, 3.0f); + } static Trigger* MediumThreat(PlayerbotAI* botAI) { return new MediumThreatTrigger(botAI); } static Trigger* Dead(PlayerbotAI* botAI) { return new DeadTrigger(botAI); } static Trigger* corpse_near(PlayerbotAI* botAI) { return new CorpseNearTrigger(botAI); } From 9aaeb67d3afbbb66f82748bcdee7d8bf977565df Mon Sep 17 00:00:00 2001 From: Yunfan Li Date: Sun, 1 Sep 2024 17:33:05 +0800 Subject: [PATCH 16/47] melee attacker arc --- src/strategy/values/AttackerWithoutAuraTargetValue.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/strategy/values/AttackerWithoutAuraTargetValue.cpp b/src/strategy/values/AttackerWithoutAuraTargetValue.cpp index e99d0f3e..99489af4 100644 --- a/src/strategy/values/AttackerWithoutAuraTargetValue.cpp +++ b/src/strategy/values/AttackerWithoutAuraTargetValue.cpp @@ -52,7 +52,7 @@ Unit* MeleeAttackerWithoutAuraTargetValue::Calculate() if (!bot->IsWithinMeleeRange(unit)) continue; - if (checkArc && !bot->HasInArc(M_PI, unit)) + if (checkArc && !bot->HasInArc(CAST_ANGLE_IN_FRONT, unit)) continue; if (unit->GetHealth() < max_health) From 6060843d3239566e27998f0490a307e8a5d97318 Mon Sep 17 00:00:00 2001 From: Yunfan Li Date: Sun, 1 Sep 2024 17:44:23 +0800 Subject: [PATCH 17/47] Ferocious bite time fix --- src/strategy/druid/DruidTriggers.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/strategy/druid/DruidTriggers.h b/src/strategy/druid/DruidTriggers.h index b0f13205..3b8fdce7 100644 --- a/src/strategy/druid/DruidTriggers.h +++ b/src/strategy/druid/DruidTriggers.h @@ -252,12 +252,12 @@ public: return false; Aura* roar = botAI->GetAura("savage roar", bot); - bool roarCheck = roar && roar->GetDuration() > 8000; + bool roarCheck = !roar || roar->GetDuration() > 8000; if (!roarCheck) return false; Aura* rip = botAI->GetAura("rip", target, true); - bool ripCheck = rip && rip->GetDuration() > 8000; + bool ripCheck = !rip || rip->GetDuration() > 8000; if (!ripCheck) return false; From 87745590da4deed58ff9dcc58169432d9697cae5 Mon Sep 17 00:00:00 2001 From: Fuzz Date: Sun, 1 Sep 2024 20:09:35 +1000 Subject: [PATCH 18/47] aq20 ossirian strat --- src/PlayerbotAI.cpp | 3 ++ src/strategy/AiObjectContext.cpp | 4 ++ src/strategy/actions/MovementActions.cpp | 16 +++++- src/strategy/actions/MovementActions.h | 5 +- src/strategy/raids/RaidStrategyContext.h | 5 +- .../raids/aq20/RaidAq20ActionContext.h | 20 ++++++++ src/strategy/raids/aq20/RaidAq20Actions.cpp | 49 +++++++++++++++++++ src/strategy/raids/aq20/RaidAq20Actions.h | 14 ++++++ src/strategy/raids/aq20/RaidAq20Strategy.cpp | 11 +++++ src/strategy/raids/aq20/RaidAq20Strategy.h | 17 +++++++ .../raids/aq20/RaidAq20TriggerContext.h | 20 ++++++++ src/strategy/raids/aq20/RaidAq20Triggers.cpp | 37 ++++++++++++++ src/strategy/raids/aq20/RaidAq20Triggers.h | 14 ++++++ src/strategy/raids/aq20/RaidAq20Utils.cpp | 38 ++++++++++++++ src/strategy/raids/aq20/RaidAq20Utils.h | 15 ++++++ 15 files changed, 263 insertions(+), 5 deletions(-) create mode 100644 src/strategy/raids/aq20/RaidAq20ActionContext.h create mode 100644 src/strategy/raids/aq20/RaidAq20Actions.cpp create mode 100644 src/strategy/raids/aq20/RaidAq20Actions.h create mode 100644 src/strategy/raids/aq20/RaidAq20Strategy.cpp create mode 100644 src/strategy/raids/aq20/RaidAq20Strategy.h create mode 100644 src/strategy/raids/aq20/RaidAq20TriggerContext.h create mode 100644 src/strategy/raids/aq20/RaidAq20Triggers.cpp create mode 100644 src/strategy/raids/aq20/RaidAq20Triggers.h create mode 100644 src/strategy/raids/aq20/RaidAq20Utils.cpp create mode 100644 src/strategy/raids/aq20/RaidAq20Utils.h diff --git a/src/PlayerbotAI.cpp b/src/PlayerbotAI.cpp index 07b07293..848ee41e 100644 --- a/src/PlayerbotAI.cpp +++ b/src/PlayerbotAI.cpp @@ -1507,6 +1507,9 @@ void PlayerbotAI::ApplyInstanceStrategies(uint32 mapId, bool tellMaster) case 409: strategyName = "mc"; break; + case 509: + strategyName = "aq20"; + break; default: break; } diff --git a/src/strategy/AiObjectContext.cpp b/src/strategy/AiObjectContext.cpp index 9052b61b..1735d56d 100644 --- a/src/strategy/AiObjectContext.cpp +++ b/src/strategy/AiObjectContext.cpp @@ -24,6 +24,8 @@ #include "raids/naxxramas/RaidNaxxTriggerContext.h" #include "raids/moltencore/RaidMcActionContext.h" #include "raids/moltencore/RaidMcTriggerContext.h" +#include "raids/aq20/RaidAq20ActionContext.h" +#include "raids/aq20/RaidAq20TriggerContext.h" AiObjectContext::AiObjectContext(PlayerbotAI* botAI) : PlayerbotAIAware(botAI) { @@ -40,6 +42,7 @@ AiObjectContext::AiObjectContext(PlayerbotAI* botAI) : PlayerbotAIAware(botAI) actionContexts.Add(new RaidNaxxActionContext()); actionContexts.Add(new RaidUlduarActionContext()); actionContexts.Add(new RaidMcActionContext()); + actionContexts.Add(new RaidAq20ActionContext()); triggerContexts.Add(new TriggerContext()); triggerContexts.Add(new ChatTriggerContext()); @@ -48,6 +51,7 @@ AiObjectContext::AiObjectContext(PlayerbotAI* botAI) : PlayerbotAIAware(botAI) triggerContexts.Add(new RaidNaxxTriggerContext()); triggerContexts.Add(new RaidUlduarTriggerContext()); triggerContexts.Add(new RaidMcTriggerContext()); + triggerContexts.Add(new RaidAq20TriggerContext()); valueContexts.Add(new ValueContext()); diff --git a/src/strategy/actions/MovementActions.cpp b/src/strategy/actions/MovementActions.cpp index 7f47f9e3..57eb4255 100644 --- a/src/strategy/actions/MovementActions.cpp +++ b/src/strategy/actions/MovementActions.cpp @@ -762,7 +762,7 @@ bool MovementAction::MoveTo(uint32 mapId, float x, float y, float z, bool idle, // return true; } -bool MovementAction::MoveTo(Unit* target, float distance, MovementPriority priority) +bool MovementAction::MoveTo(WorldObject* target, float distance, MovementPriority priority) { if (!IsMovingAllowed(target)) return false; @@ -874,7 +874,7 @@ float MovementAction::GetFollowAngle() return 0; } -bool MovementAction::IsMovingAllowed(Unit* target) +bool MovementAction::IsMovingAllowed(WorldObject* target) { if (!target) return false; @@ -1272,6 +1272,8 @@ bool MovementAction::ChaseTo(WorldObject* obj, float distance, float angle) // bot->GetMotionMaster()->Clear(); bot->GetMotionMaster()->MoveChase((Unit*)obj, distance); + + // TODO shouldnt this use "last movement" value? WaitForReach(bot->GetExactDist2d(obj) - distance); return true; } @@ -1295,6 +1297,7 @@ float MovementAction::MoveDelay(float distance) return delay; } +// TODO should this be removed? (or modified to use "last movement" value?) void MovementAction::WaitForReach(float distance) { float delay = 1000.0f * MoveDelay(distance); @@ -1313,6 +1316,15 @@ void MovementAction::WaitForReach(float distance) botAI->SetNextCheckDelay((uint32)delay); } +// similiar to botAI->SetNextCheckDelay() but only stops movement +void MovementAction::SetNextMovementDelay(float delayMillis) +{ + AI_VALUE(LastMovement&, "last movement") + .Set(bot->GetMapId(), bot->GetPositionX(), bot->GetPositionY(), bot->GetPositionZ(), bot->GetOrientation(), + delayMillis, + MovementPriority::MOVEMENT_FORCED); +} + bool MovementAction::Flee(Unit* target) { Player* master = GetMaster(); diff --git a/src/strategy/actions/MovementActions.h b/src/strategy/actions/MovementActions.h index 6ef93475..7edf119b 100644 --- a/src/strategy/actions/MovementActions.h +++ b/src/strategy/actions/MovementActions.h @@ -30,7 +30,7 @@ protected: bool MoveToLOS(WorldObject* target, bool ranged = false); bool MoveTo(uint32 mapId, float x, float y, float z, bool idle = false, bool react = false, bool normal_only = false, bool exact_waypoint = false, MovementPriority priority = MovementPriority::MOVEMENT_NORMAL); - bool MoveTo(Unit* target, float distance = 0.0f, MovementPriority priority = MovementPriority::MOVEMENT_NORMAL); + bool MoveTo(WorldObject* target, float distance = 0.0f, MovementPriority priority = MovementPriority::MOVEMENT_NORMAL); bool MoveNear(WorldObject* target, float distance = sPlayerbotAIConfig->contactDistance, MovementPriority priority = MovementPriority::MOVEMENT_NORMAL); float GetFollowAngle(); bool Follow(Unit* target, float distance = sPlayerbotAIConfig->followDistance); @@ -39,7 +39,8 @@ protected: bool ReachCombatTo(Unit* target, float distance = 0.0f); float MoveDelay(float distance); void WaitForReach(float distance); - bool IsMovingAllowed(Unit* target); + void SetNextMovementDelay(float delayMillis); + bool IsMovingAllowed(WorldObject* target); bool IsMovingAllowed(uint32 mapId, float x, float y, float z); bool IsDuplicateMove(uint32 mapId, float x, float y, float z); bool IsWaitingForLastMove(MovementPriority priority); diff --git a/src/strategy/raids/RaidStrategyContext.h b/src/strategy/raids/RaidStrategyContext.h index 29e997d2..a60ed12d 100644 --- a/src/strategy/raids/RaidStrategyContext.h +++ b/src/strategy/raids/RaidStrategyContext.h @@ -6,6 +6,7 @@ #include "RaidBwlStrategy.h" #include "RaidNaxxStrategy.h" #include "RaidMcStrategy.h" +#include "RaidAq20Strategy.h" class RaidStrategyContext : public NamedObjectContext { @@ -19,6 +20,7 @@ public: creators["bwl"] = &RaidStrategyContext::bwl; creators["uld"] = &RaidStrategyContext::uld; creators["mc"] = &RaidStrategyContext::mc; + creators["aq20"] = &RaidStrategyContext::aq20; } private: @@ -26,6 +28,7 @@ private: static Strategy* bwl(PlayerbotAI* botAI) { return new RaidBwlStrategy(botAI); } static Strategy* uld(PlayerbotAI* botAI) { return new RaidUlduarStrategy(botAI); } static Strategy* mc(PlayerbotAI* botAI) { return new RaidMcStrategy(botAI); } + static Strategy* aq20(PlayerbotAI* botAI) { return new RaidAq20Strategy(botAI); } }; -#endif \ No newline at end of file +#endif diff --git a/src/strategy/raids/aq20/RaidAq20ActionContext.h b/src/strategy/raids/aq20/RaidAq20ActionContext.h new file mode 100644 index 00000000..ea3afcf4 --- /dev/null +++ b/src/strategy/raids/aq20/RaidAq20ActionContext.h @@ -0,0 +1,20 @@ +#ifndef _PLAYERBOT_RAIDAQ20ACTIONCONTEXT_H +#define _PLAYERBOT_RAIDAQ20ACTIONCONTEXT_H + +#include "Action.h" +#include "NamedObjectContext.h" +#include "RaidAq20Actions.h" + +class RaidAq20ActionContext : public NamedObjectContext +{ +public: + RaidAq20ActionContext() + { + creators["aq20 use crystal"] = &RaidAq20ActionContext::use_crystal; + } + +private: + static Action* use_crystal(PlayerbotAI* ai) { return new Aq20UseCrystalAction(ai); } +}; + +#endif diff --git a/src/strategy/raids/aq20/RaidAq20Actions.cpp b/src/strategy/raids/aq20/RaidAq20Actions.cpp new file mode 100644 index 00000000..9b7e4065 --- /dev/null +++ b/src/strategy/raids/aq20/RaidAq20Actions.cpp @@ -0,0 +1,49 @@ +#include "RaidAq20Actions.h" + +#include "Playerbots.h" +#include "RaidAq20Utils.h" + + +bool Aq20UseCrystalAction::Execute(Event event) +{ + if (Unit* boss = AI_VALUE2(Unit*, "find target", "ossirian the unscarred")) + { + if (GameObject* crystal = RaidAq20Utils::GetNearestCrystal(boss)) + { + float botDist = bot->GetDistance(crystal); + if (botDist > INTERACTION_DISTANCE) + return MoveTo(bot->GetMapId(), + crystal->GetPositionX() + frand(-3.5f, 3.5f), + crystal->GetPositionY() + frand(-3.5f, 3.5f), + crystal->GetPositionZ()); + + // if we're already in range just wait here until it's time to activate crystal + SetNextMovementDelay(500); + + // don't activate crystal if boss too far or its already been activated + if (boss->GetDistance(crystal) > 25.0f || + crystal->HasGameObjectFlag(GO_FLAG_IN_USE)) + return false; + + // don't activate crystal if boss doesn't have buff yet AND isn't going to have it soon + // (though ideally bot should activate it ~5 seconds early due to time it takes for + // crystal to activate and remove buff) + if (!RaidAq20Utils::IsOssirianBuffActive(boss) && + RaidAq20Utils::GetOssirianDebuffTimeRemaining(boss) > 5000) + return false; + + // this makes crystal do its animation (then disappear after) + WorldPacket data1(CMSG_GAMEOBJ_USE); + data1 << crystal->GetGUID(); + bot->GetSession()->HandleGameObjectUseOpcode(data1); + + // this makes crystal actually remove the buff and put on debuff (took a while to figure that out) + WorldPacket data2(CMSG_GAMEOBJ_USE); + data2 << crystal->GetGUID(); + bot->GetSession()->HandleGameobjectReportUse(data2); + + return true; + } + } + return false; +} diff --git a/src/strategy/raids/aq20/RaidAq20Actions.h b/src/strategy/raids/aq20/RaidAq20Actions.h new file mode 100644 index 00000000..59a9b437 --- /dev/null +++ b/src/strategy/raids/aq20/RaidAq20Actions.h @@ -0,0 +1,14 @@ +#ifndef _PLAYERBOT_RAIDAQ20ACTIONS_H +#define _PLAYERBOT_RAIDAQ20ACTIONS_H + +#include "MovementActions.h" +#include "PlayerbotAI.h" +#include "Playerbots.h" + +class Aq20UseCrystalAction : public MovementAction +{ +public: + Aq20UseCrystalAction(PlayerbotAI* botAI, std::string const name = "aq20 use crystal") : MovementAction(botAI, name) {} + bool Execute(Event event) override; +}; +#endif diff --git a/src/strategy/raids/aq20/RaidAq20Strategy.cpp b/src/strategy/raids/aq20/RaidAq20Strategy.cpp new file mode 100644 index 00000000..2b8cbe8c --- /dev/null +++ b/src/strategy/raids/aq20/RaidAq20Strategy.cpp @@ -0,0 +1,11 @@ +#include "RaidAq20Strategy.h" + +#include "Strategy.h" + +void RaidAq20Strategy::InitTriggers(std::vector& triggers) +{ + triggers.push_back( + new TriggerNode("aq20 move to crystal", + NextAction::array(0, new NextAction("aq20 use crystal", ACTION_RAID), nullptr))); + +} diff --git a/src/strategy/raids/aq20/RaidAq20Strategy.h b/src/strategy/raids/aq20/RaidAq20Strategy.h new file mode 100644 index 00000000..97ff7453 --- /dev/null +++ b/src/strategy/raids/aq20/RaidAq20Strategy.h @@ -0,0 +1,17 @@ +#ifndef _PLAYERBOT_RAIDAQ20STRATEGY_H +#define _PLAYERBOT_RAIDAQ20STRATEGY_H + +#include "AiObjectContext.h" +#include "Multiplier.h" +#include "Strategy.h" + +class RaidAq20Strategy : public Strategy +{ +public: + RaidAq20Strategy(PlayerbotAI* ai) : Strategy(ai) {} + virtual std::string const getName() override { return "aq20"; } + virtual void InitTriggers(std::vector& triggers) override; + // virtual void InitMultipliers(std::vector &multipliers) override; +}; + +#endif diff --git a/src/strategy/raids/aq20/RaidAq20TriggerContext.h b/src/strategy/raids/aq20/RaidAq20TriggerContext.h new file mode 100644 index 00000000..b49ae1c6 --- /dev/null +++ b/src/strategy/raids/aq20/RaidAq20TriggerContext.h @@ -0,0 +1,20 @@ +#ifndef _PLAYERBOT_RAIDAQ20TRIGGERCONTEXT_H +#define _PLAYERBOT_RAIDAQ20TRIGGERCONTEXT_H + +#include "AiObjectContext.h" +#include "NamedObjectContext.h" +#include "RaidAq20Triggers.h" + +class RaidAq20TriggerContext : public NamedObjectContext +{ +public: + RaidAq20TriggerContext() + { + creators["aq20 move to crystal"] = &RaidAq20TriggerContext::move_to_crystal; + } + +private: + static Trigger* move_to_crystal(PlayerbotAI* ai) { return new Aq20MoveToCrystalTrigger(ai); } +}; + +#endif diff --git a/src/strategy/raids/aq20/RaidAq20Triggers.cpp b/src/strategy/raids/aq20/RaidAq20Triggers.cpp new file mode 100644 index 00000000..fbda881a --- /dev/null +++ b/src/strategy/raids/aq20/RaidAq20Triggers.cpp @@ -0,0 +1,37 @@ +#include "RaidAq20Triggers.h" + +#include "SharedDefines.h" +#include "RaidAq20Utils.h" + + +bool Aq20MoveToCrystalTrigger::IsActive() +{ + if (Unit* boss = AI_VALUE2(Unit*, "find target", "ossirian the unscarred")) + { + if (boss->IsInCombat()) + { + // if buff is active move to crystal + if (RaidAq20Utils::IsOssirianBuffActive(boss)) + return true; + + // if buff is not active a debuff will be, buff becomes active once debuff expires + // so move to crystal when debuff almost done, or based debuff time left and + // distance bot is from crystal (ie: start moving early enough to make it) + int32 debuffTimeRemaining = RaidAq20Utils::GetOssirianDebuffTimeRemaining(boss); + if (debuffTimeRemaining < 5000) + return true; + if (debuffTimeRemaining < 30000) + { + if (GameObject* crystal = RaidAq20Utils::GetNearestCrystal(boss)) + { + float botDist = bot->GetDistance(crystal); + float timeToReach = botDist / bot->GetSpeed(MOVE_RUN); + // bot should ideally activate crystal a ~5 seconds early (due to time it takes for crystal + // to activate) so aim to get there in time to do so + return debuffTimeRemaining - 5000 < timeToReach * 1000.0f; + } + } + } + } + return false; +} diff --git a/src/strategy/raids/aq20/RaidAq20Triggers.h b/src/strategy/raids/aq20/RaidAq20Triggers.h new file mode 100644 index 00000000..219de8f4 --- /dev/null +++ b/src/strategy/raids/aq20/RaidAq20Triggers.h @@ -0,0 +1,14 @@ +#ifndef _PLAYERBOT_RAIDAQ20TRIGGERS_H +#define _PLAYERBOT_RAIDAQ20TRIGGERS_H + +#include "PlayerbotAI.h" +#include "Playerbots.h" +#include "Trigger.h" + +class Aq20MoveToCrystalTrigger : public Trigger +{ +public: + Aq20MoveToCrystalTrigger(PlayerbotAI* botAI) : Trigger(botAI, "aq20 move to crystal") {} + bool IsActive() override; +}; +#endif diff --git a/src/strategy/raids/aq20/RaidAq20Utils.cpp b/src/strategy/raids/aq20/RaidAq20Utils.cpp new file mode 100644 index 00000000..41637af7 --- /dev/null +++ b/src/strategy/raids/aq20/RaidAq20Utils.cpp @@ -0,0 +1,38 @@ +#include "RaidAq20Utils.h" + +#include "SpellAuras.h" + +uint32 const OSSIRIAN_BUFF = 25176; +uint32 const OSSIRIAN_DEBUFFS[] = {25177, 25178, 25180, 25181, 25183}; +uint32 const OSSIRIAN_CRYSTAL_GO_ENTRY = 180619; + +bool RaidAq20Utils::IsOssirianBuffActive(Unit* ossirian) +{ + return ossirian && ossirian->HasAura(OSSIRIAN_BUFF); +} + +int32 RaidAq20Utils::GetOssirianDebuffTimeRemaining(Unit* ossirian) +{ + int32 retVal = 0xffffff; + if (ossirian) + { + for (uint32 debuff : OSSIRIAN_DEBUFFS) + { + if (AuraApplication* auraApplication = ossirian->GetAuraApplication(debuff)) + { + if (Aura* aura = auraApplication->GetBase()) + { + int32 duration = aura->GetDuration(); + if (retVal > duration) + retVal = duration; + } + } + } + } + return retVal; +} + +GameObject* RaidAq20Utils::GetNearestCrystal(Unit* ossirian) +{ + return ossirian ? ossirian->FindNearestGameObject(OSSIRIAN_CRYSTAL_GO_ENTRY, 200.0f) : nullptr; +} diff --git a/src/strategy/raids/aq20/RaidAq20Utils.h b/src/strategy/raids/aq20/RaidAq20Utils.h new file mode 100644 index 00000000..b78340d0 --- /dev/null +++ b/src/strategy/raids/aq20/RaidAq20Utils.h @@ -0,0 +1,15 @@ +#ifndef _PLAYERBOT_RAIDAQ20UTILS_H +#define _PLAYERBOT_RAIDAQ20UTILS_H + +#include "GameObject.h" +#include "Unit.h" + +class RaidAq20Utils +{ +public: + static bool IsOssirianBuffActive(Unit* ossirian); + static int32 GetOssirianDebuffTimeRemaining(Unit* ossirian); + static GameObject* GetNearestCrystal(Unit* ossirian); +}; + +#endif From aab8cf04d3988a3ff32e56c773a970c7743d3689 Mon Sep 17 00:00:00 2001 From: Yunfan Li Date: Sun, 1 Sep 2024 23:28:22 +0800 Subject: [PATCH 19/47] Improve estimated group dps --- src/PlayerbotAI.cpp | 31 ++++++++- src/PlayerbotAI.h | 1 + src/factory/PlayerbotFactory.cpp | 2 +- src/factory/StatsWeightCalculator.cpp | 66 ++++++++++--------- src/strategy/druid/DruidTriggers.cpp | 2 - src/strategy/druid/DruidTriggers.h | 2 - .../values/EstimatedLifetimeValue.cpp | 38 +++++++---- 7 files changed, 90 insertions(+), 52 deletions(-) diff --git a/src/PlayerbotAI.cpp b/src/PlayerbotAI.cpp index c8842cf4..c9d9d798 100644 --- a/src/PlayerbotAI.cpp +++ b/src/PlayerbotAI.cpp @@ -4323,7 +4323,7 @@ void PlayerbotAI::_fillGearScoreData(Player* player, Item* item, std::vectorInventoryType; - uint32 level = mixed ? proto->ItemLevel * (1 + proto->Quality) : proto->ItemLevel; + uint32 level = mixed ? proto->ItemLevel * PlayerbotAI::GetItemScoreMultiplier(ItemQualities(proto->Quality)) : proto->ItemLevel; switch (type) { @@ -5834,4 +5834,33 @@ void PlayerbotAI::PetFollow() charmInfo->RemoveStayPosition(); charmInfo->SetForcedSpell(0); charmInfo->SetForcedTargetGUID(); +} + +float PlayerbotAI::GetItemScoreMultiplier(ItemQualities quality) +{ + switch (quality) + { + // each quality increase 1.1x + case ITEM_QUALITY_POOR: + return 1.0f; + break; + case ITEM_QUALITY_NORMAL: + return 1.1f; + break; + case ITEM_QUALITY_UNCOMMON: + return 1.21f; + break; + case ITEM_QUALITY_RARE: + return 1.331f; + break; + case ITEM_QUALITY_EPIC: + return 1.4641f; + break; + case ITEM_QUALITY_LEGENDARY: + return 1.61051f; + break; + default: + break; + } + return 1.0f; } \ No newline at end of file diff --git a/src/PlayerbotAI.h b/src/PlayerbotAI.h index 73bc61a0..c2a94625 100644 --- a/src/PlayerbotAI.h +++ b/src/PlayerbotAI.h @@ -569,6 +569,7 @@ public: std::set GetAllCurrentQuestIds(); std::set GetCurrentIncompleteQuestIds(); void PetFollow(); + static float GetItemScoreMultiplier(ItemQualities quality); private: static void _fillGearScoreData(Player* player, Item* item, std::vector* gearScore, uint32& twoHandScore, diff --git a/src/factory/PlayerbotFactory.cpp b/src/factory/PlayerbotFactory.cpp index e03447cb..c83440db 100644 --- a/src/factory/PlayerbotFactory.cpp +++ b/src/factory/PlayerbotFactory.cpp @@ -2646,7 +2646,7 @@ void PlayerbotFactory::InitAmmo() bot->SetAmmo(entry); } -uint32 PlayerbotFactory::CalcMixedGearScore(uint32 gs, uint32 quality) { return gs * (quality + 1); } +uint32 PlayerbotFactory::CalcMixedGearScore(uint32 gs, uint32 quality) { return gs * PlayerbotAI::GetItemScoreMultiplier(ItemQualities(quality)); } void PlayerbotFactory::InitMounts() { diff --git a/src/factory/StatsWeightCalculator.cpp b/src/factory/StatsWeightCalculator.cpp index b0055272..dfb83943 100644 --- a/src/factory/StatsWeightCalculator.cpp +++ b/src/factory/StatsWeightCalculator.cpp @@ -12,6 +12,7 @@ #include "ItemTemplate.h" #include "ObjectMgr.h" #include "PlayerbotAI.h" +#include "PlayerbotFactory.h" #include "SharedDefines.h" #include "StatsCollector.h" #include "Unit.h" @@ -49,12 +50,12 @@ void StatsWeightCalculator::Reset() float StatsWeightCalculator::CalculateItem(uint32 itemId) { ItemTemplate const* proto = &sObjectMgr->GetItemTemplateStore()->at(itemId); - + if (!proto) return 0.0f; Reset(); - + collector_->CollectItemStats(proto); GenerateWeights(player_); @@ -67,25 +68,25 @@ float StatsWeightCalculator::CalculateItem(uint32 itemId) if (enable_item_set_bonus_) CalculateItemSetBonus(player_, proto); - + CalculateSocketBonus(player_, proto); if (enable_quality_blend_) // Blend with item quality and level - weight_ *= (proto->Quality + 1) * proto->ItemLevel; - + weight_ *= PlayerbotFactory::CalcMixedGearScore(proto->ItemLevel, proto->Quality); + return weight_; } float StatsWeightCalculator::CalculateEnchant(uint32 enchantId) { SpellItemEnchantmentEntry const* enchant = sSpellItemEnchantmentStore.LookupEntry(enchantId); - + if (!enchant) return 0.0f; Reset(); - + collector_->CollectEnchantStats(enchant); GenerateWeights(player_); @@ -121,7 +122,7 @@ void StatsWeightCalculator::GenerateBasicWeights(Player* player) stats_weights_[STATS_TYPE_CRIT] += 1.5f; stats_weights_[STATS_TYPE_HASTE] += 1.4f; stats_weights_[STATS_TYPE_RANGED_DPS] += 5.0f; - } + } else if (cls == CLASS_HUNTER && tab == HUNTER_TAB_MARKSMANSHIP) { stats_weights_[STATS_TYPE_AGILITY] += 2.2f; @@ -168,7 +169,7 @@ void StatsWeightCalculator::GenerateBasicWeights(Player* player) stats_weights_[STATS_TYPE_EXPERTISE] += 2.0f; stats_weights_[STATS_TYPE_MELEE_DPS] += 5.0f; } - else if (cls == CLASS_WARRIOR && tab == WARRIOR_TAB_FURY) // fury + else if (cls == CLASS_WARRIOR && tab == WARRIOR_TAB_FURY) // fury { stats_weights_[STATS_TYPE_AGILITY] += 1.8f; stats_weights_[STATS_TYPE_STRENGTH] += 2.6f; @@ -180,7 +181,7 @@ void StatsWeightCalculator::GenerateBasicWeights(Player* player) stats_weights_[STATS_TYPE_EXPERTISE] += 2.5f; stats_weights_[STATS_TYPE_MELEE_DPS] += 7.0f; } - else if (cls == CLASS_WARRIOR && tab == WARRIOR_TAB_ARMS) // arm + else if (cls == CLASS_WARRIOR && tab == WARRIOR_TAB_ARMS) // arm { stats_weights_[STATS_TYPE_AGILITY] += 1.6f; stats_weights_[STATS_TYPE_STRENGTH] += 2.3f; @@ -192,7 +193,7 @@ void StatsWeightCalculator::GenerateBasicWeights(Player* player) stats_weights_[STATS_TYPE_EXPERTISE] += 1.4f; stats_weights_[STATS_TYPE_MELEE_DPS] += 7.0f; } - else if (cls == CLASS_DEATH_KNIGHT && tab == DEATHKNIGHT_TAB_FROST) // frost dk + else if (cls == CLASS_DEATH_KNIGHT && tab == DEATHKNIGHT_TAB_FROST) // frost dk { stats_weights_[STATS_TYPE_AGILITY] += 1.8f; stats_weights_[STATS_TYPE_STRENGTH] += 2.6f; @@ -216,7 +217,7 @@ void StatsWeightCalculator::GenerateBasicWeights(Player* player) stats_weights_[STATS_TYPE_EXPERTISE] += 1.0f; stats_weights_[STATS_TYPE_MELEE_DPS] += 5.0f; } - else if (cls == CLASS_PALADIN && tab == PALADIN_TAB_RETRIBUTION) // retribution + else if (cls == CLASS_PALADIN && tab == PALADIN_TAB_RETRIBUTION) // retribution { stats_weights_[STATS_TYPE_AGILITY] += 1.1f; stats_weights_[STATS_TYPE_STRENGTH] += 2.5f; @@ -244,8 +245,7 @@ void StatsWeightCalculator::GenerateBasicWeights(Player* player) stats_weights_[STATS_TYPE_EXPERTISE] += 2.0f; stats_weights_[STATS_TYPE_MELEE_DPS] += 8.5f; } - else if (cls == CLASS_WARLOCK || - cls == CLASS_MAGE || + else if (cls == CLASS_WARLOCK || cls == CLASS_MAGE || (cls == CLASS_PRIEST && tab == PRIEST_TAB_SHADOW) || // shadow (cls == CLASS_SHAMAN && tab == SHAMAN_TAB_ELEMENTAL) || // element (cls == CLASS_DRUID && tab == DRUID_TAB_BALANCE)) // balance @@ -272,7 +272,8 @@ void StatsWeightCalculator::GenerateBasicWeights(Player* player) stats_weights_[STATS_TYPE_HASTE] += 1.0f; stats_weights_[STATS_TYPE_RANGED_DPS] += 1.0f; } - else if ((cls == CLASS_WARRIOR && tab == WARRIOR_TAB_PROTECTION) || (cls == CLASS_PALADIN && tab == PALADIN_TAB_PROTECTION)) + else if ((cls == CLASS_WARRIOR && tab == WARRIOR_TAB_PROTECTION) || + (cls == CLASS_PALADIN && tab == PALADIN_TAB_PROTECTION)) { stats_weights_[STATS_TYPE_AGILITY] += 2.0f; stats_weights_[STATS_TYPE_STRENGTH] += 1.0f; @@ -361,33 +362,33 @@ void StatsWeightCalculator::CalculateItemSetBonus(Player* player, ItemTemplate c if (player->ItemSetEff[i]) { ItemSetEffect* eff = player->ItemSetEff[i]; - + uint32 setId = eff->setid; if (itemSet != setId) continue; - - const ItemSetEntry *setEntry = sItemSetStore.LookupEntry(setId); + + const ItemSetEntry* setEntry = sItemSetStore.LookupEntry(setId); if (!setEntry) continue; - + uint32 itemCount = eff->item_count; uint32 max_items = 0; for (size_t j = 0; j < MAX_ITEM_SET_SPELLS; j++) max_items = std::max(max_items, setEntry->items_to_triggerspell[j]); if (itemCount < max_items) { - multiplier += 0.1f * itemCount; // 10% bonus for each item already equipped + multiplier += 0.1f * itemCount; // 10% bonus for each item already equipped } else { - multiplier = 1.0f; // All item set effect has been triggerred + multiplier = 1.0f; // All item set effect has been triggerred } break; } } if (i == player->ItemSetEff.size()) - multiplier = 1.05f; // this is the first item in the item set + multiplier = 1.05f; // this is the first item in the item set weight_ *= multiplier; } @@ -395,17 +396,18 @@ void StatsWeightCalculator::CalculateItemSetBonus(Player* player, ItemTemplate c void StatsWeightCalculator::CalculateSocketBonus(Player* player, ItemTemplate const* proto) { uint32 socketNum = 0; - for (uint32 enchant_slot = SOCK_ENCHANTMENT_SLOT; enchant_slot < SOCK_ENCHANTMENT_SLOT + MAX_GEM_SOCKETS; ++enchant_slot) + for (uint32 enchant_slot = SOCK_ENCHANTMENT_SLOT; enchant_slot < SOCK_ENCHANTMENT_SLOT + MAX_GEM_SOCKETS; + ++enchant_slot) { uint8 socketColor = proto->Socket[enchant_slot - SOCK_ENCHANTMENT_SLOT].Color; - - if (!socketColor) // no socket slot + + if (!socketColor) // no socket slot continue; socketNum++; } - float multiplier = 1.0f + socketNum * 0.03f; // 3% bonus for socket + float multiplier = 1.0f + socketNum * 0.03f; // 3% bonus for socket weight_ *= multiplier; } @@ -469,13 +471,13 @@ void StatsWeightCalculator::CalculateItemTypePenalty(ItemTemplate const* proto) { weight_ *= 0.1; } - if (cls == CLASS_ROGUE && player_->HasAura(13964) - && (proto->SubClass == ITEM_SUBCLASS_WEAPON_SWORD || proto->SubClass == ITEM_SUBCLASS_WEAPON_AXE)) + if (cls == CLASS_ROGUE && player_->HasAura(13964) && + (proto->SubClass == ITEM_SUBCLASS_WEAPON_SWORD || proto->SubClass == ITEM_SUBCLASS_WEAPON_AXE)) { weight_ *= 1.1; } - if (cls == CLASS_WARRIOR && player_->HasAura(12785) - && (proto->SubClass == ITEM_SUBCLASS_WEAPON_POLEARM || proto->SubClass == ITEM_SUBCLASS_WEAPON_AXE2)) + if (cls == CLASS_WARRIOR && player_->HasAura(12785) && + (proto->SubClass == ITEM_SUBCLASS_WEAPON_POLEARM || proto->SubClass == ITEM_SUBCLASS_WEAPON_AXE2)) { weight_ *= 1.1; } @@ -523,7 +525,7 @@ void StatsWeightCalculator::ApplyOverflowPenalty(Player* player) else if (hit_current >= hit_overflow * 0.8) stats_weights_[STATS_TYPE_HIT] /= 1.5; } - + { if (type_ == CollectorType::MELEE) { @@ -536,7 +538,7 @@ void StatsWeightCalculator::ApplyOverflowPenalty(Player* player) stats_weights_[STATS_TYPE_EXPERTISE] /= 1.5; } } - + { if (type_ == CollectorType::MELEE) { diff --git a/src/strategy/druid/DruidTriggers.cpp b/src/strategy/druid/DruidTriggers.cpp index d839f973..a083ade1 100644 --- a/src/strategy/druid/DruidTriggers.cpp +++ b/src/strategy/druid/DruidTriggers.cpp @@ -22,8 +22,6 @@ bool ThornsOnPartyTrigger::IsActive() return BuffOnPartyTrigger::IsActive() && !botAI->HasAura("thorns", GetTarget()); } -bool MoonfireTrigger::IsActive() { return DebuffTrigger::IsActive() && !GetTarget()->HasUnitState(UNIT_STATE_ROOT); } - bool EntanglingRootsKiteTrigger::IsActive() { return DebuffTrigger::IsActive() && AI_VALUE(uint8, "attacker count") < 3 && !GetTarget()->GetPower(POWER_MANA); diff --git a/src/strategy/druid/DruidTriggers.h b/src/strategy/druid/DruidTriggers.h index 3b8fdce7..bb8725f0 100644 --- a/src/strategy/druid/DruidTriggers.h +++ b/src/strategy/druid/DruidTriggers.h @@ -75,8 +75,6 @@ class MoonfireTrigger : public DebuffTrigger { public: MoonfireTrigger(PlayerbotAI* botAI) : DebuffTrigger(botAI, "moonfire", 1, true) {} - - bool IsActive() override; }; class FaerieFireTrigger : public DebuffTrigger diff --git a/src/strategy/values/EstimatedLifetimeValue.cpp b/src/strategy/values/EstimatedLifetimeValue.cpp index 4352934f..b69861c4 100644 --- a/src/strategy/values/EstimatedLifetimeValue.cpp +++ b/src/strategy/values/EstimatedLifetimeValue.cpp @@ -3,6 +3,7 @@ #include "AiFactory.h" #include "PlayerbotAI.h" #include "PlayerbotAIConfig.h" +#include "PlayerbotFactory.h" #include "Playerbots.h" #include "SharedDefines.h" @@ -26,13 +27,13 @@ float EstimatedGroupDpsValue::Calculate() { float totalDps; - std::vector groupPlayer={bot}; + std::vector groupPlayer = {bot}; if (Group* group = bot->GetGroup()) { for (GroupReference* gref = group->GetFirstMember(); gref; gref = gref->next()) { Player* member = gref->GetSource(); - if (member == bot) // calculated + if (member == bot) // calculated continue; if (!member || !member->IsInWorld()) @@ -40,7 +41,7 @@ float EstimatedGroupDpsValue::Calculate() if (member->GetMapId() != bot->GetMapId()) continue; - + if (member->GetExactDist(bot) > sPlayerbotAIConfig->sightDistance) continue; @@ -59,16 +60,25 @@ float EstimatedGroupDpsValue::Calculate() float basicDps = GetBasicDps(player->GetLevel()); float basicGs = GetBasicGs(player->GetLevel()); uint32 mixedGearScore = PlayerbotAI::GetMixedGearScore(player, true, false, 12); - - float gap = (float)mixedGearScore / basicGs - 1; - float gs_modifier = gap * 3 + 1; + float gs_modifier = (float)mixedGearScore / basicGs; + // bonus for wotlk epic gear + if (mixedGearScore >= 300) + { + gs_modifier *= 1 + (mixedGearScore - 300) * 0.01; + } if (gs_modifier < 0.75) gs_modifier = 0.75; if (gs_modifier > 4) gs_modifier = 4; totalDps += basicDps * roleMultiplier * gs_modifier; } - + // Group buff bonus + if (groupPlayer.size() >= 25) + totalDps *= 1.2; + else if (groupPlayer.size() >= 10) + totalDps *= 1.1; + else if (groupPlayer.size() >= 5) + totalDps *= 1.05; return totalDps; } @@ -98,11 +108,11 @@ float EstimatedGroupDpsValue::GetBasicDps(uint32 level) } else if (level <= 70) { - basic_dps = 450 + (level - 60) * 40; + basic_dps = 550 + (level - 60) * 65; } else { - basic_dps = 750 + (level - 70) * 175; + basic_dps = 1200 + (level - 70) * 200; } return basic_dps; } @@ -113,23 +123,23 @@ float EstimatedGroupDpsValue::GetBasicGs(uint32 level) if (level <= 8) { - basic_gs = (level + 5) * 2; + basic_gs = PlayerbotFactory::CalcMixedGearScore(level + 5, ITEM_QUALITY_NORMAL); } else if (level <= 15) { - basic_gs = (level + 5) * 3; + basic_gs = PlayerbotFactory::CalcMixedGearScore(level + 5, ITEM_QUALITY_UNCOMMON); } else if (level <= 60) { - basic_gs = (level + 5) * 4; + basic_gs = PlayerbotFactory::CalcMixedGearScore(level + 5, ITEM_QUALITY_RARE); } else if (level <= 70) { - basic_gs = (85 + (level - 60) * 3) * 4; + basic_gs = PlayerbotFactory::CalcMixedGearScore(85 + (level - 60) * 3, ITEM_QUALITY_RARE); } else { - basic_gs = (155 + (level - 70) * 4) * 4; + basic_gs = PlayerbotFactory::CalcMixedGearScore(155 + (level - 70) * 4, ITEM_QUALITY_RARE); } return basic_gs; } \ No newline at end of file From 4c77f71bd4fea2d991a1d24a22c09fd74b85f312 Mon Sep 17 00:00:00 2001 From: Yunfan Li Date: Mon, 2 Sep 2024 00:30:10 +0800 Subject: [PATCH 20/47] Cat druid energy control --- src/strategy/druid/CatDpsDruidStrategy.cpp | 15 +++++++++++---- src/strategy/druid/DruidTriggers.h | 4 ++-- src/strategy/triggers/GenericTriggers.cpp | 2 ++ src/strategy/triggers/GenericTriggers.h | 11 +++++++++++ src/strategy/triggers/TriggerContext.h | 6 ++++++ 5 files changed, 32 insertions(+), 6 deletions(-) diff --git a/src/strategy/druid/CatDpsDruidStrategy.cpp b/src/strategy/druid/CatDpsDruidStrategy.cpp index 0ae00e28..e640cebd 100644 --- a/src/strategy/druid/CatDpsDruidStrategy.cpp +++ b/src/strategy/druid/CatDpsDruidStrategy.cpp @@ -122,8 +122,7 @@ CatDpsDruidStrategy::CatDpsDruidStrategy(PlayerbotAI* botAI) : FeralDruidStrateg NextAction** CatDpsDruidStrategy::getDefaultActions() { - return NextAction::array(0, new NextAction("shred", ACTION_DEFAULT + 0.4f), - new NextAction("tiger's fury", ACTION_DEFAULT + 0.1f), nullptr); + return NextAction::array(0, new NextAction("tiger's fury", ACTION_DEFAULT + 0.1f), nullptr); } void CatDpsDruidStrategy::InitTriggers(std::vector& triggers) @@ -131,9 +130,17 @@ void CatDpsDruidStrategy::InitTriggers(std::vector& triggers) FeralDruidStrategy::InitTriggers(triggers); // Default priority - triggers.push_back(new TriggerNode("high energy available", + triggers.push_back(new TriggerNode("almost full energy available", + NextAction::array(0, new NextAction("shred", ACTION_DEFAULT + 0.4f), nullptr))); + triggers.push_back(new TriggerNode("combo points not full", + NextAction::array(0, new NextAction("shred", ACTION_DEFAULT + 0.4f), nullptr))); + triggers.push_back(new TriggerNode("almost full energy available", NextAction::array(0, new NextAction("mangle (cat)", ACTION_DEFAULT + 0.3f), nullptr))); - triggers.push_back(new TriggerNode("high energy available", + triggers.push_back(new TriggerNode("combo points not full and high energy", + NextAction::array(0, new NextAction("mangle (cat)", ACTION_DEFAULT + 0.3f), nullptr))); + triggers.push_back(new TriggerNode("almost full energy available", + NextAction::array(0, new NextAction("claw", ACTION_DEFAULT + 0.2f), nullptr))); + triggers.push_back(new TriggerNode("combo points not full and high energy", NextAction::array(0, new NextAction("claw", ACTION_DEFAULT + 0.2f), nullptr))); triggers.push_back( new TriggerNode("faerie fire (feral)", diff --git a/src/strategy/druid/DruidTriggers.h b/src/strategy/druid/DruidTriggers.h index bb8725f0..50020835 100644 --- a/src/strategy/druid/DruidTriggers.h +++ b/src/strategy/druid/DruidTriggers.h @@ -250,12 +250,12 @@ public: return false; Aura* roar = botAI->GetAura("savage roar", bot); - bool roarCheck = !roar || roar->GetDuration() > 8000; + bool roarCheck = !roar || roar->GetDuration() > 10000; if (!roarCheck) return false; Aura* rip = botAI->GetAura("rip", target, true); - bool ripCheck = !rip || rip->GetDuration() > 8000; + bool ripCheck = !rip || rip->GetDuration() > 10000; if (!ripCheck) return false; diff --git a/src/strategy/triggers/GenericTriggers.cpp b/src/strategy/triggers/GenericTriggers.cpp index 32253186..014530bc 100644 --- a/src/strategy/triggers/GenericTriggers.cpp +++ b/src/strategy/triggers/GenericTriggers.cpp @@ -83,6 +83,8 @@ bool EnergyAvailable::IsActive() { return AI_VALUE2(uint8, "energy", "self targe bool ComboPointsAvailableTrigger::IsActive() { return AI_VALUE2(uint8, "combo", "current target") >= amount; } +bool ComboPointsNotFullTrigger::IsActive() { return AI_VALUE2(uint8, "combo", "current target") < amount; } + bool TargetWithComboPointsLowerHealTrigger::IsActive() { Unit* target = AI_VALUE(Unit*, "current target"); diff --git a/src/strategy/triggers/GenericTriggers.h b/src/strategy/triggers/GenericTriggers.h index 674b514d..132e6b66 100644 --- a/src/strategy/triggers/GenericTriggers.h +++ b/src/strategy/triggers/GenericTriggers.h @@ -126,6 +126,17 @@ private: float lifeTime; }; +class ComboPointsNotFullTrigger : public StatAvailable +{ +public: + ComboPointsNotFullTrigger(PlayerbotAI* botAI, int32 amount = 5, std::string const name = "combo points not full") + : StatAvailable(botAI, amount, name) + { + } + + bool IsActive() override; +}; + class LoseAggroTrigger : public Trigger { public: diff --git a/src/strategy/triggers/TriggerContext.h b/src/strategy/triggers/TriggerContext.h index acad38f0..e1c4ec7d 100644 --- a/src/strategy/triggers/TriggerContext.h +++ b/src/strategy/triggers/TriggerContext.h @@ -64,6 +64,7 @@ public: creators["light energy available"] = &TriggerContext::LightEnergyAvailable; creators["medium energy available"] = &TriggerContext::MediumEnergyAvailable; creators["high energy available"] = &TriggerContext::HighEnergyAvailable; + creators["almost full energy available"] = &TriggerContext::AlmostFullEnergyAvailable; creators["loot available"] = &TriggerContext::LootAvailable; creators["no attackers"] = &TriggerContext::NoAttackers; @@ -97,6 +98,8 @@ public: creators["combo points available"] = &TriggerContext::ComboPointsAvailable; creators["combo points 3 available"] = &TriggerContext::ComboPoints3Available; creators["target with combo points almost dead"] = &TriggerContext::target_with_combo_points_almost_dead; + creators["combo points not full"] = &TriggerContext::ComboPointsNotFull; + creators["combo points not full and high energy"] = &TriggerContext::ComboPointsNotFullAndHighEnergy; creators["medium threat"] = &TriggerContext::MediumThreat; @@ -280,6 +283,7 @@ private: static Trigger* LightEnergyAvailable(PlayerbotAI* botAI) { return new LightEnergyAvailableTrigger(botAI); } static Trigger* MediumEnergyAvailable(PlayerbotAI* botAI) { return new MediumEnergyAvailableTrigger(botAI); } static Trigger* HighEnergyAvailable(PlayerbotAI* botAI) { return new HighEnergyAvailableTrigger(botAI); } + static Trigger* AlmostFullEnergyAvailable(PlayerbotAI* botAI) { return new EnergyAvailable(botAI, 90); } static Trigger* LootAvailable(PlayerbotAI* botAI) { return new LootAvailableTrigger(botAI); } static Trigger* NoAttackers(PlayerbotAI* botAI) { return new NoAttackersTrigger(botAI); } static Trigger* TankAssist(PlayerbotAI* botAI) { return new TankAssistTrigger(botAI); } @@ -314,6 +318,8 @@ private: { return new TargetWithComboPointsLowerHealTrigger(ai, 3, 3.0f); } + static Trigger* ComboPointsNotFull(PlayerbotAI* botAI) { return new ComboPointsNotFullTrigger(botAI); } + static Trigger* ComboPointsNotFullAndHighEnergy(PlayerbotAI* botAI) { return new TwoTriggers(botAI, "combo points not full", "high energy available"); } static Trigger* MediumThreat(PlayerbotAI* botAI) { return new MediumThreatTrigger(botAI); } static Trigger* Dead(PlayerbotAI* botAI) { return new DeadTrigger(botAI); } static Trigger* corpse_near(PlayerbotAI* botAI) { return new CorpseNearTrigger(botAI); } From ad0f65fffdcf548e9bcf6a5ab4a54f2726e4886c Mon Sep 17 00:00:00 2001 From: Yunfan Li Date: Mon, 2 Sep 2024 15:13:49 +0800 Subject: [PATCH 21/47] Warnings fix --- src/strategy/actions/SayAction.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/strategy/actions/SayAction.cpp b/src/strategy/actions/SayAction.cpp index 6bf973ab..aa9c2431 100644 --- a/src/strategy/actions/SayAction.cpp +++ b/src/strategy/actions/SayAction.cpp @@ -245,6 +245,8 @@ bool ChatReplyAction::HandleThunderfuryReply(Player* bot, ChatChannelSource chat GET_PLAYERBOT_AI(bot)->SayToChannel(responseMessage, ChatChannelId::GENERAL); break; } + default: + break; } GET_PLAYERBOT_AI(bot)->GetAiObjectContext()->GetValue("last said", "chat")->Set(time(0) + urand(5, 25)); @@ -309,6 +311,8 @@ bool ChatReplyAction::HandleToxicLinksReply(Player* bot, ChatChannelSource chatC GET_PLAYERBOT_AI(bot)->SayToGuild(BOT_TEXT2("suggest_toxic_links", placeholders)); break; } + default: + break; } GET_PLAYERBOT_AI(bot)->GetAiObjectContext()->GetValue("last said", "chat")->Set(time(0) + urand(5, 60)); @@ -404,6 +408,8 @@ bool ChatReplyAction::HandleWTBItemsReply(Player* bot, ChatChannelSource chatCha } break; } + default: + break; } GET_PLAYERBOT_AI(bot)->GetAiObjectContext()->GetValue("last said", "chat")->Set(time(0) + urand(5, 60)); } @@ -490,6 +496,8 @@ bool ChatReplyAction::HandleLFGQuestsReply(Player* bot, ChatChannelSource chatCh GET_PLAYERBOT_AI(bot)->Whisper(responseMessage, name); break; } + default: + break; } GET_PLAYERBOT_AI(bot)->GetAiObjectContext()->GetValue("last said", "chat")->Set(time(0) + urand(5, 25)); } From 4114ffa68b59015b9c445a1bb463025bb41914ae Mon Sep 17 00:00:00 2001 From: Yunfan Li Date: Mon, 2 Sep 2024 17:47:08 +0800 Subject: [PATCH 22/47] Drink threshold with freefood --- src/strategy/generic/UseFoodStrategy.cpp | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/strategy/generic/UseFoodStrategy.cpp b/src/strategy/generic/UseFoodStrategy.cpp index c0c29b51..04841e4a 100644 --- a/src/strategy/generic/UseFoodStrategy.cpp +++ b/src/strategy/generic/UseFoodStrategy.cpp @@ -5,12 +5,19 @@ #include "UseFoodStrategy.h" +#include "PlayerbotAIConfig.h" #include "Playerbots.h" void UseFoodStrategy::InitTriggers(std::vector& triggers) { Strategy::InitTriggers(triggers); + if (sPlayerbotAIConfig->freeFood) + triggers.push_back(new TriggerNode("medium health", NextAction::array(0, new NextAction("food", 3.0f), nullptr))); + else + triggers.push_back(new TriggerNode("low health", NextAction::array(0, new NextAction("food", 3.0f), nullptr))); - triggers.push_back(new TriggerNode("low health", NextAction::array(0, new NextAction("food", 3.0f), nullptr))); - triggers.push_back(new TriggerNode("low mana", NextAction::array(0, new NextAction("drink", 3.0f), nullptr))); + if (sPlayerbotAIConfig->freeFood) + triggers.push_back(new TriggerNode("high mana", NextAction::array(0, new NextAction("drink", 3.0f), nullptr))); + else + triggers.push_back(new TriggerNode("low mana", NextAction::array(0, new NextAction("drink", 3.0f), nullptr))); } From a84c7f35f3a8a7b5f8eead2cacff5eac02c53948 Mon Sep 17 00:00:00 2001 From: Yunfan Li Date: Mon, 2 Sep 2024 17:47:46 +0800 Subject: [PATCH 23/47] weapon type fix --- conf/playerbots.conf.dist | 1 + src/PlayerbotAIConfig.cpp | 1 + src/PlayerbotAIConfig.h | 2 +- src/factory/PlayerbotFactory.cpp | 27 +++++++++---------- src/factory/StatsWeightCalculator.cpp | 2 +- src/strategy/paladin/DpsPaladinStrategy.cpp | 23 ++++++++++------ .../paladin/GenericPaladinStrategy.cpp | 5 ++-- src/strategy/paladin/HealPaladinStrategy.cpp | 4 ++- src/strategy/paladin/TankPaladinStrategy.cpp | 2 ++ src/strategy/shaman/CasterShamanStrategy.cpp | 4 +-- src/strategy/triggers/GenericTriggers.cpp | 3 ++- 11 files changed, 43 insertions(+), 31 deletions(-) diff --git a/conf/playerbots.conf.dist b/conf/playerbots.conf.dist index 675c322b..88b5213c 100644 --- a/conf/playerbots.conf.dist +++ b/conf/playerbots.conf.dist @@ -342,6 +342,7 @@ AiPlayerbot.MediumHealth = 65 AiPlayerbot.AlmostFullHealth = 85 AiPlayerbot.LowMana = 15 AiPlayerbot.MediumMana = 40 +AiPlayerbot.HighMana = 65 # # diff --git a/src/PlayerbotAIConfig.cpp b/src/PlayerbotAIConfig.cpp index a87135f1..ffc41581 100644 --- a/src/PlayerbotAIConfig.cpp +++ b/src/PlayerbotAIConfig.cpp @@ -94,6 +94,7 @@ bool PlayerbotAIConfig::Initialize() almostFullHealth = sConfigMgr->GetOption("AiPlayerbot.AlmostFullHealth", 85); lowMana = sConfigMgr->GetOption("AiPlayerbot.LowMana", 15); mediumMana = sConfigMgr->GetOption("AiPlayerbot.MediumMana", 40); + highMana = sConfigMgr->GetOption("AiPlayerbot.HighMana", 65); autoSaveMana = sConfigMgr->GetOption("AiPlayerbot.AutoSaveMana", true); saveManaThreshold = sConfigMgr->GetOption("AiPlayerbot.SaveManaThreshold", 60); autoAvoidAoe = sConfigMgr->GetOption("AiPlayerbot.AutoAvoidAoe", true); diff --git a/src/PlayerbotAIConfig.h b/src/PlayerbotAIConfig.h index 70536935..57f3ce80 100644 --- a/src/PlayerbotAIConfig.h +++ b/src/PlayerbotAIConfig.h @@ -63,7 +63,7 @@ public: tooCloseDistance, meleeDistance, followDistance, whisperDistance, contactDistance, aoeRadius, rpgDistance, targetPosRecalcDistance, farDistance, healDistance, aggroDistance; uint32 criticalHealth, lowHealth, mediumHealth, almostFullHealth; - uint32 lowMana, mediumMana; + uint32 lowMana, mediumMana, highMana; bool autoSaveMana; uint32 saveManaThreshold; bool autoAvoidAoe; diff --git a/src/factory/PlayerbotFactory.cpp b/src/factory/PlayerbotFactory.cpp index c83440db..a5116fdf 100644 --- a/src/factory/PlayerbotFactory.cpp +++ b/src/factory/PlayerbotFactory.cpp @@ -1217,7 +1217,7 @@ bool PlayerbotFactory::CanEquipWeapon(ItemTemplate const* proto) { case CLASS_PRIEST: if (proto->SubClass != ITEM_SUBCLASS_WEAPON_STAFF && proto->SubClass != ITEM_SUBCLASS_WEAPON_WAND && - proto->SubClass != ITEM_SUBCLASS_WEAPON_MACE) + proto->SubClass != ITEM_SUBCLASS_WEAPON_MACE && proto->SubClass != ITEM_SUBCLASS_WEAPON_DAGGER) return false; break; case CLASS_MAGE: @@ -1231,15 +1231,12 @@ bool PlayerbotFactory::CanEquipWeapon(ItemTemplate const* proto) proto->SubClass != ITEM_SUBCLASS_WEAPON_POLEARM && proto->SubClass != ITEM_SUBCLASS_WEAPON_SWORD2 && proto->SubClass != ITEM_SUBCLASS_WEAPON_MACE && proto->SubClass != ITEM_SUBCLASS_WEAPON_SWORD && proto->SubClass != ITEM_SUBCLASS_WEAPON_GUN && proto->SubClass != ITEM_SUBCLASS_WEAPON_CROSSBOW && - proto->SubClass != ITEM_SUBCLASS_WEAPON_BOW && proto->SubClass != ITEM_SUBCLASS_WEAPON_THROWN) + proto->SubClass != ITEM_SUBCLASS_WEAPON_BOW && proto->SubClass != ITEM_SUBCLASS_WEAPON_THROWN && + proto->SubClass != ITEM_SUBCLASS_WEAPON_AXE2 && proto->SubClass != ITEM_SUBCLASS_WEAPON_FIST && + proto->SubClass != ITEM_SUBCLASS_WEAPON_DAGGER && proto->SubClass != ITEM_SUBCLASS_WEAPON_STAFF) return false; break; case CLASS_PALADIN: - if (proto->SubClass != ITEM_SUBCLASS_WEAPON_MACE2 && proto->SubClass != ITEM_SUBCLASS_WEAPON_SWORD2 && - proto->SubClass != ITEM_SUBCLASS_WEAPON_MACE && proto->SubClass != ITEM_SUBCLASS_WEAPON_AXE2 && - proto->SubClass != ITEM_SUBCLASS_WEAPON_SWORD) - return false; - break; case CLASS_DEATH_KNIGHT: if (proto->SubClass != ITEM_SUBCLASS_WEAPON_MACE2 && proto->SubClass != ITEM_SUBCLASS_WEAPON_POLEARM && proto->SubClass != ITEM_SUBCLASS_WEAPON_SWORD2 && proto->SubClass != ITEM_SUBCLASS_WEAPON_AXE2 && @@ -1251,28 +1248,30 @@ bool PlayerbotFactory::CanEquipWeapon(ItemTemplate const* proto) if (proto->SubClass != ITEM_SUBCLASS_WEAPON_MACE && proto->SubClass != ITEM_SUBCLASS_WEAPON_AXE && proto->SubClass != ITEM_SUBCLASS_WEAPON_FIST && proto->SubClass != ITEM_SUBCLASS_WEAPON_MACE2 && proto->SubClass != ITEM_SUBCLASS_WEAPON_AXE2 && proto->SubClass != ITEM_SUBCLASS_WEAPON_DAGGER && - proto->SubClass != ITEM_SUBCLASS_WEAPON_FIST && proto->SubClass != ITEM_SUBCLASS_WEAPON_STAFF) + proto->SubClass != ITEM_SUBCLASS_WEAPON_STAFF) return false; break; case CLASS_DRUID: if (proto->SubClass != ITEM_SUBCLASS_WEAPON_MACE && proto->SubClass != ITEM_SUBCLASS_WEAPON_MACE2 && - proto->SubClass != ITEM_SUBCLASS_WEAPON_DAGGER && proto->SubClass != ITEM_SUBCLASS_WEAPON_STAFF) + proto->SubClass != ITEM_SUBCLASS_WEAPON_DAGGER && proto->SubClass != ITEM_SUBCLASS_WEAPON_STAFF && + proto->SubClass != ITEM_SUBCLASS_WEAPON_POLEARM) return false; break; case CLASS_HUNTER: - if (proto->SubClass != ITEM_SUBCLASS_WEAPON_AXE2 && proto->SubClass != ITEM_SUBCLASS_WEAPON_AXE && - proto->SubClass != ITEM_SUBCLASS_WEAPON_SWORD2 && proto->SubClass != ITEM_SUBCLASS_WEAPON_POLEARM && + if (proto->SubClass != ITEM_SUBCLASS_WEAPON_DAGGER && proto->SubClass != ITEM_SUBCLASS_WEAPON_BOW && + proto->SubClass != ITEM_SUBCLASS_WEAPON_AXE2 && proto->SubClass != ITEM_SUBCLASS_WEAPON_AXE && + proto->SubClass != ITEM_SUBCLASS_WEAPON_SWORD2 && proto->SubClass != ITEM_SUBCLASS_WEAPON_SWORD && proto->SubClass != ITEM_SUBCLASS_WEAPON_FIST && proto->SubClass != ITEM_SUBCLASS_WEAPON_GUN && proto->SubClass != ITEM_SUBCLASS_WEAPON_CROSSBOW && proto->SubClass != ITEM_SUBCLASS_WEAPON_STAFF && - proto->SubClass != ITEM_SUBCLASS_WEAPON_BOW) + proto->SubClass != ITEM_SUBCLASS_WEAPON_POLEARM) return false; break; case CLASS_ROGUE: if (proto->SubClass != ITEM_SUBCLASS_WEAPON_DAGGER && proto->SubClass != ITEM_SUBCLASS_WEAPON_SWORD && proto->SubClass != ITEM_SUBCLASS_WEAPON_FIST && proto->SubClass != ITEM_SUBCLASS_WEAPON_MACE && - proto->SubClass != ITEM_SUBCLASS_WEAPON_AXE && proto->SubClass != ITEM_SUBCLASS_WEAPON_GUN && proto->SubClass != ITEM_SUBCLASS_WEAPON_CROSSBOW && - proto->SubClass != ITEM_SUBCLASS_WEAPON_BOW && proto->SubClass != ITEM_SUBCLASS_WEAPON_THROWN) + proto->SubClass != ITEM_SUBCLASS_WEAPON_BOW && proto->SubClass != ITEM_SUBCLASS_WEAPON_THROWN && + proto->SubClass != ITEM_SUBCLASS_WEAPON_AXE) return false; break; } diff --git a/src/factory/StatsWeightCalculator.cpp b/src/factory/StatsWeightCalculator.cpp index dfb83943..ef95fb9b 100644 --- a/src/factory/StatsWeightCalculator.cpp +++ b/src/factory/StatsWeightCalculator.cpp @@ -224,7 +224,7 @@ void StatsWeightCalculator::GenerateBasicWeights(Player* player) stats_weights_[STATS_TYPE_INTELLECT] += 0.15f; stats_weights_[STATS_TYPE_ATTACK_POWER] += 1.0f; stats_weights_[STATS_TYPE_SPELL_POWER] += 0.3f; - stats_weights_[STATS_TYPE_ARMOR_PENETRATION] += 0.8f; + stats_weights_[STATS_TYPE_ARMOR_PENETRATION] += 0.5f; stats_weights_[STATS_TYPE_HIT] += 1.9f; stats_weights_[STATS_TYPE_CRIT] += 1.2f; stats_weights_[STATS_TYPE_HASTE] += 1.3f; diff --git a/src/strategy/paladin/DpsPaladinStrategy.cpp b/src/strategy/paladin/DpsPaladinStrategy.cpp index ecbe3af7..723fffd3 100644 --- a/src/strategy/paladin/DpsPaladinStrategy.cpp +++ b/src/strategy/paladin/DpsPaladinStrategy.cpp @@ -82,9 +82,11 @@ DpsPaladinStrategy::DpsPaladinStrategy(PlayerbotAI* botAI) : GenericPaladinStrat NextAction** DpsPaladinStrategy::getDefaultActions() { - return NextAction::array(0, new NextAction("crusader strike", ACTION_DEFAULT + 0.4f), - new NextAction("judgement of wisdom", ACTION_DEFAULT + 0.3f), - new NextAction("divine storm", ACTION_DEFAULT + 0.2f), + return NextAction::array(0, + new NextAction("crusader strike", ACTION_DEFAULT + 0.6f), + new NextAction("hammer of wrath", ACTION_DEFAULT + 0.5f), + new NextAction("judgement of wisdom", ACTION_DEFAULT + 0.4f), + new NextAction("divine storm", ACTION_DEFAULT + 0.3f), new NextAction("consecration", ACTION_DEFAULT + 0.1f), new NextAction("melee", ACTION_DEFAULT), NULL); } @@ -93,12 +95,17 @@ void DpsPaladinStrategy::InitTriggers(std::vector& triggers) { GenericPaladinStrategy::InitTriggers(triggers); + triggers.push_back( + new TriggerNode("art of war", NextAction::array(0, new NextAction("exorcism", ACTION_DEFAULT + 0.2f), nullptr))); triggers.push_back( new TriggerNode("seal", NextAction::array(0, new NextAction("seal of corruption", ACTION_HIGH), NULL))); // triggers.push_back(new TriggerNode("seal", NextAction::array(0, new NextAction("seal of command", 90.0f), // nullptr))); triggers.push_back( new TriggerNode("low mana", NextAction::array(0, new NextAction("seal of wisdom", ACTION_HIGH + 5), nullptr))); + + triggers.push_back(new TriggerNode( + "avenging wrath", NextAction::array(0, new NextAction("avenging wrath", ACTION_HIGH + 2), nullptr))); // triggers.push_back(new TriggerNode("sanctity aura", NextAction::array(0, new NextAction("sanctity aura", 90.0f), // nullptr))); triggers.push_back(new TriggerNode("low health", NextAction::array(0, new NextAction("repentance or // shield", ACTION_CRITICAL_HEAL + 3), new NextAction("holy light", ACTION_CRITICAL_HEAL + 2), nullptr))); @@ -112,11 +119,11 @@ void DpsPaladinStrategy::InitTriggers(std::vector& triggers) // triggers.push_back(new TriggerNode("repentance", NextAction::array(0, new NextAction("repentance", // ACTION_INTERRUPT + 2), nullptr))); triggers.push_back(new TriggerNode( - "medium aoe", NextAction::array(0, new NextAction("consecration", ACTION_HIGH + 3), nullptr))); - triggers.push_back( - new TriggerNode("art of war", NextAction::array(0, new NextAction("exorcism", ACTION_HIGH + 2), nullptr))); - triggers.push_back(new TriggerNode("target critical health", - NextAction::array(0, new NextAction("hammer of wrath", ACTION_HIGH), nullptr))); + "medium aoe", NextAction::array(0, + new NextAction("divine storm", ACTION_HIGH + 4), + new NextAction("consecration", ACTION_HIGH + 3), nullptr))); + // triggers.push_back(new TriggerNode("target critical health", + // NextAction::array(0, new NextAction("hammer of wrath", ACTION_HIGH), nullptr))); // triggers.push_back(new TriggerNode( // "not facing target", // NextAction::array(0, new NextAction("set facing", ACTION_NORMAL + 7), NULL))); diff --git a/src/strategy/paladin/GenericPaladinStrategy.cpp b/src/strategy/paladin/GenericPaladinStrategy.cpp index 676a6159..d25a7518 100644 --- a/src/strategy/paladin/GenericPaladinStrategy.cpp +++ b/src/strategy/paladin/GenericPaladinStrategy.cpp @@ -37,7 +37,7 @@ void GenericPaladinStrategy::InitTriggers(std::vector& triggers) "protect party member", NextAction::array(0, new NextAction("blessing of protection on party", ACTION_EMERGENCY + 2), nullptr))); triggers.push_back( - new TriggerNode("medium mana", NextAction::array(0, new NextAction("divine plea", ACTION_HIGH), NULL))); + new TriggerNode("high mana", NextAction::array(0, new NextAction("divine plea", ACTION_HIGH), NULL))); } void PaladinCureStrategy::InitTriggers(std::vector& triggers) @@ -61,8 +61,7 @@ void PaladinCureStrategy::InitTriggers(std::vector& triggers) void PaladinBoostStrategy::InitTriggers(std::vector& triggers) { - triggers.push_back(new TriggerNode( - "avenging wrath", NextAction::array(0, new NextAction("avenging wrath", ACTION_HIGH + 2), nullptr))); + // triggers.push_back(new TriggerNode("divine favor", NextAction::array(0, new NextAction("divine favor", // ACTION_HIGH + 1), nullptr))); } diff --git a/src/strategy/paladin/HealPaladinStrategy.cpp b/src/strategy/paladin/HealPaladinStrategy.cpp index dd1edf23..70c461ce 100644 --- a/src/strategy/paladin/HealPaladinStrategy.cpp +++ b/src/strategy/paladin/HealPaladinStrategy.cpp @@ -50,7 +50,9 @@ void HealPaladinStrategy::InitTriggers(std::vector& triggers) triggers.push_back( new TriggerNode("medium group heal occasion", - NextAction::array(0, new NextAction("divine sacrifice", ACTION_CRITICAL_HEAL + 5), nullptr))); + NextAction::array(0, new NextAction("divine sacrifice", ACTION_CRITICAL_HEAL + 5), + new NextAction("avenging wrath", ACTION_HIGH + 4), + nullptr))); triggers.push_back( new TriggerNode("party member critical health", diff --git a/src/strategy/paladin/TankPaladinStrategy.cpp b/src/strategy/paladin/TankPaladinStrategy.cpp index 649fe21e..553d2a7d 100644 --- a/src/strategy/paladin/TankPaladinStrategy.cpp +++ b/src/strategy/paladin/TankPaladinStrategy.cpp @@ -98,6 +98,8 @@ void TankPaladinStrategy::InitTriggers(std::vector& triggers) NextAction::array(0, new NextAction("holy shield", ACTION_HIGH + 4), nullptr))); // triggers.push_back(new TriggerNode("blessing", NextAction::array(0, new NextAction("blessing of sanctuary", // ACTION_HIGH + 9), nullptr))); + triggers.push_back(new TriggerNode( + "avenging wrath", NextAction::array(0, new NextAction("avenging wrath", ACTION_HIGH + 2), nullptr))); triggers.push_back( new TriggerNode("target critical health", NextAction::array(0, new NextAction("hammer of wrath", ACTION_CRITICAL_HEAL), nullptr))); diff --git a/src/strategy/shaman/CasterShamanStrategy.cpp b/src/strategy/shaman/CasterShamanStrategy.cpp index c11c523d..746abf86 100644 --- a/src/strategy/shaman/CasterShamanStrategy.cpp +++ b/src/strategy/shaman/CasterShamanStrategy.cpp @@ -51,8 +51,8 @@ void CasterShamanStrategy::InitTriggers(std::vector& triggers) // triggers.push_back(new TriggerNode("enemy out of spell", NextAction::array(0, new NextAction("reach spell", // ACTION_NORMAL + 9), nullptr))); triggers.push_back(new TriggerNode("shaman weapon", NextAction::array(0, new // NextAction("flametongue weapon", 23.0f), nullptr))); - triggers.push_back(new TriggerNode( - "enough mana", NextAction::array(0, new NextAction("chain lightning", ACTION_DEFAULT + 0.1f), nullptr))); + // triggers.push_back(new TriggerNode( + // "enough mana", NextAction::array(0, new NextAction("chain lightning", ACTION_DEFAULT + 0.1f), nullptr))); triggers.push_back(new TriggerNode("main hand weapon no imbue", NextAction::array(0, new NextAction("flametongue weapon", 22.0f), nullptr))); diff --git a/src/strategy/triggers/GenericTriggers.cpp b/src/strategy/triggers/GenericTriggers.cpp index 014530bc..2c69b72a 100644 --- a/src/strategy/triggers/GenericTriggers.cpp +++ b/src/strategy/triggers/GenericTriggers.cpp @@ -10,6 +10,7 @@ #include "BattlegroundWS.h" #include "CreatureAI.h" #include "ObjectGuid.h" +#include "PlayerbotAIConfig.h" #include "Playerbots.h" #include "SharedDefines.h" #include "TemporarySummon.h" @@ -64,7 +65,7 @@ bool PetAttackTrigger::IsActive() bool HighManaTrigger::IsActive() { - return AI_VALUE2(bool, "has mana", "self target") && AI_VALUE2(uint8, "mana", "self target") < 65; + return AI_VALUE2(bool, "has mana", "self target") && AI_VALUE2(uint8, "mana", "self target") < sPlayerbotAIConfig->highMana; } bool AlmostFullManaTrigger::IsActive() From ddbb30498694ba0732e67a24046206cd6a587655 Mon Sep 17 00:00:00 2001 From: Yunfan Li Date: Wed, 4 Sep 2024 11:42:29 +0800 Subject: [PATCH 24/47] Rewrite stats overflow calculation --- src/factory/StatsCollector.cpp | 6 +- src/factory/StatsWeightCalculator.cpp | 129 ++++++++++++++++++-------- src/factory/StatsWeightCalculator.h | 3 +- 3 files changed, 96 insertions(+), 42 deletions(-) diff --git a/src/factory/StatsCollector.cpp b/src/factory/StatsCollector.cpp index 1216b35c..1f6c6d65 100644 --- a/src/factory/StatsCollector.cpp +++ b/src/factory/StatsCollector.cpp @@ -233,7 +233,7 @@ bool StatsCollector::SpecialSpellFilter(uint32 spellId) { if (type_ != CollectorType::SPELL_HEAL) stats[STATS_TYPE_CRIT] += 50; return true; - case 59620: + case 59620: // Berserk if (type_ == CollectorType::MELEE) stats[STATS_TYPE_ATTACK_POWER] += 120; return true; @@ -254,6 +254,10 @@ bool StatsCollector::SpecialSpellFilter(uint32 spellId) { /// Noticing that heroic item can not be triggered, probably a bug to report to AC if (type_ == CollectorType::SPELL_HEAL) return true; + break; + case 71903: // Shadowmourne + stats[STATS_TYPE_STRENGTH] += 200; + return true; default: break; } diff --git a/src/factory/StatsWeightCalculator.cpp b/src/factory/StatsWeightCalculator.cpp index ef95fb9b..c708c946 100644 --- a/src/factory/StatsWeightCalculator.cpp +++ b/src/factory/StatsWeightCalculator.cpp @@ -58,6 +58,9 @@ float StatsWeightCalculator::CalculateItem(uint32 itemId) collector_->CollectItemStats(proto); + if (enable_overflow_penalty_) + ApplyOverflowPenalty(player_); + GenerateWeights(player_); for (uint32 i = 0; i < STATS_TYPE_MAX; i++) { @@ -89,6 +92,9 @@ float StatsWeightCalculator::CalculateEnchant(uint32 enchantId) collector_->CollectEnchantStats(enchant); + if (enable_overflow_penalty_) + ApplyOverflowPenalty(player_); + GenerateWeights(player_); for (uint32 i = 0; i < STATS_TYPE_MAX; i++) { @@ -102,9 +108,6 @@ void StatsWeightCalculator::GenerateWeights(Player* player) { GenerateBasicWeights(player); GenerateAdditionalWeights(player); - - if (enable_overflow_penalty_) - ApplyOverflowPenalty(player); } void StatsWeightCalculator::GenerateBasicWeights(Player* player) @@ -219,15 +222,15 @@ void StatsWeightCalculator::GenerateBasicWeights(Player* player) } else if (cls == CLASS_PALADIN && tab == PALADIN_TAB_RETRIBUTION) // retribution { - stats_weights_[STATS_TYPE_AGILITY] += 1.1f; + stats_weights_[STATS_TYPE_AGILITY] += 1.3f; stats_weights_[STATS_TYPE_STRENGTH] += 2.5f; stats_weights_[STATS_TYPE_INTELLECT] += 0.15f; stats_weights_[STATS_TYPE_ATTACK_POWER] += 1.0f; stats_weights_[STATS_TYPE_SPELL_POWER] += 0.3f; - stats_weights_[STATS_TYPE_ARMOR_PENETRATION] += 0.5f; + stats_weights_[STATS_TYPE_ARMOR_PENETRATION] += 0.7f; stats_weights_[STATS_TYPE_HIT] += 1.9f; - stats_weights_[STATS_TYPE_CRIT] += 1.2f; - stats_weights_[STATS_TYPE_HASTE] += 1.3f; + stats_weights_[STATS_TYPE_CRIT] += 1.3f; + stats_weights_[STATS_TYPE_HASTE] += 1.2f; stats_weights_[STATS_TYPE_EXPERTISE] += 2.0f; stats_weights_[STATS_TYPE_MELEE_DPS] += 7.0f; } @@ -414,12 +417,12 @@ void StatsWeightCalculator::CalculateSocketBonus(Player* player, ItemTemplate co void StatsWeightCalculator::CalculateItemTypePenalty(ItemTemplate const* proto) { - // penalty for different type armor - if (proto->Class == ITEM_CLASS_ARMOR && proto->SubClass >= ITEM_SUBCLASS_ARMOR_CLOTH && - proto->SubClass <= ITEM_SUBCLASS_ARMOR_PLATE && NotBestArmorType(proto->SubClass)) - { - weight_ *= 0.8; - } + // // penalty for different type armor + // if (proto->Class == ITEM_CLASS_ARMOR && proto->SubClass >= ITEM_SUBCLASS_ARMOR_CLOTH && + // proto->SubClass <= ITEM_SUBCLASS_ARMOR_PLATE && NotBestArmorType(proto->SubClass)) + // { + // weight_ *= 1.0; + // } // double hand if (proto->Class == ITEM_CLASS_WEAPON) { @@ -444,7 +447,7 @@ void StatsWeightCalculator::CalculateItemTypePenalty(ItemTemplate const* proto) } // 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) || @@ -452,13 +455,13 @@ void StatsWeightCalculator::CalculateItemTypePenalty(ItemTemplate const* proto) (cls == CLASS_DEATH_KNIGHT && tab == DEATHKNIGHT_TAB_BLOOD) || (cls == CLASS_SHAMAN && tab == SHAMAN_TAB_ENHANCEMENT && !player_->CanDualWield()))) { - weight_ *= 10; + weight_ *= 0.1; } // fury with titan's grip - if (isDoubleHand && proto->SubClass != ITEM_SUBCLASS_WEAPON_POLEARM && + if ((!isDoubleHand || proto->SubClass == ITEM_SUBCLASS_WEAPON_POLEARM) && (cls == CLASS_WARRIOR && tab == WARRIOR_TAB_FURY && player_->CanTitanGrip())) { - weight_ *= 10; + weight_ *= 0.1; } } if (proto->Class == ITEM_CLASS_WEAPON) @@ -481,6 +484,9 @@ void StatsWeightCalculator::CalculateItemTypePenalty(ItemTemplate const* proto) { weight_ *= 1.1; } + bool slowDelay = proto->Delay > 2500; + if (cls == CLASS_SHAMAN && tab == SHAMAN_TAB_ENHANCEMENT && slowDelay) + weight_ *= 1.1; } } @@ -505,37 +511,57 @@ void StatsWeightCalculator::ApplyOverflowPenalty(Player* player) { { float hit_current, hit_overflow; - if (type_ == CollectorType::SPELL) + float validPoints; + // m_modMeleeHitChance = (float)GetTotalAuraModifier(SPELL_AURA_MOD_HIT_CHANCE); + // m_modMeleeHitChance += GetRatingBonusValue(CR_HIT_MELEE); + if (GetHitOverflowType(player) == CollectorType::SPELL) { - hit_current = player->GetRatingBonusValue(CR_HIT_SPELL); + hit_current = player->GetTotalAuraModifier(SPELL_AURA_MOD_SPELL_HIT_CHANCE); + hit_current += player->GetRatingBonusValue(CR_HIT_SPELL); hit_overflow = SPELL_HIT_OVERFLOW; + if (hit_overflow > hit_current) + validPoints = (hit_overflow - hit_current) / player->GetRatingMultiplier(CR_HIT_SPELL); + else + validPoints = 0; } - else if (type_ == CollectorType::MELEE) + else if (GetHitOverflowType(player) == CollectorType::MELEE) { - hit_current = player->GetRatingBonusValue(CR_HIT_MELEE); + hit_current = player->GetTotalAuraModifier(SPELL_AURA_MOD_HIT_CHANCE); + hit_current += player->GetRatingBonusValue(CR_HIT_MELEE); hit_overflow = MELEE_HIT_OVERFLOW; + if (hit_overflow > hit_current) + validPoints = (hit_overflow - hit_current) / player->GetRatingMultiplier(CR_HIT_MELEE); + else + validPoints = 0; } else { - hit_current = player->GetRatingBonusValue(CR_HIT_RANGED); + hit_current = player->GetTotalAuraModifier(SPELL_AURA_MOD_HIT_CHANCE); + hit_current += player->GetRatingBonusValue(CR_HIT_RANGED); hit_overflow = RANGED_HIT_OVERFLOW; + if (hit_overflow > hit_current) + validPoints = (hit_overflow - hit_current) / player->GetRatingMultiplier(CR_HIT_RANGED); + else + validPoints = 0; } - if (hit_current >= hit_overflow) - stats_weights_[STATS_TYPE_HIT] = 0.0f; - else if (hit_current >= hit_overflow * 0.8) - stats_weights_[STATS_TYPE_HIT] /= 1.5; + collector_->stats[STATS_TYPE_HIT] = std::min(collector_->stats[STATS_TYPE_HIT], (int)validPoints); } { if (type_ == CollectorType::MELEE) { float expertise_current, expertise_overflow; - expertise_current = player->GetRatingBonusValue(CR_EXPERTISE); + expertise_current = player->GetUInt32Value(PLAYER_EXPERTISE); + expertise_current += player->GetRatingBonusValue(CR_EXPERTISE); expertise_overflow = EXPERTISE_OVERFLOW; - if (expertise_current >= expertise_overflow) - stats_weights_[STATS_TYPE_EXPERTISE] = 0.0f; - else if (expertise_current >= expertise_overflow * 0.8) - stats_weights_[STATS_TYPE_EXPERTISE] /= 1.5; + + float validPoints; + if (expertise_overflow > expertise_current) + validPoints = (expertise_overflow - expertise_current) / player->GetRatingMultiplier(CR_EXPERTISE); + else + validPoints = 0; + + collector_->stats[STATS_TYPE_EXPERTISE] = std::min(collector_->stats[STATS_TYPE_EXPERTISE], (int)validPoints); } } @@ -545,10 +571,14 @@ void StatsWeightCalculator::ApplyOverflowPenalty(Player* player) float defense_current, defense_overflow; defense_current = player->GetRatingBonusValue(CR_DEFENSE_SKILL); defense_overflow = DEFENSE_OVERFLOW; - if (defense_current >= defense_overflow) - stats_weights_[STATS_TYPE_DEFENSE] /= 2; - else if (defense_current >= defense_overflow * 0.8) - stats_weights_[STATS_TYPE_DEFENSE] /= 1.5; + + float validPoints; + if (defense_overflow > defense_current) + validPoints = (defense_overflow - defense_current) / player->GetRatingMultiplier(CR_DEFENSE_SKILL); + else + validPoints = 0; + + collector_->stats[STATS_TYPE_DEFENSE] = std::min(collector_->stats[STATS_TYPE_DEFENSE], (int)validPoints); } } @@ -558,10 +588,29 @@ void StatsWeightCalculator::ApplyOverflowPenalty(Player* player) float armor_penetration_current, armor_penetration_overflow; armor_penetration_current = player->GetRatingBonusValue(CR_ARMOR_PENETRATION); armor_penetration_overflow = ARMOR_PENETRATION_OVERFLOW; - if (armor_penetration_current >= armor_penetration_overflow) - stats_weights_[STATS_TYPE_ARMOR_PENETRATION] = 0.0f; - if (armor_penetration_current >= armor_penetration_overflow * 0.8) - stats_weights_[STATS_TYPE_ARMOR_PENETRATION] /= 1.5; + + float validPoints; + if (armor_penetration_overflow > armor_penetration_current) + validPoints = (armor_penetration_overflow - armor_penetration_current) / player->GetRatingMultiplier(CR_ARMOR_PENETRATION); + else + validPoints = 0; + + collector_->stats[STATS_TYPE_ARMOR_PENETRATION] = std::min(collector_->stats[STATS_TYPE_ARMOR_PENETRATION], (int)validPoints); } } -} \ No newline at end of file +} + +CollectorType StatsWeightCalculator::GetHitOverflowType(Player* player) +{ + cls = player->getClass(); + tab = AiFactory::GetPlayerSpecTab(player); + if (cls == CLASS_DEATH_KNIGHT && tab == DEATHKNIGHT_TAB_UNHOLY) + return CollectorType::SPELL; + if (cls == CLASS_SHAMAN && tab == SHAMAN_TAB_ENHANCEMENT) + return CollectorType::SPELL; + if (cls == CLASS_ROGUE) + return CollectorType::SPELL; + + return type_; +} + \ No newline at end of file diff --git a/src/factory/StatsWeightCalculator.h b/src/factory/StatsWeightCalculator.h index 721eff9e..81b8aa67 100644 --- a/src/factory/StatsWeightCalculator.h +++ b/src/factory/StatsWeightCalculator.h @@ -15,7 +15,7 @@ enum StatsOverflowThreshold { - SPELL_HIT_OVERFLOW = 17, + SPELL_HIT_OVERFLOW = 14, MELEE_HIT_OVERFLOW = 8, RANGED_HIT_OVERFLOW = 8, EXPERTISE_OVERFLOW = 26, @@ -48,6 +48,7 @@ private: bool NotBestArmorType(uint32 item_subclass_armor); void ApplyOverflowPenalty(Player* player); + CollectorType GetHitOverflowType(Player* player); private: Player* player_; From 9fb8f2fdf4f65931123301c706de748a373f8276 Mon Sep 17 00:00:00 2001 From: Yunfan Li Date: Wed, 4 Sep 2024 15:12:25 +0800 Subject: [PATCH 25/47] Dps paladin --- src/strategy/paladin/DpsPaladinStrategy.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/strategy/paladin/DpsPaladinStrategy.cpp b/src/strategy/paladin/DpsPaladinStrategy.cpp index 723fffd3..efd55a6a 100644 --- a/src/strategy/paladin/DpsPaladinStrategy.cpp +++ b/src/strategy/paladin/DpsPaladinStrategy.cpp @@ -83,18 +83,20 @@ DpsPaladinStrategy::DpsPaladinStrategy(PlayerbotAI* botAI) : GenericPaladinStrat NextAction** DpsPaladinStrategy::getDefaultActions() { return NextAction::array(0, - new NextAction("crusader strike", ACTION_DEFAULT + 0.6f), - new NextAction("hammer of wrath", ACTION_DEFAULT + 0.5f), + new NextAction("hammer of wrath", ACTION_DEFAULT + 0.6f), + new NextAction("crusader strike", ACTION_DEFAULT + 0.5f), new NextAction("judgement of wisdom", ACTION_DEFAULT + 0.4f), new NextAction("divine storm", ACTION_DEFAULT + 0.3f), new NextAction("consecration", ACTION_DEFAULT + 0.1f), - new NextAction("melee", ACTION_DEFAULT), NULL); + new NextAction("melee", ACTION_DEFAULT), nullptr); } void DpsPaladinStrategy::InitTriggers(std::vector& triggers) { GenericPaladinStrategy::InitTriggers(triggers); + // triggers.push_back(new TriggerNode( + // "enough mana", NextAction::array(0, new NextAction("consecration", ACTION_DEFAULT + 0.2f), nullptr))); triggers.push_back( new TriggerNode("art of war", NextAction::array(0, new NextAction("exorcism", ACTION_DEFAULT + 0.2f), nullptr))); triggers.push_back( From 67892378431483198c329fdbd0af263b3e91f3b8 Mon Sep 17 00:00:00 2001 From: Yunfan Li Date: Wed, 4 Sep 2024 15:12:39 +0800 Subject: [PATCH 26/47] Dps hunter --- src/factory/StatsWeightCalculator.cpp | 16 +++++++++++++++- src/factory/StatsWeightCalculator.h | 1 + src/strategy/hunter/DpsHunterStrategy.cpp | 11 ++++++----- src/strategy/hunter/HunterActions.h | 11 +++++++++++ src/strategy/hunter/HunterAiObjectContext.cpp | 5 +++++ 5 files changed, 38 insertions(+), 6 deletions(-) diff --git a/src/factory/StatsWeightCalculator.cpp b/src/factory/StatsWeightCalculator.cpp index c708c946..bf08e19e 100644 --- a/src/factory/StatsWeightCalculator.cpp +++ b/src/factory/StatsWeightCalculator.cpp @@ -108,6 +108,7 @@ void StatsWeightCalculator::GenerateWeights(Player* player) { GenerateBasicWeights(player); GenerateAdditionalWeights(player); + ApplyWeightFinetune(player); } void StatsWeightCalculator::GenerateBasicWeights(Player* player) @@ -458,7 +459,7 @@ void StatsWeightCalculator::CalculateItemTypePenalty(ItemTemplate const* proto) weight_ *= 0.1; } // fury with titan's grip - if ((!isDoubleHand || proto->SubClass == ITEM_SUBCLASS_WEAPON_POLEARM) && + if ((!isDoubleHand || proto->SubClass == ITEM_SUBCLASS_WEAPON_POLEARM || proto->SubClass == ITEM_SUBCLASS_WEAPON_STAFF) && (cls == CLASS_WARRIOR && tab == WARRIOR_TAB_FURY && player_->CanTitanGrip())) { weight_ *= 0.1; @@ -600,6 +601,19 @@ void StatsWeightCalculator::ApplyOverflowPenalty(Player* player) } } +void StatsWeightCalculator::ApplyWeightFinetune(Player* player) +{ + { + if (type_ == CollectorType::MELEE || type_ == CollectorType::RANGED) + { + float armor_penetration_current, armor_penetration_overflow; + armor_penetration_current = player->GetRatingBonusValue(CR_ARMOR_PENETRATION); + if (armor_penetration_current > 50) + stats_weights_[STATS_TYPE_ARMOR_PENETRATION] *= 1.2f; + } + } +} + CollectorType StatsWeightCalculator::GetHitOverflowType(Player* player) { cls = player->getClass(); diff --git a/src/factory/StatsWeightCalculator.h b/src/factory/StatsWeightCalculator.h index 81b8aa67..f6f4bf7e 100644 --- a/src/factory/StatsWeightCalculator.h +++ b/src/factory/StatsWeightCalculator.h @@ -48,6 +48,7 @@ private: bool NotBestArmorType(uint32 item_subclass_armor); void ApplyOverflowPenalty(Player* player); + void ApplyWeightFinetune(Player* player); CollectorType GetHitOverflowType(Player* player); private: diff --git a/src/strategy/hunter/DpsHunterStrategy.cpp b/src/strategy/hunter/DpsHunterStrategy.cpp index 325f3b93..e9d0e2f5 100644 --- a/src/strategy/hunter/DpsHunterStrategy.cpp +++ b/src/strategy/hunter/DpsHunterStrategy.cpp @@ -41,12 +41,13 @@ DpsHunterStrategy::DpsHunterStrategy(PlayerbotAI* botAI) : GenericHunterStrategy NextAction** DpsHunterStrategy::getDefaultActions() { return NextAction::array( - 0, new NextAction("kill shot", ACTION_DEFAULT + 0.6f), new NextAction("chimera shot", ACTION_DEFAULT + 0.5f), - new NextAction("explosive shot", ACTION_DEFAULT + 0.4f), new NextAction("aimed shot", ACTION_DEFAULT + 0.3f), - /*new NextAction("arcane shot", ACTION_DEFAULT + 0.2f),*/ new NextAction("steady shot", ACTION_DEFAULT + 0.1f), + 0, new NextAction("kill shot", ACTION_DEFAULT + 0.8f), new NextAction("chimera shot", ACTION_DEFAULT + 0.7f), + new NextAction("explosive shot", ACTION_DEFAULT + 0.6f), new NextAction("aimed shot", ACTION_DEFAULT + 0.5f), + new NextAction("silencing shot", ACTION_DEFAULT + 0.4f), + new NextAction("kill command", ACTION_DEFAULT + 0.3f), + new NextAction("arcane shot", ACTION_DEFAULT + 0.2f), + new NextAction("steady shot", ACTION_DEFAULT + 0.1f), new NextAction("auto shot", ACTION_DEFAULT), nullptr); - // return NextAction::array(0, new NextAction("explosive shot", 11.0f), new NextAction("auto shot", 10.0f), new - // NextAction("auto attack", 9.0f), nullptr); } void DpsHunterStrategy::InitTriggers(std::vector& triggers) diff --git a/src/strategy/hunter/HunterActions.h b/src/strategy/hunter/HunterActions.h index 868905c6..87595b3b 100644 --- a/src/strategy/hunter/HunterActions.h +++ b/src/strategy/hunter/HunterActions.h @@ -78,6 +78,9 @@ END_SPELL_ACTION() BEGIN_RANGED_SPELL_ACTION(CastKillShotAction, "kill shot") END_SPELL_ACTION() +BEGIN_RANGED_SPELL_ACTION(CastSilencingShotAction, "silencing shot") +END_SPELL_ACTION() + BEGIN_RANGED_SPELL_ACTION(CastTranquilizingShotAction, "tranquilizing shot") END_SPELL_ACTION() @@ -139,6 +142,14 @@ public: std::string const GetTargetName() override { return "pet target"; } }; +class CastKillCommandAction : public CastAuraSpellAction +{ +public: + CastKillCommandAction(PlayerbotAI* botAI) : CastAuraSpellAction(botAI, "kill command") {} + + std::string const GetTargetName() override { return "pet target"; } +}; + class CastRevivePetAction : public CastBuffSpellAction { public: diff --git a/src/strategy/hunter/HunterAiObjectContext.cpp b/src/strategy/hunter/HunterAiObjectContext.cpp index 32548aad..dbfb6a54 100644 --- a/src/strategy/hunter/HunterAiObjectContext.cpp +++ b/src/strategy/hunter/HunterAiObjectContext.cpp @@ -143,6 +143,7 @@ public: creators["scorpid sting"] = &HunterAiObjectContextInternal::scorpid_sting; creators["hunter's mark"] = &HunterAiObjectContextInternal::hunters_mark; creators["mend pet"] = &HunterAiObjectContextInternal::mend_pet; + creators["kill command"] = &HunterAiObjectContextInternal::kill_command; creators["revive pet"] = &HunterAiObjectContextInternal::revive_pet; creators["call pet"] = &HunterAiObjectContextInternal::call_pet; creators["black arrow"] = &HunterAiObjectContextInternal::black_arrow; @@ -171,6 +172,7 @@ public: creators["steady shot"] = &HunterAiObjectContextInternal::steady_shot; creators["kill shot"] = &HunterAiObjectContextInternal::kill_shot; creators["misdirection on main tank"] = &HunterAiObjectContextInternal::misdirection_on_main_tank; + creators["silencing shot"] = &HunterAiObjectContextInternal::silencing_shot; } private: @@ -196,6 +198,7 @@ private: static Action* scorpid_sting(PlayerbotAI* botAI) { return new CastScorpidStingAction(botAI); } static Action* hunters_mark(PlayerbotAI* botAI) { return new CastHuntersMarkAction(botAI); } static Action* mend_pet(PlayerbotAI* botAI) { return new CastMendPetAction(botAI); } + static Action* kill_command(PlayerbotAI* botAI) { return new CastKillCommandAction(botAI); } static Action* revive_pet(PlayerbotAI* botAI) { return new CastRevivePetAction(botAI); } static Action* call_pet(PlayerbotAI* botAI) { return new CastCallPetAction(botAI); } static Action* black_arrow(PlayerbotAI* botAI) { return new CastBlackArrow(botAI); } @@ -217,6 +220,8 @@ private: static Action* steady_shot(PlayerbotAI* ai) { return new CastSteadyShotAction(ai); } static Action* kill_shot(PlayerbotAI* ai) { return new CastKillShotAction(ai); } static Action* misdirection_on_main_tank(PlayerbotAI* ai) { return new CastMisdirectionOnMainTankAction(ai); } + static Action* silencing_shot(PlayerbotAI* ai) { return new CastSilencingShotAction(ai); } + }; HunterAiObjectContext::HunterAiObjectContext(PlayerbotAI* botAI) : AiObjectContext(botAI) From 5de242e80b49d4f5e08fc43f1c0d7bb44eed5f12 Mon Sep 17 00:00:00 2001 From: Yunfan Li Date: Wed, 4 Sep 2024 15:12:48 +0800 Subject: [PATCH 27/47] Hunter pet talents init --- conf/playerbots.conf.dist | 22 +++- src/PlayerbotAIConfig.cpp | 57 +++++++- src/PlayerbotAIConfig.h | 3 + src/factory/PlayerbotFactory.cpp | 215 +++++++++++++++++++++---------- 4 files changed, 220 insertions(+), 77 deletions(-) diff --git a/conf/playerbots.conf.dist b/conf/playerbots.conf.dist index 88b5213c..9f80241b 100644 --- a/conf/playerbots.conf.dist +++ b/conf/playerbots.conf.dist @@ -821,13 +821,25 @@ AiPlayerbot.PremadeSpecGlyph.3.0 = 42912,43350,42902,43351,43338,45732 AiPlayerbot.PremadeSpecLink.3.0.60 = 51200201505112243100511351 AiPlayerbot.PremadeSpecLink.3.0.80 = 51200201505112253100531351-015305021 AiPlayerbot.PremadeSpecName.3.1 = mm pve -AiPlayerbot.PremadeSpecGlyph.3.1 = 42912,43350,42915,43351,43338,45732 -AiPlayerbot.PremadeSpecLink.3.1.60 = -015305101230013233135030051 -AiPlayerbot.PremadeSpecLink.3.1.80 = 502-035305101230013233135031351-5000002 +AiPlayerbot.PremadeSpecGlyph.3.1 = 42912,43350,42914,43351,43338,45732 +AiPlayerbot.PremadeSpecLink.3.1.60 = -005305111230013233125030151-5 +AiPlayerbot.PremadeSpecLink.3.1.80 = 502-005305131230013233135031351-5000002 AiPlayerbot.PremadeSpecName.3.2 = surv pve AiPlayerbot.PremadeSpecGlyph.3.2 = 42912,43350,45731,43351,43338,45732 -AiPlayerbot.PremadeSpecLink.3.2.60 = --5000032500033330502135001331 -AiPlayerbot.PremadeSpecLink.3.2.80 = -005305101-5000032500033330522135301331 +AiPlayerbot.PremadeSpecLink.3.2.60 = --5000032500033330502135201311 +AiPlayerbot.PremadeSpecLink.3.2.80 = -005305101-5000032500033330532135301321 + +# HUNTER PET +# +# Ferocity +AiPlayerbot.PremadeHunterPetLink.0.16 = 2100003030103010101 +AiPlayerbot.PremadeHunterPetLink.0.20 = 2100013030103010122 +# Tenacity +AiPlayerbot.PremadeHunterPetLink.1.16 = 21103000300120101001 +AiPlayerbot.PremadeHunterPetLink.1.20 = 21303010300120101002 +# Cunning +AiPlayerbot.PremadeHunterPetLink.2.16 = 2100020330000211001 +AiPlayerbot.PremadeHunterPetLink.2.20 = 21000203300002110221 # # diff --git a/src/PlayerbotAIConfig.cpp b/src/PlayerbotAIConfig.cpp index ffc41581..d285490a 100644 --- a/src/PlayerbotAIConfig.cpp +++ b/src/PlayerbotAIConfig.cpp @@ -331,6 +331,17 @@ bool PlayerbotAIConfig::Initialize() parsedSpecLinkOrder[cls][spec][level] = ParseTempTalentsOrder(cls, premadeSpecLink[cls][spec][level]); } } + for (uint32 spec = 0; spec < 3; ++spec) + { + for (uint32 points = 0; points < 21; ++points) + { + std::ostringstream os; + os << "AiPlayerbot.PremadeHunterPetLink." << spec << "." << points; + premadeHunterPetLink[spec][points] = sConfigMgr->GetOption(os.str().c_str(), "", false); + parsedHunterPetLinkOrder[spec][points] = + ParseTempPetTalentsOrder(spec, premadeHunterPetLink[spec][points]); + } + } for (uint32 spec = 0; spec < MAX_SPECNO; ++spec) { std::ostringstream os; @@ -486,7 +497,7 @@ bool PlayerbotAIConfig::Initialize() RandomPlayerbotFactory::CreateRandomBots(); if (sPlayerbotAIConfig->addClassCommand) sRandomPlayerbotMgr->PrepareAddclassCache(); - + if (World::IsStopped()) { return true; @@ -760,3 +771,47 @@ std::vector> PlayerbotAIConfig::ParseTempTalentsOrder(uint32 } return res; } + +std::vector> PlayerbotAIConfig::ParseTempPetTalentsOrder(uint32 spec, std::string tab_link) +{ + // check bad link + // uint32 classMask = 1 << (cls - 1); + std::vector spells; + std::vector> orders; + for (uint32 i = 0; i < sTalentStore.GetNumRows(); ++i) + { + TalentEntry const* talentInfo = sTalentStore.LookupEntry(i); + if (!talentInfo) + continue; + + TalentTabEntry const* talentTabInfo = sTalentTabStore.LookupEntry(talentInfo->TalentTab); + if (!talentTabInfo) + continue; + + if (!((1 << spec) & talentTabInfo->petTalentMask)) + continue; + // skip some duplicate spells like dash/dive + if (talentInfo->TalentID == 2201 || talentInfo->TalentID == 2208 || talentInfo->TalentID == 2219 || talentInfo->TalentID == 2203) + continue; + + spells.push_back(talentInfo); + } + std::sort(spells.begin(), spells.end(), + [&](TalentEntry const* lhs, TalentEntry const* rhs) + { return lhs->Row != rhs->Row ? lhs->Row < rhs->Row : lhs->Col < rhs->Col; }); + for (int i = 0; i < tab_link.size(); i++) + { + if (i >= spells.size()) + { + break; + } + int lvl = tab_link[i] - '0'; + if (lvl == 0) + continue; + orders.push_back({spells[i]->Row, spells[i]->Col, (uint32)lvl}); + } + // sort by talent tab size + std::sort(orders.begin(), orders.end(), [&](auto& lhs, auto& rhs) { return lhs.size() > rhs.size(); }); + + return orders; +} diff --git a/src/PlayerbotAIConfig.h b/src/PlayerbotAIConfig.h index 57f3ce80..f3757131 100644 --- a/src/PlayerbotAIConfig.h +++ b/src/PlayerbotAIConfig.h @@ -195,7 +195,9 @@ public: std::string premadeSpecGlyph[MAX_CLASSES][MAX_SPECNO]; std::vector parsedSpecGlyph[MAX_CLASSES][MAX_SPECNO]; std::string premadeSpecLink[MAX_CLASSES][MAX_SPECNO][MAX_LEVEL]; + std::string premadeHunterPetLink[3][21]; std::vector> parsedSpecLinkOrder[MAX_CLASSES][MAX_SPECNO][MAX_LEVEL]; + std::vector> parsedHunterPetLinkOrder[3][21]; uint32 randomClassSpecProb[MAX_CLASSES][MAX_SPECNO]; uint32 randomClassSpecIndex[MAX_CLASSES][MAX_SPECNO]; @@ -325,6 +327,7 @@ public: void loadWorldBuf(uint32 factionId, uint32 classId, uint32 minLevel, uint32 maxLevel); static std::vector> ParseTempTalentsOrder(uint32 cls, std::string temp_talents_order); + static std::vector> ParseTempPetTalentsOrder(uint32 spec, std::string temp_talents_order); }; #define sPlayerbotAIConfig PlayerbotAIConfig::instance() diff --git a/src/factory/PlayerbotFactory.cpp b/src/factory/PlayerbotFactory.cpp index a5116fdf..3dfc50dc 100644 --- a/src/factory/PlayerbotFactory.cpp +++ b/src/factory/PlayerbotFactory.cpp @@ -39,6 +39,7 @@ #define PLAYER_SKILL_INDEX(x) (PLAYER_SKILL_INFO_1_1 + ((x)*3)) +const uint64 diveMask = (1LL << 7) | (1LL << 44) | (1LL << 37) | (1LL << 38) | (1LL << 26) | (1LL << 30) | (1LL << 27) | (1LL << 33) | (1LL << 24) | (1LL << 34); 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, @@ -611,8 +612,10 @@ void PlayerbotFactory::InitPetTalents() // pet_family->petTalentType); return; } - // pet->resetTalents(); std::unordered_map> spells; + bool diveTypePet = (1LL << ci->family) & diveMask; + LOG_INFO("playerbots", "DIVEMASK:{}", diveMask); + for (uint32 i = 0; i < sTalentStore.GetNumRows(); ++i) { TalentEntry const* talentInfo = sTalentStore.LookupEntry(i); @@ -624,49 +627,112 @@ void PlayerbotFactory::InitPetTalents() // prevent learn talent for different family (cheating) if (!((1 << pet_family->petTalentType) & talentTabInfo->petTalentMask)) continue; - + bool diveClass = talentInfo->TalentID == 2201 || talentInfo->TalentID == 2208 || talentInfo->TalentID == 2219 || talentInfo->TalentID == 2203; + if (diveClass && !diveTypePet) + continue; + bool dashClass = talentInfo->TalentID == 2119 || talentInfo->TalentID == 2207 || talentInfo->TalentID == 2111 || talentInfo->TalentID == 2109; + if (dashClass && diveTypePet) + continue; spells[talentInfo->Row].push_back(talentInfo); } - uint32 curTalentPoints = pet->GetFreeTalentPoints(); + std::vector> order = sPlayerbotAIConfig->parsedHunterPetLinkOrder[pet_family->petTalentType][20]; uint32 maxTalentPoints = pet->GetMaxTalentPointsForLevel(pet->GetLevel()); - int row = 0; - // LOG_INFO("playerbots", "{} learning, max talent points: {}, cur: {}", bot->GetName().c_str(), maxTalentPoints, - // curTalentPoints); - for (auto i = spells.begin(); i != spells.end(); ++i, ++row) - { - std::vector& spells_row = i->second; - if (spells_row.empty()) - { - LOG_INFO("playerbots", "{}: No spells for talent row {}", bot->GetName().c_str(), i->first); - continue; - } - int attemptCount = 0; - // keep learning for the last row - while (!spells_row.empty() && - ((((int)maxTalentPoints - (int)pet->GetFreeTalentPoints()) < 3 * (row + 1)) || (row == 5)) && - attemptCount++ < 10 && pet->GetFreeTalentPoints()) - { - int index = urand(0, spells_row.size() - 1); - TalentEntry const* talentInfo = spells_row[index]; - int maxRank = 0; - for (int rank = 0; rank < std::min((uint32)MAX_TALENT_RANK, (uint32)pet->GetFreeTalentPoints()); ++rank) - { - uint32 spellId = talentInfo->RankID[rank]; - if (!spellId) - continue; - maxRank = rank; - } - // LOG_INFO("playerbots", "{} learn pet talent {}({})", bot->GetName().c_str(), talentInfo->TalentID, - // maxRank); - if (talentInfo->DependsOn) + if (order.empty()) + { + int row = 0; + for (auto i = spells.begin(); i != spells.end(); ++i, ++row) + { + std::vector& spells_row = i->second; + if (spells_row.empty()) { - bot->LearnPetTalent(pet->GetGUID(), talentInfo->DependsOn, - std::min(talentInfo->DependsOnRank, bot->GetFreeTalentPoints() - 1)); + LOG_INFO("playerbots", "{}: No spells for talent row {}", bot->GetName().c_str(), i->first); + continue; + } + int attemptCount = 0; + // keep learning for the last row + while (!spells_row.empty() && + ((((int)maxTalentPoints - (int)pet->GetFreeTalentPoints()) < 3 * (row + 1)) || (row == 5)) && + attemptCount++ < 10 && pet->GetFreeTalentPoints()) + { + int index = urand(0, spells_row.size() - 1); + TalentEntry const* talentInfo = spells_row[index]; + int maxRank = 0; + for (int rank = 0; rank < std::min((uint32)MAX_TALENT_RANK, (uint32)pet->GetFreeTalentPoints()); ++rank) + { + uint32 spellId = talentInfo->RankID[rank]; + if (!spellId) + continue; + + maxRank = rank; + } + // LOG_INFO("playerbots", "{} learn pet talent {}({})", bot->GetName().c_str(), talentInfo->TalentID, + // maxRank); + if (talentInfo->DependsOn) + { + bot->LearnPetTalent(pet->GetGUID(), talentInfo->DependsOn, + std::min(talentInfo->DependsOnRank, bot->GetFreeTalentPoints() - 1)); + } + bot->LearnPetTalent(pet->GetGUID(), talentInfo->TalentID, maxRank); + spells_row.erase(spells_row.begin() + index); + } + } + } + else + { + uint32 spec = pet_family->petTalentType; + uint32 startPoints = pet->GetMaxTalentPointsForLevel(pet->GetLevel()); + while (startPoints > 1 && startPoints < 20 && + sPlayerbotAIConfig->parsedHunterPetLinkOrder[spec][startPoints].size() == 0) + { + startPoints--; + } + + for (uint32 points = startPoints; points <= 20; points++) + { + if (sPlayerbotAIConfig->parsedHunterPetLinkOrder[spec][points].size() == 0) + continue; + for (std::vector& p : sPlayerbotAIConfig->parsedHunterPetLinkOrder[spec][points]) + { + uint32 row = p[0], col = p[1], lvl = p[2]; + uint32 talentID = 0; + uint32 learnLevel = 0; + std::vector& spell = spells[row]; + for (TalentEntry const* talentInfo : spell) + { + if (talentInfo->Col != col) + { + continue; + } + if (talentInfo->DependsOn) + { + bot->LearnPetTalent(pet->GetGUID(),talentInfo->DependsOn, + std::min(talentInfo->DependsOnRank, bot->GetFreeTalentPoints() - 1)); + } + talentID = talentInfo->TalentID; + + uint32 currentTalentRank = 0; + for (uint8 rank = 0; rank < MAX_TALENT_RANK; ++rank) + { + if (talentInfo->RankID[rank] && pet->HasSpell(talentInfo->RankID[rank])) + { + currentTalentRank = rank + 1; + break; + } + } + learnLevel = std::min(lvl, pet->GetFreeTalentPoints() + currentTalentRank) - 1; + } + bot->LearnPetTalent(pet->GetGUID(), talentID, learnLevel); + if (pet->GetFreeTalentPoints() == 0) + { + break; + } + } + if (pet->GetFreeTalentPoints() == 0) + { + break; } - bot->LearnPetTalent(pet->GetGUID(), talentInfo->TalentID, maxRank); - spells_row.erase(spells_row.begin() + index); } } bot->SendTalentsInfoData(true); @@ -1467,6 +1533,7 @@ void PlayerbotFactory::InitEquipment(bool incremental) else if (blevel == 80) delta = 9; + StatsWeightCalculator calculator(bot); for (uint8 slot = 0; slot < EQUIPMENT_SLOT_END; ++slot) { if (slot == EQUIPMENT_SLOT_TABARD || slot == EQUIPMENT_SLOT_BODY) @@ -1484,6 +1551,18 @@ void PlayerbotFactory::InitEquipment(bool incremental) if (level < 20 && (slot == EQUIPMENT_SLOT_FINGER1 || slot == EQUIPMENT_SLOT_FINGER2)) continue; + Item* oldItem = bot->GetItemByPos(INVENTORY_SLOT_BAG_0, slot); + + if (!incremental && oldItem) + { + continue; + } + + if (oldItem) + { + bot->DestroyItem(INVENTORY_SLOT_BAG_0, slot, true); + } + uint32 desiredQuality = itemQuality; if (urand(0, 100) < 100 * sPlayerbotAIConfig->randomGearLoweringChance && desiredQuality > ITEM_QUALITY_NORMAL) { @@ -1555,41 +1634,19 @@ void PlayerbotFactory::InitEquipment(bool incremental) break; } } while (items[slot].size() < 25 && desiredQuality-- > ITEM_QUALITY_NORMAL); - } - - StatsWeightCalculator calculator(bot); - for (uint8 slot = 0; slot < EQUIPMENT_SLOT_END; ++slot) - { - if (slot == EQUIPMENT_SLOT_TABARD || slot == EQUIPMENT_SLOT_BODY) - continue; - - if (level < 40 && (slot == EQUIPMENT_SLOT_TRINKET1 || slot == EQUIPMENT_SLOT_TRINKET2)) - continue; - - if (level < 25 && slot == EQUIPMENT_SLOT_NECK) - continue; - - if (level < 25 && slot == EQUIPMENT_SLOT_HEAD) - continue; std::vector& ids = items[slot]; if (ids.empty()) { continue; } - Item* oldItem = bot->GetItemByPos(INVENTORY_SLOT_BAG_0, slot); - - if (incremental && !IsDesiredReplacement(oldItem)) - { - continue; - } - + float bestScoreForSlot = -1; uint32 bestItemForSlot = 0; for (int index = 0; index < ids.size(); index++) { uint32 skipProb = 25; - if (urand(0, 100) <= skipProb) + if (urand(1, 100) <= skipProb) continue; uint32 newItemId = ids[index]; @@ -1601,9 +1658,6 @@ void PlayerbotFactory::InitEquipment(bool incremental) if (!CanEquipItem(proto)) continue; - if (oldItem && oldItem->GetTemplate()->ItemId == newItemId) - continue; - if (!CanEquipUnseenItem(slot, dest, newItemId)) continue; @@ -1618,10 +1672,7 @@ void PlayerbotFactory::InitEquipment(bool incremental) { continue; } - if (oldItem) - { - bot->DestroyItem(INVENTORY_SLOT_BAG_0, slot, true); - } + uint16 dest; if (!CanEquipUnseenItem(slot, dest, bestItemForSlot)) { @@ -1632,10 +1683,28 @@ void PlayerbotFactory::InitEquipment(bool incremental) { newItem->AddToWorld(); newItem->AddToUpdateQueueOf(bot); - // bot->AutoUnequipOffhandIfNeed(); - // EnchantItem(newItem); } } + // secondary init for better equips + if (!incremental) + InitEquipment(true); + + // for (uint8 slot = 0; slot < EQUIPMENT_SLOT_END; ++slot) + // { + // if (slot == EQUIPMENT_SLOT_TABARD || slot == EQUIPMENT_SLOT_BODY) + // continue; + + // if (level < 40 && (slot == EQUIPMENT_SLOT_TRINKET1 || slot == EQUIPMENT_SLOT_TRINKET2)) + // continue; + + // if (level < 25 && slot == EQUIPMENT_SLOT_NECK) + // continue; + + // if (level < 25 && slot == EQUIPMENT_SLOT_HEAD) + // continue; + + + // } } bool PlayerbotFactory::IsDesiredReplacement(Item* item) @@ -3818,7 +3887,7 @@ void PlayerbotFactory::ApplyEnchantAndGemsNew(bool destoryOld) if (!gemProperties) continue; - if ((socketColor & gemProperties->color) == 0) + if ((socketColor & gemProperties->color) == 0 && gemProperties->color == 1) // meta socket continue; uint32 enchant_id = gemProperties->spellitemenchantement; @@ -3829,6 +3898,7 @@ void PlayerbotFactory::ApplyEnchantAndGemsNew(bool destoryOld) StatsWeightCalculator calculator(bot); float score = calculator.CalculateEnchant(enchant_id); if (curCount[0] != 0) + { // Ensure meta gem activation for (int i = 1; i < curCount.size(); i++) { @@ -3838,6 +3908,9 @@ void PlayerbotFactory::ApplyEnchantAndGemsNew(bool destoryOld) break; } } + } + if (socketColor & gemProperties->color) + score *= 1.2; if (score > bestGemScore) { enchantIdChosen = enchant_id; From 484a2ae4583a34c6aa2b28e46f13e34ee11e6355 Mon Sep 17 00:00:00 2001 From: Yunfan Li Date: Wed, 4 Sep 2024 18:08:19 +0800 Subject: [PATCH 28/47] Item usage and equip initialization --- src/PlayerbotMgr.cpp | 32 +++++++++++++++ src/factory/PlayerbotFactory.cpp | 56 +++++++++++++------------- src/factory/PlayerbotFactory.h | 2 +- src/strategy/values/ItemUsageValue.cpp | 2 +- 4 files changed, 63 insertions(+), 29 deletions(-) diff --git a/src/PlayerbotMgr.cpp b/src/PlayerbotMgr.cpp index 1958f9aa..7389c72c 100644 --- a/src/PlayerbotMgr.cpp +++ b/src/PlayerbotMgr.cpp @@ -845,6 +845,22 @@ std::vector PlayerbotHolder::HandlePlayerbotCommand(char const* arg if (!strncmp(cmd, "initself=", 9)) { + if (!strcmp(cmd, "initself=uncommon")) + { + if (master->GetSession()->GetSecurity() >= SEC_GAMEMASTER) + { + // OnBotLogin(master); + PlayerbotFactory factory(master, master->GetLevel(), ITEM_QUALITY_UNCOMMON); + factory.Randomize(false); + messages.push_back("initself ok"); + return messages; + } + else + { + messages.push_back("ERROR: Only GM can use this command."); + return messages; + } + } if (!strcmp(cmd, "initself=rare")) { if (master->GetSession()->GetSecurity() >= SEC_GAMEMASTER) @@ -877,6 +893,22 @@ std::vector PlayerbotHolder::HandlePlayerbotCommand(char const* arg return messages; } } + if (!strcmp(cmd, "initself=legendary")) + { + if (master->GetSession()->GetSecurity() >= SEC_GAMEMASTER) + { + // OnBotLogin(master); + PlayerbotFactory factory(master, master->GetLevel(), ITEM_QUALITY_LEGENDARY); + factory.Randomize(false); + messages.push_back("initself ok"); + return messages; + } + else + { + messages.push_back("ERROR: Only GM can use this command."); + return messages; + } + } int32 gs; if (sscanf(cmd, "initself=%d", &gs) != -1) { diff --git a/src/factory/PlayerbotFactory.cpp b/src/factory/PlayerbotFactory.cpp index 3dfc50dc..a40e28a4 100644 --- a/src/factory/PlayerbotFactory.cpp +++ b/src/factory/PlayerbotFactory.cpp @@ -614,7 +614,6 @@ void PlayerbotFactory::InitPetTalents() } std::unordered_map> spells; bool diveTypePet = (1LL << ci->family) & diveMask; - LOG_INFO("playerbots", "DIVEMASK:{}", diveMask); for (uint32 i = 0; i < sTalentStore.GetNumRows(); ++i) { @@ -1513,7 +1512,7 @@ void Shuffle(std::vector& items) // } // } -void PlayerbotFactory::InitEquipment(bool incremental) +void PlayerbotFactory::InitEquipment(bool incremental, bool second_chance) { std::unordered_map> items; // int tab = AiFactory::GetPlayerSpecTab(bot); @@ -1553,16 +1552,13 @@ void PlayerbotFactory::InitEquipment(bool incremental) Item* oldItem = bot->GetItemByPos(INVENTORY_SLOT_BAG_0, slot); - if (!incremental && oldItem) - { - continue; - } - - if (oldItem) + if (second_chance && oldItem) { bot->DestroyItem(INVENTORY_SLOT_BAG_0, slot, true); } + oldItem = bot->GetItemByPos(INVENTORY_SLOT_BAG_0, slot); + uint32 desiredQuality = itemQuality; if (urand(0, 100) < 100 * sPlayerbotAIConfig->randomGearLoweringChance && desiredQuality > ITEM_QUALITY_NORMAL) { @@ -1668,17 +1664,38 @@ void PlayerbotFactory::InitEquipment(bool incremental) bestItemForSlot = newItemId; } } + if (bestItemForSlot == 0) { continue; } - uint16 dest; if (!CanEquipUnseenItem(slot, dest, bestItemForSlot)) { continue; } + + if (incremental && oldItem) + { + float old_score = calculator.CalculateItem(oldItem->GetEntry()); + if (bestScoreForSlot < 1.2f * old_score) + continue; + } + + if (oldItem) + { + uint8 bagIndex = oldItem->GetBagSlot(); + uint8 slot = oldItem->GetSlot(); + uint8 dstBag = NULL_BAG; + + WorldPacket packet(CMSG_AUTOSTORE_BAG_ITEM, 3); + packet << bagIndex << slot << dstBag; + bot->GetSession()->HandleAutoStoreBagItemOpcode(packet); + } + Item* newItem = bot->EquipNewItem(dest, bestItemForSlot, true); + bot->AutoUnequipOffhandIfNeed(); + // bot->AutoUnequipOffhandIfNeed(); if (newItem) { newItem->AddToWorld(); @@ -1686,25 +1703,10 @@ void PlayerbotFactory::InitEquipment(bool incremental) } } // secondary init for better equips - if (!incremental) - InitEquipment(true); + if (!incremental && !second_chance) + InitEquipment(incremental, true); - // for (uint8 slot = 0; slot < EQUIPMENT_SLOT_END; ++slot) - // { - // if (slot == EQUIPMENT_SLOT_TABARD || slot == EQUIPMENT_SLOT_BODY) - // continue; - // if (level < 40 && (slot == EQUIPMENT_SLOT_TRINKET1 || slot == EQUIPMENT_SLOT_TRINKET2)) - // continue; - - // if (level < 25 && slot == EQUIPMENT_SLOT_NECK) - // continue; - - // if (level < 25 && slot == EQUIPMENT_SLOT_HEAD) - // continue; - - - // } } bool PlayerbotFactory::IsDesiredReplacement(Item* item) @@ -1978,7 +1980,7 @@ bool PlayerbotFactory::CanEquipUnseenItem(uint8 slot, uint16& dest, uint32 item) if (Item* pItem = Item::CreateItem(item, 1, bot, false, 0, true)) { - InventoryResult result = bot->CanEquipItem(slot, dest, pItem, true, false); + InventoryResult result = botAI->CanEquipItem(slot, dest, pItem, true, true); pItem->RemoveFromUpdateQueueOf(bot); delete pItem; return result == EQUIP_ERR_OK; diff --git a/src/factory/PlayerbotFactory.h b/src/factory/PlayerbotFactory.h index f88ce96e..e77947e5 100644 --- a/src/factory/PlayerbotFactory.h +++ b/src/factory/PlayerbotFactory.h @@ -122,7 +122,7 @@ public: static void InitTalentsByParsedSpecLink(Player* bot, std::vector> parsedSpecLink, bool reset); void InitAvailableSpells(); void InitClassSpells(); - void InitEquipment(bool incremental); + void InitEquipment(bool incremental, bool second_chance = false); void InitPet(); void InitAmmo(); static uint32 CalcMixedGearScore(uint32 gs, uint32 quality); diff --git a/src/strategy/values/ItemUsageValue.cpp b/src/strategy/values/ItemUsageValue.cpp index 9bdf206d..ca688a2d 100644 --- a/src/strategy/values/ItemUsageValue.cpp +++ b/src/strategy/values/ItemUsageValue.cpp @@ -166,7 +166,7 @@ ItemUsage ItemUsageValue::QueryItemUsageForEquip(ItemTemplate const* itemProto) return ITEM_USAGE_NONE; uint16 dest; - InventoryResult result = botAI->CanEquipItem(NULL_SLOT, dest, pItem, true, false); + InventoryResult result = botAI->CanEquipItem(NULL_SLOT, dest, pItem, true, true); pItem->RemoveFromUpdateQueueOf(bot); delete pItem; From 406949f6ddb84ffe5b3269658d10d707ea9b9c27 Mon Sep 17 00:00:00 2001 From: Yunfan Li Date: Wed, 4 Sep 2024 20:08:44 +0800 Subject: [PATCH 29/47] Misdirection and tricks of the trade --- src/PlayerbotMgr.cpp | 4 ++-- src/strategy/hunter/DpsHunterStrategy.cpp | 2 +- src/strategy/hunter/GenericHunterStrategy.cpp | 2 +- .../rogue/AssassinationRogueStrategy.cpp | 2 +- src/strategy/rogue/DpsRogueStrategy.cpp | 2 +- src/strategy/triggers/GenericTriggers.cpp | 17 +++++++++++++++++ src/strategy/triggers/GenericTriggers.h | 8 ++++++++ src/strategy/triggers/TriggerContext.h | 3 +++ 8 files changed, 34 insertions(+), 6 deletions(-) diff --git a/src/PlayerbotMgr.cpp b/src/PlayerbotMgr.cpp index 7389c72c..491e93ec 100644 --- a/src/PlayerbotMgr.cpp +++ b/src/PlayerbotMgr.cpp @@ -724,14 +724,14 @@ std::string const PlayerbotHolder::ProcessBotCommand(std::string const cmd, Obje sPlayerbotAIConfig->autoInitEquipLevelLimitRatio; PlayerbotFactory factory(bot, master->GetLevel(), ITEM_QUALITY_LEGENDARY, mixedGearScore); factory.Randomize(false); - return "ok, gear score limit: " + std::to_string(mixedGearScore / (ITEM_QUALITY_EPIC + 1)) + + return "ok, gear score limit: " + std::to_string(mixedGearScore / PlayerbotAI::GetItemScoreMultiplier(ItemQualities(ITEM_QUALITY_EPIC))) + "(for epic)"; } else if (cmd.starts_with("init=") && sscanf(cmd.c_str(), "init=%d", &gs) != -1) { PlayerbotFactory factory(bot, master->GetLevel(), ITEM_QUALITY_LEGENDARY, gs); factory.Randomize(false); - return "ok, gear score limit: " + std::to_string(gs / (ITEM_QUALITY_EPIC + 1)) + "(for epic)"; + return "ok, gear score limit: " + std::to_string(gs / PlayerbotAI::GetItemScoreMultiplier(ItemQualities(ITEM_QUALITY_EPIC))) + "(for epic)"; } } diff --git a/src/strategy/hunter/DpsHunterStrategy.cpp b/src/strategy/hunter/DpsHunterStrategy.cpp index e9d0e2f5..3ba171a2 100644 --- a/src/strategy/hunter/DpsHunterStrategy.cpp +++ b/src/strategy/hunter/DpsHunterStrategy.cpp @@ -45,7 +45,7 @@ NextAction** DpsHunterStrategy::getDefaultActions() new NextAction("explosive shot", ACTION_DEFAULT + 0.6f), new NextAction("aimed shot", ACTION_DEFAULT + 0.5f), new NextAction("silencing shot", ACTION_DEFAULT + 0.4f), new NextAction("kill command", ACTION_DEFAULT + 0.3f), - new NextAction("arcane shot", ACTION_DEFAULT + 0.2f), + // new NextAction("arcane shot", ACTION_DEFAULT + 0.2f), new NextAction("steady shot", ACTION_DEFAULT + 0.1f), new NextAction("auto shot", ACTION_DEFAULT), nullptr); } diff --git a/src/strategy/hunter/GenericHunterStrategy.cpp b/src/strategy/hunter/GenericHunterStrategy.cpp index e59df4cd..0974e2d4 100644 --- a/src/strategy/hunter/GenericHunterStrategy.cpp +++ b/src/strategy/hunter/GenericHunterStrategy.cpp @@ -95,7 +95,7 @@ void GenericHunterStrategy::InitTriggers(std::vector& triggers) triggers.push_back(new TriggerNode("enemy too close for auto shot", NextAction::array(0, new NextAction("flee", ACTION_MOVE + 9), nullptr))); triggers.push_back( - new TriggerNode("misdirection on main tank", + new TriggerNode("low tank threat", NextAction::array(0, new NextAction("misdirection on main tank", ACTION_HIGH + 7), NULL))); triggers.push_back( new TriggerNode("low health", NextAction::array(0, new NextAction("deterrence", ACTION_HIGH + 5), nullptr))); diff --git a/src/strategy/rogue/AssassinationRogueStrategy.cpp b/src/strategy/rogue/AssassinationRogueStrategy.cpp index 380ac292..5f3d2211 100644 --- a/src/strategy/rogue/AssassinationRogueStrategy.cpp +++ b/src/strategy/rogue/AssassinationRogueStrategy.cpp @@ -83,7 +83,7 @@ void AssassinationRogueStrategy::InitTriggers(std::vector& trigger new TriggerNode("medium aoe", NextAction::array(0, new NextAction("fan of knives", ACTION_NORMAL + 5), NULL))); triggers.push_back(new TriggerNode( - "tricks of the trade on main tank", + "low tank threat", NextAction::array(0, new NextAction("tricks of the trade on main tank", ACTION_HIGH + 7), NULL))); triggers.push_back(new TriggerNode( diff --git a/src/strategy/rogue/DpsRogueStrategy.cpp b/src/strategy/rogue/DpsRogueStrategy.cpp index 91cc8044..0fd0d003 100644 --- a/src/strategy/rogue/DpsRogueStrategy.cpp +++ b/src/strategy/rogue/DpsRogueStrategy.cpp @@ -141,7 +141,7 @@ void DpsRogueStrategy::InitTriggers(std::vector& triggers) NextAction::array(0, new NextAction("expose armor", ACTION_HIGH + 3), nullptr))); triggers.push_back(new TriggerNode( - "tricks of the trade on main tank", + "low tank threat", NextAction::array(0, new NextAction("tricks of the trade on main tank", ACTION_HIGH + 7), nullptr))); } diff --git a/src/strategy/triggers/GenericTriggers.cpp b/src/strategy/triggers/GenericTriggers.cpp index 2c69b72a..7826616f 100644 --- a/src/strategy/triggers/GenericTriggers.cpp +++ b/src/strategy/triggers/GenericTriggers.cpp @@ -14,6 +14,7 @@ #include "Playerbots.h" #include "SharedDefines.h" #include "TemporarySummon.h" +#include "ThreatMgr.h" #include "Timer.h" bool LowManaTrigger::IsActive() @@ -185,6 +186,22 @@ bool MyAttackerCountTrigger::IsActive() return AI_VALUE2(bool, "combat", "self target") && AI_VALUE(uint8, "my attacker count") >= amount; } +bool LowTankThreatTrigger::IsActive() +{ + Unit* mt = AI_VALUE(Unit*, "main tank"); + if (!mt) + return false; + + Unit* current_target = AI_VALUE(Unit*, "current target"); + if (!current_target) + return false; + + ThreatMgr& mgr = current_target->GetThreatMgr(); + float threat = mgr.GetThreat(bot); + float tankThreat = mgr.GetThreat(mt); + return tankThreat == 0.0f || threat > tankThreat * 0.5f; +} + bool AoeTrigger::IsActive() { Unit* current_target = AI_VALUE(Unit*, "current target"); diff --git a/src/strategy/triggers/GenericTriggers.h b/src/strategy/triggers/GenericTriggers.h index 132e6b66..b7217447 100644 --- a/src/strategy/triggers/GenericTriggers.h +++ b/src/strategy/triggers/GenericTriggers.h @@ -10,6 +10,7 @@ #include "HealthTriggers.h" #include "RangeTriggers.h" +#include "Trigger.h" class PlayerbotAI; class Unit; @@ -248,6 +249,13 @@ public: MediumThreatTrigger(PlayerbotAI* botAI) : MyAttackerCountTrigger(botAI, 2) {} }; +class LowTankThreatTrigger : public Trigger +{ +public: + LowTankThreatTrigger(PlayerbotAI* botAI) : Trigger(botAI, "low tank threat") {} + bool IsActive() override; +}; + class AoeTrigger : public AttackerCountTrigger { public: diff --git a/src/strategy/triggers/TriggerContext.h b/src/strategy/triggers/TriggerContext.h index e1c4ec7d..860ee682 100644 --- a/src/strategy/triggers/TriggerContext.h +++ b/src/strategy/triggers/TriggerContext.h @@ -102,6 +102,7 @@ public: creators["combo points not full and high energy"] = &TriggerContext::ComboPointsNotFullAndHighEnergy; creators["medium threat"] = &TriggerContext::MediumThreat; + creators["low tank threat"] = &TriggerContext::low_tank_threat; creators["dead"] = &TriggerContext::Dead; creators["corpse near"] = &TriggerContext::corpse_near; @@ -321,6 +322,8 @@ private: static Trigger* ComboPointsNotFull(PlayerbotAI* botAI) { return new ComboPointsNotFullTrigger(botAI); } static Trigger* ComboPointsNotFullAndHighEnergy(PlayerbotAI* botAI) { return new TwoTriggers(botAI, "combo points not full", "high energy available"); } static Trigger* MediumThreat(PlayerbotAI* botAI) { return new MediumThreatTrigger(botAI); } + static Trigger* low_tank_threat(PlayerbotAI* botAI) { return new LowTankThreatTrigger(botAI); } + // static Trigger* MediumThreat(PlayerbotAI* botAI) { return new MediumThreatTrigger(botAI); } static Trigger* Dead(PlayerbotAI* botAI) { return new DeadTrigger(botAI); } static Trigger* corpse_near(PlayerbotAI* botAI) { return new CorpseNearTrigger(botAI); } static Trigger* PartyMemberDead(PlayerbotAI* botAI) { return new PartyMemberDeadTrigger(botAI); } From 6ba4384184b3e2a0a86dd89aab41bf909d27b301 Mon Sep 17 00:00:00 2001 From: Yunfan Li Date: Wed, 4 Sep 2024 22:50:02 +0800 Subject: [PATCH 30/47] Init equipment and init available spell speed up --- conf/playerbots.conf.dist | 12 +- src/PlayerbotAI.cpp | 10 +- src/factory/PlayerbotFactory.cpp | 162 ++++++++++++++++++++------ src/factory/StatsWeightCalculator.cpp | 28 ++--- src/factory/StatsWeightCalculator.h | 2 +- 5 files changed, 150 insertions(+), 64 deletions(-) diff --git a/conf/playerbots.conf.dist b/conf/playerbots.conf.dist index 9f80241b..f987fe79 100644 --- a/conf/playerbots.conf.dist +++ b/conf/playerbots.conf.dist @@ -822,8 +822,8 @@ AiPlayerbot.PremadeSpecLink.3.0.60 = 51200201505112243100511351 AiPlayerbot.PremadeSpecLink.3.0.80 = 51200201505112253100531351-015305021 AiPlayerbot.PremadeSpecName.3.1 = mm pve AiPlayerbot.PremadeSpecGlyph.3.1 = 42912,43350,42914,43351,43338,45732 -AiPlayerbot.PremadeSpecLink.3.1.60 = -005305111230013233125030151-5 -AiPlayerbot.PremadeSpecLink.3.1.80 = 502-005305131230013233135031351-5000002 +AiPlayerbot.PremadeSpecLink.3.1.60 = -015305101230013233125031051 +AiPlayerbot.PremadeSpecLink.3.1.80 = 502-035305101230013233135031351-5000002 AiPlayerbot.PremadeSpecName.3.2 = surv pve AiPlayerbot.PremadeSpecGlyph.3.2 = 42912,43350,45731,43351,43338,45732 AiPlayerbot.PremadeSpecLink.3.2.60 = --5000032500033330502135201311 @@ -977,7 +977,7 @@ AiPlayerbot.PremadeSpecLink.9.0.70 = 2350022001113510053500131151--55 AiPlayerbot.PremadeSpecLink.9.0.80 = 2350022001113510253500331151--5500000501 AiPlayerbot.PremadeSpecName.9.1 = emo pve AiPlayerbot.PremadeSpecGlyph.9.1 = 45785,43390,50077,43394,43393,42459 -AiPlayerbot.PremadeSpecLink.9.1.60 = -003203301135112530131201-55 +AiPlayerbot.PremadeSpecLink.9.1.60 = -003203301135112530135201051 AiPlayerbot.PremadeSpecLink.9.1.70 = -003203301135112530135201051-55 AiPlayerbot.PremadeSpecLink.9.1.80 = -003203301135112530135221351-55000005 AiPlayerbot.PremadeSpecName.9.2 = destro pve @@ -1177,11 +1177,11 @@ AiPlayerbot.RandomClassSpecIndex.8.2 = 2 # # -AiPlayerbot.RandomClassSpecProb.9.0 = 40 +AiPlayerbot.RandomClassSpecProb.9.0 = 45 AiPlayerbot.RandomClassSpecIndex.9.0 = 0 -AiPlayerbot.RandomClassSpecProb.9.1 = 40 +AiPlayerbot.RandomClassSpecProb.9.1 = 45 AiPlayerbot.RandomClassSpecIndex.9.1 = 1 -AiPlayerbot.RandomClassSpecProb.9.2 = 20 +AiPlayerbot.RandomClassSpecProb.9.2 = 10 AiPlayerbot.RandomClassSpecIndex.9.2 = 2 # diff --git a/src/PlayerbotAI.cpp b/src/PlayerbotAI.cpp index e80304be..5df7402b 100644 --- a/src/PlayerbotAI.cpp +++ b/src/PlayerbotAI.cpp @@ -5277,11 +5277,9 @@ InventoryResult PlayerbotAI::CanEquipItem(uint8 slot, uint16& dest, Item* pItem, if (pItem->IsBindedNotWith(bot)) return EQUIP_ERR_DONT_OWN_THAT_ITEM; - // Yunfan: skip it - // // check count of items (skip for auto move for same player from bank) - // InventoryResult res = bot->CanTakeMoreSimilarItems(pItem); - // if (res != EQUIP_ERR_OK) - // return res; + InventoryResult res = bot->CanTakeMoreSimilarItems(pItem); + if (res != EQUIP_ERR_OK) + return res; ScalingStatDistributionEntry const* ssd = pProto->ScalingStatDistribution @@ -5300,7 +5298,7 @@ InventoryResult PlayerbotAI::CanEquipItem(uint8 slot, uint16& dest, Item* pItem, if (!bot->CanUseAttackType(bot->GetAttackBySlot(eslot))) return EQUIP_ERR_NOT_WHILE_DISARMED; - InventoryResult res = bot->CanUseItem(pItem, not_loading); + res = bot->CanUseItem(pItem, not_loading); if (res != EQUIP_ERR_OK) return res; diff --git a/src/factory/PlayerbotFactory.cpp b/src/factory/PlayerbotFactory.cpp index a40e28a4..a2bca1b9 100644 --- a/src/factory/PlayerbotFactory.cpp +++ b/src/factory/PlayerbotFactory.cpp @@ -287,7 +287,7 @@ void PlayerbotFactory::Randomize(bool incremental) LOG_DEBUG("playerbots", "Initializing equipmemt..."); if (!sPlayerbotAIConfig->equipmentPersistence || bot->GetLevel() < sPlayerbotAIConfig->equipmentPersistenceLevel) { - InitEquipment(incremental); + InitEquipment(incremental, incremental ? false : true); } // bot->SaveToDB(false, false); if (pmo) @@ -951,7 +951,12 @@ void PlayerbotFactory::InitTalentsTree(bool increment /*false*/, bool use_templa } else { - uint32 point = urand(1, 100); + uint32 pointSum = 0; + for (int i = 0; i < MAX_SPECNO; i++) + { + pointSum += sPlayerbotAIConfig->randomClassSpecProb[cls][i]; + } + uint32 point = urand(1, pointSum); uint32 currentP = 0; int i; for (i = 0; i < MAX_SPECNO; i++) @@ -1356,7 +1361,8 @@ bool PlayerbotFactory::CanEquipItem(ItemTemplate const* proto) return true; uint32 requiredLevel = proto->RequiredLevel; - bool hasItem = bot->HasItemCount(proto->ItemId, 1, true); + // disable since bad performance + bool hasItem = bot->HasItemCount(proto->ItemId, 1, false); // bot->GetItemCount() // !requiredLevel -> it's a quest reward item if (!requiredLevel && hasItem) @@ -1573,10 +1579,13 @@ void PlayerbotFactory::InitEquipment(bool incremental, bool second_chance) { for (uint32 itemId : sRandomItemMgr->GetCachedEquipments(requiredLevel, inventoryType)) { - if (itemId == 46978) - { // shaman earth ring totem + if (itemId == 46978) // shaman earth ring totem + { continue; } + uint32 skipProb = 25; + if (urand(1, 100) <= skipProb) + continue; // disable next expansion gear if (sPlayerbotAIConfig->limitGearExpansion && bot->GetLevel() <= 60 && itemId >= 23728) @@ -1603,8 +1612,8 @@ void PlayerbotFactory::InitEquipment(bool incremental, bool second_chance) if (proto->Quality != desiredQuality) continue; - if (!CanEquipItem(proto)) - continue; + // if (!CanEquipItem(proto)) + // continue; if (proto->Class == ITEM_CLASS_ARMOR && (slot == EQUIPMENT_SLOT_HEAD || slot == EQUIPMENT_SLOT_SHOULDERS || @@ -1621,9 +1630,9 @@ void PlayerbotFactory::InitEquipment(bool incremental, bool second_chance) proto->Class != ITEM_CLASS_WEAPON) continue; - uint16 dest = 0; - if (CanEquipUnseenItem(slot, dest, itemId)) - items[slot].push_back(itemId); + // uint16 dest = 0; + // if (CanEquipUnseenItem(slot, dest, itemId)) + items[slot].push_back(itemId); } } if (items[slot].size() >= 25) @@ -1640,26 +1649,20 @@ void PlayerbotFactory::InitEquipment(bool incremental, bool second_chance) float bestScoreForSlot = -1; uint32 bestItemForSlot = 0; for (int index = 0; index < ids.size(); index++) - { - uint32 skipProb = 25; - if (urand(1, 100) <= skipProb) - continue; - + { uint32 newItemId = ids[index]; - uint16 dest; - ItemTemplate const* proto = sObjectMgr->GetItemTemplate(newItemId); - if (!CanEquipItem(proto)) - continue; - - if (!CanEquipUnseenItem(slot, dest, newItemId)) - continue; - float cur_score = calculator.CalculateItem(newItemId); if (cur_score > bestScoreForSlot) { + // delay heavy check to here + if (!CanEquipItem(proto)) + continue; + uint16 dest; + if (!CanEquipUnseenItem(slot, dest, newItemId)) + continue; bestScoreForSlot = cur_score; bestItemForSlot = newItemId; } @@ -1695,18 +1698,107 @@ void PlayerbotFactory::InitEquipment(bool incremental, bool second_chance) Item* newItem = bot->EquipNewItem(dest, bestItemForSlot, true); bot->AutoUnequipOffhandIfNeed(); - // bot->AutoUnequipOffhandIfNeed(); if (newItem) { newItem->AddToWorld(); newItem->AddToUpdateQueueOf(bot); } } - // secondary init for better equips - if (!incremental && !second_chance) - InitEquipment(incremental, true); - + // Secondary init for better equips + /// @todo: clean up duplicate code + if (!incremental && second_chance) + { + for (uint8 slot = 0; slot < EQUIPMENT_SLOT_END; ++slot) + { + if (slot == EQUIPMENT_SLOT_TABARD || slot == EQUIPMENT_SLOT_BODY) + continue; + if (level < 40 && (slot == EQUIPMENT_SLOT_TRINKET1 || slot == EQUIPMENT_SLOT_TRINKET2)) + continue; + + if (level < 30 && slot == EQUIPMENT_SLOT_NECK) + continue; + + if (level < 25 && slot == EQUIPMENT_SLOT_HEAD) + continue; + + if (level < 20 && (slot == EQUIPMENT_SLOT_FINGER1 || slot == EQUIPMENT_SLOT_FINGER2)) + continue; + + Item* oldItem = bot->GetItemByPos(INVENTORY_SLOT_BAG_0, slot); + + if (oldItem) + { + bot->DestroyItem(INVENTORY_SLOT_BAG_0, slot, true); + } + + oldItem = bot->GetItemByPos(INVENTORY_SLOT_BAG_0, slot); + + std::vector& ids = items[slot]; + if (ids.empty()) + { + continue; + } + + float bestScoreForSlot = -1; + uint32 bestItemForSlot = 0; + for (int index = 0; index < ids.size(); index++) + { + uint32 newItemId = ids[index]; + + ItemTemplate const* proto = sObjectMgr->GetItemTemplate(newItemId); + + float cur_score = calculator.CalculateItem(newItemId); + if (cur_score > bestScoreForSlot) + { + // delay heavy check to here + if (!CanEquipItem(proto)) + continue; + uint16 dest; + if (!CanEquipUnseenItem(slot, dest, newItemId)) + continue; + bestScoreForSlot = cur_score; + bestItemForSlot = newItemId; + } + } + + if (bestItemForSlot == 0) + { + continue; + } + uint16 dest; + if (!CanEquipUnseenItem(slot, dest, bestItemForSlot)) + { + continue; + } + + if (incremental && oldItem) + { + float old_score = calculator.CalculateItem(oldItem->GetEntry()); + if (bestScoreForSlot < 1.2f * old_score) + continue; + } + + if (oldItem) + { + uint8 bagIndex = oldItem->GetBagSlot(); + uint8 slot = oldItem->GetSlot(); + uint8 dstBag = NULL_BAG; + + WorldPacket packet(CMSG_AUTOSTORE_BAG_ITEM, 3); + packet << bagIndex << slot << dstBag; + bot->GetSession()->HandleAutoStoreBagItemOpcode(packet); + } + + Item* newItem = bot->EquipNewItem(dest, bestItemForSlot, true); + bot->AutoUnequipOffhandIfNeed(); + if (newItem) + { + newItem->AddToWorld(); + newItem->AddToUpdateQueueOf(bot); + } + } + } } bool PlayerbotFactory::IsDesiredReplacement(Item* item) @@ -1876,11 +1968,11 @@ void PlayerbotFactory::InitBags(bool destroyOld) continue; } Item* newItem = bot->EquipNewItem(dest, newItemId, true); - if (newItem) - { - newItem->AddToWorld(); - newItem->AddToUpdateQueueOf(bot); - } + // if (newItem) + // { + // newItem->AddToWorld(); + // newItem->AddToUpdateQueueOf(bot); + // } } } @@ -2254,6 +2346,7 @@ void PlayerbotFactory::InitAvailableSpells() trainerIdCache.push_back(trainerId); } } + uint32 learnedCounter = 0; for (uint32 trainerId : trainerIdCache) { TrainerSpellData const* trainer_spells = sObjectMgr->GetNpcTrainerSpells(trainerId); @@ -2297,7 +2390,6 @@ void PlayerbotFactory::InitAvailableSpells() { continue; } - if (tSpell->learnedSpell[0]) { bot->learnSpell(tSpell->learnedSpell[0], false); @@ -2307,6 +2399,8 @@ void PlayerbotFactory::InitAvailableSpells() botAI->CastSpell(tSpell->spell, bot); } } + if (++learnedCounter > 20) + break; } } diff --git a/src/factory/StatsWeightCalculator.cpp b/src/factory/StatsWeightCalculator.cpp index bf08e19e..c3a328f0 100644 --- a/src/factory/StatsWeightCalculator.cpp +++ b/src/factory/StatsWeightCalculator.cpp @@ -32,6 +32,15 @@ StatsWeightCalculator::StatsWeightCalculator(Player* player) : player_(player) cls = player->getClass(); tab = AiFactory::GetPlayerSpecTab(player); + if (cls == CLASS_DEATH_KNIGHT && tab == DEATHKNIGHT_TAB_UNHOLY) + hitOverflowType_ = CollectorType::SPELL; + else if (cls == CLASS_SHAMAN && tab == SHAMAN_TAB_ENHANCEMENT) + hitOverflowType_ = CollectorType::SPELL; + else if (cls == CLASS_ROGUE) + hitOverflowType_ = CollectorType::SPELL; + else + hitOverflowType_ = type_; + enable_overflow_penalty_ = true; enable_item_set_bonus_ = true; enable_quality_blend_ = true; @@ -515,7 +524,7 @@ void StatsWeightCalculator::ApplyOverflowPenalty(Player* player) float validPoints; // m_modMeleeHitChance = (float)GetTotalAuraModifier(SPELL_AURA_MOD_HIT_CHANCE); // m_modMeleeHitChance += GetRatingBonusValue(CR_HIT_MELEE); - if (GetHitOverflowType(player) == CollectorType::SPELL) + if (hitOverflowType_ == CollectorType::SPELL) { hit_current = player->GetTotalAuraModifier(SPELL_AURA_MOD_SPELL_HIT_CHANCE); hit_current += player->GetRatingBonusValue(CR_HIT_SPELL); @@ -525,7 +534,7 @@ void StatsWeightCalculator::ApplyOverflowPenalty(Player* player) else validPoints = 0; } - else if (GetHitOverflowType(player) == CollectorType::MELEE) + else if (hitOverflowType_ == CollectorType::MELEE) { hit_current = player->GetTotalAuraModifier(SPELL_AURA_MOD_HIT_CHANCE); hit_current += player->GetRatingBonusValue(CR_HIT_MELEE); @@ -613,18 +622,3 @@ void StatsWeightCalculator::ApplyWeightFinetune(Player* player) } } } - -CollectorType StatsWeightCalculator::GetHitOverflowType(Player* player) -{ - cls = player->getClass(); - tab = AiFactory::GetPlayerSpecTab(player); - if (cls == CLASS_DEATH_KNIGHT && tab == DEATHKNIGHT_TAB_UNHOLY) - return CollectorType::SPELL; - if (cls == CLASS_SHAMAN && tab == SHAMAN_TAB_ENHANCEMENT) - return CollectorType::SPELL; - if (cls == CLASS_ROGUE) - return CollectorType::SPELL; - - return type_; -} - \ No newline at end of file diff --git a/src/factory/StatsWeightCalculator.h b/src/factory/StatsWeightCalculator.h index f6f4bf7e..d65966ff 100644 --- a/src/factory/StatsWeightCalculator.h +++ b/src/factory/StatsWeightCalculator.h @@ -49,11 +49,11 @@ private: void ApplyOverflowPenalty(Player* player); void ApplyWeightFinetune(Player* player); - CollectorType GetHitOverflowType(Player* player); private: Player* player_; CollectorType type_; + CollectorType hitOverflowType_; std::unique_ptr collector_; uint8 cls; int tab; From 0526948a29a8bbe4dc06f314ff025b8a8199891d Mon Sep 17 00:00:00 2001 From: Yunfan Li Date: Wed, 4 Sep 2024 22:58:29 +0800 Subject: [PATCH 31/47] TwoRoundsGearInit config option --- conf/playerbots.conf.dist | 3 + src/PlayerbotAIConfig.cpp | 1 + src/PlayerbotAIConfig.h | 1 + src/factory/PlayerbotFactory.cpp | 94 +++++++++++++------------------- 4 files changed, 44 insertions(+), 55 deletions(-) diff --git a/conf/playerbots.conf.dist b/conf/playerbots.conf.dist index f987fe79..4eabcb34 100644 --- a/conf/playerbots.conf.dist +++ b/conf/playerbots.conf.dist @@ -216,6 +216,9 @@ AiPlayerbot.AutoEquipUpgradeLoot = 1 # Default: 1.1 (Equip when the equipment score is 1.1 times higher than the current) AiPlayerbot.EquipUpgradeThreshold = 1.1 +# Two rounds of equipment initialization to create more suitable gear +AiPlayerbot.TwoRoundsGearInit = 0 + # # # diff --git a/src/PlayerbotAIConfig.cpp b/src/PlayerbotAIConfig.cpp index d285490a..c97ab4f8 100644 --- a/src/PlayerbotAIConfig.cpp +++ b/src/PlayerbotAIConfig.cpp @@ -471,6 +471,7 @@ bool PlayerbotAIConfig::Initialize() autoPickReward = sConfigMgr->GetOption("AiPlayerbot.AutoPickReward", "yes"); autoEquipUpgradeLoot = sConfigMgr->GetOption("AiPlayerbot.AutoEquipUpgradeLoot", true); equipUpgradeThreshold = sConfigMgr->GetOption("AiPlayerbot.EquipUpgradeThreshold", 1.1f); + twoRoundsGearInit = sConfigMgr->GetOption("AiPlayerbot.TwoRoundsGearInit", false); syncQuestWithPlayer = sConfigMgr->GetOption("AiPlayerbot.SyncQuestWithPlayer", true); syncQuestForPlayer = sConfigMgr->GetOption("AiPlayerbot.SyncQuestForPlayer", false); autoTrainSpells = sConfigMgr->GetOption("AiPlayerbot.AutoTrainSpells", "yes"); diff --git a/src/PlayerbotAIConfig.h b/src/PlayerbotAIConfig.h index f3757131..3aa47617 100644 --- a/src/PlayerbotAIConfig.h +++ b/src/PlayerbotAIConfig.h @@ -271,6 +271,7 @@ public: std::string autoPickReward; bool autoEquipUpgradeLoot; float equipUpgradeThreshold; + bool twoRoundsGearInit; bool syncQuestWithPlayer; bool syncQuestForPlayer; std::string autoTrainSpells; diff --git a/src/factory/PlayerbotFactory.cpp b/src/factory/PlayerbotFactory.cpp index a2bca1b9..a5ada525 100644 --- a/src/factory/PlayerbotFactory.cpp +++ b/src/factory/PlayerbotFactory.cpp @@ -39,7 +39,8 @@ #define PLAYER_SKILL_INDEX(x) (PLAYER_SKILL_INFO_1_1 + ((x)*3)) -const uint64 diveMask = (1LL << 7) | (1LL << 44) | (1LL << 37) | (1LL << 38) | (1LL << 26) | (1LL << 30) | (1LL << 27) | (1LL << 33) | (1LL << 24) | (1LL << 34); +const uint64 diveMask = (1LL << 7) | (1LL << 44) | (1LL << 37) | (1LL << 38) | (1LL << 26) | (1LL << 30) | (1LL << 27) | + (1LL << 33) | (1LL << 24) | (1LL << 34); 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, @@ -287,7 +288,7 @@ void PlayerbotFactory::Randomize(bool incremental) LOG_DEBUG("playerbots", "Initializing equipmemt..."); if (!sPlayerbotAIConfig->equipmentPersistence || bot->GetLevel() < sPlayerbotAIConfig->equipmentPersistenceLevel) { - InitEquipment(incremental, incremental ? false : true); + InitEquipment(incremental, incremental ? false : sPlayerbotAIConfig->twoRoundsGearInit); } // bot->SaveToDB(false, false); if (pmo) @@ -626,16 +627,19 @@ void PlayerbotFactory::InitPetTalents() // prevent learn talent for different family (cheating) if (!((1 << pet_family->petTalentType) & talentTabInfo->petTalentMask)) continue; - bool diveClass = talentInfo->TalentID == 2201 || talentInfo->TalentID == 2208 || talentInfo->TalentID == 2219 || talentInfo->TalentID == 2203; + bool diveClass = talentInfo->TalentID == 2201 || talentInfo->TalentID == 2208 || talentInfo->TalentID == 2219 || + talentInfo->TalentID == 2203; if (diveClass && !diveTypePet) continue; - bool dashClass = talentInfo->TalentID == 2119 || talentInfo->TalentID == 2207 || talentInfo->TalentID == 2111 || talentInfo->TalentID == 2109; + bool dashClass = talentInfo->TalentID == 2119 || talentInfo->TalentID == 2207 || talentInfo->TalentID == 2111 || + talentInfo->TalentID == 2109; if (dashClass && diveTypePet) continue; spells[talentInfo->Row].push_back(talentInfo); } - std::vector> order = sPlayerbotAIConfig->parsedHunterPetLinkOrder[pet_family->petTalentType][20]; + std::vector> order = + sPlayerbotAIConfig->parsedHunterPetLinkOrder[pet_family->petTalentType][20]; uint32 maxTalentPoints = pet->GetMaxTalentPointsForLevel(pet->GetLevel()); if (order.empty()) @@ -652,8 +656,8 @@ void PlayerbotFactory::InitPetTalents() int attemptCount = 0; // keep learning for the last row while (!spells_row.empty() && - ((((int)maxTalentPoints - (int)pet->GetFreeTalentPoints()) < 3 * (row + 1)) || (row == 5)) && - attemptCount++ < 10 && pet->GetFreeTalentPoints()) + ((((int)maxTalentPoints - (int)pet->GetFreeTalentPoints()) < 3 * (row + 1)) || (row == 5)) && + attemptCount++ < 10 && pet->GetFreeTalentPoints()) { int index = urand(0, spells_row.size() - 1); TalentEntry const* talentInfo = spells_row[index]; @@ -683,7 +687,7 @@ void PlayerbotFactory::InitPetTalents() uint32 spec = pet_family->petTalentType; uint32 startPoints = pet->GetMaxTalentPointsForLevel(pet->GetLevel()); while (startPoints > 1 && startPoints < 20 && - sPlayerbotAIConfig->parsedHunterPetLinkOrder[spec][startPoints].size() == 0) + sPlayerbotAIConfig->parsedHunterPetLinkOrder[spec][startPoints].size() == 0) { startPoints--; } @@ -706,7 +710,7 @@ void PlayerbotFactory::InitPetTalents() } if (talentInfo->DependsOn) { - bot->LearnPetTalent(pet->GetGUID(),talentInfo->DependsOn, + bot->LearnPetTalent(pet->GetGUID(), talentInfo->DependsOn, std::min(talentInfo->DependsOnRank, bot->GetFreeTalentPoints() - 1)); } talentID = talentInfo->TalentID; @@ -946,9 +950,10 @@ void PlayerbotFactory::InitTalentsTree(bool increment /*false*/, bool use_templa /// @todo: match current talent with template specTab = AiFactory::GetPlayerSpecTab(bot); /// @todo: fix cat druid hardcode - if (bot->getClass() == CLASS_DRUID && specTab == DRUID_TAB_FERAL && bot->GetLevel() >= 20 && !bot->HasAura(16931)) + if (bot->getClass() == CLASS_DRUID && specTab == DRUID_TAB_FERAL && bot->GetLevel() >= 20 && + !bot->HasAura(16931)) specTab = 3; - } + } else { uint32 pointSum = 0; @@ -1579,7 +1584,7 @@ void PlayerbotFactory::InitEquipment(bool incremental, bool second_chance) { for (uint32 itemId : sRandomItemMgr->GetCachedEquipments(requiredLevel, inventoryType)) { - if (itemId == 46978) // shaman earth ring totem + if (itemId == 46978) // shaman earth ring totem { continue; } @@ -1614,7 +1619,7 @@ void PlayerbotFactory::InitEquipment(bool incremental, bool second_chance) // if (!CanEquipItem(proto)) // continue; - + if (proto->Class == ITEM_CLASS_ARMOR && (slot == EQUIPMENT_SLOT_HEAD || slot == EQUIPMENT_SLOT_SHOULDERS || slot == EQUIPMENT_SLOT_CHEST || slot == EQUIPMENT_SLOT_WAIST || @@ -1645,11 +1650,11 @@ void PlayerbotFactory::InitEquipment(bool incremental, bool second_chance) { continue; } - + float bestScoreForSlot = -1; uint32 bestItemForSlot = 0; for (int index = 0; index < ids.size(); index++) - { + { uint32 newItemId = ids[index]; ItemTemplate const* proto = sObjectMgr->GetItemTemplate(newItemId); @@ -1677,7 +1682,7 @@ void PlayerbotFactory::InitEquipment(bool incremental, bool second_chance) { continue; } - + if (incremental && oldItem) { float old_score = calculator.CalculateItem(oldItem->GetEntry()); @@ -1695,7 +1700,7 @@ void PlayerbotFactory::InitEquipment(bool incremental, bool second_chance) packet << bagIndex << slot << dstBag; bot->GetSession()->HandleAutoStoreBagItemOpcode(packet); } - + Item* newItem = bot->EquipNewItem(dest, bestItemForSlot, true); bot->AutoUnequipOffhandIfNeed(); if (newItem) @@ -1706,7 +1711,7 @@ void PlayerbotFactory::InitEquipment(bool incremental, bool second_chance) } // Secondary init for better equips /// @todo: clean up duplicate code - if (!incremental && second_chance) + if (second_chance) { for (uint8 slot = 0; slot < EQUIPMENT_SLOT_END; ++slot) { @@ -1725,25 +1730,17 @@ void PlayerbotFactory::InitEquipment(bool incremental, bool second_chance) if (level < 20 && (slot == EQUIPMENT_SLOT_FINGER1 || slot == EQUIPMENT_SLOT_FINGER2)) continue; - Item* oldItem = bot->GetItemByPos(INVENTORY_SLOT_BAG_0, slot); - - if (oldItem) - { + if (Item* oldItem = bot->GetItemByPos(INVENTORY_SLOT_BAG_0, slot)) bot->DestroyItem(INVENTORY_SLOT_BAG_0, slot, true); - } - - oldItem = bot->GetItemByPos(INVENTORY_SLOT_BAG_0, slot); std::vector& ids = items[slot]; if (ids.empty()) - { continue; - } - + float bestScoreForSlot = -1; uint32 bestItemForSlot = 0; for (int index = 0; index < ids.size(); index++) - { + { uint32 newItemId = ids[index]; ItemTemplate const* proto = sObjectMgr->GetItemTemplate(newItemId); @@ -1771,25 +1768,6 @@ void PlayerbotFactory::InitEquipment(bool incremental, bool second_chance) { continue; } - - if (incremental && oldItem) - { - float old_score = calculator.CalculateItem(oldItem->GetEntry()); - if (bestScoreForSlot < 1.2f * old_score) - continue; - } - - if (oldItem) - { - uint8 bagIndex = oldItem->GetBagSlot(); - uint8 slot = oldItem->GetSlot(); - uint8 dstBag = NULL_BAG; - - WorldPacket packet(CMSG_AUTOSTORE_BAG_ITEM, 3); - packet << bagIndex << slot << dstBag; - bot->GetSession()->HandleAutoStoreBagItemOpcode(packet); - } - Item* newItem = bot->EquipNewItem(dest, bestItemForSlot, true); bot->AutoUnequipOffhandIfNeed(); if (newItem) @@ -2810,7 +2788,10 @@ void PlayerbotFactory::InitAmmo() bot->SetAmmo(entry); } -uint32 PlayerbotFactory::CalcMixedGearScore(uint32 gs, uint32 quality) { return gs * PlayerbotAI::GetItemScoreMultiplier(ItemQualities(quality)); } +uint32 PlayerbotFactory::CalcMixedGearScore(uint32 gs, uint32 quality) +{ + return gs * PlayerbotAI::GetItemScoreMultiplier(ItemQualities(quality)); +} void PlayerbotFactory::InitMounts() { @@ -2978,7 +2959,8 @@ std::vector PlayerbotFactory::GetCurrentGemsCount() Item* pItem2 = bot->GetItemByPos(INVENTORY_SLOT_BAG_0, i); if (pItem2 && !pItem2->IsBroken() && pItem2->HasSocket()) { - for (uint32 enchant_slot = SOCK_ENCHANTMENT_SLOT; enchant_slot <= PRISMATIC_ENCHANTMENT_SLOT; ++enchant_slot) + for (uint32 enchant_slot = SOCK_ENCHANTMENT_SLOT; enchant_slot <= PRISMATIC_ENCHANTMENT_SLOT; + ++enchant_slot) { if (enchant_slot == BONUS_ENCHANTMENT_SLOT) continue; @@ -3486,10 +3468,10 @@ void PlayerbotFactory::InitInventoryEquip() if (proto->Class == ITEM_CLASS_WEAPON && !CanEquipWeapon(proto)) continue; - + if (proto->Quality != desiredQuality) continue; - + if (!CanEquipItem(proto)) continue; @@ -3936,7 +3918,9 @@ void PlayerbotFactory::ApplyEnchantAndGemsNew(bool destoryOld) { continue; } - if (enchant->requiredSkill && (!bot->HasSkill(enchant->requiredSkill) || (bot->GetSkillValue(enchant->requiredSkill) < enchant->requiredSkillValue))) + if (enchant->requiredSkill && + (!bot->HasSkill(enchant->requiredSkill) || + (bot->GetSkillValue(enchant->requiredSkill) < enchant->requiredSkillValue))) { continue; } @@ -3973,7 +3957,7 @@ void PlayerbotFactory::ApplyEnchantAndGemsNew(bool destoryOld) int32 enchantIdChosen = -1; int32 colorChosen; float bestGemScore = -1; - for (uint32 &enchantGem : availableGems) + for (uint32& enchantGem : availableGems) { ItemTemplate const* gemTemplate = sObjectMgr->GetItemTemplate(enchantGem); if (!gemTemplate) @@ -3983,7 +3967,7 @@ void PlayerbotFactory::ApplyEnchantAndGemsNew(bool destoryOld) if (!gemProperties) continue; - if ((socketColor & gemProperties->color) == 0 && gemProperties->color == 1) // meta socket + if ((socketColor & gemProperties->color) == 0 && gemProperties->color == 1) // meta socket continue; uint32 enchant_id = gemProperties->spellitemenchantement; From bbc95c953a65f3256a130f2d3b0b6422273e5dc0 Mon Sep 17 00:00:00 2001 From: Yunfan Li Date: Thu, 5 Sep 2024 01:10:42 +0800 Subject: [PATCH 32/47] Fix DeleteRandomBotAccounts crash --- src/PlayerbotAIConfig.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/PlayerbotAIConfig.cpp b/src/PlayerbotAIConfig.cpp index c97ab4f8..60b20a95 100644 --- a/src/PlayerbotAIConfig.cpp +++ b/src/PlayerbotAIConfig.cpp @@ -496,13 +496,13 @@ bool PlayerbotAIConfig::Initialize() selfBotLevel = sConfigMgr->GetOption("AiPlayerbot.SelfBotLevel", 1); RandomPlayerbotFactory::CreateRandomBots(); - if (sPlayerbotAIConfig->addClassCommand) - sRandomPlayerbotMgr->PrepareAddclassCache(); - if (World::IsStopped()) { return true; } + if (sPlayerbotAIConfig->addClassCommand) + sRandomPlayerbotMgr->PrepareAddclassCache(); + sRandomItemMgr->Init(); sRandomItemMgr->InitAfterAhBot(); sPlayerbotTextMgr->LoadBotTexts(); From 369f3373edbb238c197e37e930baab0b7c2dfdab Mon Sep 17 00:00:00 2001 From: Yunfan Li Date: Thu, 5 Sep 2024 15:04:46 +0800 Subject: [PATCH 33/47] Fix unique equip init and bots login --- src/RandomPlayerbotMgr.cpp | 5 ++- src/factory/PlayerbotFactory.cpp | 49 ++++++++++++++--------- src/strategy/actions/ListSpellsAction.cpp | 12 ++++-- 3 files changed, 42 insertions(+), 24 deletions(-) diff --git a/src/RandomPlayerbotMgr.cpp b/src/RandomPlayerbotMgr.cpp index 21bd012a..0f06e0a5 100644 --- a/src/RandomPlayerbotMgr.cpp +++ b/src/RandomPlayerbotMgr.cpp @@ -370,7 +370,7 @@ void RandomPlayerbotMgr::UpdateAIInternal(uint32 elapsed, bool /*minimal*/) break; } - if (loginBots) + if (loginBots && botLoading.empty()) { loginBots += updateBots; loginBots = std::min(loginBots, maxNewBots); @@ -1041,6 +1041,9 @@ bool RandomPlayerbotMgr::ProcessBot(uint32 bot) SetEventValue(bot, "login", 0, 0); + if (!player->IsInWorld()) + return false; + if (player->GetGroup() || player->HasUnitState(UNIT_STATE_IN_FLIGHT)) return false; diff --git a/src/factory/PlayerbotFactory.cpp b/src/factory/PlayerbotFactory.cpp index a5ada525..47db56b6 100644 --- a/src/factory/PlayerbotFactory.cpp +++ b/src/factory/PlayerbotFactory.cpp @@ -235,6 +235,7 @@ void PlayerbotFactory::Randomize(bool incremental) pmo = sPerformanceMonitor->start(PERF_MON_RNDBOT, "PlayerbotFactory_Spells1"); LOG_DEBUG("playerbots", "Initializing spells (step 1)..."); // bot->LearnDefaultSkills(); + bot->LearnDefaultSkills(); InitClassSpells(); InitAvailableSpells(); if (pmo) @@ -425,7 +426,10 @@ void PlayerbotFactory::Refresh() InitFood(); InitReagents(); // InitPotions(); - InitTalentsTree(true, true, true); + if (!sPlayerbotAIConfig->equipmentPersistence || bot->GetLevel() < sPlayerbotAIConfig->equipmentPersistenceLevel) + { + InitTalentsTree(true, true, true); + } InitPet(); InitPetTalents(); InitClassSpells(); @@ -885,7 +889,7 @@ void PlayerbotFactory::ClearEverything() ClearSpells(); ClearInventory(); ResetQuests(); - bot->SaveToDB(false, false); + // bot->SaveToDB(false, false); } void PlayerbotFactory::ClearSpells() @@ -1616,7 +1620,7 @@ void PlayerbotFactory::InitEquipment(bool incremental, bool second_chance) if (proto->Quality != desiredQuality) continue; - + // delay heavy check // if (!CanEquipItem(proto)) // continue; @@ -1634,7 +1638,7 @@ void PlayerbotFactory::InitEquipment(bool incremental, bool second_chance) if (slot == EQUIPMENT_SLOT_OFFHAND && bot->getClass() == CLASS_ROGUE && proto->Class != ITEM_CLASS_WEAPON) continue; - + // delay heavy check // uint16 dest = 0; // if (CanEquipUnseenItem(slot, dest, itemId)) items[slot].push_back(itemId); @@ -1690,6 +1694,7 @@ void PlayerbotFactory::InitEquipment(bool incremental, bool second_chance) continue; } + if (oldItem) { uint8 bagIndex = oldItem->GetBagSlot(); @@ -1701,13 +1706,18 @@ void PlayerbotFactory::InitEquipment(bool incremental, bool second_chance) bot->GetSession()->HandleAutoStoreBagItemOpcode(packet); } + oldItem = bot->GetItemByPos(INVENTORY_SLOT_BAG_0, slot); + // fail to store in bag + if (oldItem) + continue; + Item* newItem = bot->EquipNewItem(dest, bestItemForSlot, true); bot->AutoUnequipOffhandIfNeed(); - if (newItem) - { - newItem->AddToWorld(); - newItem->AddToUpdateQueueOf(bot); - } + // if (newItem) + // { + // newItem->AddToWorld(); + // newItem->AddToUpdateQueueOf(bot); + // } } // Secondary init for better equips /// @todo: clean up duplicate code @@ -1770,11 +1780,11 @@ void PlayerbotFactory::InitEquipment(bool incremental, bool second_chance) } Item* newItem = bot->EquipNewItem(dest, bestItemForSlot, true); bot->AutoUnequipOffhandIfNeed(); - if (newItem) - { - newItem->AddToWorld(); - newItem->AddToUpdateQueueOf(bot); - } + // if (newItem) + // { + // newItem->AddToWorld(); + // newItem->AddToUpdateQueueOf(bot); + // } } } } @@ -2305,8 +2315,6 @@ void PlayerbotFactory::SetRandomSkill(uint16 id) void PlayerbotFactory::InitAvailableSpells() { - bot->LearnDefaultSkills(); - if (trainerIdCache.empty()) { CreatureTemplateContainer const* creatureTemplateContainer = sObjectMgr->GetCreatureTemplates(); @@ -2324,7 +2332,8 @@ void PlayerbotFactory::InitAvailableSpells() trainerIdCache.push_back(trainerId); } } - uint32 learnedCounter = 0; + // uint32 learnedCounter = 0; + // uint32 oktest = 0; for (uint32 trainerId : trainerIdCache) { TrainerSpellData const* trainer_spells = sObjectMgr->GetNpcTrainerSpells(trainerId); @@ -2368,6 +2377,7 @@ void PlayerbotFactory::InitAvailableSpells() { continue; } + // oktest++; if (tSpell->learnedSpell[0]) { bot->learnSpell(tSpell->learnedSpell[0], false); @@ -2377,8 +2387,9 @@ void PlayerbotFactory::InitAvailableSpells() botAI->CastSpell(tSpell->spell, bot); } } - if (++learnedCounter > 20) - break; + // LOG_INFO("playerbots", "C: {}, ok: {}", ++learnedCounter, oktest); + // if (++learnedCounter > 20) + // break; } } diff --git a/src/strategy/actions/ListSpellsAction.cpp b/src/strategy/actions/ListSpellsAction.cpp index 75d25ac9..80277ea8 100644 --- a/src/strategy/actions/ListSpellsAction.cpp +++ b/src/strategy/actions/ListSpellsAction.cpp @@ -11,13 +11,13 @@ std::map ListSpellsAction::skillSpells; std::set ListSpellsAction::vendorItems; -bool CompareSpells(std::pair& s1, std::pair& s2) +bool CompareSpells(const std::pair& s1, const std::pair& s2) { SpellInfo const* si1 = sSpellMgr->GetSpellInfo(s1.first); SpellInfo const* si2 = sSpellMgr->GetSpellInfo(s2.first); if (!si1 || !si2) { - LOG_ERROR("playerbots", "SpellInfo missing."); + LOG_ERROR("playerbots", "SpellInfo missing. {} {}", s1.first, s2.first); return false; } uint32 p1 = si1->SchoolMask * 20000; @@ -54,7 +54,7 @@ bool CompareSpells(std::pair& s1, std::pairSpellName[0], si1->SpellName[1]) > 0; + return strcmp(si1->SpellName[0], si2->SpellName[0]) > 0; } return p1 > p2; @@ -273,7 +273,11 @@ std::vector> ListSpellsAction::GetSpellList(std:: if (out.str().empty()) continue; - + + if (itr->first == 0) + { + LOG_ERROR("playerbots", "?! {}", itr->first); + } spells.push_back(std::pair(itr->first, out.str())); alreadySeenList += spellInfo->SpellName[0]; alreadySeenList += ","; From 73f699fe8983a65e08d0cee165950003f8a340a4 Mon Sep 17 00:00:00 2001 From: Yunfan Li Date: Thu, 5 Sep 2024 15:57:32 +0800 Subject: [PATCH 34/47] Fix initself crash --- src/PlayerbotAI.cpp | 1 + src/factory/PlayerbotFactory.cpp | 11 ++++++----- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/PlayerbotAI.cpp b/src/PlayerbotAI.cpp index 5df7402b..0504b6b7 100644 --- a/src/PlayerbotAI.cpp +++ b/src/PlayerbotAI.cpp @@ -5257,6 +5257,7 @@ bool PlayerbotAI::EqualLowercaseName(std::string s1, std::string s2) return true; } +// A custom CanEquipItem (remove AutoUnequipOffhand in FindEquipSlot to prevent unequip on `item usage` calculation) InventoryResult PlayerbotAI::CanEquipItem(uint8 slot, uint16& dest, Item* pItem, bool swap, bool not_loading) const { dest = 0; diff --git a/src/factory/PlayerbotFactory.cpp b/src/factory/PlayerbotFactory.cpp index 47db56b6..a6dc88b7 100644 --- a/src/factory/PlayerbotFactory.cpp +++ b/src/factory/PlayerbotFactory.cpp @@ -2060,7 +2060,8 @@ bool PlayerbotFactory::CanEquipUnseenItem(uint8 slot, uint16& dest, uint32 item) if (Item* pItem = Item::CreateItem(item, 1, bot, false, 0, true)) { - InventoryResult result = botAI->CanEquipItem(slot, dest, pItem, true, true); + InventoryResult result = botAI ? botAI->CanEquipItem(slot, dest, pItem, true, true) + : bot->CanEquipItem(slot, dest, pItem, true, true); pItem->RemoveFromUpdateQueueOf(bot); delete pItem; return result == EQUIP_ERR_OK; @@ -2382,10 +2383,10 @@ void PlayerbotFactory::InitAvailableSpells() { bot->learnSpell(tSpell->learnedSpell[0], false); } - else - { - botAI->CastSpell(tSpell->spell, bot); - } + // else + // { + // botAI->CastSpell(tSpell->spell, bot); + // } } // LOG_INFO("playerbots", "C: {}, ok: {}", ++learnedCounter, oktest); // if (++learnedCounter > 20) From 79fa75e15087bfd9850c707f922cf2da08b8a307 Mon Sep 17 00:00:00 2001 From: antony Date: Thu, 5 Sep 2024 10:35:20 +0200 Subject: [PATCH 35/47] Optimised ExtractAllQuestIds ExtractAllItemIds --- src/ChatHelper.cpp | 47 +++++++++++++++++------------- src/PlayerbotAI.cpp | 39 ++++++++++++------------- src/strategy/actions/SayAction.cpp | 31 +++++++++----------- 3 files changed, 59 insertions(+), 58 deletions(-) diff --git a/src/ChatHelper.cpp b/src/ChatHelper.cpp index 156dc0ea..8f464d5d 100644 --- a/src/ChatHelper.cpp +++ b/src/ChatHelper.cpp @@ -593,34 +593,41 @@ void ChatHelper::eraseAllSubStr(std::string& mainStr, std::string const toErase) } } -std::set ChatHelper::ExtractAllQuestIds(const std::string& text) +std::set extractGeneric(std::string_view text, std::string_view prefix) { - std::set ids; + std::set ids; + std::string_view text_view = text; - std::regex rgx("Hquest:[0-9]+"); - auto begin = std::sregex_iterator(text.begin(), text.end(), rgx); - auto end = std::sregex_iterator(); - for (std::sregex_iterator i = begin; i != end; ++i) + size_t pos = 0; + while ((pos = text_view.find(prefix, pos)) != std::string::npos) { - std::smatch match = *i; - ids.insert(std::stoi(match.str().erase(0, 7))); + // skip "Hquest:/Hitem:" + pos += prefix.size(); + + // extract everything after "Hquest:/Hitem:" + size_t end_pos = text_view.find_first_not_of("0123456789", pos); + std::string_view number_str = text_view.substr(pos, end_pos - pos); + + uint32 number = 0; + + auto [ptr, ec] = std::from_chars(number_str.data(), number_str.data() + number_str.size(), number); + + if (ec == std::errc()) + { + ids.insert(number); + } + pos = end_pos; } return ids; } +std::set ChatHelper::ExtractAllQuestIds(const std::string& text) +{ + return extractGeneric(text, "Hquest:"); +} + std::set ChatHelper::ExtractAllItemIds(const std::string& text) { - std::set ids; - - std::regex rgx("Hitem:[0-9]+"); - auto begin = std::sregex_iterator(text.begin(), text.end(), rgx); - auto end = std::sregex_iterator(); - for (std::sregex_iterator i = begin; i != end; ++i) - { - std::smatch match = *i; - ids.insert(std::stoi(match.str().erase(0, 6))); - } - - return ids; + return extractGeneric(text, "Hitem:"); } diff --git a/src/PlayerbotAI.cpp b/src/PlayerbotAI.cpp index e80304be..ae8d1213 100644 --- a/src/PlayerbotAI.cpp +++ b/src/PlayerbotAI.cpp @@ -1005,26 +1005,25 @@ void PlayerbotAI::HandleBotOutgoingPacket(WorldPacket const& packet) if (lang == LANG_ADDON) return; - // Disable since ExtractAllItemIds bad performance - // if (message.starts_with(sPlayerbotAIConfig->toxicLinksPrefix) && - // (GetChatHelper()->ExtractAllItemIds(message).size() > 0 || - // GetChatHelper()->ExtractAllQuestIds(message).size() > 0) && - // sPlayerbotAIConfig->toxicLinksRepliesChance) - // { - // if (urand(0, 50) > 0 || urand(1, 100) > sPlayerbotAIConfig->toxicLinksRepliesChance) - // { - // return; - // } - // } - // else if ((GetChatHelper()->ExtractAllItemIds(message).count(19019) && - // sPlayerbotAIConfig->thunderfuryRepliesChance)) - // { - // if (urand(0, 60) > 0 || urand(1, 100) > sPlayerbotAIConfig->thunderfuryRepliesChance) - // { - // return; - // } - // } - // else + if (message.starts_with(sPlayerbotAIConfig->toxicLinksPrefix) && + (GetChatHelper()->ExtractAllItemIds(message).size() > 0 || + GetChatHelper()->ExtractAllQuestIds(message).size() > 0) && + sPlayerbotAIConfig->toxicLinksRepliesChance) + { + if (urand(0, 50) > 0 || urand(1, 100) > sPlayerbotAIConfig->toxicLinksRepliesChance) + { + return; + } + } + else if ((GetChatHelper()->ExtractAllItemIds(message).count(19019) && + sPlayerbotAIConfig->thunderfuryRepliesChance)) + { + if (urand(0, 60) > 0 || urand(1, 100) > sPlayerbotAIConfig->thunderfuryRepliesChance) + { + return; + } + } + else { if (isFromFreeBot && urand(0, 20)) return; diff --git a/src/strategy/actions/SayAction.cpp b/src/strategy/actions/SayAction.cpp index 5b12fabf..93d36a00 100644 --- a/src/strategy/actions/SayAction.cpp +++ b/src/strategy/actions/SayAction.cpp @@ -205,21 +205,20 @@ void ChatReplyAction::ChatReplyDo(Player* bot, uint32& type, uint32& guid1, uint return; } - // Disable since ExtractAllItemIds bad performance - // //toxic links - // if (msg.starts_with(sPlayerbotAIConfig->toxicLinksPrefix) - // && (GET_PLAYERBOT_AI(bot)->GetChatHelper()->ExtractAllItemIds(msg).size() > 0 || GET_PLAYERBOT_AI(bot)->GetChatHelper()->ExtractAllQuestIds(msg).size() > 0)) - // { - // HandleToxicLinksReply(bot, chatChannelSource, msg, name); - // return; - // } + //toxic links + if (msg.starts_with(sPlayerbotAIConfig->toxicLinksPrefix) + && (GET_PLAYERBOT_AI(bot)->GetChatHelper()->ExtractAllItemIds(msg).size() > 0 || GET_PLAYERBOT_AI(bot)->GetChatHelper()->ExtractAllQuestIds(msg).size() > 0)) + { + HandleToxicLinksReply(bot, chatChannelSource, msg, name); + return; + } - // //thunderfury - // if (GET_PLAYERBOT_AI(bot)->GetChatHelper()->ExtractAllItemIds(msg).count(19019)) - // { - // HandleThunderfuryReply(bot, chatChannelSource, msg, name); - // return; - // } + //thunderfury + if (GET_PLAYERBOT_AI(bot)->GetChatHelper()->ExtractAllItemIds(msg).count(19019)) + { + HandleThunderfuryReply(bot, chatChannelSource, msg, name); + return; + } auto messageRepy = GenerateReplyMessage(bot, msg, guid1, name); SendGeneralResponse(bot, chatChannelSource, messageRepy, name); @@ -321,8 +320,6 @@ bool ChatReplyAction::HandleToxicLinksReply(Player* bot, ChatChannelSource chatC } bool ChatReplyAction::HandleWTBItemsReply(Player* bot, ChatChannelSource chatChannelSource, std::string& msg, std::string& name) { - // Disable since ExtractAllItemIds bad performance - return false; auto messageItemIds = GET_PLAYERBOT_AI(bot)->GetChatHelper()->ExtractAllItemIds(msg); if (messageItemIds.empty()) @@ -418,8 +415,6 @@ bool ChatReplyAction::HandleWTBItemsReply(Player* bot, ChatChannelSource chatCha } bool ChatReplyAction::HandleLFGQuestsReply(Player* bot, ChatChannelSource chatChannelSource, std::string& msg, std::string& name) { - // Disable since ExtractAllQuestIds bad performance - return false; auto messageQuestIds = GET_PLAYERBOT_AI(bot)->GetChatHelper()->ExtractAllQuestIds(msg); if (messageQuestIds.empty()) From 927d893945bc93be2ab39dec965e0870ec4d06a6 Mon Sep 17 00:00:00 2001 From: Yunfan Li Date: Fri, 6 Sep 2024 12:33:54 +0800 Subject: [PATCH 36/47] Improve combat reach and dps target, allowing spell interruption --- src/PlayerbotAI.cpp | 54 ++++++++++------------- src/factory/PlayerbotFactory.cpp | 3 +- src/strategy/actions/MovementActions.cpp | 36 +++++---------- src/strategy/actions/TellTargetAction.cpp | 3 +- src/strategy/values/DpsTargetValue.cpp | 37 +++++++++------- 5 files changed, 60 insertions(+), 73 deletions(-) diff --git a/src/PlayerbotAI.cpp b/src/PlayerbotAI.cpp index 0504b6b7..2c917074 100644 --- a/src/PlayerbotAI.cpp +++ b/src/PlayerbotAI.cpp @@ -310,24 +310,22 @@ void PlayerbotAI::UpdateAI(uint32 elapsed, bool minimal) bot->SetPower(bot->getPowerType(), bot->GetMaxPower(bot->getPowerType())); } - if (!CanUpdateAI()) - return; - - // check activity AllowActivity(); - // if (bot->GetCurrentSpell(CURRENT_CHANNELED_SPELL)) { - // return; - // } Spell* currentSpell = bot->GetCurrentSpell(CURRENT_GENERIC_SPELL); - if (currentSpell && currentSpell->getState() == SPELL_STATE_CASTING && currentSpell->GetCastTime()) + if (currentSpell && currentSpell->getState() == SPELL_STATE_PREPARING) { - nextAICheckDelay = currentSpell->GetCastTime() + sPlayerbotAIConfig->reactDelay; - SetNextCheckDelay(nextAICheckDelay); - if (!CanUpdateAI()) - return; + if (currentSpell->m_targets.GetUnitTarget() && !currentSpell->m_targets.GetUnitTarget()->IsAlive()) + { + bot->InterruptSpell(CURRENT_GENERIC_SPELL); + SetNextCheckDelay(sPlayerbotAIConfig->reactDelay); + } + return; } + if (!CanUpdateAI()) + return; + if (!bot->InBattleground() && !bot->inRandomLfgDungeon() && bot->GetGroup()) { Player* leader = bot->GetGroup()->GetLeader(); @@ -898,9 +896,9 @@ void PlayerbotAI::HandleBotOutgoingPacket(WorldPacket const& packet) p >> casterGuid.ReadAsPacked(); if (casterGuid != bot->GetGUID()) return; - + uint8 count, result; uint32 spellId; - p >> spellId; + p >> count >> spellId >> result; SpellInterrupted(spellId); return; } @@ -1135,21 +1133,15 @@ void PlayerbotAI::HandleBotOutgoingPacket(WorldPacket const& packet) void PlayerbotAI::SpellInterrupted(uint32 spellid) { + for (uint8 type = CURRENT_MELEE_SPELL; type < CURRENT_CHANNELED_SPELL; type++) + { + Spell* spell = bot->GetCurrentSpell((CurrentSpellTypes)type); + if (!spell) + continue; + if (spell->GetSpellInfo()->Id == spellid) + bot->InterruptSpell((CurrentSpellTypes)type); + } LastSpellCast& lastSpell = aiObjectContext->GetValue("last spell cast")->Get(); - if (!spellid || lastSpell.id != spellid) - return; - - time_t now = time(nullptr); - if (now <= lastSpell.timer) - return; - - uint32 castTimeSpent = 1000 * (now - lastSpell.timer); - int32 globalCooldown = CalculateGlobalCooldown(lastSpell.id); - if (static_cast(castTimeSpent) < globalCooldown) - SetNextCheckDelay(globalCooldown - castTimeSpent); - else - SetNextCheckDelay(sPlayerbotAIConfig->reactDelay); - lastSpell.id = 0; } @@ -1513,14 +1505,15 @@ void PlayerbotAI::ApplyInstanceStrategies(uint32 mapId, bool tellMaster) default: break; } - + if (strategyName.empty()) + return; engines[BOT_STATE_COMBAT]->addStrategy(strategyName); engines[BOT_STATE_NON_COMBAT]->addStrategy(strategyName); if (tellMaster && !strategyName.empty()) { std::ostringstream out; out << "Add " << strategyName << " instance strategy"; - TellMaster(out.str()); + TellMasterNoFacing(out.str()); } } @@ -3617,6 +3610,7 @@ bool PlayerbotAI::IsInVehicle(bool canControl, bool canCast, bool canAttack, boo void PlayerbotAI::WaitForSpellCast(Spell* spell) { + return; SpellInfo const* spellInfo = spell->GetSpellInfo(); uint32 castTime = spell->GetCastTime(); // float castTime = spell->GetCastTime(); diff --git a/src/factory/PlayerbotFactory.cpp b/src/factory/PlayerbotFactory.cpp index a6dc88b7..5b5eba2e 100644 --- a/src/factory/PlayerbotFactory.cpp +++ b/src/factory/PlayerbotFactory.cpp @@ -3880,7 +3880,7 @@ void PlayerbotFactory::ApplyEnchantAndGemsNew(bool destoryOld) } availableGems.push_back(enchantGem); } - + StatsWeightCalculator calculator(bot); for (uint8 slot = 0; slot < EQUIPMENT_SLOT_END; ++slot) { if (slot == EQUIPMENT_SLOT_TABARD || slot == EQUIPMENT_SLOT_BODY) @@ -3940,7 +3940,6 @@ void PlayerbotFactory::ApplyEnchantAndGemsNew(bool destoryOld) { continue; } - StatsWeightCalculator calculator(bot); float score = calculator.CalculateEnchant(enchant_id); if (score >= bestScore) { diff --git a/src/strategy/actions/MovementActions.cpp b/src/strategy/actions/MovementActions.cpp index 57eb4255..bafccd83 100644 --- a/src/strategy/actions/MovementActions.cpp +++ b/src/strategy/actions/MovementActions.cpp @@ -814,39 +814,25 @@ bool MovementAction::ReachCombatTo(Unit* target, float distance) float combatDistance = bot->GetCombatReach() + target->GetCombatReach(); distance += combatDistance; - if (target->HasUnitMovementFlag(MOVEMENTFLAG_FORWARD)) // target is moving forward, predict the position - { - float needToGo = bot->GetExactDist(target) - distance; - float timeToGo = MoveDelay(abs(needToGo)) + sPlayerbotAIConfig->reactDelay / 1000.0f; - float targetMoveDist = timeToGo * target->GetSpeed(MOVE_RUN); - targetMoveDist = std::min(5.0f, targetMoveDist); - tx += targetMoveDist * cos(target->GetOrientation()); - ty += targetMoveDist * sin(target->GetOrientation()); - if (!target->GetMap()->CheckCollisionAndGetValidCoords(target, target->GetPositionX(), target->GetPositionY(), - target->GetPositionZ(), tx, ty, tz)) - { - // disable prediction if position is invalid - tx = target->GetPositionX(); - ty = target->GetPositionY(); - tz = target->GetPositionZ(); - } - // Prediction may cause this, which makes ShortenPathUntilDist fail - if (bot->GetExactDist(tx, ty, tz) <= distance) - { - tx = target->GetPositionX(); - ty = target->GetPositionY(); - tz = target->GetPositionZ(); - } - } if (bot->GetExactDist(tx, ty, tz) <= distance) return false; + PathGenerator path(bot); path.CalculatePath(tx, ty, tz, false); PathType type = path.GetPathType(); int typeOk = PATHFIND_NORMAL | PATHFIND_INCOMPLETE; if (!(type & typeOk)) return false; - path.ShortenPathUntilDist(G3D::Vector3(tx, ty, tz), distance); + float shortenTo = distance; + + // Avoid walking too far when moving towards each other + if (bot->GetDistance(tx, ty, tz) >= 10.0f) + shortenTo = std::max(distance, bot->GetDistance(tx, ty, tz) / 2); + + if (bot->GetExactDist(tx, ty, tz) <= shortenTo) + return false; + + path.ShortenPathUntilDist(G3D::Vector3(tx, ty, tz), shortenTo); G3D::Vector3 endPos = path.GetPath().back(); return MoveTo(target->GetMapId(), endPos.x, endPos.y, endPos.z, false, false, false, false, MovementPriority::MOVEMENT_COMBAT); diff --git a/src/strategy/actions/TellTargetAction.cpp b/src/strategy/actions/TellTargetAction.cpp index d1d05384..f6f05639 100644 --- a/src/strategy/actions/TellTargetAction.cpp +++ b/src/strategy/actions/TellTargetAction.cpp @@ -29,13 +29,14 @@ bool TellAttackersAction::Execute(Event event) botAI->TellMaster("--- Attackers ---"); GuidVector attackers = context->GetValue("attackers")->Get(); + int32 count = 0; for (ObjectGuid const guid : attackers) { Unit* unit = botAI->GetUnit(guid); if (!unit || !unit->IsAlive()) continue; - botAI->TellMaster(unit->GetName()); + botAI->TellMaster(std::to_string(++count) + std::string(".") + unit->GetName()); } botAI->TellMaster("--- Threat ---"); diff --git a/src/strategy/values/DpsTargetValue.cpp b/src/strategy/values/DpsTargetValue.cpp index 5fc276df..88c52ccf 100644 --- a/src/strategy/values/DpsTargetValue.cpp +++ b/src/strategy/values/DpsTargetValue.cpp @@ -50,6 +50,7 @@ public: CasterFindTargetSmartStrategy(PlayerbotAI* botAI, float dps) : FindTargetStrategy(botAI), dps_(dps), targetExpectedLifeTime(1000000) { + result = nullptr; } void CheckAttacker(Unit* attacker, ThreatMgr* threatMgr) override @@ -86,13 +87,15 @@ public: { float new_time = new_unit->GetHealth() / dps_; float old_time = old_unit->GetHealth() / dps_; - // [5-20] > (5-0] > (20-inf) - if (GetIntervalLevel(new_unit) > GetIntervalLevel(old_unit)) + // [5-30] > (5-0] > (20-inf) + int new_level = GetIntervalLevel(new_unit); + int old_level = GetIntervalLevel(old_unit); + if (new_level != old_level) { - return true; + return new_level > old_level; } - int32_t level = GetIntervalLevel(new_unit); - if (level % 10 == 2 || level % 10 == 1) + int32_t level = new_level; + if (level % 10 == 2 || level % 10 == 0) { return new_time < old_time; } @@ -116,15 +119,15 @@ public: botAI->IsRanged(botAI->GetBot()) ? sPlayerbotAIConfig->spellDistance : sPlayerbotAIConfig->meleeDistance; attackRange += 5.0f; int level = dis < attackRange ? 10 : 0; - if (time >= 3 && time <= 20) + if (time >= 5 && time <= 30) { return level + 2; } - if (time > 20) + if (time > 30) { - return level + 1; + return level; } - return level; + return level + 1; } protected: @@ -176,12 +179,14 @@ public: float new_time = new_unit->GetHealth() / dps_; float old_time = old_unit->GetHealth() / dps_; // [5-20] > (5-0] > (20-inf) - if (GetIntervalLevel(new_unit) > GetIntervalLevel(old_unit)) + int new_level = GetIntervalLevel(new_unit); + int old_level = GetIntervalLevel(old_unit); + if (new_level != old_level) { - return true; + return new_level > old_level; } // attack enemy in range and with lowest health - int level = GetIntervalLevel(new_unit); + int level = new_level; if (level == 10) { return new_time < old_time; @@ -249,12 +254,14 @@ public: float new_time = new_unit->GetHealth() / dps_; float old_time = old_unit->GetHealth() / dps_; // [5-20] > (5-0] > (20-inf) - if (GetIntervalLevel(new_unit) > GetIntervalLevel(old_unit)) + int new_level = GetIntervalLevel(new_unit); + int old_level = GetIntervalLevel(old_unit); + if (new_level != old_level) { - return true; + return new_level > old_level; } // attack enemy in range and with lowest health - int level = GetIntervalLevel(new_unit); + int level = new_level; Player* bot = botAI->GetBot(); if (level == 10) { From 593b30bbb047a080ab5dc4b8f898a935082730bd Mon Sep 17 00:00:00 2001 From: Yunfan Li Date: Fri, 6 Sep 2024 20:29:38 +0800 Subject: [PATCH 37/47] Arm warrior and dk --- conf/playerbots.conf.dist | 14 +- src/factory/StatsCollector.cpp | 14 +- src/factory/StatsCollector.h | 3 +- src/factory/StatsWeightCalculator.cpp | 126 ++++++++++-------- src/strategy/deathknight/BloodDKStrategy.cpp | 8 +- .../deathknight/DKAiObjectContext.cpp | 4 + src/strategy/deathknight/DKTriggers.cpp | 15 ++- src/strategy/deathknight/DKTriggers.h | 14 ++ src/strategy/deathknight/FrostDKStrategy.cpp | 32 +++-- .../deathknight/GenericDKStrategy.cpp | 15 +-- src/strategy/deathknight/UnholyDKStrategy.cpp | 56 +++++--- src/strategy/values/Arrow.cpp | 4 +- src/strategy/values/Formations.cpp | 20 ++- src/strategy/warrior/ArmsWarriorStrategy.cpp | 12 +- 14 files changed, 211 insertions(+), 126 deletions(-) diff --git a/conf/playerbots.conf.dist b/conf/playerbots.conf.dist index 4eabcb34..87008706 100644 --- a/conf/playerbots.conf.dist +++ b/conf/playerbots.conf.dist @@ -774,12 +774,12 @@ AiPlayerbot.RandomBotInWorldWithRotationDisabled = 31104000 AiPlayerbot.PremadeSpecName.1.0 = arms pve AiPlayerbot.PremadeSpecGlyph.1.0 = 43418,43395,43423,43399,49084,43421 -AiPlayerbot.PremadeSpecLink.1.0.60 = 3022032023335100202012013031241 -AiPlayerbot.PremadeSpecLink.1.0.80 = 3022032123335100202012013031251-32505010002 +AiPlayerbot.PremadeSpecLink.1.0.60 = 3022032023335100002012211231241 +AiPlayerbot.PremadeSpecLink.1.0.80 = 3022032023335100102012213231251-305-2033 AiPlayerbot.PremadeSpecName.1.1 = fury pve AiPlayerbot.PremadeSpecGlyph.1.1 = 43418,43395,43414,43399,49084,43432 AiPlayerbot.PremadeSpecLink.1.1.60 = -305053000500310053120501351 -AiPlayerbot.PremadeSpecLink.1.1.80 = 30202300233-305053000500310153120511351 +AiPlayerbot.PremadeSpecLink.1.1.80 = 32002300233-305053000500310153120511351 AiPlayerbot.PremadeSpecName.1.2 = prot pve AiPlayerbot.PremadeSpecGlyph.1.2 = 43424,43395,43425,43399,49084,45793 AiPlayerbot.PremadeSpecLink.1.2.60 = --053351225000210521030113321 @@ -825,8 +825,8 @@ AiPlayerbot.PremadeSpecLink.3.0.60 = 51200201505112243100511351 AiPlayerbot.PremadeSpecLink.3.0.80 = 51200201505112253100531351-015305021 AiPlayerbot.PremadeSpecName.3.1 = mm pve AiPlayerbot.PremadeSpecGlyph.3.1 = 42912,43350,42914,43351,43338,45732 -AiPlayerbot.PremadeSpecLink.3.1.60 = -015305101230013233125031051 -AiPlayerbot.PremadeSpecLink.3.1.80 = 502-035305101230013233135031351-5000002 +AiPlayerbot.PremadeSpecLink.3.1.60 = -025315101030013233125031051 +AiPlayerbot.PremadeSpecLink.3.1.80 = 502-025335101030013233135031351-5000002 AiPlayerbot.PremadeSpecName.3.2 = surv pve AiPlayerbot.PremadeSpecGlyph.3.2 = 42912,43350,45731,43351,43338,45732 AiPlayerbot.PremadeSpecLink.3.2.60 = --5000032500033330502135201311 @@ -910,8 +910,8 @@ AiPlayerbot.PremadeSpecLink.6.1.60 = -32003350332203012300023101351 AiPlayerbot.PremadeSpecLink.6.1.80 = -32002350352203012300033101351-230200305003 AiPlayerbot.PremadeSpecName.6.2 = unholy pve AiPlayerbot.PremadeSpecGlyph.6.2 = 43542,43673,45804,43544,43672,43549 -AiPlayerbot.PremadeSpecLink.6.2.60 = --2300303050032152000150213130051 -AiPlayerbot.PremadeSpecLink.6.2.80 = -320053500002-2300303050032152000150213130051 +AiPlayerbot.PremadeSpecLink.6.2.60 = --2301303050032151000150013113151 +AiPlayerbot.PremadeSpecLink.6.2.80 = -320033500002-2301303050032151000150013133151 AiPlayerbot.PremadeSpecName.6.3 = double aura blood pve AiPlayerbot.PremadeSpecGlyph.6.3 = 45805,43673,43827,43544,43672,43554 AiPlayerbot.PremadeSpecLink.6.3.60 = 005512153330030320102013-305 diff --git a/src/factory/StatsCollector.cpp b/src/factory/StatsCollector.cpp index 1f6c6d65..bc73fcb6 100644 --- a/src/factory/StatsCollector.cpp +++ b/src/factory/StatsCollector.cpp @@ -11,7 +11,7 @@ #include "SpellMgr.h" #include "UpdateFields.h" -StatsCollector::StatsCollector(CollectorType type) : type_(type) { Reset(); } +StatsCollector::StatsCollector(CollectorType type, int32 cls) : type_(type), cls_(cls) { Reset(); } void StatsCollector::Reset() { @@ -243,6 +243,18 @@ bool StatsCollector::SpecialSpellFilter(uint32 spellId) { case 67771: // Death's Verdict (heroic) stats[STATS_TYPE_ATTACK_POWER] += 260; return true; + case 71406: // Tiny Abomination in a Jar + if (cls_ == CLASS_PALADIN) + stats[STATS_TYPE_ATTACK_POWER] += 600; + else + stats[STATS_TYPE_ATTACK_POWER] += 150; + return true; + case 71545: // Tiny Abomination in a Jar (heroic) + if (cls_ == CLASS_PALADIN) + stats[STATS_TYPE_ATTACK_POWER] += 800; + else + stats[STATS_TYPE_ATTACK_POWER] += 200; + return true; case 71519: // Deathbringer's Will stats[STATS_TYPE_ATTACK_POWER] += 350; return true; diff --git a/src/factory/StatsCollector.h b/src/factory/StatsCollector.h index fa0634ce..b51dddf9 100644 --- a/src/factory/StatsCollector.h +++ b/src/factory/StatsCollector.h @@ -57,7 +57,7 @@ enum CollectorType : uint8 class StatsCollector { public: - StatsCollector(CollectorType type); + StatsCollector(CollectorType type, int32 cls = -1); StatsCollector(StatsCollector& stats) = default; void Reset(); void CollectItemStats(ItemTemplate const* proto); @@ -78,6 +78,7 @@ private: private: CollectorType type_; + uint32 cls_; }; #endif \ No newline at end of file diff --git a/src/factory/StatsWeightCalculator.cpp b/src/factory/StatsWeightCalculator.cpp index c3a328f0..f6270c73 100644 --- a/src/factory/StatsWeightCalculator.cpp +++ b/src/factory/StatsWeightCalculator.cpp @@ -27,10 +27,10 @@ StatsWeightCalculator::StatsWeightCalculator(Player* player) : player_(player) type_ = CollectorType::MELEE; else type_ = CollectorType::RANGED; - collector_ = std::make_unique(type_); - cls = player->getClass(); tab = AiFactory::GetPlayerSpecTab(player); + collector_ = std::make_unique(type_, cls); + if (cls == CLASS_DEATH_KNIGHT && tab == DEATHKNIGHT_TAB_UNHOLY) hitOverflowType_ = CollectorType::SPELL; @@ -128,58 +128,58 @@ void StatsWeightCalculator::GenerateBasicWeights(Player* player) if (cls == CLASS_HUNTER && (tab == HUNTER_TAB_BEASTMASTER || tab == HUNTER_TAB_SURVIVAL)) { - stats_weights_[STATS_TYPE_AGILITY] += 2.4f; + stats_weights_[STATS_TYPE_AGILITY] += 2.5f; stats_weights_[STATS_TYPE_ATTACK_POWER] += 1.0f; - stats_weights_[STATS_TYPE_ARMOR_PENETRATION] += 1.3f; - stats_weights_[STATS_TYPE_HIT] += 1.6f; - stats_weights_[STATS_TYPE_CRIT] += 1.5f; - stats_weights_[STATS_TYPE_HASTE] += 1.4f; - stats_weights_[STATS_TYPE_RANGED_DPS] += 5.0f; + stats_weights_[STATS_TYPE_ARMOR_PENETRATION] += 1.5f; + stats_weights_[STATS_TYPE_HIT] += 1.7f; + stats_weights_[STATS_TYPE_CRIT] += 1.4f; + stats_weights_[STATS_TYPE_HASTE] += 1.6f; + stats_weights_[STATS_TYPE_RANGED_DPS] += 7.5f; } else if (cls == CLASS_HUNTER && tab == HUNTER_TAB_MARKSMANSHIP) { - stats_weights_[STATS_TYPE_AGILITY] += 2.2f; + stats_weights_[STATS_TYPE_AGILITY] += 2.3f; stats_weights_[STATS_TYPE_ATTACK_POWER] += 1.0f; - stats_weights_[STATS_TYPE_ARMOR_PENETRATION] += 2.2f; + stats_weights_[STATS_TYPE_ARMOR_PENETRATION] += 2.25f; stats_weights_[STATS_TYPE_HIT] += 2.1f; stats_weights_[STATS_TYPE_CRIT] += 2.0f; stats_weights_[STATS_TYPE_HASTE] += 1.8f; - stats_weights_[STATS_TYPE_RANGED_DPS] += 5.0f; + stats_weights_[STATS_TYPE_RANGED_DPS] += 10.0f; } else if (cls == CLASS_ROGUE && tab == ROGUE_TAB_COMBAT) { - stats_weights_[STATS_TYPE_AGILITY] += 1.8f; + stats_weights_[STATS_TYPE_AGILITY] += 1.9f; stats_weights_[STATS_TYPE_STRENGTH] += 1.1f; stats_weights_[STATS_TYPE_ATTACK_POWER] += 1.0f; - stats_weights_[STATS_TYPE_ARMOR_PENETRATION] += 1.2f; - stats_weights_[STATS_TYPE_HIT] += 2.0f; - stats_weights_[STATS_TYPE_CRIT] += 1.6f; - stats_weights_[STATS_TYPE_HASTE] += 1.4f; + stats_weights_[STATS_TYPE_ARMOR_PENETRATION] += 1.8f; + stats_weights_[STATS_TYPE_HIT] += 2.1f; + stats_weights_[STATS_TYPE_CRIT] += 1.4f; + stats_weights_[STATS_TYPE_HASTE] += 1.7f; stats_weights_[STATS_TYPE_EXPERTISE] += 2.0f; - stats_weights_[STATS_TYPE_MELEE_DPS] += 5.0f; + stats_weights_[STATS_TYPE_MELEE_DPS] += 7.0f; } else if (cls == CLASS_DRUID && tab == DRUID_TAB_FERAL && !PlayerbotAI::IsTank(player)) { - stats_weights_[STATS_TYPE_AGILITY] += 2.4f; - stats_weights_[STATS_TYPE_STRENGTH] += 2.3f; + stats_weights_[STATS_TYPE_AGILITY] += 2.2f; + stats_weights_[STATS_TYPE_STRENGTH] += 2.4f; stats_weights_[STATS_TYPE_ATTACK_POWER] += 1.0f; - stats_weights_[STATS_TYPE_ARMOR_PENETRATION] += 2.1f; + stats_weights_[STATS_TYPE_ARMOR_PENETRATION] += 2.3f; stats_weights_[STATS_TYPE_HIT] += 1.9f; - stats_weights_[STATS_TYPE_CRIT] += 1.8f; - stats_weights_[STATS_TYPE_HASTE] += 1.4f; - stats_weights_[STATS_TYPE_EXPERTISE] += 2.0f; - stats_weights_[STATS_TYPE_MELEE_DPS] += 5.0f; + stats_weights_[STATS_TYPE_CRIT] += 1.5f; + stats_weights_[STATS_TYPE_HASTE] += 2.1f; + stats_weights_[STATS_TYPE_EXPERTISE] += 2.1f; + stats_weights_[STATS_TYPE_MELEE_DPS] += 15.0f; } else if (cls == CLASS_ROGUE && (tab == ROGUE_TAB_ASSASSINATION || tab == ROGUE_TAB_SUBTLETY)) { - stats_weights_[STATS_TYPE_AGILITY] += 1.7f; + stats_weights_[STATS_TYPE_AGILITY] += 1.5f; stats_weights_[STATS_TYPE_STRENGTH] += 1.1f; stats_weights_[STATS_TYPE_ATTACK_POWER] += 1.0f; - stats_weights_[STATS_TYPE_ARMOR_PENETRATION] += 1.0f; - stats_weights_[STATS_TYPE_HIT] += 1.6f; - stats_weights_[STATS_TYPE_CRIT] += 1.3f; - stats_weights_[STATS_TYPE_HASTE] += 1.5f; - stats_weights_[STATS_TYPE_EXPERTISE] += 2.0f; + stats_weights_[STATS_TYPE_ARMOR_PENETRATION] += 1.2f; + stats_weights_[STATS_TYPE_HIT] += 2.1f; + stats_weights_[STATS_TYPE_CRIT] += 1.1f; + stats_weights_[STATS_TYPE_HASTE] += 1.8f; + stats_weights_[STATS_TYPE_EXPERTISE] += 2.1f; stats_weights_[STATS_TYPE_MELEE_DPS] += 5.0f; } else if (cls == CLASS_WARRIOR && tab == WARRIOR_TAB_FURY) // fury @@ -208,70 +208,86 @@ void StatsWeightCalculator::GenerateBasicWeights(Player* player) } else if (cls == CLASS_DEATH_KNIGHT && tab == DEATHKNIGHT_TAB_FROST) // frost dk { - stats_weights_[STATS_TYPE_AGILITY] += 1.8f; - stats_weights_[STATS_TYPE_STRENGTH] += 2.6f; + stats_weights_[STATS_TYPE_AGILITY] += 1.7f; + stats_weights_[STATS_TYPE_STRENGTH] += 2.8f; stats_weights_[STATS_TYPE_ATTACK_POWER] += 1.0f; - stats_weights_[STATS_TYPE_ARMOR_PENETRATION] += 2.1f; + stats_weights_[STATS_TYPE_ARMOR_PENETRATION] += 2.7f; stats_weights_[STATS_TYPE_HIT] += 2.3f; stats_weights_[STATS_TYPE_CRIT] += 2.2f; - stats_weights_[STATS_TYPE_HASTE] += 1.8f; + stats_weights_[STATS_TYPE_HASTE] += 2.1f; stats_weights_[STATS_TYPE_EXPERTISE] += 2.5f; stats_weights_[STATS_TYPE_MELEE_DPS] += 7.0f; } else if (cls == CLASS_DEATH_KNIGHT && tab == DEATHKNIGHT_TAB_UNHOLY) { - stats_weights_[STATS_TYPE_AGILITY] += 0.5f; + stats_weights_[STATS_TYPE_AGILITY] += 0.9f; stats_weights_[STATS_TYPE_STRENGTH] += 2.5f; stats_weights_[STATS_TYPE_ATTACK_POWER] += 1.0f; stats_weights_[STATS_TYPE_ARMOR_PENETRATION] += 1.0f; stats_weights_[STATS_TYPE_HIT] += 1.8f; - stats_weights_[STATS_TYPE_CRIT] += 1.0f; + stats_weights_[STATS_TYPE_CRIT] += 1.4f; stats_weights_[STATS_TYPE_HASTE] += 1.7f; - stats_weights_[STATS_TYPE_EXPERTISE] += 1.0f; + stats_weights_[STATS_TYPE_EXPERTISE] += 1.5f; stats_weights_[STATS_TYPE_MELEE_DPS] += 5.0f; } else if (cls == CLASS_PALADIN && tab == PALADIN_TAB_RETRIBUTION) // retribution { - stats_weights_[STATS_TYPE_AGILITY] += 1.3f; + stats_weights_[STATS_TYPE_AGILITY] += 1.6f; stats_weights_[STATS_TYPE_STRENGTH] += 2.5f; - stats_weights_[STATS_TYPE_INTELLECT] += 0.15f; + stats_weights_[STATS_TYPE_INTELLECT] += 0.1f; stats_weights_[STATS_TYPE_ATTACK_POWER] += 1.0f; stats_weights_[STATS_TYPE_SPELL_POWER] += 0.3f; - stats_weights_[STATS_TYPE_ARMOR_PENETRATION] += 0.7f; + stats_weights_[STATS_TYPE_ARMOR_PENETRATION] += 1.5f; stats_weights_[STATS_TYPE_HIT] += 1.9f; - stats_weights_[STATS_TYPE_CRIT] += 1.3f; - stats_weights_[STATS_TYPE_HASTE] += 1.2f; + stats_weights_[STATS_TYPE_CRIT] += 1.7f; + stats_weights_[STATS_TYPE_HASTE] += 1.6f; stats_weights_[STATS_TYPE_EXPERTISE] += 2.0f; - stats_weights_[STATS_TYPE_MELEE_DPS] += 7.0f; + stats_weights_[STATS_TYPE_MELEE_DPS] += 9.0f; } else if ((cls == CLASS_SHAMAN && tab == SHAMAN_TAB_ENHANCEMENT)) // enhancement { - stats_weights_[STATS_TYPE_AGILITY] += 1.6f; + stats_weights_[STATS_TYPE_AGILITY] += 1.4f; stats_weights_[STATS_TYPE_STRENGTH] += 1.1f; - stats_weights_[STATS_TYPE_INTELLECT] += 0.5f; + stats_weights_[STATS_TYPE_INTELLECT] += 0.3f; stats_weights_[STATS_TYPE_ATTACK_POWER] += 1.0f; - stats_weights_[STATS_TYPE_SPELL_POWER] += 0.9f; - stats_weights_[STATS_TYPE_ARMOR_PENETRATION] += 1.2f; - stats_weights_[STATS_TYPE_HIT] += 1.7f; - stats_weights_[STATS_TYPE_CRIT] += 1.4f; + stats_weights_[STATS_TYPE_SPELL_POWER] += 0.95f; + stats_weights_[STATS_TYPE_ARMOR_PENETRATION] += 0.9f; + stats_weights_[STATS_TYPE_HIT] += 2.1f; + stats_weights_[STATS_TYPE_CRIT] += 1.5f; stats_weights_[STATS_TYPE_HASTE] += 1.8f; stats_weights_[STATS_TYPE_EXPERTISE] += 2.0f; stats_weights_[STATS_TYPE_MELEE_DPS] += 8.5f; } - else if (cls == CLASS_WARLOCK || cls == CLASS_MAGE || + else if (cls == CLASS_WARLOCK || (cls == CLASS_MAGE && tab != MAGE_TAB_FIRE) || (cls == CLASS_PRIEST && tab == PRIEST_TAB_SHADOW) || // shadow - (cls == CLASS_SHAMAN && tab == SHAMAN_TAB_ELEMENTAL) || // element (cls == CLASS_DRUID && tab == DRUID_TAB_BALANCE)) // balance { - stats_weights_[STATS_TYPE_INTELLECT] += 0.5f; - stats_weights_[STATS_TYPE_SPIRIT] += 0.4f; + stats_weights_[STATS_TYPE_INTELLECT] += 0.3f; + stats_weights_[STATS_TYPE_SPIRIT] += 0.6f; stats_weights_[STATS_TYPE_SPELL_POWER] += 1.0f; - stats_weights_[STATS_TYPE_SPELL_PENETRATION] += 1.0f; stats_weights_[STATS_TYPE_HIT] += 1.1f; stats_weights_[STATS_TYPE_CRIT] += 0.8f; stats_weights_[STATS_TYPE_HASTE] += 1.0f; stats_weights_[STATS_TYPE_RANGED_DPS] += 1.0f; } + else if (cls == CLASS_MAGE && tab == MAGE_TAB_FIRE) + { + stats_weights_[STATS_TYPE_INTELLECT] += 0.3f; + stats_weights_[STATS_TYPE_SPIRIT] += 0.7f; + stats_weights_[STATS_TYPE_SPELL_POWER] += 1.0f; + stats_weights_[STATS_TYPE_HIT] += 1.2f; + stats_weights_[STATS_TYPE_CRIT] += 1.1f; + stats_weights_[STATS_TYPE_HASTE] += 0.8f; + stats_weights_[STATS_TYPE_RANGED_DPS] += 1.0f; + } + else if (cls == CLASS_SHAMAN && tab == SHAMAN_TAB_ELEMENTAL) + { + stats_weights_[STATS_TYPE_INTELLECT] += 0.25f; + stats_weights_[STATS_TYPE_SPELL_POWER] += 1.0f; + stats_weights_[STATS_TYPE_HIT] += 1.1f; + 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_PRIEST && tab != PRIEST_TAB_SHADOW) || // discipline / holy (cls == CLASS_SHAMAN && tab == SHAMAN_TAB_RESTORATION) || // heal @@ -349,6 +365,8 @@ void StatsWeightCalculator::GenerateAdditionalWeights(Player* player) { if (player->HasAura(34484)) stats_weights_[STATS_TYPE_INTELLECT] += 1.1f; + if (player->HasAura(56341)) + stats_weights_[STATS_TYPE_STAMINA] += 0.3f; } else if (cls == CLASS_WARRIOR) { diff --git a/src/strategy/deathknight/BloodDKStrategy.cpp b/src/strategy/deathknight/BloodDKStrategy.cpp index f76d6795..7daf494d 100644 --- a/src/strategy/deathknight/BloodDKStrategy.cpp +++ b/src/strategy/deathknight/BloodDKStrategy.cpp @@ -100,9 +100,9 @@ void BloodDKStrategy::InitTriggers(std::vector& triggers) triggers.push_back( new TriggerNode("lose aggro", NextAction::array(0, new NextAction("dark command", ACTION_HIGH + 3), nullptr))); triggers.push_back( - new TriggerNode("low health", NextAction::array(0, new NextAction("army of the dead", ACTION_HIGH + 5), - new NextAction("vampiric blood", ACTION_HIGH + 4), + new TriggerNode("low health", NextAction::array(0, new NextAction("army of the dead", ACTION_HIGH + 4), new NextAction("death strike", ACTION_HIGH + 3), nullptr))); - // triggers.push_back(new TriggerNode("army of the dead", NextAction::array(0, new NextAction("army of the dead", - // ACTION_HIGH + 6), nullptr))); + triggers.push_back( + new TriggerNode("critical health", NextAction::array(0, new NextAction("vampiric blood", ACTION_HIGH + 5), nullptr))); + } diff --git a/src/strategy/deathknight/DKAiObjectContext.cpp b/src/strategy/deathknight/DKAiObjectContext.cpp index 9023f8ac..5f7eb941 100644 --- a/src/strategy/deathknight/DKAiObjectContext.cpp +++ b/src/strategy/deathknight/DKAiObjectContext.cpp @@ -87,6 +87,8 @@ public: creators["chains of ice"] = &DeathKnightTriggerFactoryInternal::chains_of_ice; creators["unbreakable armor"] = &DeathKnightTriggerFactoryInternal::unbreakable_armor; creators["high blood rune"] = &DeathKnightTriggerFactoryInternal::high_blood_rune; + creators["high frost rune"] = &DeathKnightTriggerFactoryInternal::high_frost_rune; + creators["high unholy rune"] = &DeathKnightTriggerFactoryInternal::high_unholy_rune; creators["freezing fog"] = &DeathKnightTriggerFactoryInternal::freezing_fog; creators["no desolation"] = &DeathKnightTriggerFactoryInternal::no_desolation; creators["death and decay cooldown"] = &DeathKnightTriggerFactoryInternal::death_and_decay_cooldown; @@ -122,6 +124,8 @@ private: static Trigger* chains_of_ice(PlayerbotAI* botAI) { return new ChainsOfIceSnareTrigger(botAI); } static Trigger* unbreakable_armor(PlayerbotAI* botAI) { return new UnbreakableArmorTrigger(botAI); } static Trigger* high_blood_rune(PlayerbotAI* botAI) { return new HighBloodRuneTrigger(botAI); } + static Trigger* high_frost_rune(PlayerbotAI* botAI) { return new HighFrostRuneTrigger(botAI); } + static Trigger* high_unholy_rune(PlayerbotAI* botAI) { return new HighUnholyRuneTrigger(botAI); } static Trigger* freezing_fog(PlayerbotAI* botAI) { return new FreezingFogTrigger(botAI); } static Trigger* no_desolation(PlayerbotAI* botAI) { return new DesolationTrigger(botAI); } static Trigger* death_and_decay_cooldown(PlayerbotAI* botAI) { return new DeathAndDecayCooldownTrigger(botAI); } diff --git a/src/strategy/deathknight/DKTriggers.cpp b/src/strategy/deathknight/DKTriggers.cpp index 281ed586..ebec33cc 100644 --- a/src/strategy/deathknight/DKTriggers.cpp +++ b/src/strategy/deathknight/DKTriggers.cpp @@ -39,11 +39,18 @@ bool PestilenceGlyphTrigger::IsActive() bool HighBloodRuneTrigger::IsActive() { - // bot->Say(std::to_string(bot->GetBaseRune(0)) + "_" + std::to_string(bot->GetRuneCooldown(0)) + " " + - // std::to_string(bot->GetBaseRune(1)) + "_" + std::to_string(bot->GetRuneCooldown(1)), LANG_UNIVERSAL); return !bot->GetRuneCooldown(0) && !bot->GetRuneCooldown(1); } +bool HighFrostRuneTrigger::IsActive() +{ + return !bot->GetRuneCooldown(2) && !bot->GetRuneCooldown(3); +} + +bool HighUnholyRuneTrigger::IsActive() +{ + return !bot->GetRuneCooldown(4) && !bot->GetRuneCooldown(5); +} bool DesolationTrigger::IsActive() { return bot->HasAura(66817) && !botAI->HasAura("desolation", GetTarget(), false, true, -1, true); @@ -54,6 +61,6 @@ bool DeathAndDecayCooldownTrigger::IsActive() uint32 spellId = AI_VALUE2(uint32, "spell id", name); if (!spellId) return true; - - return bot->HasSpellCooldown(spellId); + + return bot->GetSpellCooldownDelay(spellId) >= 2000; } \ No newline at end of file diff --git a/src/strategy/deathknight/DKTriggers.h b/src/strategy/deathknight/DKTriggers.h index ecbf7964..7f6c6c81 100644 --- a/src/strategy/deathknight/DKTriggers.h +++ b/src/strategy/deathknight/DKTriggers.h @@ -139,6 +139,20 @@ public: bool IsActive() override; }; +class HighFrostRuneTrigger : public Trigger +{ +public: + HighFrostRuneTrigger(PlayerbotAI* botAI) : Trigger(botAI, "high frost rune") {} + bool IsActive() override; +}; + +class HighUnholyRuneTrigger : public Trigger +{ +public: + HighUnholyRuneTrigger(PlayerbotAI* botAI) : Trigger(botAI, "high unholy rune") {} + bool IsActive() override; +}; + class FreezingFogTrigger : public HasAuraTrigger { public: diff --git a/src/strategy/deathknight/FrostDKStrategy.cpp b/src/strategy/deathknight/FrostDKStrategy.cpp index 87d12b17..b05060c7 100644 --- a/src/strategy/deathknight/FrostDKStrategy.cpp +++ b/src/strategy/deathknight/FrostDKStrategy.cpp @@ -26,7 +26,7 @@ public: // creators["icebound fortitude"] = &icebound_fortitude; // creators["mind freeze"] = &mind_freeze; // creators["hungering cold"] = &hungering_cold; - // creators["unbreakable armor"] = &unbreakable_armor; + creators["unbreakable armor"] = &unbreakable_armor; // creators["improved icy talons"] = &improved_icy_talons; } @@ -70,6 +70,13 @@ private: /*A*/ nullptr, /*C*/ nullptr); } + static ActionNode* unbreakable_armor([[maybe_unused]] PlayerbotAI* botAI) + { + return new ActionNode("unbreakable armor", + /*P*/ NextAction::array(0, new NextAction("blood tap"), nullptr), + /*A*/ nullptr, + /*C*/ nullptr); + } }; FrostDKStrategy::FrostDKStrategy(PlayerbotAI* botAI) : GenericDKStrategy(botAI) @@ -80,23 +87,28 @@ FrostDKStrategy::FrostDKStrategy(PlayerbotAI* botAI) : GenericDKStrategy(botAI) NextAction** FrostDKStrategy::getDefaultActions() { return NextAction::array( - 0, new NextAction("obliterate", ACTION_DEFAULT + 0.5f), new NextAction("frost strike", ACTION_DEFAULT + 0.4f), - // new NextAction("death strike", ACTION_NORMAL + 3), - new NextAction("empower rune weapon", ACTION_DEFAULT + 0.2f), + 0, new NextAction("obliterate", ACTION_DEFAULT + 0.7f), + new NextAction("frost strike", ACTION_DEFAULT + 0.4f), + new NextAction("empower rune weapon", ACTION_DEFAULT + 0.3f), new NextAction("horn of winter", ACTION_DEFAULT + 0.1f), new NextAction("melee", ACTION_DEFAULT), NULL); } void FrostDKStrategy::InitTriggers(std::vector& triggers) { GenericDKStrategy::InitTriggers(triggers); + + triggers.push_back(new TriggerNode( + "unbreakable armor", NextAction::array(0, new NextAction("unbreakable armor", ACTION_DEFAULT + 0.6f), nullptr))); + + triggers.push_back(new TriggerNode( + "freezing fog", NextAction::array(0, new NextAction("howling blast", ACTION_DEFAULT + 0.5f), nullptr))); + + triggers.push_back(new TriggerNode( + "high blood rune", NextAction::array(0, new NextAction("blood strike", ACTION_DEFAULT + 0.2f), nullptr))); + triggers.push_back(new TriggerNode( "army of the dead", NextAction::array(0, new NextAction("army of the dead", ACTION_HIGH + 6), nullptr))); - triggers.push_back(new TriggerNode( - "unbreakable armor", NextAction::array(0, new NextAction("unbreakable armor", ACTION_NORMAL + 4), nullptr))); - triggers.push_back(new TriggerNode( - "high blood rune", NextAction::array(0, new NextAction("blood strike", ACTION_NORMAL + 1), nullptr))); - triggers.push_back(new TriggerNode( - "freezing fog", NextAction::array(0, new NextAction("howling blast", ACTION_HIGH + 1), nullptr))); + // triggers.push_back(new TriggerNode("empower rune weapon", NextAction::array(0, new NextAction("empower rune // weapon", ACTION_NORMAL + 4), nullptr))); diff --git a/src/strategy/deathknight/GenericDKStrategy.cpp b/src/strategy/deathknight/GenericDKStrategy.cpp index 23693bcf..5fb8556f 100644 --- a/src/strategy/deathknight/GenericDKStrategy.cpp +++ b/src/strategy/deathknight/GenericDKStrategy.cpp @@ -135,7 +135,7 @@ private: { return new ActionNode("death and decay", /*P*/ nullptr, - /*A*/ NextAction::array(0, new NextAction("blood tap"), nullptr), + /*A*/ nullptr, /*C*/ nullptr); } @@ -176,18 +176,17 @@ void GenericDKStrategy::InitTriggers(std::vector& triggers) triggers.push_back( new TriggerNode("mind freeze on enemy healer", NextAction::array(0, new NextAction("mind freeze on enemy healer", ACTION_HIGH + 1), nullptr))); - triggers.push_back(new TriggerNode( - "bone shield", NextAction::array(0, new NextAction("bone shield", ACTION_NORMAL + 1), nullptr))); triggers.push_back(new TriggerNode( "horn of winter", NextAction::array(0, new NextAction("horn of winter", ACTION_NORMAL + 1), nullptr))); - // triggers.push_back(new TriggerNode("enemy out of melee", NextAction::array(0, new NextAction("reach melee", - // ACTION_NORMAL + 8), nullptr))); + triggers.push_back(new TriggerNode("critical health", + NextAction::array(0, new NextAction("death pact", ACTION_HIGH + 5), nullptr))); + triggers.push_back( new TriggerNode("low health", NextAction::array(0, new NextAction("icebound fortitude", ACTION_HIGH + 5), new NextAction("rune tap", ACTION_HIGH + 4), nullptr))); - triggers.push_back(new TriggerNode("medium health", - NextAction::array(0, new NextAction("rune tap", ACTION_NORMAL + 4), - new NextAction("death strike", ACTION_NORMAL + 3), nullptr))); + // triggers.push_back(new TriggerNode("medium health", + // NextAction::array(0, new NextAction("rune tap", ACTION_NORMAL + 4), + // new NextAction("death strike", ACTION_NORMAL + 3), nullptr))); triggers.push_back( new TriggerNode("icy touch", NextAction::array(0, new NextAction("icy touch", ACTION_HIGH + 2), nullptr))); triggers.push_back( diff --git a/src/strategy/deathknight/UnholyDKStrategy.cpp b/src/strategy/deathknight/UnholyDKStrategy.cpp index a5174f36..ac106a7f 100644 --- a/src/strategy/deathknight/UnholyDKStrategy.cpp +++ b/src/strategy/deathknight/UnholyDKStrategy.cpp @@ -26,7 +26,7 @@ public: // creators["summon gargoyle"] = &army of the dead; // creators["anti magic shell"] = &anti_magic_shell; // creators["anti magic zone"] = &anti_magic_zone; - // creators["ghoul frenzy"] = &ghoul_frenzy; + creators["ghoul frenzy"] = &ghoul_frenzy; creators["corpse explosion"] = &corpse_explosion; creators["icy touch"] = &icy_touch; } @@ -35,15 +35,21 @@ private: static ActionNode* death_strike([[maybe_unused]] PlayerbotAI* botAI) { return new ActionNode("death strike", - /*P*/ NextAction::array(0, new NextAction("unholy presence"), nullptr), + /*P*/ NextAction::array(0, new NextAction("blood presence"), nullptr), + /*A*/ nullptr, + /*C*/ nullptr); + } + static ActionNode* ghoul_frenzy([[maybe_unused]] PlayerbotAI* botAI) + { + return new ActionNode("ghoul frenzy", + /*P*/ nullptr, /*A*/ nullptr, /*C*/ nullptr); } - static ActionNode* corpse_explosion([[maybe_unused]] PlayerbotAI* botAI) { return new ActionNode("corpse explosion", - /*P*/ NextAction::array(0, new NextAction("unholy presence"), nullptr), + /*P*/ NextAction::array(0, new NextAction("blood presence"), nullptr), /*A*/ nullptr, /*C*/ nullptr); } @@ -51,14 +57,14 @@ private: static ActionNode* scourge_strike([[maybe_unused]] PlayerbotAI* botAI) { return new ActionNode("scourge strike", - /*P*/ NextAction::array(0, new NextAction("unholy presence"), nullptr), + /*P*/ NextAction::array(0, new NextAction("blood presence"), nullptr), /*A*/ nullptr, /*C*/ nullptr); } static ActionNode* icy_touch([[maybe_unused]] PlayerbotAI* botAI) { return new ActionNode("icy touch", - /*P*/ NextAction::array(0, new NextAction("unholy presence"), nullptr), + /*P*/ NextAction::array(0, new NextAction("blood presence"), nullptr), /*A*/ nullptr, /*C*/ nullptr); } @@ -72,29 +78,43 @@ UnholyDKStrategy::UnholyDKStrategy(PlayerbotAI* botAI) : GenericDKStrategy(botAI NextAction** UnholyDKStrategy::getDefaultActions() { return NextAction::array( - 0, new NextAction("death and decay", ACTION_DEFAULT + 0.5f), - new NextAction("horn of winter", ACTION_DEFAULT + 0.4f), - new NextAction("summon gargoyle", ACTION_DEFAULT + 0.3f), new NextAction("death coil", ACTION_DEFAULT + 0.2f), - new NextAction("scourge strike", ACTION_NORMAL + 0.1f), new NextAction("melee", ACTION_DEFAULT), nullptr); + 0, new NextAction("death and decay", ACTION_DEFAULT + 1.0f), + new NextAction("summon gargoyle", ACTION_DEFAULT + 0.4f), + new NextAction("empower rune weapon", ACTION_DEFAULT + 0.3f), + new NextAction("death coil", ACTION_DEFAULT + 0.2f), + new NextAction("horn of winter", ACTION_DEFAULT + 0.1f), + new NextAction("melee", ACTION_DEFAULT), nullptr); } void UnholyDKStrategy::InitTriggers(std::vector& triggers) { GenericDKStrategy::InitTriggers(triggers); + triggers.push_back(new TriggerNode( + "death and decay cooldown", NextAction::array(0, + new NextAction("ghoul frenzy", ACTION_DEFAULT + 0.9f), + new NextAction("scourge strike", ACTION_DEFAULT + 0.8f), + new NextAction("blood boil", ACTION_DEFAULT + 0.7f), + new NextAction("icy touch", ACTION_DEFAULT + 0.6f), + nullptr))); + + triggers.push_back(new TriggerNode( + "high frost rune", NextAction::array(0, new NextAction("icy touch", ACTION_DEFAULT + 0.6f), nullptr))); + + triggers.push_back(new TriggerNode( + "high unholy rune", NextAction::array(0, + new NextAction("ghoul frenzy", ACTION_DEFAULT + 0.9f), + new NextAction("plague strike", ACTION_DEFAULT + 0.5f), nullptr))); + + triggers.push_back(new TriggerNode( + "high blood rune", NextAction::array(0, new NextAction("blood boil", ACTION_DEFAULT + 0.7f), nullptr))); // triggers.push_back(new TriggerNode("often", NextAction::array(0, new NextAction(, ACTION_NORMAL + 2), nullptr))); triggers.push_back(new TriggerNode( "army of the dead", NextAction::array(0, new NextAction("army of the dead", ACTION_HIGH + 6), nullptr))); - triggers.push_back(new TriggerNode("critical health", - NextAction::array(0, new NextAction("death pact", ACTION_HIGH + 5), nullptr))); triggers.push_back(new TriggerNode("no desolation", NextAction::array(0, new NextAction("blood strike", ACTION_HIGH + 4), nullptr))); - triggers.push_back(new TriggerNode( - "death and decay cooldown", NextAction::array(0, new NextAction("ghoul frenzy", ACTION_NORMAL + 5.0f), - new NextAction("scourge strike", ACTION_NORMAL + 4.0f), - new NextAction("blood boil", ACTION_NORMAL + 3.0f), - new NextAction("icy touch", ACTION_NORMAL + 2.0f), - new NextAction("plague strike", ACTION_NORMAL + 1.0f), nullptr))); + triggers.push_back( + new TriggerNode("bone shield", NextAction::array(0, new NextAction("bone shield", ACTION_HIGH + 1), nullptr))); } void UnholyDKAoeStrategy::InitTriggers(std::vector& triggers) diff --git a/src/strategy/values/Arrow.cpp b/src/strategy/values/Arrow.cpp index f52dc670..0a8afb50 100644 --- a/src/strategy/values/Arrow.cpp +++ b/src/strategy/values/Arrow.cpp @@ -46,14 +46,14 @@ WorldLocation ArrowFormation::GetLocationInternal() float x = master->GetPositionX() - masterUnit->GetX() + botUnit->GetX(); float y = master->GetPositionY() - masterUnit->GetY() + botUnit->GetY(); - float z = master->GetPositionZ(); + float z = master->GetPositionZ() + master->GetHoverHeight(); if (!master->GetMap()->CheckCollisionAndGetValidCoords(master, master->GetPositionX(), master->GetPositionY(), master->GetPositionZ(), x, y, z)) { x = master->GetPositionX() - masterUnit->GetX() + botUnit->GetX(); y = master->GetPositionY() - masterUnit->GetY() + botUnit->GetY(); z = master->GetPositionZ() + master->GetHoverHeight(); - z = master->GetMapHeight(x, y, z); + master->UpdateAllowedPositionZ(x, y, z); } return WorldLocation(master->GetMapId(), x, y, z); } diff --git a/src/strategy/values/Formations.cpp b/src/strategy/values/Formations.cpp index 9854b420..8c918c56 100644 --- a/src/strategy/values/Formations.cpp +++ b/src/strategy/values/Formations.cpp @@ -88,7 +88,7 @@ public: float angle = GetFollowAngle(); float x = master->GetPositionX() + cos(angle) * range; float y = master->GetPositionY() + sin(angle) * range; - float z = master->GetPositionZ(); + float z = master->GetPositionZ() + master->GetHoverHeight(); if (!master->GetMap()->CheckCollisionAndGetValidCoords(master, master->GetPositionX(), master->GetPositionY(), master->GetPositionZ(), x, y, z)) { @@ -138,15 +138,14 @@ public: float x = master->GetPositionX() + cos(angle) * range + dx; float y = master->GetPositionY() + sin(angle) * range + dy; - float z = master->GetPositionZ(); - z = bot->GetMapHeight(x, y, z + 5.0f); + float z = master->GetPositionZ() + master->GetHoverHeight(); if (!master->GetMap()->CheckCollisionAndGetValidCoords( master, master->GetPositionX(), master->GetPositionY(), master->GetPositionZ(), x, y, z)) { x = master->GetPositionX() + cos(angle) * range + dx; y = master->GetPositionY() + sin(angle) * range + dy; z = master->GetPositionZ() + master->GetHoverHeight(); - z = master->GetMapHeight(x, y, z); + master->UpdateAllowedPositionZ(x, y, z); } // bot->GetMap()->CheckCollisionAndGetValidCoords(bot, bot->GetPositionX(), bot->GetPositionY(), // bot->GetPositionZ(), x, y, z); @@ -155,15 +154,14 @@ public: float x = master->GetPositionX() + cos(angle) * range + dx; float y = master->GetPositionY() + sin(angle) * range + dy; - float z = master->GetPositionZ(); - z = bot->GetMapHeight(x, y, z + 5.0f); + float z = master->GetPositionZ() + master->GetHoverHeight(); if (!master->GetMap()->CheckCollisionAndGetValidCoords(master, master->GetPositionX(), master->GetPositionY(), master->GetPositionZ(), x, y, z)) { x = master->GetPositionX() + cos(angle) * range + dx; y = master->GetPositionY() + sin(angle) * range + dy; z = master->GetPositionZ() + master->GetHoverHeight(); - z = master->GetMapHeight(x, y, z); + master->UpdateAllowedPositionZ(x, y, z); } return WorldLocation(master->GetMapId(), x, y, z); } @@ -221,8 +219,8 @@ public: { x = target->GetPositionX() + cos(angle) * range; y = target->GetPositionY() + sin(angle) * range; - z = target->GetPositionZ() + target->GetHoverHeight(); - z = target->GetMapHeight(x, y, z); + z = target->GetPositionZ(); + target->UpdateAllowedPositionZ(x, y, z); } return WorldLocation(bot->GetMapId(), x, y, z); } @@ -389,7 +387,7 @@ public: x = master->GetPositionX() + cos(angle) * range + cos(followAngle) * followRange; y = master->GetPositionY() + sin(angle) * range + sin(followAngle) * followRange; z = master->GetPositionZ() + master->GetHoverHeight(); - z = master->GetMapHeight(x, y, z); + master->UpdateAllowedPositionZ(x, y, z); } return WorldLocation(bot->GetMapId(), minX, minY, z); } @@ -403,7 +401,7 @@ public: x = master->GetPositionX() + cos(angle) * range + cos(followAngle) * followRange; y = master->GetPositionY() + sin(angle) * range + sin(followAngle) * followRange; z = master->GetPositionZ() + master->GetHoverHeight(); - z = master->GetMapHeight(x, y, z); + master->UpdateAllowedPositionZ(x, y, z); } return WorldLocation(bot->GetMapId(), x, y, z); } diff --git a/src/strategy/warrior/ArmsWarriorStrategy.cpp b/src/strategy/warrior/ArmsWarriorStrategy.cpp index bf56e891..540b6cd0 100644 --- a/src/strategy/warrior/ArmsWarriorStrategy.cpp +++ b/src/strategy/warrior/ArmsWarriorStrategy.cpp @@ -34,8 +34,9 @@ ArmsWarriorStrategy::ArmsWarriorStrategy(PlayerbotAI* botAI) : GenericWarriorStr NextAction** ArmsWarriorStrategy::getDefaultActions() { - return NextAction::array(0, new NextAction("bladestorm", ACTION_DEFAULT + 0.1f), - new NextAction("melee", ACTION_DEFAULT), nullptr); + return NextAction::array(0, new NextAction("bladestorm", ACTION_DEFAULT + 0.2f), + new NextAction("mortal strike", ACTION_DEFAULT + 0.1f), + new NextAction("melee", ACTION_DEFAULT), nullptr); } void ArmsWarriorStrategy::InitTriggers(std::vector& triggers) @@ -63,9 +64,8 @@ void ArmsWarriorStrategy::InitTriggers(std::vector& triggers) triggers.push_back(new TriggerNode( "victory rush", NextAction::array(0, new NextAction("victory rush", ACTION_INTERRUPT), nullptr))); triggers.push_back(new TriggerNode( - "medium rage available", NextAction::array(0, new NextAction("heroic strike", ACTION_HIGH + 10), nullptr))); - /*triggers.push_back(new TriggerNode("high rage available", NextAction::array(0, new NextAction("slam", ACTION_HIGH - * + 1), nullptr)));*/ + "high rage available", NextAction::array(0, new NextAction("heroic strike", ACTION_HIGH + 10), nullptr))); + triggers.push_back(new TriggerNode("medium rage available", NextAction::array(0, new NextAction("slam", ACTION_HIGH + 1), nullptr))); triggers.push_back( new TriggerNode("bloodrage", NextAction::array(0, new NextAction("bloodrage", ACTION_HIGH + 2), nullptr))); triggers.push_back( @@ -75,6 +75,6 @@ void ArmsWarriorStrategy::InitTriggers(std::vector& triggers) "rend on attacker", NextAction::array(0, new NextAction("rend on attacker", ACTION_HIGH + 5), nullptr))); triggers.push_back(new TriggerNode( "critical health", NextAction::array(0, new NextAction("intimidating shout", ACTION_EMERGENCY), nullptr))); - triggers.push_back(new TriggerNode("medium rage available", + triggers.push_back(new TriggerNode("medium aoe", NextAction::array(0, new NextAction("thunder clap", ACTION_HIGH + 1), nullptr))); } From f20d3aea6c91c32106bfba9e5297caf482cf36c3 Mon Sep 17 00:00:00 2001 From: Yunfan Li Date: Fri, 6 Sep 2024 20:29:56 +0800 Subject: [PATCH 38/47] Unholy dk --- conf/playerbots.conf.dist | 4 +-- src/strategy/actions/GenericSpellActions.cpp | 14 +++++--- src/strategy/actions/GenericSpellActions.h | 8 +++-- src/strategy/deathknight/BloodDKStrategy.cpp | 5 ++- src/strategy/deathknight/DKActions.h | 4 +-- .../deathknight/DKAiObjectContext.cpp | 11 ++++++ src/strategy/deathknight/DKTriggers.cpp | 4 +-- src/strategy/deathknight/DKTriggers.h | 18 ++++++++-- src/strategy/deathknight/FrostDKStrategy.cpp | 5 ++- .../deathknight/GenericDKStrategy.cpp | 18 ---------- src/strategy/deathknight/UnholyDKStrategy.cpp | 36 +++++++++++++------ src/strategy/triggers/GenericTriggers.cpp | 11 +++++- src/strategy/triggers/GenericTriggers.h | 10 ++++-- 13 files changed, 98 insertions(+), 50 deletions(-) diff --git a/conf/playerbots.conf.dist b/conf/playerbots.conf.dist index 87008706..37228098 100644 --- a/conf/playerbots.conf.dist +++ b/conf/playerbots.conf.dist @@ -910,8 +910,8 @@ AiPlayerbot.PremadeSpecLink.6.1.60 = -32003350332203012300023101351 AiPlayerbot.PremadeSpecLink.6.1.80 = -32002350352203012300033101351-230200305003 AiPlayerbot.PremadeSpecName.6.2 = unholy pve AiPlayerbot.PremadeSpecGlyph.6.2 = 43542,43673,45804,43544,43672,43549 -AiPlayerbot.PremadeSpecLink.6.2.60 = --2301303050032151000150013113151 -AiPlayerbot.PremadeSpecLink.6.2.80 = -320033500002-2301303050032151000150013133151 +AiPlayerbot.PremadeSpecLink.6.2.60 = --2301303050032151000150013132051 +AiPlayerbot.PremadeSpecLink.6.2.80 = -320043500002-2301303050032151000150013133051 AiPlayerbot.PremadeSpecName.6.3 = double aura blood pve AiPlayerbot.PremadeSpecGlyph.6.3 = 45805,43673,43827,43544,43672,43554 AiPlayerbot.PremadeSpecLink.6.3.60 = 005512153330030320102013-305 diff --git a/src/strategy/actions/GenericSpellActions.cpp b/src/strategy/actions/GenericSpellActions.cpp index 2425a223..9285f20a 100644 --- a/src/strategy/actions/GenericSpellActions.cpp +++ b/src/strategy/actions/GenericSpellActions.cpp @@ -162,8 +162,14 @@ bool CastMeleeDebuffSpellAction::isUseful() bool CastAuraSpellAction::isUseful() { - return GetTarget() && (GetTarget() != nullptr) && CastSpellAction::isUseful() && - !botAI->HasAura(spell, GetTarget(), false, isOwner); + if (!GetTarget() || !CastSpellAction::isUseful()) + return false; + Aura* aura = botAI->GetAura(spell, GetTarget(), isOwner, checkDuration); + if (!aura) + return true; + if (beforeDuration && aura->GetDuration() < beforeDuration) + return true; + return false; } CastEnchantItemAction::CastEnchantItemAction(PlayerbotAI* botAI, std::string const spell) @@ -251,8 +257,8 @@ Value* CastDebuffSpellOnMeleeAttackerAction::GetTargetValue() return context->GetValue("melee attacker without aura", spell); } -CastBuffSpellAction::CastBuffSpellAction(PlayerbotAI* botAI, std::string const spell, bool checkIsOwner) - : CastAuraSpellAction(botAI, spell, checkIsOwner) +CastBuffSpellAction::CastBuffSpellAction(PlayerbotAI* botAI, std::string const spell, bool checkIsOwner, uint32 beforeDuration) + : CastAuraSpellAction(botAI, spell, checkIsOwner, false, beforeDuration) { range = botAI->GetRange("spell"); } diff --git a/src/strategy/actions/GenericSpellActions.h b/src/strategy/actions/GenericSpellActions.h index b9870011..2ab49bb0 100644 --- a/src/strategy/actions/GenericSpellActions.h +++ b/src/strategy/actions/GenericSpellActions.h @@ -37,16 +37,20 @@ protected: class CastAuraSpellAction : public CastSpellAction { public: - CastAuraSpellAction(PlayerbotAI* botAI, std::string const spell, bool isOwner = false) + CastAuraSpellAction(PlayerbotAI* botAI, std::string const spell, bool isOwner = false, bool checkDuration = false, uint32 beforeDuration = 0) : CastSpellAction(botAI, spell) { this->isOwner = isOwner; + this->beforeDuration = beforeDuration; + this->checkDuration = checkDuration; } bool isUseful() override; protected: bool isOwner; + bool checkDuration; + uint32 beforeDuration; }; class CastMeleeSpellAction : public CastSpellAction @@ -107,7 +111,7 @@ public: class CastBuffSpellAction : public CastAuraSpellAction { public: - CastBuffSpellAction(PlayerbotAI* botAI, std::string const spell, bool checkIsOwner = false); + CastBuffSpellAction(PlayerbotAI* botAI, std::string const spell, bool checkIsOwner = false, uint32 beforeDuration = 0); std::string const GetTargetName() override { return "self target"; } }; diff --git a/src/strategy/deathknight/BloodDKStrategy.cpp b/src/strategy/deathknight/BloodDKStrategy.cpp index 7daf494d..03bc454c 100644 --- a/src/strategy/deathknight/BloodDKStrategy.cpp +++ b/src/strategy/deathknight/BloodDKStrategy.cpp @@ -104,5 +104,8 @@ void BloodDKStrategy::InitTriggers(std::vector& triggers) new NextAction("death strike", ACTION_HIGH + 3), nullptr))); triggers.push_back( new TriggerNode("critical health", NextAction::array(0, new NextAction("vampiric blood", ACTION_HIGH + 5), nullptr))); - + triggers.push_back( + new TriggerNode("icy touch", NextAction::array(0, new NextAction("icy touch", ACTION_HIGH + 2), nullptr))); + triggers.push_back(new TriggerNode( + "plague strike", NextAction::array(0, new NextAction("plague strike", ACTION_HIGH + 2), nullptr))); } diff --git a/src/strategy/deathknight/DKActions.h b/src/strategy/deathknight/DKActions.h index 2fe9c708..355a5c52 100644 --- a/src/strategy/deathknight/DKActions.h +++ b/src/strategy/deathknight/DKActions.h @@ -165,7 +165,7 @@ public: class CastGhoulFrenzyAction : public CastBuffSpellAction { public: - CastGhoulFrenzyAction(PlayerbotAI* botAI) : CastBuffSpellAction(botAI, "ghoul frenzy") {} + CastGhoulFrenzyAction(PlayerbotAI* botAI) : CastBuffSpellAction(botAI, "ghoul frenzy", false, 5000) {} std::string const GetTargetName() override { return "pet target"; } }; @@ -242,7 +242,7 @@ class CastDeathAndDecayAction : public CastSpellAction { public: CastDeathAndDecayAction(PlayerbotAI* botAI) : CastSpellAction(botAI, "death and decay") {} - ActionThreatType getThreatType() override { return ActionThreatType::Aoe; } + // ActionThreatType getThreatType() override { return ActionThreatType::Aoe; } }; class CastHornOfWinterAction : public CastSpellAction diff --git a/src/strategy/deathknight/DKAiObjectContext.cpp b/src/strategy/deathknight/DKAiObjectContext.cpp index 5f7eb941..45d4b65f 100644 --- a/src/strategy/deathknight/DKAiObjectContext.cpp +++ b/src/strategy/deathknight/DKAiObjectContext.cpp @@ -10,6 +10,7 @@ #include "DKTriggers.h" #include "FrostDKStrategy.h" #include "GenericDKNonCombatStrategy.h" +#include "GenericTriggers.h" #include "Playerbots.h" #include "PullStrategy.h" #include "UnholyDKStrategy.h" @@ -73,10 +74,14 @@ public: creators["plague strike"] = &DeathKnightTriggerFactoryInternal::plague_strike; creators["plague strike on attacker"] = &DeathKnightTriggerFactoryInternal::plague_strike_on_attacker; creators["icy touch"] = &DeathKnightTriggerFactoryInternal::icy_touch; + creators["icy touch 8s"] = &DeathKnightTriggerFactoryInternal::icy_touch_8s; + creators["dd cd and icy touch 8s"] = &DeathKnightTriggerFactoryInternal::dd_cd_and_icy_touch_8s; creators["death coil"] = &DeathKnightTriggerFactoryInternal::death_coil; creators["icy touch on attacker"] = &DeathKnightTriggerFactoryInternal::icy_touch_on_attacker; creators["improved icy talons"] = &DeathKnightTriggerFactoryInternal::improved_icy_talons; creators["plague strike"] = &DeathKnightTriggerFactoryInternal::plague_strike; + creators["plague strike 8s"] = &DeathKnightTriggerFactoryInternal::plague_strike_8s; + creators["dd cd and plague strike 8s"] = &DeathKnightTriggerFactoryInternal::dd_cd_and_plague_strike_8s; creators["horn of winter"] = &DeathKnightTriggerFactoryInternal::horn_of_winter; creators["mind freeze"] = &DeathKnightTriggerFactoryInternal::mind_freeze; creators["mind freeze on enemy healer"] = &DeathKnightTriggerFactoryInternal::mind_freeze_on_enemy_healer; @@ -91,6 +96,7 @@ public: creators["high unholy rune"] = &DeathKnightTriggerFactoryInternal::high_unholy_rune; creators["freezing fog"] = &DeathKnightTriggerFactoryInternal::freezing_fog; creators["no desolation"] = &DeathKnightTriggerFactoryInternal::no_desolation; + creators["dd cd and no desolation"] = &DeathKnightTriggerFactoryInternal::dd_cd_and_no_desolation; creators["death and decay cooldown"] = &DeathKnightTriggerFactoryInternal::death_and_decay_cooldown; creators["army of the dead"] = &DeathKnightTriggerFactoryInternal::army_of_the_dead; } @@ -100,11 +106,15 @@ private: static Trigger* pestilence_glyph(PlayerbotAI* botAI) { return new PestilenceGlyphTrigger(botAI); } static Trigger* blood_strike(PlayerbotAI* botAI) { return new BloodStrikeTrigger(botAI); } static Trigger* plague_strike(PlayerbotAI* botAI) { return new PlagueStrikeDebuffTrigger(botAI); } + static Trigger* plague_strike_8s(PlayerbotAI* botAI) { return new PlagueStrike8sDebuffTrigger(botAI); } + static Trigger* dd_cd_and_plague_strike_8s(PlayerbotAI* botAI) { return new TwoTriggers(botAI, "death and decay cooldown", "plague strike 8s"); } static Trigger* plague_strike_on_attacker(PlayerbotAI* botAI) { return new PlagueStrikeDebuffOnAttackerTrigger(botAI); } static Trigger* icy_touch(PlayerbotAI* botAI) { return new IcyTouchDebuffTrigger(botAI); } + static Trigger* icy_touch_8s(PlayerbotAI* botAI) { return new IcyTouch8sDebuffTrigger(botAI); } + static Trigger* dd_cd_and_icy_touch_8s(PlayerbotAI* botAI) { return new TwoTriggers(botAI, "death and decay cooldown", "icy touch 8s"); } static Trigger* death_coil(PlayerbotAI* botAI) { return new DeathCoilTrigger(botAI); } static Trigger* icy_touch_on_attacker(PlayerbotAI* botAI) { return new IcyTouchDebuffOnAttackerTrigger(botAI); } static Trigger* improved_icy_talons(PlayerbotAI* botAI) { return new ImprovedIcyTalonsTrigger(botAI); } @@ -128,6 +138,7 @@ private: static Trigger* high_unholy_rune(PlayerbotAI* botAI) { return new HighUnholyRuneTrigger(botAI); } static Trigger* freezing_fog(PlayerbotAI* botAI) { return new FreezingFogTrigger(botAI); } static Trigger* no_desolation(PlayerbotAI* botAI) { return new DesolationTrigger(botAI); } + static Trigger* dd_cd_and_no_desolation(PlayerbotAI* botAI) { return new TwoTriggers(botAI, "death and decay cooldown", "no desolation"); } static Trigger* death_and_decay_cooldown(PlayerbotAI* botAI) { return new DeathAndDecayCooldownTrigger(botAI); } static Trigger* army_of_the_dead(PlayerbotAI* botAI) { return new ArmyOfTheDeadTrigger(botAI); } }; diff --git a/src/strategy/deathknight/DKTriggers.cpp b/src/strategy/deathknight/DKTriggers.cpp index ebec33cc..2d1cbb15 100644 --- a/src/strategy/deathknight/DKTriggers.cpp +++ b/src/strategy/deathknight/DKTriggers.cpp @@ -53,7 +53,7 @@ bool HighUnholyRuneTrigger::IsActive() } bool DesolationTrigger::IsActive() { - return bot->HasAura(66817) && !botAI->HasAura("desolation", GetTarget(), false, true, -1, true); + return bot->HasAura(66817) && BuffTrigger::IsActive(); } bool DeathAndDecayCooldownTrigger::IsActive() @@ -62,5 +62,5 @@ bool DeathAndDecayCooldownTrigger::IsActive() if (!spellId) return true; - return bot->GetSpellCooldownDelay(spellId) >= 2000; + return bot->GetSpellCooldownDelay(spellId) >= 3000; } \ No newline at end of file diff --git a/src/strategy/deathknight/DKTriggers.h b/src/strategy/deathknight/DKTriggers.h index 7f6c6c81..ef82394b 100644 --- a/src/strategy/deathknight/DKTriggers.h +++ b/src/strategy/deathknight/DKTriggers.h @@ -17,14 +17,26 @@ BUFF_TRIGGER(ImprovedIcyTalonsTrigger, "improved icy talons"); class PlagueStrikeDebuffTrigger : public DebuffTrigger { public: - PlagueStrikeDebuffTrigger(PlayerbotAI* botAI) : DebuffTrigger(botAI, "blood plague", true, .0f) {} + PlagueStrikeDebuffTrigger(PlayerbotAI* botAI) : DebuffTrigger(botAI, "blood plague", 1, true, .0f) {} +}; + +class PlagueStrike8sDebuffTrigger : public DebuffTrigger +{ +public: + PlagueStrike8sDebuffTrigger(PlayerbotAI* botAI) : DebuffTrigger(botAI, "blood plague", 1, true, .0f, 3000) {} }; // DEBUFF_CHECKISOWNER_TRIGGER(IcyTouchDebuffTrigger, "frost fever"); class IcyTouchDebuffTrigger : public DebuffTrigger { public: - IcyTouchDebuffTrigger(PlayerbotAI* botAI) : DebuffTrigger(botAI, "frost fever", true, .0f) {} + IcyTouchDebuffTrigger(PlayerbotAI* botAI) : DebuffTrigger(botAI, "frost fever", 1, true, .0f) {} +}; + +class IcyTouch8sDebuffTrigger : public DebuffTrigger +{ +public: + IcyTouch8sDebuffTrigger(PlayerbotAI* botAI) : DebuffTrigger(botAI, "frost fever", 1, true, .0f, 3000) {} }; BUFF_TRIGGER(UnbreakableArmorTrigger, "unbreakable armor"); @@ -162,7 +174,7 @@ public: class DesolationTrigger : public BuffTrigger { public: - DesolationTrigger(PlayerbotAI* botAI) : BuffTrigger(botAI, "desolation") {} + DesolationTrigger(PlayerbotAI* botAI) : BuffTrigger(botAI, "desolation", 1, false, true, 10000) {} bool IsActive() override; }; diff --git a/src/strategy/deathknight/FrostDKStrategy.cpp b/src/strategy/deathknight/FrostDKStrategy.cpp index b05060c7..6ec84558 100644 --- a/src/strategy/deathknight/FrostDKStrategy.cpp +++ b/src/strategy/deathknight/FrostDKStrategy.cpp @@ -109,7 +109,10 @@ void FrostDKStrategy::InitTriggers(std::vector& triggers) triggers.push_back(new TriggerNode( "army of the dead", NextAction::array(0, new NextAction("army of the dead", ACTION_HIGH + 6), nullptr))); - + triggers.push_back( + new TriggerNode("icy touch", NextAction::array(0, new NextAction("icy touch", ACTION_HIGH + 2), nullptr))); + triggers.push_back(new TriggerNode( + "plague strike", NextAction::array(0, new NextAction("plague strike", ACTION_HIGH + 2), nullptr))); // triggers.push_back(new TriggerNode("empower rune weapon", NextAction::array(0, new NextAction("empower rune // weapon", ACTION_NORMAL + 4), nullptr))); } diff --git a/src/strategy/deathknight/GenericDKStrategy.cpp b/src/strategy/deathknight/GenericDKStrategy.cpp index 5fb8556f..55146a56 100644 --- a/src/strategy/deathknight/GenericDKStrategy.cpp +++ b/src/strategy/deathknight/GenericDKStrategy.cpp @@ -184,24 +184,6 @@ void GenericDKStrategy::InitTriggers(std::vector& triggers) triggers.push_back( new TriggerNode("low health", NextAction::array(0, new NextAction("icebound fortitude", ACTION_HIGH + 5), new NextAction("rune tap", ACTION_HIGH + 4), nullptr))); - // triggers.push_back(new TriggerNode("medium health", - // NextAction::array(0, new NextAction("rune tap", ACTION_NORMAL + 4), - // new NextAction("death strike", ACTION_NORMAL + 3), nullptr))); - triggers.push_back( - new TriggerNode("icy touch", NextAction::array(0, new NextAction("icy touch", ACTION_HIGH + 2), nullptr))); - triggers.push_back( - new TriggerNode("icy touch on attacker", - NextAction::array(0, new NextAction("icy touch on attacker", ACTION_HIGH + 1), nullptr))); - triggers.push_back(new TriggerNode( - "plague strike", NextAction::array(0, new NextAction("plague strike", ACTION_HIGH + 2), nullptr))); - triggers.push_back( - new TriggerNode("plague strike on attacker", - NextAction::array(0, new NextAction("plague strike on attacker", ACTION_HIGH + 1), nullptr))); - // triggers.push_back(new TriggerNode("high aoe", - // NextAction::array(0, - // new NextAction("death and decay", ACTION_NORMAL + 5), - // new NextAction("pestilence", ACTION_NORMAL + 4), - // new NextAction("blood boil", ACTION_NORMAL + 3), nullptr))); triggers.push_back( new TriggerNode("medium aoe", NextAction::array(0, new NextAction("death and decay", ACTION_HIGH + 9), new NextAction("pestilence", ACTION_NORMAL + 4), diff --git a/src/strategy/deathknight/UnholyDKStrategy.cpp b/src/strategy/deathknight/UnholyDKStrategy.cpp index ac106a7f..c05d0d19 100644 --- a/src/strategy/deathknight/UnholyDKStrategy.cpp +++ b/src/strategy/deathknight/UnholyDKStrategy.cpp @@ -42,7 +42,7 @@ private: static ActionNode* ghoul_frenzy([[maybe_unused]] PlayerbotAI* botAI) { return new ActionNode("ghoul frenzy", - /*P*/ nullptr, + /*P*/ NextAction::array(0, new NextAction("blood presence"), nullptr), /*A*/ nullptr, /*C*/ nullptr); } @@ -78,11 +78,11 @@ UnholyDKStrategy::UnholyDKStrategy(PlayerbotAI* botAI) : GenericDKStrategy(botAI NextAction** UnholyDKStrategy::getDefaultActions() { return NextAction::array( - 0, new NextAction("death and decay", ACTION_DEFAULT + 1.0f), + 0, new NextAction("death and decay", ACTION_HIGH + 5), new NextAction("summon gargoyle", ACTION_DEFAULT + 0.4f), new NextAction("empower rune weapon", ACTION_DEFAULT + 0.3f), - new NextAction("death coil", ACTION_DEFAULT + 0.2f), - new NextAction("horn of winter", ACTION_DEFAULT + 0.1f), + new NextAction("horn of winter", ACTION_DEFAULT + 0.2f), + new NextAction("death coil", ACTION_DEFAULT + 0.1f), new NextAction("melee", ACTION_DEFAULT), nullptr); } @@ -95,24 +95,38 @@ void UnholyDKStrategy::InitTriggers(std::vector& triggers) new NextAction("scourge strike", ACTION_DEFAULT + 0.8f), new NextAction("blood boil", ACTION_DEFAULT + 0.7f), new NextAction("icy touch", ACTION_DEFAULT + 0.6f), + new NextAction("plague strike", ACTION_DEFAULT + 0.5f), nullptr))); + triggers.push_back(new TriggerNode("dd cd and no desolation", + NextAction::array(0, new NextAction("blood strike", ACTION_DEFAULT + 0.75f), nullptr))); + + triggers.push_back( + new TriggerNode("icy touch", NextAction::array(0, new NextAction("icy touch", ACTION_HIGH + 2), nullptr))); triggers.push_back(new TriggerNode( - "high frost rune", NextAction::array(0, new NextAction("icy touch", ACTION_DEFAULT + 0.6f), nullptr))); + "plague strike", NextAction::array(0, new NextAction("plague strike", ACTION_HIGH + 1), nullptr))); triggers.push_back(new TriggerNode( - "high unholy rune", NextAction::array(0, - new NextAction("ghoul frenzy", ACTION_DEFAULT + 0.9f), - new NextAction("plague strike", ACTION_DEFAULT + 0.5f), nullptr))); + "high frost rune", NextAction::array(0, + new NextAction("icy touch", ACTION_NORMAL + 3), nullptr))); triggers.push_back(new TriggerNode( - "high blood rune", NextAction::array(0, new NextAction("blood boil", ACTION_DEFAULT + 0.7f), nullptr))); + "high unholy rune", NextAction::array(0, + new NextAction("plague strike", ACTION_NORMAL + 2), nullptr))); + + triggers.push_back(new TriggerNode( + "high blood rune", NextAction::array(0, new NextAction("blood strike", ACTION_NORMAL + 1), nullptr))); + + // triggers.push_back( + // new TriggerNode("dd cd and plague strike 8s", NextAction::array(0, new NextAction("plague strike", ACTION_HIGH + 2), nullptr))); + + // triggers.push_back( + // new TriggerNode("dd cd and icy touch 8s", NextAction::array(0, new NextAction("icy touch", ACTION_HIGH + 1), nullptr))); + // triggers.push_back(new TriggerNode("often", NextAction::array(0, new NextAction(, ACTION_NORMAL + 2), nullptr))); triggers.push_back(new TriggerNode( "army of the dead", NextAction::array(0, new NextAction("army of the dead", ACTION_HIGH + 6), nullptr))); - triggers.push_back(new TriggerNode("no desolation", - NextAction::array(0, new NextAction("blood strike", ACTION_HIGH + 4), nullptr))); triggers.push_back( new TriggerNode("bone shield", NextAction::array(0, new NextAction("bone shield", ACTION_HIGH + 1), nullptr))); } diff --git a/src/strategy/triggers/GenericTriggers.cpp b/src/strategy/triggers/GenericTriggers.cpp index 7826616f..f700e472 100644 --- a/src/strategy/triggers/GenericTriggers.cpp +++ b/src/strategy/triggers/GenericTriggers.cpp @@ -152,7 +152,16 @@ bool OutNumberedTrigger::IsActive() bool BuffTrigger::IsActive() { Unit* target = GetTarget(); - return SpellTrigger::IsActive() && !botAI->HasAura(spell, target, false, checkIsOwner); + if (!target) + return false; + if (!SpellTrigger::IsActive()) + return false; + Aura* aura = botAI->GetAura(spell, target, checkIsOwner, checkDuration); + if (!aura) + return true; + if (beforeDuration && aura->GetDuration() < beforeDuration) + return true; + return false; } Value* BuffOnPartyTrigger::GetTargetValue() diff --git a/src/strategy/triggers/GenericTriggers.h b/src/strategy/triggers/GenericTriggers.h index b7217447..e9990c3e 100644 --- a/src/strategy/triggers/GenericTriggers.h +++ b/src/strategy/triggers/GenericTriggers.h @@ -308,10 +308,12 @@ public: class BuffTrigger : public SpellTrigger { public: - BuffTrigger(PlayerbotAI* botAI, std::string const spell, int32 checkInterval = 1, bool checkIsOwner = false) + BuffTrigger(PlayerbotAI* botAI, std::string const spell, int32 checkInterval = 1, bool checkIsOwner = false, bool checkDuration = false, uint32 beforeDuration = 0) : SpellTrigger(botAI, spell, checkInterval) { this->checkIsOwner = checkIsOwner; + this->checkDuration = checkDuration; + this->beforeDuration = beforeDuration; } public: @@ -320,6 +322,8 @@ public: protected: bool checkIsOwner; + bool checkDuration; + uint32 beforeDuration; }; class BuffOnPartyTrigger : public BuffTrigger @@ -379,8 +383,8 @@ class DebuffTrigger : public BuffTrigger { public: DebuffTrigger(PlayerbotAI* botAI, std::string const spell, int32 checkInterval = 1, bool checkIsOwner = false, - float needLifeTime = 8.0f) - : BuffTrigger(botAI, spell, checkInterval, checkIsOwner), needLifeTime(needLifeTime) + float needLifeTime = 8.0f, uint32 beforeDuration = 0) + : BuffTrigger(botAI, spell, checkInterval, checkIsOwner, false, beforeDuration), needLifeTime(needLifeTime) { } From 95f0768a8ba1061d336c2496ba7cd96cb85e5afd Mon Sep 17 00:00:00 2001 From: Yunfan Li Date: Fri, 6 Sep 2024 20:51:36 +0800 Subject: [PATCH 39/47] Fix avoid aoe crash --- src/strategy/values/NearestGameObjects.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/strategy/values/NearestGameObjects.cpp b/src/strategy/values/NearestGameObjects.cpp index 29b3e45a..a600bc65 100644 --- a/src/strategy/values/NearestGameObjects.cpp +++ b/src/strategy/values/NearestGameObjects.cpp @@ -77,7 +77,7 @@ GuidVector NearestTrapWithDamageValue::Calculate() continue; } const SpellInfo* spellInfo = sSpellMgr->GetSpellInfo(spellId); - if (spellInfo->IsPositive()) + if (!spellInfo || spellInfo->IsPositive()) { continue; } From 9d7a300c6d47def47cf25045bc2d43a53e251d5d Mon Sep 17 00:00:00 2001 From: Yunfan Li Date: Sat, 7 Sep 2024 00:04:37 +0800 Subject: [PATCH 40/47] Unholy dk --- conf/playerbots.conf.dist | 4 ++-- src/factory/StatsWeightCalculator.cpp | 8 ++++---- src/strategy/deathknight/UnholyDKStrategy.cpp | 16 ++++++++-------- 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/conf/playerbots.conf.dist b/conf/playerbots.conf.dist index 37228098..7888b6b4 100644 --- a/conf/playerbots.conf.dist +++ b/conf/playerbots.conf.dist @@ -910,8 +910,8 @@ AiPlayerbot.PremadeSpecLink.6.1.60 = -32003350332203012300023101351 AiPlayerbot.PremadeSpecLink.6.1.80 = -32002350352203012300033101351-230200305003 AiPlayerbot.PremadeSpecName.6.2 = unholy pve AiPlayerbot.PremadeSpecGlyph.6.2 = 43542,43673,45804,43544,43672,43549 -AiPlayerbot.PremadeSpecLink.6.2.60 = --2301303050032151000150013132051 -AiPlayerbot.PremadeSpecLink.6.2.80 = -320043500002-2301303050032151000150013133051 +AiPlayerbot.PremadeSpecLink.6.2.60 = --2301303050032151000150013131151 +AiPlayerbot.PremadeSpecLink.6.2.80 = -320033500002-2301303050032151000150013133151 AiPlayerbot.PremadeSpecName.6.3 = double aura blood pve AiPlayerbot.PremadeSpecGlyph.6.3 = 45805,43673,43827,43544,43672,43554 AiPlayerbot.PremadeSpecLink.6.3.60 = 005512153330030320102013-305 diff --git a/src/factory/StatsWeightCalculator.cpp b/src/factory/StatsWeightCalculator.cpp index f6270c73..e77ac102 100644 --- a/src/factory/StatsWeightCalculator.cpp +++ b/src/factory/StatsWeightCalculator.cpp @@ -223,10 +223,10 @@ void StatsWeightCalculator::GenerateBasicWeights(Player* player) stats_weights_[STATS_TYPE_AGILITY] += 0.9f; stats_weights_[STATS_TYPE_STRENGTH] += 2.5f; stats_weights_[STATS_TYPE_ATTACK_POWER] += 1.0f; - stats_weights_[STATS_TYPE_ARMOR_PENETRATION] += 1.0f; - stats_weights_[STATS_TYPE_HIT] += 1.8f; - stats_weights_[STATS_TYPE_CRIT] += 1.4f; - stats_weights_[STATS_TYPE_HASTE] += 1.7f; + stats_weights_[STATS_TYPE_ARMOR_PENETRATION] += 1.3f; + stats_weights_[STATS_TYPE_HIT] += 2.2f; + stats_weights_[STATS_TYPE_CRIT] += 1.7f; + stats_weights_[STATS_TYPE_HASTE] += 1.8f; stats_weights_[STATS_TYPE_EXPERTISE] += 1.5f; stats_weights_[STATS_TYPE_MELEE_DPS] += 5.0f; } diff --git a/src/strategy/deathknight/UnholyDKStrategy.cpp b/src/strategy/deathknight/UnholyDKStrategy.cpp index c05d0d19..fb1baca4 100644 --- a/src/strategy/deathknight/UnholyDKStrategy.cpp +++ b/src/strategy/deathknight/UnholyDKStrategy.cpp @@ -101,10 +101,10 @@ void UnholyDKStrategy::InitTriggers(std::vector& triggers) triggers.push_back(new TriggerNode("dd cd and no desolation", NextAction::array(0, new NextAction("blood strike", ACTION_DEFAULT + 0.75f), nullptr))); - triggers.push_back( - new TriggerNode("icy touch", NextAction::array(0, new NextAction("icy touch", ACTION_HIGH + 2), nullptr))); - triggers.push_back(new TriggerNode( - "plague strike", NextAction::array(0, new NextAction("plague strike", ACTION_HIGH + 1), nullptr))); + // triggers.push_back( + // new TriggerNode("icy touch", NextAction::array(0, new NextAction("icy touch", ACTION_HIGH + 2), nullptr))); + // triggers.push_back(new TriggerNode( + // "plague strike", NextAction::array(0, new NextAction("plague strike", ACTION_HIGH + 1), nullptr))); triggers.push_back(new TriggerNode( "high frost rune", NextAction::array(0, @@ -117,11 +117,11 @@ void UnholyDKStrategy::InitTriggers(std::vector& triggers) triggers.push_back(new TriggerNode( "high blood rune", NextAction::array(0, new NextAction("blood strike", ACTION_NORMAL + 1), nullptr))); - // triggers.push_back( - // new TriggerNode("dd cd and plague strike 8s", NextAction::array(0, new NextAction("plague strike", ACTION_HIGH + 2), nullptr))); + triggers.push_back( + new TriggerNode("dd cd and plague strike 8s", NextAction::array(0, new NextAction("plague strike", ACTION_HIGH + 2), nullptr))); - // triggers.push_back( - // new TriggerNode("dd cd and icy touch 8s", NextAction::array(0, new NextAction("icy touch", ACTION_HIGH + 1), nullptr))); + triggers.push_back( + new TriggerNode("dd cd and icy touch 8s", NextAction::array(0, new NextAction("icy touch", ACTION_HIGH + 1), nullptr))); // triggers.push_back(new TriggerNode("often", NextAction::array(0, new NextAction(, ACTION_NORMAL + 2), nullptr))); From d3f380d04b2d2b53daf91af424b729f121b7adf3 Mon Sep 17 00:00:00 2001 From: Yunfan Li Date: Sat, 7 Sep 2024 11:52:29 +0800 Subject: [PATCH 41/47] Spellcooldown and instance refresh on randomize --- src/factory/PlayerbotFactory.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/factory/PlayerbotFactory.cpp b/src/factory/PlayerbotFactory.cpp index 5b5eba2e..eb4ee127 100644 --- a/src/factory/PlayerbotFactory.cpp +++ b/src/factory/PlayerbotFactory.cpp @@ -207,7 +207,8 @@ void PlayerbotFactory::Randomize(bool incremental) { ClearAllItems(); } - // bot->SaveToDB(false, false); + bot->RemoveAllSpellCooldown(); + UnbindInstance(); bot->GiveLevel(level); bot->InitStatsForLevel(); From 311bdbd91b9f127226f29f0d9e9350c2ec806cbf Mon Sep 17 00:00:00 2001 From: Yunfan Li Date: Sat, 7 Sep 2024 12:08:38 +0800 Subject: [PATCH 42/47] Serveral crashes fix --- src/PlayerbotMgr.cpp | 4 +++- .../actions/ChooseRpgTargetAction.cpp | 16 +++++++-------- .../actions/UseMeetingStoneAction.cpp | 20 +++++++------------ 3 files changed, 18 insertions(+), 22 deletions(-) diff --git a/src/PlayerbotMgr.cpp b/src/PlayerbotMgr.cpp index 491e93ec..d1b92d7a 100644 --- a/src/PlayerbotMgr.cpp +++ b/src/PlayerbotMgr.cpp @@ -128,6 +128,7 @@ void PlayerbotHolder::HandlePlayerBotLoginCallback(PlayerbotLoginQueryHolder con allowed = false; out << "Failure: You are not allowed to control bot " << bot->GetName().c_str(); } + if (allowed && masterSession) { Player* player = masterSession->GetPlayer(); @@ -145,6 +146,7 @@ void PlayerbotHolder::HandlePlayerBotLoginCallback(PlayerbotLoginQueryHolder con out << "Failure: You have added too many bots for this class"; } } + if (allowed) { sRandomPlayerbotMgr->OnPlayerLogin(bot); @@ -1305,7 +1307,7 @@ uint32 PlayerbotHolder::GetPlayerbotsCountByClass(uint32 cls) for (PlayerBotMap::const_iterator it = GetPlayerBotsBegin(); it != GetPlayerBotsEnd(); ++it) { Player* const bot = it->second; - if (bot->getClass() == cls) + if (bot && bot->IsInWorld() && bot->getClass() == cls) { count++; } diff --git a/src/strategy/actions/ChooseRpgTargetAction.cpp b/src/strategy/actions/ChooseRpgTargetAction.cpp index dfd1cef5..4b00cfe5 100644 --- a/src/strategy/actions/ChooseRpgTargetAction.cpp +++ b/src/strategy/actions/ChooseRpgTargetAction.cpp @@ -311,7 +311,7 @@ bool ChooseRpgTargetAction::isFollowValid(Player* bot, WorldObject* target) bool ChooseRpgTargetAction::isFollowValid(Player* bot, WorldPosition pos) { PlayerbotAI* botAI = GET_PLAYERBOT_AI(bot); - Player* master = botAI->GetGroupMaster(); + Player* gmaster = botAI->GetGroupMaster(); Player* realMaster = botAI->GetMaster(); AiObjectContext* context = botAI->GetAiObjectContext(); @@ -327,30 +327,30 @@ bool ChooseRpgTargetAction::isFollowValid(Player* bot, WorldPosition pos) return false; } - if (!master || bot == master) + if (!gmaster || bot == gmaster) return true; if (!botAI->HasStrategy("follow", BOT_STATE_NON_COMBAT)) return true; - if (bot->GetDistance(master) > sPlayerbotAIConfig->rpgDistance * 2) + if (bot->GetDistance(gmaster) > sPlayerbotAIConfig->rpgDistance * 2) return false; Formation* formation = AI_VALUE(Formation*, "formation"); - float distance = master->GetDistance2d(pos.getX(), pos.getY()); + float distance = gmaster->GetDistance2d(pos.getX(), pos.getY()); if (!botAI->HasActivePlayerMaster() && distance < 50.0f) { - Player* player = master; - if (!master->isMoving() || + Player* player = gmaster; + if (gmaster && !gmaster->isMoving() || PAI_VALUE(WorldPosition, "last long move").distance(pos) < sPlayerbotAIConfig->reactDistance) return true; } - if ((inDungeon || !master->HasPlayerFlag(PLAYER_FLAGS_RESTING)) && realMaster == master && distance > 5.0f) + if ((inDungeon || !gmaster->HasPlayerFlag(PLAYER_FLAGS_RESTING)) && realMaster == gmaster && distance > 5.0f) return false; - if (!master->isMoving() && distance < 25.0f) + if (!gmaster->isMoving() && distance < 25.0f) return true; if (distance < formation->GetMaxDistance()) diff --git a/src/strategy/actions/UseMeetingStoneAction.cpp b/src/strategy/actions/UseMeetingStoneAction.cpp index 0c0b7285..f5d61b01 100644 --- a/src/strategy/actions/UseMeetingStoneAction.cpp +++ b/src/strategy/actions/UseMeetingStoneAction.cpp @@ -165,16 +165,10 @@ bool SummonAction::SummonUsingNpcs(Player* summoner, Player* player) bool SummonAction::Teleport(Player* summoner, Player* player) { - Player* master = GetMaster(); - // if (master->GetMap() && master->GetMap()->IsDungeon()) { - // InstanceMap* map = master->GetMap()->ToInstanceMap(); - // if (map) { - // if (map->CannotEnter(player, true) == Map::CANNOT_ENTER_MAX_PLAYERS) { - // botAI->TellError("I can not enter this dungeon"); - // return false; - // } - // } - // } + // Player* master = GetMaster(); + if (!summoner) + return false; + if (player->GetVehicle()) { botAI->TellError("You cannot summon me while I'm on a vehicle"); @@ -197,13 +191,13 @@ bool SummonAction::Teleport(Player* summoner, Player* player) ->botRepairWhenSummon) // .conf option to repair bot gear when summoned 0 = off, 1 = on bot->DurabilityRepairAll(false, 1.0f, false); - if (master->IsInCombat() && !sPlayerbotAIConfig->allowSummonInCombat) + if (summoner->IsInCombat() && !sPlayerbotAIConfig->allowSummonInCombat) { botAI->TellError("You cannot summon me while you're in combat"); return false; } - if (!master->IsAlive() && !sPlayerbotAIConfig->allowSummonWhenMasterIsDead) + if (!summoner->IsAlive() && !sPlayerbotAIConfig->allowSummonWhenMasterIsDead) { botAI->TellError("You cannot summon me while you're dead"); return false; @@ -218,7 +212,7 @@ bool SummonAction::Teleport(Player* summoner, Player* player) bool revive = sPlayerbotAIConfig->reviveBotWhenSummoned == 2 || - (sPlayerbotAIConfig->reviveBotWhenSummoned == 1 && !master->IsInCombat() && master->IsAlive()); + (sPlayerbotAIConfig->reviveBotWhenSummoned == 1 && !summoner->IsInCombat() && summoner->IsAlive()); if (bot->isDead() && revive) { From 35c5353b6a28b9dc2a1b6d5b510fee916214ce18 Mon Sep 17 00:00:00 2001 From: Yunfan Li Date: Sat, 7 Sep 2024 15:08:50 +0800 Subject: [PATCH 43/47] Fix spell allowing dead target --- src/PlayerbotAI.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/PlayerbotAI.cpp b/src/PlayerbotAI.cpp index 2f1583b4..13561a42 100644 --- a/src/PlayerbotAI.cpp +++ b/src/PlayerbotAI.cpp @@ -315,7 +315,8 @@ void PlayerbotAI::UpdateAI(uint32 elapsed, bool minimal) Spell* currentSpell = bot->GetCurrentSpell(CURRENT_GENERIC_SPELL); if (currentSpell && currentSpell->getState() == SPELL_STATE_PREPARING) { - if (currentSpell->m_targets.GetUnitTarget() && !currentSpell->m_targets.GetUnitTarget()->IsAlive()) + if (currentSpell->m_targets.GetUnitTarget() && !currentSpell->m_targets.GetUnitTarget()->IsAlive() && + currentSpell->GetSpellInfo() && !currentSpell->GetSpellInfo()->IsAllowingDeadTarget()) { bot->InterruptSpell(CURRENT_GENERIC_SPELL); SetNextCheckDelay(sPlayerbotAIConfig->reactDelay); From 4de02481be2a3a00c00d673727d05c1047f7ff3d Mon Sep 17 00:00:00 2001 From: Yunfan Li Date: Sun, 8 Sep 2024 12:53:08 +0800 Subject: [PATCH 44/47] Avoid aoe max radius and whitelist --- conf/playerbots.conf.dist | 14 +++-- src/PlayerbotAI.cpp | 24 ++++---- src/PlayerbotAI.h | 16 ++--- src/PlayerbotAIConfig.cpp | 23 +++++++- src/PlayerbotAIConfig.h | 2 + src/RandomPlayerbotMgr.cpp | 59 ++++--------------- src/strategy/actions/MovementActions.cpp | 26 +++++++- .../raids/naxxramas/RaidNaxxActions.h | 1 + src/strategy/values/AoeValues.cpp | 3 + src/strategy/warrior/ArmsWarriorStrategy.cpp | 7 ++- 10 files changed, 96 insertions(+), 79 deletions(-) diff --git a/conf/playerbots.conf.dist b/conf/playerbots.conf.dist index 7888b6b4..aa751a7d 100644 --- a/conf/playerbots.conf.dist +++ b/conf/playerbots.conf.dist @@ -357,7 +357,7 @@ AiPlayerbot.HighMana = 65 # # -# Bots pick their quest reward (yes = picks first useful item, no = list all rewards, ask = pick useful item and lists if multiple) +# Bots pick their quest reward (yes = picks the most useful item, no = list all rewards, ask = pick useful item and lists if multiple) AiPlayerbot.AutoPickReward = yes # Sync quests with player (Bots will complete quests the moment you hand them in. Bots will ignore looting quest items.) @@ -385,9 +385,11 @@ AiPlayerbot.ApplyInstanceStrategies = 1 # Default: 1 (enable) AiPlayerbot.AutoAvoidAoe = 1 -# Tell which spell is avoiding (experimental) -# Default: 1 (enable) -AiPlayerbot.TellWhenAvoidAoe = 1 +# Only avoid aoe spells with a radius smaller than this value +AiPlayerbot.MaxAoeAvoidRadius = 15.0 + +# A whitelist of aoe spell IDs that should not be avoided +AiPlayerbot.AoeAvoidSpellWhitelist = 50759,57491 # Enable healer bot save mana # Default: 1 (enable) @@ -1281,6 +1283,10 @@ AiPlayerbot.LogInGroupOnly = 1 AiPlayerbot.LogValuesPerTick = 0 AiPlayerbot.RandomChangeMultiplier = 1 +# Tell which spell is avoiding (experimental) +# Default: 0 (disable) +AiPlayerbot.TellWhenAvoidAoe = 0 + # Enables/Disables performance monitor AiPlayerbot.PerfMonEnabled = 0 diff --git a/src/PlayerbotAI.cpp b/src/PlayerbotAI.cpp index 13561a42..453544ff 100644 --- a/src/PlayerbotAI.cpp +++ b/src/PlayerbotAI.cpp @@ -1624,10 +1624,10 @@ void PlayerbotAI::ResetStrategies(bool load) // sPlayerbotDbStore->Load(this); } -bool PlayerbotAI::IsRanged(Player* player) +bool PlayerbotAI::IsRanged(Player* player, bool bySpec) { PlayerbotAI* botAi = GET_PLAYERBOT_AI(player); - if (botAi) + if (!bySpec && botAi) return botAi->ContainsStrategy(STRATEGY_TYPE_RANGED); int tab = AiFactory::GetPlayerSpecTab(player); @@ -1660,18 +1660,18 @@ bool PlayerbotAI::IsRanged(Player* player) return true; } -bool PlayerbotAI::IsMelee(Player* player) { return !IsRanged(player); } +bool PlayerbotAI::IsMelee(Player* player, bool bySpec) { return !IsRanged(player, bySpec); } -bool PlayerbotAI::IsCaster(Player* player) { return IsRanged(player) && player->getClass() != CLASS_HUNTER; } +bool PlayerbotAI::IsCaster(Player* player, bool bySpec) { return IsRanged(player, bySpec) && player->getClass() != CLASS_HUNTER; } -bool PlayerbotAI::IsCombo(Player* player) +bool PlayerbotAI::IsCombo(Player* player, bool bySpec) { // int tab = AiFactory::GetPlayerSpecTab(player); return player->getClass() == CLASS_ROGUE || (player->getClass() == CLASS_DRUID && player->HasAura(768)); // cat druid } -bool PlayerbotAI::IsRangedDps(Player* player) { return IsRanged(player) && IsDps(player); } +bool PlayerbotAI::IsRangedDps(Player* player, bool bySpec) { return IsRanged(player, bySpec) && IsDps(player, bySpec); } bool PlayerbotAI::IsHealAssistantOfIndex(Player* player, int index) { @@ -1894,10 +1894,10 @@ int32 PlayerbotAI::GetMeleeIndex(Player* player) return 0; } -bool PlayerbotAI::IsTank(Player* player) +bool PlayerbotAI::IsTank(Player* player, bool bySpec) { PlayerbotAI* botAi = GET_PLAYERBOT_AI(player); - if (botAi) + if (!bySpec && botAi) return botAi->ContainsStrategy(STRATEGY_TYPE_TANK); int tab = AiFactory::GetPlayerSpecTab(player); @@ -1932,10 +1932,10 @@ bool PlayerbotAI::IsTank(Player* player) return false; } -bool PlayerbotAI::IsHeal(Player* player) +bool PlayerbotAI::IsHeal(Player* player, bool bySpec) { PlayerbotAI* botAi = GET_PLAYERBOT_AI(player); - if (botAi) + if (!bySpec && botAi) return botAi->ContainsStrategy(STRATEGY_TYPE_HEAL); int tab = AiFactory::GetPlayerSpecTab(player); @@ -1969,10 +1969,10 @@ bool PlayerbotAI::IsHeal(Player* player) return false; } -bool PlayerbotAI::IsDps(Player* player) +bool PlayerbotAI::IsDps(Player* player, bool bySpec) { PlayerbotAI* botAi = GET_PLAYERBOT_AI(player); - if (botAi) + if (!bySpec && botAi) return botAi->ContainsStrategy(STRATEGY_TYPE_DPS); int tab = AiFactory::GetPlayerSpecTab(player); diff --git a/src/PlayerbotAI.h b/src/PlayerbotAI.h index c2a94625..062b7d60 100644 --- a/src/PlayerbotAI.h +++ b/src/PlayerbotAI.h @@ -401,14 +401,14 @@ public: void ResetStrategies(bool load = false); void ReInitCurrentEngine(); void Reset(bool full = false); - static bool IsTank(Player* player); - static bool IsHeal(Player* player); - static bool IsDps(Player* player); - static bool IsRanged(Player* player); - static bool IsMelee(Player* player); - static bool IsCaster(Player* player); - static bool IsCombo(Player* player); - static bool IsRangedDps(Player* player); + static bool IsTank(Player* player, bool bySpec = false); + static bool IsHeal(Player* player, bool bySpec = false); + static bool IsDps(Player* player, bool bySpec = false); + static bool IsRanged(Player* player, bool bySpec = false); + static bool IsMelee(Player* player, bool bySpec = false); + static bool IsCaster(Player* player, bool bySpec = false); + static bool IsCombo(Player* player, bool bySpec = false); + static bool IsRangedDps(Player* player, bool bySpec = false); static bool IsMainTank(Player* player); bool IsAssistTank(Player* player); bool IsAssistTankOfIndex(Player* player, int index); diff --git a/src/PlayerbotAIConfig.cpp b/src/PlayerbotAIConfig.cpp index 60b20a95..4fe41134 100644 --- a/src/PlayerbotAIConfig.cpp +++ b/src/PlayerbotAIConfig.cpp @@ -29,6 +29,19 @@ void LoadList(std::string const value, T& list) } } +template +void LoadSet(std::string const value, T& set) +{ + std::vector ids = split(value, ','); + for (std::vector::iterator i = ids.begin(); i != ids.end(); i++) + { + uint32 id = atoi((*i).c_str()); + // if (!id) + // continue; + set.insert(id); + } +} + template void LoadListString(std::string const value, T& list) { @@ -98,7 +111,10 @@ bool PlayerbotAIConfig::Initialize() autoSaveMana = sConfigMgr->GetOption("AiPlayerbot.AutoSaveMana", true); saveManaThreshold = sConfigMgr->GetOption("AiPlayerbot.SaveManaThreshold", 60); autoAvoidAoe = sConfigMgr->GetOption("AiPlayerbot.AutoAvoidAoe", true); - tellWhenAvoidAoe = sConfigMgr->GetOption("AiPlayerbot.TellWhenAvoidAoe", true); + maxAoeAvoidRadius = sConfigMgr->GetOption("AiPlayerbot.MaxAoeAvoidRadius", 15.0f); + LoadSet>(sConfigMgr->GetOption("AiPlayerbot.AoeAvoidSpellWhitelist", "50759,57491"), + aoeAvoidSpellWhitelist); + tellWhenAvoidAoe = sConfigMgr->GetOption("AiPlayerbot.TellWhenAvoidAoe", false); randomGearLoweringChance = sConfigMgr->GetOption("AiPlayerbot.RandomGearLoweringChance", 0.0f); randomGearQualityLimit = sConfigMgr->GetOption("AiPlayerbot.RandomGearQualityLimit", 3); @@ -792,9 +808,10 @@ std::vector> PlayerbotAIConfig::ParseTempPetTalentsOrder(uin if (!((1 << spec) & talentTabInfo->petTalentMask)) continue; // skip some duplicate spells like dash/dive - if (talentInfo->TalentID == 2201 || talentInfo->TalentID == 2208 || talentInfo->TalentID == 2219 || talentInfo->TalentID == 2203) + if (talentInfo->TalentID == 2201 || talentInfo->TalentID == 2208 || talentInfo->TalentID == 2219 || + talentInfo->TalentID == 2203) continue; - + spells.push_back(talentInfo); } std::sort(spells.begin(), spells.end(), diff --git a/src/PlayerbotAIConfig.h b/src/PlayerbotAIConfig.h index 3aa47617..6caf1179 100644 --- a/src/PlayerbotAIConfig.h +++ b/src/PlayerbotAIConfig.h @@ -67,6 +67,8 @@ public: bool autoSaveMana; uint32 saveManaThreshold; bool autoAvoidAoe; + float maxAoeAvoidRadius; + std::set aoeAvoidSpellWhitelist; bool tellWhenAvoidAoe; uint32 openGoSpell; diff --git a/src/RandomPlayerbotMgr.cpp b/src/RandomPlayerbotMgr.cpp index 0f06e0a5..3595190b 100644 --- a/src/RandomPlayerbotMgr.cpp +++ b/src/RandomPlayerbotMgr.cpp @@ -1867,6 +1867,7 @@ void RandomPlayerbotMgr::GetBots() PlayerbotsDatabase.GetPreparedStatement(PLAYERBOTS_SEL_RANDOM_BOTS_BY_OWNER_AND_EVENT); stmt->SetData(0, 0); stmt->SetData(1, "add"); + uint32 maxAllowedBotCount = GetEventValue(0, "bot_count"); if (PreparedQueryResult result = PlayerbotsDatabase.Query(stmt)) { do @@ -1875,6 +1876,9 @@ void RandomPlayerbotMgr::GetBots() uint32 bot = fields[0].Get(); if (GetEventValue(bot, "add")) currentBots.push_back(bot); + + if (currentBots.size() >= maxAllowedBotCount) + break; } while (result->NextRow()); } } @@ -2402,53 +2406,14 @@ void RandomPlayerbotMgr::PrintStats() ++engine_combat; else ++engine_dead; - - uint8 spec = AiFactory::GetPlayerSpecTab(bot); - switch (bot->getClass()) - { - case CLASS_DRUID: - if (spec == 2) - ++heal; - else - ++dps; - break; - case CLASS_PALADIN: - if (spec == 1) - ++tank; - else if (spec == 0) - ++heal; - else - ++dps; - break; - case CLASS_PRIEST: - if (spec != 2) - ++heal; - else - ++dps; - break; - case CLASS_SHAMAN: - if (spec == 2) - ++heal; - else - ++dps; - break; - case CLASS_WARRIOR: - if (spec == 2) - ++tank; - else - ++dps; - break; - case CLASS_DEATH_KNIGHT: - if (spec == 0) - tank++; - else - dps++; - break; - default: - ++dps; - break; - } - + + if (botAI->IsHeal(bot, true)) + ++heal; + else if (botAI->IsTank(bot, true)) + ++tank; + else + ++dps; + if (TravelTarget* target = botAI->GetAiObjectContext()->GetValue("travel target")->Get()) { TravelState state = target->getTravelState(); diff --git a/src/strategy/actions/MovementActions.cpp b/src/strategy/actions/MovementActions.cpp index bafccd83..49c8df2b 100644 --- a/src/strategy/actions/MovementActions.cpp +++ b/src/strategy/actions/MovementActions.cpp @@ -1819,12 +1819,17 @@ bool AvoidAoeAction::AvoidAuraWithDynamicObj() { return false; } + if (sPlayerbotAIConfig->aoeAvoidSpellWhitelist.find(spellInfo->Id) != sPlayerbotAIConfig->aoeAvoidSpellWhitelist.end()) + return false; + DynamicObject* dynOwner = aura->GetDynobjOwner(); if (!dynOwner || !dynOwner->IsInWorld()) { return false; } float radius = dynOwner->GetRadius(); + if (!radius || radius > sPlayerbotAIConfig->maxAoeAvoidRadius) + return false; if (bot->GetDistance(dynOwner) > radius) { return false; @@ -1838,7 +1843,7 @@ bool AvoidAoeAction::AvoidAuraWithDynamicObj() lastTellTimer = time(NULL); lastMoveTimer = getMSTime(); std::ostringstream out; - out << "I'm avoiding " << name.str() << "..."; + out << "I'm avoiding " << name.str() << " (" << spellInfo->Id << ")" << " Radius " << radius << " - [Aura]"; bot->Say(out.str(), LANG_UNIVERSAL); } return true; @@ -1869,17 +1874,28 @@ bool AvoidAoeAction::AvoidGameObjectWithDamage() { continue; } + // 0 trap with no despawn after cast. 1 trap despawns after cast. 2 bomb casts on spawn. + if (goInfo->trap.type != 0) + continue; + uint32 spellId = goInfo->trap.spellId; if (!spellId) { continue; } + + if (sPlayerbotAIConfig->aoeAvoidSpellWhitelist.find(spellId) != sPlayerbotAIConfig->aoeAvoidSpellWhitelist.end()) + continue; + const SpellInfo* spellInfo = sSpellMgr->GetSpellInfo(spellId); if (!spellInfo || spellInfo->IsPositive()) { continue; } + float radius = (float)goInfo->trap.diameter / 2; + if (!radius || radius > sPlayerbotAIConfig->maxAoeAvoidRadius) + continue; // for (int i = 0; i < MAX_SPELL_EFFECTS; i++) { // if (spellInfo->Effects[i].Effect == SPELL_EFFECT_APPLY_AURA) { // if (spellInfo->Effects[i].ApplyAuraName == SPELL_AURA_PERIODIC_DAMAGE) { @@ -1903,7 +1919,7 @@ bool AvoidAoeAction::AvoidGameObjectWithDamage() lastTellTimer = time(NULL); lastMoveTimer = getMSTime(); std::ostringstream out; - out << "I'm avoiding " << name.str() << "..."; + out << "I'm avoiding " << name.str() << " (" << spellInfo->Id << ")" << " Radius " << radius << " - [Trap]"; bot->Say(out.str(), LANG_UNIVERSAL); } return true; @@ -1946,6 +1962,8 @@ bool AvoidAoeAction::AvoidUnitWithDamageAura() sSpellMgr->GetSpellInfo(spellInfo->Effects[aurEff->GetEffIndex()].TriggerSpell); if (!triggerSpellInfo) continue; + if (sPlayerbotAIConfig->aoeAvoidSpellWhitelist.find(triggerSpellInfo->Id) != sPlayerbotAIConfig->aoeAvoidSpellWhitelist.end()) + return false; for (int j = 0; j < MAX_SPELL_EFFECTS; j++) { if (triggerSpellInfo->Effects[j].Effect == SPELL_EFFECT_SCHOOL_DAMAGE) @@ -1955,6 +1973,8 @@ bool AvoidAoeAction::AvoidUnitWithDamageAura() { break; } + if (!radius || radius > sPlayerbotAIConfig->maxAoeAvoidRadius) + continue; std::ostringstream name; name << triggerSpellInfo->SpellName[LOCALE_enUS]; //<< "] (unit)"; if (FleePosition(unit->GetPosition(), radius)) @@ -1964,7 +1984,7 @@ bool AvoidAoeAction::AvoidUnitWithDamageAura() lastTellTimer = time(NULL); lastMoveTimer = getMSTime(); std::ostringstream out; - out << "I'm avoiding " << name.str() << "..."; + out << "I'm avoiding " << name.str() << " (" << triggerSpellInfo->Id << ")" << " Radius " << radius << " - [Unit Trigger]"; bot->Say(out.str(), LANG_UNIVERSAL); } } diff --git a/src/strategy/raids/naxxramas/RaidNaxxActions.h b/src/strategy/raids/naxxramas/RaidNaxxActions.h index 4c877510..b065ba74 100644 --- a/src/strategy/raids/naxxramas/RaidNaxxActions.h +++ b/src/strategy/raids/naxxramas/RaidNaxxActions.h @@ -64,6 +64,7 @@ public: this->prev_phase = 0; this->prev_erupt = 0; this->prev_timer = 0; + ResetSafe(); waypoints.push_back(std::make_pair(2793.58f, -3665.93f)); waypoints.push_back(std::make_pair(2775.49f, -3674.43f)); waypoints.push_back(std::make_pair(2762.30f, -3684.59f)); diff --git a/src/strategy/values/AoeValues.cpp b/src/strategy/values/AoeValues.cpp index 069acc73..67508f66 100644 --- a/src/strategy/values/AoeValues.cpp +++ b/src/strategy/values/AoeValues.cpp @@ -145,6 +145,9 @@ Aura* AreaDebuffValue::Calculate() { continue; } + // float radius = dynOwner->GetRadius(); + // if (radius > 12.0f) + // continue; return aura; } } diff --git a/src/strategy/warrior/ArmsWarriorStrategy.cpp b/src/strategy/warrior/ArmsWarriorStrategy.cpp index 540b6cd0..c77e25c2 100644 --- a/src/strategy/warrior/ArmsWarriorStrategy.cpp +++ b/src/strategy/warrior/ArmsWarriorStrategy.cpp @@ -65,7 +65,10 @@ void ArmsWarriorStrategy::InitTriggers(std::vector& triggers) "victory rush", NextAction::array(0, new NextAction("victory rush", ACTION_INTERRUPT), nullptr))); triggers.push_back(new TriggerNode( "high rage available", NextAction::array(0, new NextAction("heroic strike", ACTION_HIGH + 10), nullptr))); - triggers.push_back(new TriggerNode("medium rage available", NextAction::array(0, new NextAction("slam", ACTION_HIGH + 1), nullptr))); + triggers.push_back(new TriggerNode("medium rage available", + NextAction::array(0, new NextAction("slam", ACTION_HIGH + 1), + new NextAction("thunder clap", ACTION_HIGH), + nullptr))); triggers.push_back( new TriggerNode("bloodrage", NextAction::array(0, new NextAction("bloodrage", ACTION_HIGH + 2), nullptr))); triggers.push_back( @@ -76,5 +79,5 @@ void ArmsWarriorStrategy::InitTriggers(std::vector& triggers) triggers.push_back(new TriggerNode( "critical health", NextAction::array(0, new NextAction("intimidating shout", ACTION_EMERGENCY), nullptr))); triggers.push_back(new TriggerNode("medium aoe", - NextAction::array(0, new NextAction("thunder clap", ACTION_HIGH + 1), nullptr))); + NextAction::array(0, new NextAction("thunder clap", ACTION_HIGH + 2), nullptr))); } From 2a0bae60c8c767421721bacd02181d42ac715897 Mon Sep 17 00:00:00 2001 From: Yunfan Li Date: Sun, 8 Sep 2024 12:53:21 +0800 Subject: [PATCH 45/47] Quest auto pick "yes" --- src/strategy/actions/TalkToQuestGiverAction.cpp | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/src/strategy/actions/TalkToQuestGiverAction.cpp b/src/strategy/actions/TalkToQuestGiverAction.cpp index f6e62bb0..772d3ff7 100644 --- a/src/strategy/actions/TalkToQuestGiverAction.cpp +++ b/src/strategy/actions/TalkToQuestGiverAction.cpp @@ -11,6 +11,7 @@ #include "Object.h" #include "Playerbots.h" #include "QuestDef.h" +#include "StatsWeightCalculator.h" #include "WorldPacket.h" #include "BroadcastHelper.h" @@ -176,8 +177,20 @@ void TalkToQuestGiverAction::RewardMultipleItem(Quest const* quest, Object* ques bestIds = BestRewards(quest); if (!bestIds.empty()) { - ItemTemplate const* item = sObjectMgr->GetItemTemplate(quest->RewardChoiceItemId[*bestIds.begin()]); - bot->RewardQuest(quest, *bestIds.begin(), questGiver, true); + StatsWeightCalculator calc(bot); + uint32 best = 0; + float bestScore = 0; + for (uint32 id : bestIds) + { + float score = calc.CalculateItem(quest->RewardChoiceItemId[id]); + if (score > bestScore) + { + bestScore = score; + best = id; + } + } + ItemTemplate const* item = sObjectMgr->GetItemTemplate(quest->RewardChoiceItemId[best]); + bot->RewardQuest(quest, best, questGiver, true); out << "Rewarded " << ChatHelper::FormatItem(item); } else From a6ece8fe33154df262d23147f3289322f7b7e1f7 Mon Sep 17 00:00:00 2001 From: Yunfan Li Date: Sun, 8 Sep 2024 14:22:05 +0800 Subject: [PATCH 46/47] Can equip weapon type fix --- src/RandomItemMgr.cpp | 42 +++++++++++++++++++++++++++--------------- 1 file changed, 27 insertions(+), 15 deletions(-) diff --git a/src/RandomItemMgr.cpp b/src/RandomItemMgr.cpp index fdde9c95..157ca308 100644 --- a/src/RandomItemMgr.cpp +++ b/src/RandomItemMgr.cpp @@ -769,49 +769,61 @@ bool RandomItemMgr::CanEquipWeapon(uint8 clazz, ItemTemplate const* proto) { case CLASS_PRIEST: if (proto->SubClass != ITEM_SUBCLASS_WEAPON_STAFF && proto->SubClass != ITEM_SUBCLASS_WEAPON_WAND && - proto->SubClass != ITEM_SUBCLASS_WEAPON_MACE) + proto->SubClass != ITEM_SUBCLASS_WEAPON_MACE && proto->SubClass != ITEM_SUBCLASS_WEAPON_DAGGER) return false; break; case CLASS_MAGE: case CLASS_WARLOCK: if (proto->SubClass != ITEM_SUBCLASS_WEAPON_STAFF && proto->SubClass != ITEM_SUBCLASS_WEAPON_WAND && - proto->SubClass != ITEM_SUBCLASS_WEAPON_SWORD) + proto->SubClass != ITEM_SUBCLASS_WEAPON_DAGGER && proto->SubClass != ITEM_SUBCLASS_WEAPON_SWORD) return false; break; case CLASS_WARRIOR: - if (proto->SubClass != ITEM_SUBCLASS_WEAPON_MACE2 && proto->SubClass != ITEM_SUBCLASS_WEAPON_SWORD2 && + if (proto->SubClass != ITEM_SUBCLASS_WEAPON_MACE2 && proto->SubClass != ITEM_SUBCLASS_WEAPON_AXE && + proto->SubClass != ITEM_SUBCLASS_WEAPON_POLEARM && proto->SubClass != ITEM_SUBCLASS_WEAPON_SWORD2 && proto->SubClass != ITEM_SUBCLASS_WEAPON_MACE && proto->SubClass != ITEM_SUBCLASS_WEAPON_SWORD && proto->SubClass != ITEM_SUBCLASS_WEAPON_GUN && proto->SubClass != ITEM_SUBCLASS_WEAPON_CROSSBOW && - proto->SubClass != ITEM_SUBCLASS_WEAPON_BOW && proto->SubClass != ITEM_SUBCLASS_WEAPON_AXE && - proto->SubClass != ITEM_SUBCLASS_WEAPON_AXE2 && proto->SubClass != ITEM_SUBCLASS_WEAPON_THROWN) + proto->SubClass != ITEM_SUBCLASS_WEAPON_BOW && proto->SubClass != ITEM_SUBCLASS_WEAPON_THROWN && + proto->SubClass != ITEM_SUBCLASS_WEAPON_AXE2 && proto->SubClass != ITEM_SUBCLASS_WEAPON_FIST && + proto->SubClass != ITEM_SUBCLASS_WEAPON_DAGGER && proto->SubClass != ITEM_SUBCLASS_WEAPON_STAFF) return false; break; case CLASS_PALADIN: - if (proto->SubClass != ITEM_SUBCLASS_WEAPON_MACE2 && proto->SubClass != ITEM_SUBCLASS_WEAPON_SWORD2 && - proto->SubClass != ITEM_SUBCLASS_WEAPON_MACE && proto->SubClass != ITEM_SUBCLASS_WEAPON_SWORD) + case CLASS_DEATH_KNIGHT: + if (proto->SubClass != ITEM_SUBCLASS_WEAPON_MACE2 && proto->SubClass != ITEM_SUBCLASS_WEAPON_POLEARM && + proto->SubClass != ITEM_SUBCLASS_WEAPON_SWORD2 && proto->SubClass != ITEM_SUBCLASS_WEAPON_AXE2 && + proto->SubClass != ITEM_SUBCLASS_WEAPON_AXE && proto->SubClass != ITEM_SUBCLASS_WEAPON_MACE && + proto->SubClass != ITEM_SUBCLASS_WEAPON_SWORD) return false; break; case CLASS_SHAMAN: - if (proto->SubClass != ITEM_SUBCLASS_WEAPON_MACE && proto->SubClass != ITEM_SUBCLASS_WEAPON_MACE2 && + if (proto->SubClass != ITEM_SUBCLASS_WEAPON_MACE && proto->SubClass != ITEM_SUBCLASS_WEAPON_AXE && + proto->SubClass != ITEM_SUBCLASS_WEAPON_FIST && proto->SubClass != ITEM_SUBCLASS_WEAPON_MACE2 && + proto->SubClass != ITEM_SUBCLASS_WEAPON_AXE2 && proto->SubClass != ITEM_SUBCLASS_WEAPON_DAGGER && proto->SubClass != ITEM_SUBCLASS_WEAPON_STAFF) return false; break; case CLASS_DRUID: if (proto->SubClass != ITEM_SUBCLASS_WEAPON_MACE && proto->SubClass != ITEM_SUBCLASS_WEAPON_MACE2 && - proto->SubClass != ITEM_SUBCLASS_WEAPON_DAGGER && proto->SubClass != ITEM_SUBCLASS_WEAPON_STAFF) + proto->SubClass != ITEM_SUBCLASS_WEAPON_DAGGER && proto->SubClass != ITEM_SUBCLASS_WEAPON_STAFF && + proto->SubClass != ITEM_SUBCLASS_WEAPON_POLEARM) return false; break; case CLASS_HUNTER: - if (proto->SubClass != ITEM_SUBCLASS_WEAPON_AXE2 && proto->SubClass != ITEM_SUBCLASS_WEAPON_SWORD2 && - proto->SubClass != ITEM_SUBCLASS_WEAPON_GUN && proto->SubClass != ITEM_SUBCLASS_WEAPON_CROSSBOW && - proto->SubClass != ITEM_SUBCLASS_WEAPON_BOW) + if (proto->SubClass != ITEM_SUBCLASS_WEAPON_DAGGER && proto->SubClass != ITEM_SUBCLASS_WEAPON_BOW && + proto->SubClass != ITEM_SUBCLASS_WEAPON_AXE2 && proto->SubClass != ITEM_SUBCLASS_WEAPON_AXE && + proto->SubClass != ITEM_SUBCLASS_WEAPON_SWORD2 && proto->SubClass != ITEM_SUBCLASS_WEAPON_SWORD && + proto->SubClass != ITEM_SUBCLASS_WEAPON_FIST && proto->SubClass != ITEM_SUBCLASS_WEAPON_GUN && + proto->SubClass != ITEM_SUBCLASS_WEAPON_CROSSBOW && proto->SubClass != ITEM_SUBCLASS_WEAPON_STAFF && + proto->SubClass != ITEM_SUBCLASS_WEAPON_POLEARM) return false; break; case CLASS_ROGUE: if (proto->SubClass != ITEM_SUBCLASS_WEAPON_DAGGER && proto->SubClass != ITEM_SUBCLASS_WEAPON_SWORD && - proto->SubClass != ITEM_SUBCLASS_WEAPON_MACE && proto->SubClass != ITEM_SUBCLASS_WEAPON_GUN && - proto->SubClass != ITEM_SUBCLASS_WEAPON_CROSSBOW && proto->SubClass != ITEM_SUBCLASS_WEAPON_BOW && - proto->SubClass != ITEM_SUBCLASS_WEAPON_THROWN) + proto->SubClass != ITEM_SUBCLASS_WEAPON_FIST && proto->SubClass != ITEM_SUBCLASS_WEAPON_MACE && + proto->SubClass != ITEM_SUBCLASS_WEAPON_GUN && proto->SubClass != ITEM_SUBCLASS_WEAPON_CROSSBOW && + proto->SubClass != ITEM_SUBCLASS_WEAPON_BOW && proto->SubClass != ITEM_SUBCLASS_WEAPON_THROWN && + proto->SubClass != ITEM_SUBCLASS_WEAPON_AXE) return false; break; } From 694f400f20691503fbd6155a24a4c76927d261e5 Mon Sep 17 00:00:00 2001 From: Yunfan Li Date: Sun, 8 Sep 2024 15:48:28 +0800 Subject: [PATCH 47/47] Remove mana potion -> drink alternative --- conf/playerbots.conf.dist | 2 +- src/PlayerbotAIConfig.cpp | 2 +- src/strategy/Strategy.cpp | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/conf/playerbots.conf.dist b/conf/playerbots.conf.dist index aa751a7d..518ae5ea 100644 --- a/conf/playerbots.conf.dist +++ b/conf/playerbots.conf.dist @@ -389,7 +389,7 @@ AiPlayerbot.AutoAvoidAoe = 1 AiPlayerbot.MaxAoeAvoidRadius = 15.0 # A whitelist of aoe spell IDs that should not be avoided -AiPlayerbot.AoeAvoidSpellWhitelist = 50759,57491 +AiPlayerbot.AoeAvoidSpellWhitelist = 50759,57491,13810 # Enable healer bot save mana # Default: 1 (enable) diff --git a/src/PlayerbotAIConfig.cpp b/src/PlayerbotAIConfig.cpp index 4fe41134..0e111958 100644 --- a/src/PlayerbotAIConfig.cpp +++ b/src/PlayerbotAIConfig.cpp @@ -112,7 +112,7 @@ bool PlayerbotAIConfig::Initialize() saveManaThreshold = sConfigMgr->GetOption("AiPlayerbot.SaveManaThreshold", 60); autoAvoidAoe = sConfigMgr->GetOption("AiPlayerbot.AutoAvoidAoe", true); maxAoeAvoidRadius = sConfigMgr->GetOption("AiPlayerbot.MaxAoeAvoidRadius", 15.0f); - LoadSet>(sConfigMgr->GetOption("AiPlayerbot.AoeAvoidSpellWhitelist", "50759,57491"), + LoadSet>(sConfigMgr->GetOption("AiPlayerbot.AoeAvoidSpellWhitelist", "50759,57491,13810"), aoeAvoidSpellWhitelist); tellWhenAvoidAoe = sConfigMgr->GetOption("AiPlayerbot.TellWhenAvoidAoe", false); diff --git a/src/strategy/Strategy.cpp b/src/strategy/Strategy.cpp index e6fe779d..3f1c3010 100644 --- a/src/strategy/Strategy.cpp +++ b/src/strategy/Strategy.cpp @@ -94,7 +94,7 @@ private: { return new ActionNode("mana potion", /*P*/ nullptr, - /*A*/ NextAction::array(0, new NextAction("drink"), nullptr), + /*A*/ nullptr, /*C*/ nullptr); }