From 25da0af70e4aca9dfce31999f89498b113305fae Mon Sep 17 00:00:00 2001 From: Yunfan Li Date: Fri, 2 Jun 2023 15:49:49 +0800 Subject: [PATCH] tank target, formation arrow, mount fix, miscs --- conf/playerbots.conf.dist | 4 +- src/PlayerbotAI.cpp | 116 ++++++++++++++---- src/PlayerbotAI.h | 61 +++++++++ src/PlayerbotMgr.cpp | 8 ++ src/RandomPlayerbotMgr.cpp | 4 +- .../actions/CheckMountStateAction.cpp | 6 + src/strategy/actions/ChooseTargetActions.cpp | 24 ++-- .../generic/WorldPacketHandlerStrategy.cpp | 2 +- src/strategy/triggers/GenericTriggers.cpp | 4 +- src/strategy/triggers/GenericTriggers.h | 4 +- src/strategy/values/Arrow.cpp | 5 +- src/strategy/values/TankTargetValue.cpp | 17 ++- 12 files changed, 206 insertions(+), 49 deletions(-) diff --git a/conf/playerbots.conf.dist b/conf/playerbots.conf.dist index 35ab9be5..e58224ac 100644 --- a/conf/playerbots.conf.dist +++ b/conf/playerbots.conf.dist @@ -489,10 +489,10 @@ AiPlayerbot.HealDistance = 38.5 AiPlayerbot.LootDistance = 15.0 AiPlayerbot.FleeDistance = 5.0 AiPlayerbot.TooCloseDistance = 5.0 -AiPlayerbot.MeleeDistance = 1.5 +AiPlayerbot.MeleeDistance = 0.01 AiPlayerbot.FollowDistance = 1.5 AiPlayerbot.WhisperDistance = 6000.0 -AiPlayerbot.ContactDistance = 0.5 +AiPlayerbot.ContactDistance = 0.01 AiPlayerbot.AoeRadius = 10 AiPlayerbot.RpgDistance = 200 AiPlayerbot.AggroDistance = 22 diff --git a/src/PlayerbotAI.cpp b/src/PlayerbotAI.cpp index b5978db8..9fcd0349 100644 --- a/src/PlayerbotAI.cpp +++ b/src/PlayerbotAI.cpp @@ -2,6 +2,7 @@ * Copyright (C) 2016+ AzerothCore , released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version. */ +#include "ObjectGuid.h" #include "Playerbots.h" #include "AiFactory.h" #include "BudgetValues.h" @@ -1263,55 +1264,124 @@ void PlayerbotAI::ResetStrategies(bool load) bool PlayerbotAI::IsRanged(Player* player) { - PlayerbotAI* botAI = GET_PLAYERBOT_AI(player); - if (botAI && !player->InBattleground()) - return botAI->ContainsStrategy(STRATEGY_TYPE_RANGED); + PlayerbotAI* botAi = GET_PLAYERBOT_AI(player); + if (botAi) + return botAi->ContainsStrategy(STRATEGY_TYPE_RANGED); + int tab = AiFactory::GetPlayerSpecTab(player); switch (player->getClass()) { - case CLASS_PALADIN: - case CLASS_WARRIOR: - case CLASS_ROGUE: - case CLASS_DEATH_KNIGHT: + case CLASS_DEATH_KNIGHT: + case CLASS_WARRIOR: + case CLASS_ROGUE: + return false; + break; + case CLASS_DRUID: + if (tab == 1) { return false; - case CLASS_DRUID: - return !HasAnyAuraOf(player, "cat form", "bear form", "dire bear form", nullptr); + } + break; + case CLASS_PALADIN: + if (tab != 0) { + return false; + } + break; + case CLASS_SHAMAN: + if (tab == 1) { + return false; + } + break; } - return true; } bool PlayerbotAI::IsTank(Player* player) { - if (PlayerbotAI* botAI = GET_PLAYERBOT_AI(player)) - return botAI->ContainsStrategy(STRATEGY_TYPE_TANK); + PlayerbotAI* botAi = GET_PLAYERBOT_AI(player); + if (botAi) + return botAi->ContainsStrategy(STRATEGY_TYPE_TANK); + int tab = AiFactory::GetPlayerSpecTab(player); switch (player->getClass()) { - case CLASS_PALADIN: - case CLASS_WARRIOR: case CLASS_DEATH_KNIGHT: - return true; + if (tab == 0) { + return true; + } + break; + case CLASS_PALADIN: + if (tab == 1) { + return true; + } + break; + case CLASS_WARRIOR: + if (tab == 2) { + return true; + } + break; case CLASS_DRUID: - return HasAnyAuraOf(player, "bear form", "dire bear form", nullptr); + if (tab == 1 && HasAnyAuraOf(player, "bear form", "dire bear form", "thick hide", NULL)) { + return true; + } + break; } - return false; } bool PlayerbotAI::IsHeal(Player* player) { - if (PlayerbotAI* botAI = GET_PLAYERBOT_AI(player)) - return botAI->ContainsStrategy(STRATEGY_TYPE_HEAL); + PlayerbotAI* botAi = GET_PLAYERBOT_AI(player); + if (botAi) + return botAi->ContainsStrategy(STRATEGY_TYPE_HEAL); + int tab = AiFactory::GetPlayerSpecTab(player); switch (player->getClass()) { - case CLASS_PRIEST: + case CLASS_PRIEST: + if (tab == PRIEST_TAB_DISIPLINE || tab == PRIEST_TAB_HOLY) { return true; - case CLASS_DRUID: - return HasAnyAuraOf(player, "tree of life form", nullptr); + } + break; + case CLASS_DRUID: + if (tab == DRUID_TAB_RESTORATION) { + return true; + } + break; + case CLASS_SHAMAN: + if (tab == SHAMAN_TAB_RESTORATION) { + return true; + } + break; + case CLASS_PALADIN: + if (tab == PALADIN_TAB_HOLY) { + return true; + } + break; } + return false; +} +bool PlayerbotAI::IsMainTank(Player* player) +{ + Group* group = bot->GetGroup(); + if (!group) { + return false; + } + ObjectGuid mainTank = ObjectGuid(); + Group::MemberSlotList const& slots = group->GetMemberSlots(); + for (Group::member_citerator itr = slots.begin(); itr != slots.end(); ++itr) { + if (itr->flags & MEMBER_FLAG_MAINTANK) + mainTank = itr->guid; + } + if (mainTank != ObjectGuid::Empty) { + return player->GetGUID() == mainTank; + } + for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next()) { + Player* member = ref->GetSource(); + if (IsTank(member)) { + return player == member; + } + } return false; } @@ -1987,7 +2057,7 @@ bool PlayerbotAI::CastSpell(uint32 spellId, Unit* target, Item* itemTarget) { // WorldLocation aoe = aiObjectContext->GetValue("aoe position")->Get(); // targets.SetDst(aoe); - targets.SetDst(*bot); + targets.SetDst(*target); } else if (spellInfo->Targets & TARGET_FLAG_SOURCE_LOCATION) { diff --git a/src/PlayerbotAI.h b/src/PlayerbotAI.h index 28817ebd..0b5b000a 100644 --- a/src/PlayerbotAI.h +++ b/src/PlayerbotAI.h @@ -208,6 +208,66 @@ enum BotRoles : uint8 BOT_ROLE_DPS = 0x04 }; +enum HUNTER_TABS { + HUNTER_TAB_BEASTMASTER, + HUNTER_TAB_MARKSMANSHIP, + HUNTER_TAB_SURVIVAL, +}; + +enum ROGUE_TABS { + ROGUE_TAB_ASSASSINATION, + ROGUE_TAB_COMBAT, + ROGUE_TAB_SUBTLETY +}; + +enum PRIEST_TABS { + PRIEST_TAB_DISIPLINE, + PRIEST_TAB_HOLY, + PRIEST_TAB_SHADOW, +}; + +enum DEATHKNIGT_TABS { + DEATHKNIGT_TAB_BLOOD, + DEATHKNIGT_TAB_FROST, + DEATHKNIGT_TAB_UNHOLY, +}; + +enum DRUID_TABS { + DRUID_TAB_BALANCE, + DRUID_TAB_FERAL, + DRUID_TAB_RESTORATION, +}; + +enum MAGE_TABS { + MAGE_TAB_ARCANE, + MAGE_TAB_FIRE, + MAGE_TAB_FROST, +}; + +enum SHAMAN_TABS { + SHAMAN_TAB_ELEMENTAL, + SHAMAN_TAB_ENHANCEMENT, + SHAMAN_TAB_RESTORATION, +}; + +enum PALADIN_TABS { + PALADIN_TAB_HOLY, + PALADIN_TAB_PROTECTION, + PALADIN_TAB_RETRIBUTION, +}; + +enum WARLOCK_TABS { + WARLOCK_TAB_AFFLICATION, + WARLOCK_TAB_DEMONOLOGY, + WARLOCK_TAB_DESTRUCTION, +}; + +enum WARRIOR_TABS { + WARRIOR_TAB_ARMS, + WARRIOR_TAB_FURY, + WARRIOR_TAB_PROTECTION, +}; + class PacketHandlingHelper { public: @@ -270,6 +330,7 @@ class PlayerbotAI : public PlayerbotAIBase bool IsTank(Player* player); bool IsHeal(Player* player); bool IsRanged(Player* player); + bool IsMainTank(Player* player); Creature* GetCreature(ObjectGuid guid); Unit* GetUnit(ObjectGuid guid); Player* GetPlayer(ObjectGuid guid); diff --git a/src/PlayerbotMgr.cpp b/src/PlayerbotMgr.cpp index d1c5bf9c..72774793 100644 --- a/src/PlayerbotMgr.cpp +++ b/src/PlayerbotMgr.cpp @@ -427,6 +427,14 @@ void PlayerbotHolder::OnBotLogin(Player* const bot) master->GetGroup()->AddMember(bot); } + uint32 accountId = bot->GetSession()->GetAccountId(); + bool isRandomAccount = sPlayerbotAIConfig->IsInRandomAccountList(accountId); + + if (master && isRandomAccount) { + PlayerbotFactory factory(bot, master->getLevel()); + factory.Randomize(false); + } + // bots join World chat if not solo oriented if (bot->getLevel() >= 10 && sRandomPlayerbotMgr->IsRandomBot(bot) && GET_PLAYERBOT_AI(bot) && GET_PLAYERBOT_AI(bot)->GetGrouperType() != GrouperType::SOLO) { diff --git a/src/RandomPlayerbotMgr.cpp b/src/RandomPlayerbotMgr.cpp index 3a9eca28..863ee80e 100644 --- a/src/RandomPlayerbotMgr.cpp +++ b/src/RandomPlayerbotMgr.cpp @@ -992,14 +992,14 @@ void RandomPlayerbotMgr::RandomTeleport(Player* bot, std::vector& std::vector tlocs; for (auto& loc : locs) tlocs.push_back(WorldPosition(loc)); - LOG_INFO("playerbots", "Locs {} collected.", tlocs.size()); + // LOG_INFO("playerbots", "Locs {} collected.", tlocs.size()); //Do not teleport to maps disabled in config tlocs.erase(std::remove_if(tlocs.begin(), tlocs.end(), [bot](WorldPosition l) { std::vector::iterator i = find(sPlayerbotAIConfig->randomBotMaps.begin(), sPlayerbotAIConfig->randomBotMaps.end(), l.getMapId()); return i == sPlayerbotAIConfig->randomBotMaps.end(); }), tlocs.end()); - LOG_INFO("playerbots", "Locs {} after disabled in config.", tlocs.size()); + // LOG_INFO("playerbots", "Locs {} after disabled in config.", tlocs.size()); // Check locs again in case all possible locations were removed if (tlocs.empty()) { diff --git a/src/strategy/actions/CheckMountStateAction.cpp b/src/strategy/actions/CheckMountStateAction.cpp index dc8509d0..6c6295c4 100644 --- a/src/strategy/actions/CheckMountStateAction.cpp +++ b/src/strategy/actions/CheckMountStateAction.cpp @@ -52,6 +52,12 @@ bool CheckMountStateAction::Execute(Event event) return Mount(); } + if (!master->IsMounted() && bot->IsMounted()) + { + WorldPacket emptyPacket; + bot->GetSession()->HandleCancelMountAuraOpcode(emptyPacket); + return true; + } // if (!bot->IsMounted() && (chasedistance || (farFromMaster && botAI->HasStrategy("follow", BOT_STATE_NON_COMBAT))) && !bot->IsInCombat() && !dps) // return Mount(); diff --git a/src/strategy/actions/ChooseTargetActions.cpp b/src/strategy/actions/ChooseTargetActions.cpp index 26395d35..c0c2b0ec 100644 --- a/src/strategy/actions/ChooseTargetActions.cpp +++ b/src/strategy/actions/ChooseTargetActions.cpp @@ -77,18 +77,18 @@ bool DropTargetAction::Execute(Event event) botAI->InterruptSpell(); bot->AttackStop(); - if (Pet* pet = bot->GetPet()) - { - if (CreatureAI* creatureAI = ((Creature*)pet)->AI()) - { - pet->SetReactState(REACT_PASSIVE); - pet->GetCharmInfo()->SetCommandState(COMMAND_FOLLOW); - pet->GetCharmInfo()->SetIsCommandFollow(true); - pet->AttackStop(); - pet->GetCharmInfo()->IsReturning(); - pet->GetMotionMaster()->MoveFollow(bot, PET_FOLLOW_DIST, pet->GetFollowAngle()); - } - } + // if (Pet* pet = bot->GetPet()) + // { + // if (CreatureAI* creatureAI = ((Creature*)pet)->AI()) + // { + // pet->SetReactState(REACT_PASSIVE); + // pet->GetCharmInfo()->SetCommandState(COMMAND_FOLLOW); + // pet->GetCharmInfo()->SetIsCommandFollow(true); + // pet->AttackStop(); + // pet->GetCharmInfo()->IsReturning(); + // pet->GetMotionMaster()->MoveFollow(bot, PET_FOLLOW_DIST, pet->GetFollowAngle()); + // } + // } return true; } diff --git a/src/strategy/generic/WorldPacketHandlerStrategy.cpp b/src/strategy/generic/WorldPacketHandlerStrategy.cpp index c5a531f7..5e27b7e9 100644 --- a/src/strategy/generic/WorldPacketHandlerStrategy.cpp +++ b/src/strategy/generic/WorldPacketHandlerStrategy.cpp @@ -26,7 +26,7 @@ void WorldPacketHandlerStrategy::InitTriggers(std::vector& trigger triggers.push_back(new TriggerNode("loot response", NextAction::array(0, new NextAction("store loot", relevance), nullptr))); triggers.push_back(new TriggerNode("item push result", NextAction::array(0, new NextAction("query item usage", relevance), new NextAction("equip upgrades", relevance), nullptr))); triggers.push_back(new TriggerNode("ready check finished", NextAction::array(0, new NextAction("finish ready check", relevance), nullptr))); - triggers.push_back(new TriggerNode("often", NextAction::array(0, new NextAction("security check", relevance), new NextAction("check mail", relevance), nullptr))); + // triggers.push_back(new TriggerNode("often", NextAction::array(0, new NextAction("security check", relevance), new NextAction("check mail", relevance), nullptr))); triggers.push_back(new TriggerNode("guild invite", NextAction::array(0, new NextAction("guild accept", relevance), nullptr))); triggers.push_back(new TriggerNode("petition offer", NextAction::array(0, new NextAction("petition sign", relevance), nullptr))); triggers.push_back(new TriggerNode("lfg proposal", NextAction::array(0, new NextAction("lfg accept", relevance), nullptr))); diff --git a/src/strategy/triggers/GenericTriggers.cpp b/src/strategy/triggers/GenericTriggers.cpp index d661a82c..00830735 100644 --- a/src/strategy/triggers/GenericTriggers.cpp +++ b/src/strategy/triggers/GenericTriggers.cpp @@ -18,11 +18,11 @@ bool MediumManaTrigger::IsActive() bool NoPetTrigger::IsActive() { - return !AI_VALUE(Unit*, "pet target") && !bot->GetGuardianPet() && !AI_VALUE2(bool, "mounted", "self target"); + return (!AI_VALUE(Unit*, "pet target")) && (!bot->GetGuardianPet()) && (!bot->GetFirstControlled()) && (!AI_VALUE2(bool, "mounted", "self target")); } bool HasPetTrigger::IsActive() { - return (AI_VALUE(Unit*, "pet target") || bot->GetGuardianPet()) && !AI_VALUE2(bool, "mounted", "self target");; + return (AI_VALUE(Unit*, "pet target")) && !AI_VALUE2(bool, "mounted", "self target");; } bool HighManaTrigger::IsActive() diff --git a/src/strategy/triggers/GenericTriggers.h b/src/strategy/triggers/GenericTriggers.h index 0ed7dc79..d2852e9a 100644 --- a/src/strategy/triggers/GenericTriggers.h +++ b/src/strategy/triggers/GenericTriggers.h @@ -415,13 +415,13 @@ class NoPetTrigger : public Trigger public: NoPetTrigger(PlayerbotAI* botAI) : Trigger(botAI, "no pet", 5) { } - bool IsActive() override; + virtual bool IsActive() override; }; class HasPetTrigger : public Trigger { public: - HasPetTrigger(PlayerbotAI* ai) : Trigger(ai, "no pet", 5) {} + HasPetTrigger(PlayerbotAI* ai) : Trigger(ai, "has pet", 5) {} virtual bool IsActive() override; }; diff --git a/src/strategy/values/Arrow.cpp b/src/strategy/values/Arrow.cpp index 3e6c0e42..3a378bb4 100644 --- a/src/strategy/values/Arrow.cpp +++ b/src/strategy/values/Arrow.cpp @@ -19,6 +19,9 @@ WorldLocation ArrowFormation::GetLocationInternal() float offset = 0.f; Player* master = botAI->GetMaster(); + if (!master) { + return Formation::NullLocation; + } float orientation = master->GetOrientation(); MultiLineUnitPlacer placer(orientation); @@ -29,7 +32,7 @@ WorldLocation ArrowFormation::GetLocationInternal() melee.PlaceUnits(&placer); melee.Move(-cos(orientation) * offset, -sin(orientation) * offset); - offset += meleeLines * sPlayerbotAIConfig->followDistance; + offset += meleeLines * sPlayerbotAIConfig->followDistance + sPlayerbotAIConfig->tooCloseDistance; ranged.PlaceUnits(&placer); ranged.Move(-cos(orientation) * offset, -sin(orientation) * offset); diff --git a/src/strategy/values/TankTargetValue.cpp b/src/strategy/values/TankTargetValue.cpp index 52c60c85..9eede72d 100644 --- a/src/strategy/values/TankTargetValue.cpp +++ b/src/strategy/values/TankTargetValue.cpp @@ -14,11 +14,20 @@ class FindTargetForTankStrategy : public FindNonCcTargetStrategy void CheckAttacker(Unit* creature, ThreatMgr* threatMgr) override { Player* bot = botAI->GetBot(); - if (IsCcTarget(creature)) - return; - float threat = threatMgr->GetThreat(bot); - if (!result || (minThreat - threat) > 0.1f) + if (!result) { + minThreat = threat; + result = creature; + } + // neglect if victim is main tank, or no victim (for untauntable target) + if (threatMgr->getCurrentVictim()) { + // float max_threat = threatMgr->GetThreat(threatMgr->getCurrentVictim()->getTarget()); + Unit* victim = threatMgr->getCurrentVictim()->getTarget(); + if (victim && victim->ToPlayer() && botAI->IsMainTank(victim->ToPlayer())) { + return; + } + } + if (minThreat >= threat) { minThreat = threat; result = creature;