From 4de02481be2a3a00c00d673727d05c1047f7ff3d Mon Sep 17 00:00:00 2001 From: Yunfan Li Date: Sun, 8 Sep 2024 12:53:08 +0800 Subject: [PATCH] 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))); }