From b7e240804710f63245cb537a2c72dd6d2581452f Mon Sep 17 00:00:00 2001 From: Yunfan Li Date: Fri, 5 Jul 2024 11:55:07 +0800 Subject: [PATCH 01/13] [Configuration] Enable free food --- conf/playerbots.conf.dist | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/conf/playerbots.conf.dist b/conf/playerbots.conf.dist index 7d4f9a34..eb2fb3f1 100644 --- a/conf/playerbots.conf.dist +++ b/conf/playerbots.conf.dist @@ -224,8 +224,8 @@ AiPlayerbot.SyncQuestForPlayer = 0 AiPlayerbot.SyncLevelWithPlayers = 0 # Give free food to random bots -# Default: 0 (disabled) -AiPlayerbot.FreeFood = 0 +# Default: 1 (enabled) +AiPlayerbot.FreeFood = 1 # Bot automatically trains spells when talking to trainer (yes = train all available spells as long as the bot has the money, free = auto trains with no money cost, no = only list spells) # Only for random bots @@ -352,6 +352,10 @@ AiPlayerbot.AutoAvoidAoe = 1 # Default: 1 (enable) AiPlayerbot.TellWhenAvoidAoe = 1 +# Premade spell to avoid (undetected spells) +# spellid-radius, ... +AiPlayerbot.PremadeAvoidAoe = 62234-4 + # Random bot default strategies (applied after defaults) AiPlayerbot.RandomBotCombatStrategies = "+dps,+dps assist,-threat" # AiPlayerbot.RandomBotNonCombatStrategies = "+grind,+loot,+rpg,+custom::say" From 98560a18555824b172d54c017b9deabbe9d8215f Mon Sep 17 00:00:00 2001 From: Yunfan Li Date: Fri, 5 Jul 2024 12:01:25 +0800 Subject: [PATCH 02/13] [Initialization] Stop server after accounts deletion --- src/PlayerbotAIConfig.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/PlayerbotAIConfig.cpp b/src/PlayerbotAIConfig.cpp index 34e7a1bb..d468533c 100644 --- a/src/PlayerbotAIConfig.cpp +++ b/src/PlayerbotAIConfig.cpp @@ -327,6 +327,9 @@ bool PlayerbotAIConfig::Initialize() selfBotLevel = sConfigMgr->GetOption("AiPlayerbot.SelfBotLevel", 1); RandomPlayerbotFactory::CreateRandomBots(); + if (World::IsStopped()) { + return true; + } PlayerbotFactory::Init(); sRandomItemMgr->Init(); sRandomItemMgr->InitAfterAhBot(); From c63ee90cb284e96f580f405f5fd63785606dcf20 Mon Sep 17 00:00:00 2001 From: Yunfan Li Date: Fri, 5 Jul 2024 14:17:39 +0800 Subject: [PATCH 03/13] [Crash fix] Duplicate bots login --- src/PlayerbotMgr.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/PlayerbotMgr.cpp b/src/PlayerbotMgr.cpp index 4c8f984f..bd004267 100644 --- a/src/PlayerbotMgr.cpp +++ b/src/PlayerbotMgr.cpp @@ -363,6 +363,11 @@ Player* PlayerbotHolder::GetPlayerBot(ObjectGuid::LowType lowGuid) const void PlayerbotHolder::OnBotLogin(Player* const bot) { + // Prevent duplicate login + if (playerBots.find(bot->GetGUID()) != playerBots.end()) { + return; + } + sPlayerbotsMgr->AddPlayerbotData(bot, true); playerBots[bot->GetGUID()] = bot; OnBotLoginInternal(bot); From f26227027daf4b45bead7a91f25deb980373e1e8 Mon Sep 17 00:00:00 2001 From: Yunfan Li Date: Fri, 5 Jul 2024 18:00:35 +0800 Subject: [PATCH 04/13] [Combat formation] Combat formation set up --- src/AiFactory.cpp | 1 + src/strategy/StrategyContext.h | 2 + src/strategy/Value.h | 6 + src/strategy/actions/ActionContext.h | 4 + src/strategy/actions/MovementActions.cpp | 195 ++++++++++++++++-- src/strategy/actions/MovementActions.h | 47 ++++- .../generic/ChatCommandHandlerStrategy.cpp | 1 + src/strategy/generic/CombatStrategy.cpp | 9 +- src/strategy/generic/CombatStrategy.h | 8 + src/strategy/triggers/ChatTriggerContext.h | 2 + src/strategy/values/ValueContext.h | 2 + 11 files changed, 251 insertions(+), 26 deletions(-) diff --git a/src/AiFactory.cpp b/src/AiFactory.cpp index 063f3411..09401a82 100644 --- a/src/AiFactory.cpp +++ b/src/AiFactory.cpp @@ -274,6 +274,7 @@ void AiFactory::AddDefaultCombatStrategies(Player* player, PlayerbotAI* const fa { engine->addStrategy("avoid aoe"); } + engine->addStrategy("combat formation"); switch (player->getClass()) { case CLASS_PRIEST: diff --git a/src/strategy/StrategyContext.h b/src/strategy/StrategyContext.h index 55bdc4bc..82090440 100644 --- a/src/strategy/StrategyContext.h +++ b/src/strategy/StrategyContext.h @@ -112,6 +112,7 @@ class StrategyContext : public NamedObjectContext creators["grind"] = &StrategyContext::grind; creators["avoid aoe"] = &StrategyContext::avoid_aoe; creators["move random"] = &StrategyContext::move_random; + creators["combat formation"] = &StrategyContext::combat_formation; } private: @@ -174,6 +175,7 @@ class StrategyContext : public NamedObjectContext static Strategy* grind(PlayerbotAI* botAI) { return new GrindingStrategy(botAI); } static Strategy* avoid_aoe(PlayerbotAI* botAI) { return new AvoidAoeStrategy(botAI); } static Strategy* move_random(PlayerbotAI* ai) { return new MoveRandomStrategy(ai); } + static Strategy* combat_formation(PlayerbotAI* ai) { return new CombatFormationStrategy(ai); } }; class MovementStrategyContext : public NamedObjectContext diff --git a/src/strategy/Value.h b/src/strategy/Value.h index 9cba6efe..e27600ad 100644 --- a/src/strategy/Value.h +++ b/src/strategy/Value.h @@ -326,4 +326,10 @@ class UnitManualSetValue : public ManualSetValue Unit* Get() override; }; +class DisperseDistanceValue : public ManualSetValue +{ + public: + DisperseDistanceValue(PlayerbotAI* botAI, float defaultValue = -1.0f, std::string const name = "disperse value") : + ManualSetValue(botAI, defaultValue, name) { } +}; #endif diff --git a/src/strategy/actions/ActionContext.h b/src/strategy/actions/ActionContext.h index 6cdc68b1..f2c1062c 100644 --- a/src/strategy/actions/ActionContext.h +++ b/src/strategy/actions/ActionContext.h @@ -89,6 +89,8 @@ class ActionContext : public NamedObjectContext creators["flee"] = &ActionContext::flee; creators["flee with pet"] = &ActionContext::flee_with_pet; creators["avoid aoe"] = &ActionContext::avoid_aoe; + creators["combat formation move"] = &ActionContext::combat_formation_move; + creators["disperse set"] = &ActionContext::disperse_set; creators["gift of the naaru"] = &ActionContext::gift_of_the_naaru; creators["shoot"] = &ActionContext::shoot; creators["lifeblood"] = &ActionContext::lifeblood; @@ -265,6 +267,8 @@ class ActionContext : public NamedObjectContext static Action* flee(PlayerbotAI* botAI) { return new FleeAction(botAI); } static Action* flee_with_pet(PlayerbotAI* botAI) { return new FleeWithPetAction(botAI); } static Action* avoid_aoe(PlayerbotAI* botAI) { return new AvoidAoeAction(botAI); } + static Action* combat_formation_move(PlayerbotAI* botAI) { return new CombatFormationMoveAction(botAI); } + static Action* disperse_set(PlayerbotAI* botAI) { return new DisperseSetAction(botAI); } static Action* gift_of_the_naaru(PlayerbotAI* botAI) { return new CastGiftOfTheNaaruAction(botAI); } static Action* lifeblood(PlayerbotAI* botAI) { return new CastLifeBloodAction(botAI); } static Action* arcane_torrent(PlayerbotAI* botAI) { return new CastArcaneTorrentAction(botAI); } diff --git a/src/strategy/actions/MovementActions.cpp b/src/strategy/actions/MovementActions.cpp index f2652168..0a47c8ea 100644 --- a/src/strategy/actions/MovementActions.cpp +++ b/src/strategy/actions/MovementActions.cpp @@ -12,6 +12,7 @@ #include "ObjectGuid.h" #include "PathGenerator.h" #include "PlayerbotAIConfig.h" +#include "Position.h" #include "Random.h" #include "SharedDefines.h" #include "SpellAuraEffects.h" @@ -25,10 +26,13 @@ #include "LootObjectStack.h" #include "Playerbots.h" #include "ServerFacade.h" +#include "Timer.h" #include "Transport.h" #include "Unit.h" #include "Vehicle.h" #include "WaypointMovementGenerator.h" +#include +#include MovementAction::MovementAction(PlayerbotAI* botAI, std::string const name) : Action(botAI, name) { @@ -1495,6 +1499,9 @@ bool FleeWithPetAction::Execute(Event event) bool AvoidAoeAction::isUseful() { + if (getMSTime() - moveInterval < lastMoveTimer) { + return false; + } GuidVector traps = AI_VALUE(GuidVector, "nearest trap with damage"); GuidVector triggers = AI_VALUE(GuidVector, "possible triggers"); return AI_VALUE(Aura*, "area debuff") || !traps.empty() || !triggers.empty(); @@ -1504,14 +1511,17 @@ bool AvoidAoeAction::Execute(Event event) { // Case #1: Aura with dynamic object (e.g. rain of fire) if (AvoidAuraWithDynamicObj()) { + lastMoveTimer = getMSTime(); return true; } // Case #2: Trap game object with spell (e.g. lava bomb) if (AvoidGameObjectWithDamage()) { + lastMoveTimer = getMSTime(); return true; } // Case #3: Trigger npc (e.g. Lesser shadow fissure) if (AvoidUnitWithDamageAura()) { + lastMoveTimer = getMSTime(); return true; } return false; @@ -1541,7 +1551,13 @@ bool AvoidAoeAction::AvoidAuraWithDynamicObj() } std::ostringstream name; name << spellInfo->SpellName[0]; // << "] (aura)"; - if (FleePosition(dynOwner->GetPosition(), radius, name.str())) { + if (FleePosition(dynOwner->GetPosition(), radius)) { + if (sPlayerbotAIConfig->tellWhenAvoidAoe && lastTellTimer < time(NULL) - 10) { + lastTellTimer = time(NULL); + std::ostringstream out; + out << "I'm avoiding " << name.str() << "..."; + bot->Say(out.str(), LANG_UNIVERSAL); + } return true; } return false; @@ -1592,7 +1608,13 @@ bool AvoidAoeAction::AvoidGameObjectWithDamage() } std::ostringstream name; name << spellInfo->SpellName[0]; // << "] (object)"; - if (FleePosition(go->GetPosition(), radius, name.str())) { + if (FleePosition(go->GetPosition(), radius)) { + if (sPlayerbotAIConfig->tellWhenAvoidAoe && lastTellTimer < time(NULL) - 10) { + lastTellTimer = time(NULL); + std::ostringstream out; + out << "I'm avoiding " << name.str() << "..."; + bot->Say(out.str(), LANG_UNIVERSAL); + } return true; } @@ -1634,8 +1656,13 @@ bool AvoidAoeAction::AvoidUnitWithDamageAura() } std::ostringstream name; name << triggerSpellInfo->SpellName[0]; //<< "] (unit)"; - if (FleePosition(unit->GetPosition(), radius, name.str())) { - return true; + if (FleePosition(unit->GetPosition(), radius)) { + if (sPlayerbotAIConfig->tellWhenAvoidAoe && lastTellTimer < time(NULL) - 10) { + lastTellTimer = time(NULL); + std::ostringstream out; + out << "I'm avoiding " << name.str() << "..."; + bot->Say(out.str(), LANG_UNIVERSAL); + } } } } @@ -1645,7 +1672,7 @@ bool AvoidAoeAction::AvoidUnitWithDamageAura() return false; } -Position AvoidAoeAction::BestPositionForMelee(Position pos, float radius) +Position MovementAction::BestPositionForMeleeToFlee(Position pos, float radius) { Unit* currentTarget = AI_VALUE(Unit*, "current target"); std::vector possibleAngles; @@ -1671,7 +1698,7 @@ Position AvoidAoeAction::BestPositionForMelee(Position pos, float radius) for (CheckAngle &checkAngle : possibleAngles) { float angle = checkAngle.angle; bool strict = checkAngle.strict; - float fleeDis = sPlayerbotAIConfig->fleeDistance; + float fleeDis = std::min(radius + 1.0f, sPlayerbotAIConfig->fleeDistance); Position fleePos{bot->GetPositionX() + cos(angle) * fleeDis, bot->GetPositionY() + sin(angle) * fleeDis, bot->GetPositionZ()}; @@ -1690,7 +1717,7 @@ Position AvoidAoeAction::BestPositionForMelee(Position pos, float radius) return Position(); } -Position AvoidAoeAction::BestPositionForRanged(Position pos, float radius) +Position MovementAction::BestPositionForRangedToFlee(Position pos, float radius) { Unit* currentTarget = AI_VALUE(Unit*, "current target"); std::vector possibleAngles; @@ -1714,7 +1741,7 @@ Position AvoidAoeAction::BestPositionForRanged(Position pos, float radius) for (CheckAngle &checkAngle : possibleAngles) { float angle = checkAngle.angle; bool strict = checkAngle.strict; - float fleeDis = sPlayerbotAIConfig->fleeDistance; + float fleeDis = std::min(radius + 1.0f, sPlayerbotAIConfig->fleeDistance); Position fleePos{bot->GetPositionX() + cos(angle) * fleeDis, bot->GetPositionY() + sin(angle) * fleeDis, bot->GetPositionZ()}; @@ -1737,28 +1764,162 @@ Position AvoidAoeAction::BestPositionForRanged(Position pos, float radius) return Position(); } -bool AvoidAoeAction::FleePosition(Position pos, float radius, std::string name) +bool MovementAction::FleePosition(Position pos, float radius) { Position bestPos; if (botAI->IsMelee(bot)) { - bestPos = BestPositionForMelee(pos, radius); + bestPos = BestPositionForMeleeToFlee(pos, radius); } else { - bestPos = BestPositionForRanged(pos, radius); + bestPos = BestPositionForRangedToFlee(pos, radius); } if (bestPos != Position()) { if (MoveTo(bot->GetMapId(), bestPos.GetPositionX(), bestPos.GetPositionY(), bestPos.GetPositionZ(), false, false, true)) { - if (sPlayerbotAIConfig->tellWhenAvoidAoe && lastTellTimer < time(NULL) - 10) { - lastTellTimer = time(NULL); - std::ostringstream out; - out << "I'm avoiding " << name << "..."; - bot->Say(out.str(), LANG_UNIVERSAL); - } return true; } } return false; } +bool CombatFormationMoveAction::isUseful() +{ + if (getMSTime() - moveInterval < lastMoveTimer) { + return false; + } + if (bot->GetCurrentSpell(CURRENT_CHANNELED_SPELL) != nullptr) { + return false; + } + float dis = AI_VALUE(float, "disperse distance"); + return dis > 0.0f; +} + +bool CombatFormationMoveAction::Execute(Event event) +{ + float dis = AI_VALUE(float, "disperse distance"); + Player* playerToLeave = NearestGroupMember(dis); + if (playerToLeave && bot->GetExactDist(playerToLeave) < dis) { + if (FleePosition(playerToLeave->GetPosition(), dis)) { + lastMoveTimer = getMSTime(); + } + } + return false; +} + +Position CombatFormationMoveAction::AverageGroupPos(float dis) +{ + float averageX = 0, averageY = 0, averageZ = 0; + int cnt = 0; + Group* group = bot->GetGroup(); + if (!group) { + return Position(); + } + Group::MemberSlotList const& groupSlot = group->GetMemberSlots(); + for (Group::member_citerator itr = groupSlot.begin(); itr != groupSlot.end(); itr++) + { + Player *member = ObjectAccessor::FindPlayer(itr->guid); + if (!member || !member->IsAlive() || member->GetMapId() != bot->GetMapId() || member->IsCharmed() || sServerFacade->GetDistance2d(bot, member) > dis) + continue; + cnt++; + averageX += member->GetPositionX(); + averageY += member->GetPositionY(); + averageZ += member->GetPositionZ(); + } + averageX /= cnt; + averageY /= cnt; + averageZ /= cnt; + return Position(averageX, averageY, averageZ); +} + +Player* CombatFormationMoveAction::NearestGroupMember(float dis) +{ + float nearestDis = 10000.0f; + Player* result = nullptr; + Group* group = bot->GetGroup(); + if (!group) { + return result; + } + Group::MemberSlotList const& groupSlot = group->GetMemberSlots(); + for (Group::member_citerator itr = groupSlot.begin(); itr != groupSlot.end(); itr++) + { + Player *member = ObjectAccessor::FindPlayer(itr->guid); + if (!member || !member->IsAlive() || member == bot || member->GetMapId() != bot->GetMapId() || member->IsCharmed() || sServerFacade->GetDistance2d(bot, member) > dis) + continue; + if (nearestDis > bot->GetExactDist(member)) { + result = member; + nearestDis = bot->GetExactDist(member); + } + } + return result; +} + +bool DisperseSetAction::Execute(Event event) +{ + std::string const text = event.getParam(); + if (text == "disable") { + RESET_AI_VALUE(float, "disperse distance"); + botAI->TellMasterNoFacing("Disable disperse"); + return true; + } + if (text == "enable" || text == "reset") { + if (botAI->IsMelee(bot)) { + SET_AI_VALUE(float, "disperse distance", DEFAULT_DISPERSE_DISTANCE_MELEE); + } else { + SET_AI_VALUE(float, "disperse distance", DEFAULT_DISPERSE_DISTANCE_RANGED); + } + float dis = AI_VALUE(float, "disperse distance"); + std::ostringstream out; + out << "Enable disperse distance " << std::setprecision(2) << dis; + botAI->TellMasterNoFacing(out.str()); + return true; + } + if (text == "increase") { + float dis = AI_VALUE(float, "disperse distance"); + std::ostringstream out; + if (dis <= 0.0f) { + out << "Enable disperse first"; + botAI->TellMasterNoFacing(out.str()); + return true; + } + dis += 1.0f; + SET_AI_VALUE(float, "disperse distance", dis); + out << "Increase disperse distance to " << std::setprecision(2) << dis; + botAI->TellMasterNoFacing(out.str()); + return true; + } + if (text == "decrease") { + float dis = AI_VALUE(float, "disperse distance"); + dis -= 1.0f; + if (dis <= 0.0f) { + dis += 1.0f; + } + SET_AI_VALUE(float, "disperse distance", dis); + std::ostringstream out; + out << "Increase disperse distance to " << std::setprecision(2) << dis; + botAI->TellMasterNoFacing(out.str()); + return true; + } + if (text.starts_with("set")) { + float dis = -1.0f;; + sscanf(text.c_str(), "set %f", &dis); + std::ostringstream out; + if (dis < 0 || dis > 100.0f) { + out << "Invalid disperse distance " << std::setprecision(2) << dis; + } else { + SET_AI_VALUE(float, "disperse distance", dis); + out << "Set disperse distance to " << std::setprecision(2) << dis; + } + botAI->TellMasterNoFacing(out.str()); + return true; + } + std::ostringstream out; + out << "Usage: disperse [enable|disable|increase|decrease|set {distance}]" << "\n"; + float dis = AI_VALUE(float, "disperse distance"); + if (dis > 0.0f) { + out << "Current disperse distance: " << std::setprecision(2) << dis; + } + botAI->TellMasterNoFacing(out.str()); + return true; +} + bool RunAwayAction::Execute(Event event) { return Flee(AI_VALUE(Unit*, "master target")); diff --git a/src/strategy/actions/MovementActions.h b/src/strategy/actions/MovementActions.h index f26991a4..ed9527af 100644 --- a/src/strategy/actions/MovementActions.h +++ b/src/strategy/actions/MovementActions.h @@ -41,6 +41,14 @@ class MovementAction : public Action bool MoveAway(Unit* target); bool MoveInside(uint32 mapId, float x, float y, float z, float distance = sPlayerbotAIConfig->followDistance); void CreateWp(Player* wpOwner, float x, float y, float z, float o, uint32 entry, bool important = false); + Position BestPositionForMeleeToFlee(Position pos, float radius); + Position BestPositionForRangedToFlee(Position pos, float radius); + bool FleePosition(Position pos, float radius); + protected: + struct CheckAngle { + float angle; + bool strict; + }; private: // float SearchBestGroundZForPath(float x, float y, float z, bool generatePath, float range = 20.0f, bool normal_only = false, float step = 8.0f); const Movement::PointsArray SearchForBestPath(float x, float y, float z, float &modified_z, int maxSearchCount = 5, bool normal_only = false, float step = 8.0f); @@ -69,7 +77,8 @@ class FleeWithPetAction : public MovementAction class AvoidAoeAction : public MovementAction { public: - AvoidAoeAction(PlayerbotAI* botAI) : MovementAction(botAI, "avoid aoe") { } + AvoidAoeAction(PlayerbotAI* botAI, int moveInterval = 1000) : MovementAction(botAI, "avoid aoe"), + moveInterval(moveInterval) { } bool isUseful() override; bool Execute(Event event) override; @@ -78,14 +87,36 @@ class AvoidAoeAction : public MovementAction bool AvoidAuraWithDynamicObj(); bool AvoidGameObjectWithDamage(); bool AvoidUnitWithDamageAura(); - Position BestPositionForMelee(Position pos, float radius); - Position BestPositionForRanged(Position pos, float radius); - bool FleePosition(Position pos, float radius, std::string name); time_t lastTellTimer = 0; - struct CheckAngle { - float angle; - bool strict; - }; + int lastMoveTimer = 0; + int moveInterval; + +}; + +class CombatFormationMoveAction : public MovementAction +{ + public: + CombatFormationMoveAction(PlayerbotAI* botAI, int moveInterval = 1000) : MovementAction(botAI, "combat formation move"), + moveInterval(moveInterval) { } + + bool isUseful() override; + bool Execute(Event event) override; + + protected: + Position AverageGroupPos(float dis = sPlayerbotAIConfig->sightDistance); + Player* NearestGroupMember(float dis = sPlayerbotAIConfig->sightDistance); + int lastMoveTimer = 0; + int moveInterval; +}; + +class DisperseSetAction : public Action +{ + public: + DisperseSetAction(PlayerbotAI* botAI, std::string const name = "disperse set") : Action(botAI, name) { } + + bool Execute(Event event) override; + float DEFAULT_DISPERSE_DISTANCE_RANGED = 5.0f; + float DEFAULT_DISPERSE_DISTANCE_MELEE = 2.0f; }; class RunAwayAction : public MovementAction diff --git a/src/strategy/generic/ChatCommandHandlerStrategy.cpp b/src/strategy/generic/ChatCommandHandlerStrategy.cpp index 4da74e5b..0f149960 100644 --- a/src/strategy/generic/ChatCommandHandlerStrategy.cpp +++ b/src/strategy/generic/ChatCommandHandlerStrategy.cpp @@ -61,6 +61,7 @@ void ChatCommandHandlerStrategy::InitTriggers(std::vector& trigger triggers.push_back(new TriggerNode("naxx", NextAction::array(0, new NextAction("naxx chat shortcut", relevance), NULL))); 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))); + triggers.push_back(new TriggerNode("disperse", NextAction::array(0, new NextAction("disperse set", relevance), NULL))); } ChatCommandHandlerStrategy::ChatCommandHandlerStrategy(PlayerbotAI* botAI) : PassTroughStrategy(botAI) diff --git a/src/strategy/generic/CombatStrategy.cpp b/src/strategy/generic/CombatStrategy.cpp index 3b72af7f..c9e3a95c 100644 --- a/src/strategy/generic/CombatStrategy.cpp +++ b/src/strategy/generic/CombatStrategy.cpp @@ -81,4 +81,11 @@ void AvoidAoeStrategy::InitTriggers(std::vector& triggers) void AvoidAoeStrategy::InitMultipliers(std::vector& multipliers) { // multipliers.push_back(new AvoidAoeStrategyMultiplier(botAI)); -} \ No newline at end of file +} + +NextAction** CombatFormationStrategy::getDefaultActions() +{ + return NextAction::array(0, + new NextAction("combat formation move", ACTION_EMERGENCY), + nullptr); +} diff --git a/src/strategy/generic/CombatStrategy.h b/src/strategy/generic/CombatStrategy.h index 33813615..7f618064 100644 --- a/src/strategy/generic/CombatStrategy.h +++ b/src/strategy/generic/CombatStrategy.h @@ -28,4 +28,12 @@ public: void InitTriggers(std::vector& triggers) override; }; +class CombatFormationStrategy : public Strategy +{ +public: + CombatFormationStrategy(PlayerbotAI* ai): Strategy(ai) {} + const std::string getName() override { return "combat formation"; } + NextAction** getDefaultActions() override; +}; + #endif diff --git a/src/strategy/triggers/ChatTriggerContext.h b/src/strategy/triggers/ChatTriggerContext.h index af52cd7e..598154ef 100644 --- a/src/strategy/triggers/ChatTriggerContext.h +++ b/src/strategy/triggers/ChatTriggerContext.h @@ -119,6 +119,7 @@ class ChatTriggerContext : public NamedObjectContext creators["naxx"] = &ChatTriggerContext::naxx; creators["bwl"] = &ChatTriggerContext::bwl; creators["dps"] = &ChatTriggerContext::dps; + creators["disperse"] = &ChatTriggerContext::disperse; } private: @@ -218,6 +219,7 @@ class ChatTriggerContext : public NamedObjectContext static Trigger* naxx(PlayerbotAI* ai) { return new ChatCommandTrigger(ai, "naxx"); } static Trigger* bwl(PlayerbotAI* ai) { return new ChatCommandTrigger(ai, "bwl"); } static Trigger* dps(PlayerbotAI* ai) { return new ChatCommandTrigger(ai, "dps"); } + static Trigger* disperse(PlayerbotAI* ai) { return new ChatCommandTrigger(ai, "disperse"); } }; #endif diff --git a/src/strategy/values/ValueContext.h b/src/strategy/values/ValueContext.h index 4298b44e..fe08de8b 100644 --- a/src/strategy/values/ValueContext.h +++ b/src/strategy/values/ValueContext.h @@ -301,6 +301,7 @@ class ValueContext : public NamedObjectContext creators["expected 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; } private: @@ -505,6 +506,7 @@ class ValueContext : public NamedObjectContext static UntypedValue* expected_group_dps(PlayerbotAI* ai) { return new ExpectedGroupDpsValue(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); } }; #endif From 8ff016a67ec628ceb86554b0785aab35807b8cce Mon Sep 17 00:00:00 2001 From: Yunfan Li Date: Fri, 5 Jul 2024 18:41:20 +0800 Subject: [PATCH 05/13] [Class spell] Seed of corruption --- src/strategy/warlock/DpsWarlockStrategy.cpp | 5 ++++- src/strategy/warlock/WarlockActions.h | 20 +++++++++++++++++++ .../warlock/WarlockAiObjectContext.cpp | 2 ++ src/strategy/warlock/WarlockTriggers.h | 14 ++++++++++++- 4 files changed, 39 insertions(+), 2 deletions(-) diff --git a/src/strategy/warlock/DpsWarlockStrategy.cpp b/src/strategy/warlock/DpsWarlockStrategy.cpp index f5247c3f..3e79dfa8 100644 --- a/src/strategy/warlock/DpsWarlockStrategy.cpp +++ b/src/strategy/warlock/DpsWarlockStrategy.cpp @@ -87,7 +87,10 @@ void DpsWarlockStrategy::InitTriggers(std::vector& triggers) void DpsAoeWarlockStrategy::InitTriggers(std::vector& triggers) { - triggers.push_back(new TriggerNode("medium aoe", NextAction::array(0, new NextAction("rain of fire", 37.0f), nullptr))); + triggers.push_back(new TriggerNode("medium aoe", NextAction::array(0, + new NextAction("seed of corruption", 39.0f), + new NextAction("seed of corruption on attacker", 38.0f), + new NextAction("rain of fire", 37.0f), nullptr))); triggers.push_back(new TriggerNode("corruption on attacker", NextAction::array(0, new NextAction("corruption on attacker", 27.0f), nullptr))); triggers.push_back(new TriggerNode("unstable affliction on attacker", NextAction::array(0, new NextAction("unstable affliction on attacker", 26.0f), NULL))); triggers.push_back(new TriggerNode("curse of agony on attacker", NextAction::array(0, new NextAction("curse of agony on attacker", 25.0f), nullptr))); diff --git a/src/strategy/warlock/WarlockActions.h b/src/strategy/warlock/WarlockActions.h index 0ec8cfbb..813374a0 100644 --- a/src/strategy/warlock/WarlockActions.h +++ b/src/strategy/warlock/WarlockActions.h @@ -67,12 +67,20 @@ class CastCorruptionAction : public CastDebuffSpellAction { public: CastCorruptionAction(PlayerbotAI* botAI) : CastDebuffSpellAction(botAI, "corruption", true) { } + bool isUseful() override { + return CastDebuffSpellAction::isUseful() && !botAI->HasAura("seed of corruption", GetTarget(), false, true) ; + } + ActionThreatType getThreatType() override { return ActionThreatType::Aoe; } }; class CastCorruptionOnAttackerAction : public CastDebuffSpellOnAttackerAction { public: CastCorruptionOnAttackerAction(PlayerbotAI* botAI) : CastDebuffSpellOnAttackerAction(botAI, "corruption", true) { } + bool isUseful() override { + return CastDebuffSpellOnAttackerAction::isUseful() && !botAI->HasAura("seed of corruption", GetTarget(), false, true) ; + } + ActionThreatType getThreatType() override { return ActionThreatType::Aoe; } }; class CastCurseOfAgonyOnAttackerAction : public CastDebuffSpellOnAttackerAction @@ -142,6 +150,18 @@ class CastSeedOfCorruptionAction : public CastDebuffSpellAction { public: CastSeedOfCorruptionAction(PlayerbotAI* botAI) : CastDebuffSpellAction(botAI, "seed of corruption", true, 0) { } + bool isUseful() override { + return CastDebuffSpellAction::isUseful() && !botAI->HasAura("corruption", GetTarget(), false, true) ; + } +}; + +class CastSeedOfCorruptionOnAttackerAction : public CastDebuffSpellOnAttackerAction +{ + public: + CastSeedOfCorruptionOnAttackerAction(PlayerbotAI* botAI) : CastDebuffSpellOnAttackerAction(botAI, "seed of corruption", true, 0) { } + bool isUseful() override { + return CastDebuffSpellOnAttackerAction::isUseful() && !botAI->HasAura("corruption", GetTarget(), false, true) ; + } }; class CastRainOfFireAction : public CastSpellAction diff --git a/src/strategy/warlock/WarlockAiObjectContext.cpp b/src/strategy/warlock/WarlockAiObjectContext.cpp index 52e3a414..486c2cc2 100644 --- a/src/strategy/warlock/WarlockAiObjectContext.cpp +++ b/src/strategy/warlock/WarlockAiObjectContext.cpp @@ -158,6 +158,7 @@ class WarlockAiObjectContextInternal : public NamedObjectContext creators["banish"] = &WarlockAiObjectContextInternal::banish; creators["banish on cc"] = &WarlockAiObjectContextInternal::banish_on_cc; creators["seed of corruption"] = &WarlockAiObjectContextInternal::seed_of_corruption; + creators["seed of corruption on attacker"] = &WarlockAiObjectContextInternal::seed_of_corruption_on_attacker; creators["rain of fire"] = &WarlockAiObjectContextInternal::rain_of_fire; creators["shadowfury"] = &WarlockAiObjectContextInternal::shadowfury; creators["life tap"] = &WarlockAiObjectContextInternal::life_tap; @@ -209,6 +210,7 @@ class WarlockAiObjectContextInternal : public NamedObjectContext static Action* banish(PlayerbotAI* botAI) { return new CastBanishAction(botAI); } static Action* banish_on_cc(PlayerbotAI* botAI) { return new CastBanishAction(botAI); } static Action* seed_of_corruption(PlayerbotAI* botAI) { return new CastSeedOfCorruptionAction(botAI); } + static Action* seed_of_corruption_on_attacker(PlayerbotAI* botAI) { return new CastSeedOfCorruptionOnAttackerAction(botAI); } static Action* rain_of_fire(PlayerbotAI* botAI) { return new CastRainOfFireAction(botAI); } static Action* shadowfury(PlayerbotAI* botAI) { return new CastShadowfuryAction(botAI); } static Action* life_tap(PlayerbotAI* botAI) { return new CastLifeTapAction(botAI); } diff --git a/src/strategy/warlock/WarlockTriggers.h b/src/strategy/warlock/WarlockTriggers.h index 8578e43a..42c773f7 100644 --- a/src/strategy/warlock/WarlockTriggers.h +++ b/src/strategy/warlock/WarlockTriggers.h @@ -6,6 +6,7 @@ #define _PLAYERBOT_WARLOCKTRIGGERS_H #include "GenericTriggers.h" +#include "PlayerbotAI.h" class PlayerbotAI; @@ -32,13 +33,24 @@ class CurseOfAgonyTrigger : public DebuffTrigger CurseOfAgonyTrigger(PlayerbotAI* botAI) : DebuffTrigger(botAI, "curse of agony", 1, true, 20.0f) { } }; -DEBUFF_CHECKISOWNER_TRIGGER(CorruptionTrigger, "corruption"); +class CorruptionTrigger : public DebuffTrigger +{ + public: + CorruptionTrigger(PlayerbotAI* botAI) : DebuffTrigger(botAI, "corruption", 1, true) { } \ + bool IsActive() override { + return DebuffTrigger::IsActive() && !botAI->HasAura("seed of corruption", GetTarget(), false, true) ; + } +}; + DEBUFF_CHECKISOWNER_TRIGGER(SiphonLifeTrigger, "siphon life"); class CorruptionOnAttackerTrigger : public DebuffOnAttackerTrigger { public: CorruptionOnAttackerTrigger(PlayerbotAI* botAI) : DebuffOnAttackerTrigger(botAI, "corruption", true) { } + bool IsActive() override { + return DebuffOnAttackerTrigger::IsActive() && !botAI->HasAura("seed of corruption", GetTarget(), false, true) ; + } }; class CastCurseOfAgonyOnAttackerTrigger : public DebuffOnAttackerTrigger From 21ce849da7d900e5967faf710b39596e2e7e00fb Mon Sep 17 00:00:00 2001 From: Yunfan Li Date: Sun, 7 Jul 2024 13:32:26 +0800 Subject: [PATCH 06/13] [Command] Autogear command ammo --- src/strategy/actions/TrainerAction.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/strategy/actions/TrainerAction.cpp b/src/strategy/actions/TrainerAction.cpp index 2454e563..af1e7a7c 100644 --- a/src/strategy/actions/TrainerAction.cpp +++ b/src/strategy/actions/TrainerAction.cpp @@ -198,6 +198,7 @@ bool AutoGearAction::Execute(Event event) sPlayerbotAIConfig->autoGearQualityLimit, gs); factory.InitEquipment(true); + factory.InitAmmo(); if (bot->getLevel() >= sPlayerbotAIConfig->minEnchantingBotLevel) { factory.ApplyEnchantAndGemsNew(); } From 32d30eaf6bcf6a6414a7b9c134da0524478ce08c Mon Sep 17 00:00:00 2001 From: Yunfan Li Date: Sun, 7 Jul 2024 13:32:43 +0800 Subject: [PATCH 07/13] [Command] Disperse command tips --- src/strategy/actions/MovementActions.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/strategy/actions/MovementActions.cpp b/src/strategy/actions/MovementActions.cpp index 0a47c8ea..d9ab66fc 100644 --- a/src/strategy/actions/MovementActions.cpp +++ b/src/strategy/actions/MovementActions.cpp @@ -1911,10 +1911,10 @@ bool DisperseSetAction::Execute(Event event) return true; } std::ostringstream out; - out << "Usage: disperse [enable|disable|increase|decrease|set {distance}]" << "\n"; + out << "Usage: disperse [enable | disable | increase | decrease | set {distance}]"; float dis = AI_VALUE(float, "disperse distance"); if (dis > 0.0f) { - out << "Current disperse distance: " << std::setprecision(2) << dis; + out << "(Current disperse distance: " << std::setprecision(2) << dis << ")"; } botAI->TellMasterNoFacing(out.str()); return true; From 7325ba7dcb844227feccab19246117b08be1b01e Mon Sep 17 00:00:00 2001 From: Yunfan Li Date: Sun, 7 Jul 2024 21:51:19 +0800 Subject: [PATCH 08/13] [Class spell] Aoe threat check --- src/strategy/actions/UseMeetingStoneAction.cpp | 2 +- src/strategy/deathknight/DKActions.h | 2 -- src/strategy/mage/MageActions.h | 2 ++ src/strategy/priest/PriestActions.h | 1 + src/strategy/shaman/ShamanActions.h | 1 + src/strategy/values/PartyMemberToHeal.cpp | 2 +- src/strategy/warlock/WarlockActions.h | 4 ++-- 7 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/strategy/actions/UseMeetingStoneAction.cpp b/src/strategy/actions/UseMeetingStoneAction.cpp index 3ef46e21..b0a68990 100644 --- a/src/strategy/actions/UseMeetingStoneAction.cpp +++ b/src/strategy/actions/UseMeetingStoneAction.cpp @@ -82,7 +82,7 @@ bool SummonAction::Execute(Event event) } if (master->GetSession()->GetSecurity() >= SEC_PLAYER) { - botAI->GetAiObjectContext()->GetValue("prioritized targets")->Set({}); + // botAI->GetAiObjectContext()->GetValue("prioritized targets")->Set({}); return Teleport(master, bot); } diff --git a/src/strategy/deathknight/DKActions.h b/src/strategy/deathknight/DKActions.h index 6106af18..e4907ac8 100644 --- a/src/strategy/deathknight/DKActions.h +++ b/src/strategy/deathknight/DKActions.h @@ -234,8 +234,6 @@ class CastDeathAndDecayAction : public CastSpellAction { public: CastDeathAndDecayAction(PlayerbotAI* botAI) : CastSpellAction(botAI, "death and decay") { } - - ActionThreatType getThreatType() override { return ActionThreatType::Aoe; } }; class CastHornOfWinterAction : public CastSpellAction diff --git a/src/strategy/mage/MageActions.h b/src/strategy/mage/MageActions.h index a2b83bf8..1303498a 100644 --- a/src/strategy/mage/MageActions.h +++ b/src/strategy/mage/MageActions.h @@ -203,12 +203,14 @@ class CastDragonsBreathAction : public CastSpellAction { public: CastDragonsBreathAction(PlayerbotAI* botAI) : CastSpellAction(botAI, "dragon's breath") { } + ActionThreatType getThreatType() override { return ActionThreatType::Aoe; } }; class CastBlastWaveAction : public CastSpellAction { public: CastBlastWaveAction(PlayerbotAI* botAI) : CastSpellAction(botAI, "blast wave") { } + ActionThreatType getThreatType() override { return ActionThreatType::Aoe; } }; class CastInvisibilityAction : public CastBuffSpellAction diff --git a/src/strategy/priest/PriestActions.h b/src/strategy/priest/PriestActions.h index 9aab4eb3..5e4737b9 100644 --- a/src/strategy/priest/PriestActions.h +++ b/src/strategy/priest/PriestActions.h @@ -161,5 +161,6 @@ class CastMindSearAction : public CastSpellAction { public: CastMindSearAction(PlayerbotAI* ai) : CastSpellAction(ai, "mind sear") {} + ActionThreatType getThreatType() override { return ActionThreatType::Aoe; } }; #endif diff --git a/src/strategy/shaman/ShamanActions.h b/src/strategy/shaman/ShamanActions.h index 24226b7a..c9467e49 100644 --- a/src/strategy/shaman/ShamanActions.h +++ b/src/strategy/shaman/ShamanActions.h @@ -323,6 +323,7 @@ class CastChainLightningAction : public CastSpellAction { public: CastChainLightningAction(PlayerbotAI* botAI) : CastSpellAction(botAI, "chain lightning") { } + ActionThreatType getThreatType() override { return ActionThreatType::Aoe; } }; class CastLightningBoltAction : public CastSpellAction diff --git a/src/strategy/values/PartyMemberToHeal.cpp b/src/strategy/values/PartyMemberToHeal.cpp index 0f76b6e7..a7cba48e 100644 --- a/src/strategy/values/PartyMemberToHeal.cpp +++ b/src/strategy/values/PartyMemberToHeal.cpp @@ -74,7 +74,7 @@ bool PartyMemberToHeal::Check(Unit* player) { // return player && player != bot && player->GetMapId() == bot->GetMapId() && player->IsInWorld() && // sServerFacade->GetDistance2d(bot, player) < (player->IsPlayer() && botAI->IsTank((Player*)player) ? 50.0f : 40.0f); - return player->GetMapId() == bot->GetMapId() && + return player->GetMapId() == bot->GetMapId() && !player->IsCharmed() && bot->GetDistance2d(player) < sPlayerbotAIConfig->healDistance * 2 && bot->IsWithinLOS(player->GetPositionX(), player->GetPositionY(), player->GetPositionZ()); } diff --git a/src/strategy/warlock/WarlockActions.h b/src/strategy/warlock/WarlockActions.h index 813374a0..28f689d3 100644 --- a/src/strategy/warlock/WarlockActions.h +++ b/src/strategy/warlock/WarlockActions.h @@ -70,7 +70,6 @@ class CastCorruptionAction : public CastDebuffSpellAction bool isUseful() override { return CastDebuffSpellAction::isUseful() && !botAI->HasAura("seed of corruption", GetTarget(), false, true) ; } - ActionThreatType getThreatType() override { return ActionThreatType::Aoe; } }; class CastCorruptionOnAttackerAction : public CastDebuffSpellOnAttackerAction @@ -80,7 +79,6 @@ class CastCorruptionOnAttackerAction : public CastDebuffSpellOnAttackerAction bool isUseful() override { return CastDebuffSpellOnAttackerAction::isUseful() && !botAI->HasAura("seed of corruption", GetTarget(), false, true) ; } - ActionThreatType getThreatType() override { return ActionThreatType::Aoe; } }; class CastCurseOfAgonyOnAttackerAction : public CastDebuffSpellOnAttackerAction @@ -153,6 +151,7 @@ class CastSeedOfCorruptionAction : public CastDebuffSpellAction bool isUseful() override { return CastDebuffSpellAction::isUseful() && !botAI->HasAura("corruption", GetTarget(), false, true) ; } + ActionThreatType getThreatType() override { return ActionThreatType::Aoe; } }; class CastSeedOfCorruptionOnAttackerAction : public CastDebuffSpellOnAttackerAction @@ -162,6 +161,7 @@ class CastSeedOfCorruptionOnAttackerAction : public CastDebuffSpellOnAttackerAct bool isUseful() override { return CastDebuffSpellOnAttackerAction::isUseful() && !botAI->HasAura("corruption", GetTarget(), false, true) ; } + ActionThreatType getThreatType() override { return ActionThreatType::Aoe; } }; class CastRainOfFireAction : public CastSpellAction From b55c9b14d1280d34d6b6cad40fd1ce564ede2da2 Mon Sep 17 00:00:00 2001 From: Yunfan Li Date: Tue, 9 Jul 2024 00:08:20 +0800 Subject: [PATCH 09/13] [Combat formation] Last flee angle check --- src/strategy/Value.h | 17 +++++++++- src/strategy/actions/MovementActions.cpp | 42 ++++++++++++++++++++---- src/strategy/actions/MovementActions.h | 1 + src/strategy/values/ValueContext.h | 4 +++ 4 files changed, 57 insertions(+), 7 deletions(-) diff --git a/src/strategy/Value.h b/src/strategy/Value.h index e27600ad..1a7e05ef 100644 --- a/src/strategy/Value.h +++ b/src/strategy/Value.h @@ -329,7 +329,22 @@ class UnitManualSetValue : public ManualSetValue class DisperseDistanceValue : public ManualSetValue { public: - DisperseDistanceValue(PlayerbotAI* botAI, float defaultValue = -1.0f, std::string const name = "disperse value") : + DisperseDistanceValue(PlayerbotAI* botAI, float defaultValue = -1.0f, std::string const name = "disperse distance") : ManualSetValue(botAI, defaultValue, name) { } }; + +class LastFleeAngleValue : public ManualSetValue +{ + public: + LastFleeAngleValue(PlayerbotAI* botAI, float defaultValue = 0.0f, std::string const name = "last flee angle") : + ManualSetValue(botAI, defaultValue, name) { } +}; + +class LastFleeTimestampValue : public ManualSetValue +{ + public: + LastFleeTimestampValue(PlayerbotAI* botAI, uint32 defaultValue = 0, std::string const name = "last flee timestamp") : + ManualSetValue(botAI, defaultValue, name) { } +}; + #endif diff --git a/src/strategy/actions/MovementActions.cpp b/src/strategy/actions/MovementActions.cpp index d9ab66fc..06bc7d74 100644 --- a/src/strategy/actions/MovementActions.cpp +++ b/src/strategy/actions/MovementActions.cpp @@ -4,6 +4,7 @@ #include "MovementActions.h" #include "GameObject.h" +#include "Geometry.h" #include "Map.h" #include "MotionMaster.h" #include "MoveSplineInitArgs.h" @@ -31,6 +32,8 @@ #include "Unit.h" #include "Vehicle.h" #include "WaypointMovementGenerator.h" +#include +#include #include #include @@ -1511,17 +1514,14 @@ bool AvoidAoeAction::Execute(Event event) { // Case #1: Aura with dynamic object (e.g. rain of fire) if (AvoidAuraWithDynamicObj()) { - lastMoveTimer = getMSTime(); return true; } // Case #2: Trap game object with spell (e.g. lava bomb) if (AvoidGameObjectWithDamage()) { - lastMoveTimer = getMSTime(); return true; } // Case #3: Trigger npc (e.g. Lesser shadow fissure) if (AvoidUnitWithDamageAura()) { - lastMoveTimer = getMSTime(); return true; } return false; @@ -1550,10 +1550,11 @@ bool AvoidAoeAction::AvoidAuraWithDynamicObj() return false; } std::ostringstream name; - name << spellInfo->SpellName[0]; // << "] (aura)"; + name << spellInfo->SpellName[sWorld->GetDefaultDbcLocale()]; // << "] (aura)"; if (FleePosition(dynOwner->GetPosition(), radius)) { if (sPlayerbotAIConfig->tellWhenAvoidAoe && lastTellTimer < time(NULL) - 10) { lastTellTimer = time(NULL); + lastMoveTimer = getMSTime(); std::ostringstream out; out << "I'm avoiding " << name.str() << "..."; bot->Say(out.str(), LANG_UNIVERSAL); @@ -1607,10 +1608,11 @@ bool AvoidAoeAction::AvoidGameObjectWithDamage() continue; } std::ostringstream name; - name << spellInfo->SpellName[0]; // << "] (object)"; + name << spellInfo->SpellName[sWorld->GetDefaultDbcLocale()]; // << "] (object)"; if (FleePosition(go->GetPosition(), radius)) { if (sPlayerbotAIConfig->tellWhenAvoidAoe && lastTellTimer < time(NULL) - 10) { lastTellTimer = time(NULL); + lastMoveTimer = getMSTime(); std::ostringstream out; out << "I'm avoiding " << name.str() << "..."; bot->Say(out.str(), LANG_UNIVERSAL); @@ -1655,10 +1657,11 @@ bool AvoidAoeAction::AvoidUnitWithDamageAura() break; } std::ostringstream name; - name << triggerSpellInfo->SpellName[0]; //<< "] (unit)"; + name << triggerSpellInfo->SpellName[sWorld->GetDefaultDbcLocale()]; //<< "] (unit)"; if (FleePosition(unit->GetPosition(), radius)) { if (sPlayerbotAIConfig->tellWhenAvoidAoe && lastTellTimer < time(NULL) - 10) { lastTellTimer = time(NULL); + lastMoveTimer = getMSTime(); std::ostringstream out; out << "I'm avoiding " << name.str() << "..."; bot->Say(out.str(), LANG_UNIVERSAL); @@ -1697,6 +1700,11 @@ Position MovementAction::BestPositionForMeleeToFlee(Position pos, float radius) Position bestPos; for (CheckAngle &checkAngle : possibleAngles) { float angle = checkAngle.angle; + float lastFleeAngle = AI_VALUE(float, "last flee angle"); + uint32 lastFleeTimestamp = AI_VALUE(uint32, "last flee timestamp"); + if (!CheckLastFlee(angle, lastFleeAngle, lastFleeTimestamp)) { + continue; + } bool strict = checkAngle.strict; float fleeDis = std::min(radius + 1.0f, sPlayerbotAIConfig->fleeDistance); Position fleePos{bot->GetPositionX() + cos(angle) * fleeDis, @@ -1740,6 +1748,11 @@ Position MovementAction::BestPositionForRangedToFlee(Position pos, float radius) Position bestPos; for (CheckAngle &checkAngle : possibleAngles) { float angle = checkAngle.angle; + float lastFleeAngle = AI_VALUE(float, "last flee angle"); + uint32 lastFleeTimestamp = AI_VALUE(uint32, "last flee timestamp"); + if (!CheckLastFlee(angle, lastFleeAngle, lastFleeTimestamp)) { + continue; + } bool strict = checkAngle.strict; float fleeDis = std::min(radius + 1.0f, sPlayerbotAIConfig->fleeDistance); Position fleePos{bot->GetPositionX() + cos(angle) * fleeDis, @@ -1774,12 +1787,29 @@ bool MovementAction::FleePosition(Position pos, float radius) } if (bestPos != Position()) { if (MoveTo(bot->GetMapId(), bestPos.GetPositionX(), bestPos.GetPositionY(), bestPos.GetPositionZ(), false, false, true)) { + SET_AI_VALUE(float, "last flee angle", bot->GetAngle(&bestPos)); + SET_AI_VALUE(uint32, "last flee timestamp", getMSTime()); return true; } } return false; } +bool MovementAction::CheckLastFlee(float curAngle, float lastAngle, uint32 lastTS) +{ + // more than 5 sec + if (lastTS + 5000 < getMSTime()) { + return true; + } + float revAngle = fmod(lastAngle + M_PI, 2 * M_PI); + curAngle = fmod(curAngle, 2 * M_PI); + // angle too close + if (fabs(revAngle - curAngle) < M_PI / 8) { + return false; + } + return true; +} + bool CombatFormationMoveAction::isUseful() { if (getMSTime() - moveInterval < lastMoveTimer) { diff --git a/src/strategy/actions/MovementActions.h b/src/strategy/actions/MovementActions.h index ed9527af..0db8e7cd 100644 --- a/src/strategy/actions/MovementActions.h +++ b/src/strategy/actions/MovementActions.h @@ -44,6 +44,7 @@ class MovementAction : public Action Position BestPositionForMeleeToFlee(Position pos, float radius); Position BestPositionForRangedToFlee(Position pos, float radius); bool FleePosition(Position pos, float radius); + bool CheckLastFlee(float curAngle, float lastAngle, uint32 lastTS); protected: struct CheckAngle { float angle; diff --git a/src/strategy/values/ValueContext.h b/src/strategy/values/ValueContext.h index fe08de8b..e202ff8d 100644 --- a/src/strategy/values/ValueContext.h +++ b/src/strategy/values/ValueContext.h @@ -302,6 +302,8 @@ class ValueContext : public NamedObjectContext creators["area debuff"] = &ValueContext::area_debuff; creators["nearest trap with damage"] = &ValueContext::nearest_trap_with_damange; creators["disperse distance"] = &ValueContext::disperse_distance; + creators["last flee angle"] = &ValueContext::last_flee_angle; + creators["last flee timestamp"] = &ValueContext::last_flee_timestamp; } private: @@ -507,6 +509,8 @@ class ValueContext : public NamedObjectContext 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); } + static UntypedValue* last_flee_angle(PlayerbotAI* ai) { return new LastFleeAngleValue(ai); } + static UntypedValue* last_flee_timestamp(PlayerbotAI* ai) { return new LastFleeTimestampValue(ai); } }; #endif From fe64d9ce005a9857a6b97ff59b31215c874bb94c Mon Sep 17 00:00:00 2001 From: Yunfan Li Date: Tue, 9 Jul 2024 16:39:50 +0800 Subject: [PATCH 10/13] [Combat formation] Avoid flee repeatly --- src/Playerbots.h | 2 + src/strategy/Value.h | 40 ++++++++++++++- src/strategy/actions/MovementActions.cpp | 50 ++++++++++++------- src/strategy/actions/MovementActions.h | 3 +- .../actions/UseMeetingStoneAction.cpp | 1 + src/strategy/values/ValueContext.h | 2 + 6 files changed, 79 insertions(+), 19 deletions(-) diff --git a/src/Playerbots.h b/src/Playerbots.h index 66ec850c..7e678af3 100644 --- a/src/Playerbots.h +++ b/src/Playerbots.h @@ -34,6 +34,8 @@ int strcmpi(char const* s1, char const* s2); #define AI_VALUE_LAZY(type, name) context->GetValue(name)->LazyGet() #define AI_VALUE2_LAZY(type, name, param) context->GetValue(name, param)->LazyGet() +#define AI_VALUE_REF(type, name) context->GetValue(name)->RefGet() + #define SET_AI_VALUE(type, name, value) context->GetValue(name)->Set(value) #define SET_AI_VALUE2(type, name, param, value) context->GetValue(name, param)->Set(value) #define RESET_AI_VALUE(type, name) context->GetValue(name)->Reset() diff --git a/src/strategy/Value.h b/src/strategy/Value.h index 1a7e05ef..4d4196a3 100644 --- a/src/strategy/Value.h +++ b/src/strategy/Value.h @@ -16,6 +16,16 @@ class PlayerbotAI; class Unit; +class FleeInfo +{ + public: + Position fromPos; + float radius; + float angle; + uint32 timestamp; + int GetAngleRangeIndex() { return (angle + 2 * M_PI) / (M_PI / 2); } // [0, 7) +}; + struct CreatureData; class UntypedValue : public AiNamedObject @@ -37,6 +47,7 @@ class Value virtual ~Value() { } virtual T Get() = 0; virtual T LazyGet() = 0; + virtual T& RefGet() = 0; virtual void Reset() { } virtual void Set(T value) = 0; operator T() { return Get(); } @@ -79,7 +90,26 @@ class CalculatedValue : public UntypedValue, public Value return value; } - + T& RefGet() override + { + if (checkInterval < 2) { + // PerformanceMonitorOperation* pmo = sPerformanceMonitor->start(PERF_MON_VALUE, this->getName(), this->context ? &this->context->performanceStack : nullptr); + value = Calculate(); + // if (pmo) + // pmo->finish(); + } else { + time_t now = getMSTime(); + if (!lastCheckTime || now - lastCheckTime >= checkInterval) + { + lastCheckTime = now; + // PerformanceMonitorOperation* pmo = sPerformanceMonitor->start(PERF_MON_VALUE, this->getName(), this->context ? &this->context->performanceStack : nullptr); + value = Calculate(); + // if (pmo) + // pmo->finish(); + } + } + return value; + } void Set(T val) override { value = val; } void Update() override {} void Reset() override { lastCheckTime = 0; } @@ -304,6 +334,7 @@ class ManualSetValue : public UntypedValue, public Value T Get() override { return value; } T LazyGet() override { return value; } + T& RefGet() override { return value; } void Set(T val) override { value = val; } void Update() override {} void Reset() override @@ -347,4 +378,11 @@ class LastFleeTimestampValue : public ManualSetValue ManualSetValue(botAI, defaultValue, name) { } }; +class RecentlyFleeInfo : public ManualSetValue> +{ + public: + RecentlyFleeInfo(PlayerbotAI* botAI, std::list defaultValue = {}, std::string const name = "recently flee info") : + ManualSetValue>(botAI, defaultValue, name) { } +}; + #endif diff --git a/src/strategy/actions/MovementActions.cpp b/src/strategy/actions/MovementActions.cpp index 06bc7d74..b1a1fbe3 100644 --- a/src/strategy/actions/MovementActions.cpp +++ b/src/strategy/actions/MovementActions.cpp @@ -1700,9 +1700,8 @@ Position MovementAction::BestPositionForMeleeToFlee(Position pos, float radius) Position bestPos; for (CheckAngle &checkAngle : possibleAngles) { float angle = checkAngle.angle; - float lastFleeAngle = AI_VALUE(float, "last flee angle"); - uint32 lastFleeTimestamp = AI_VALUE(uint32, "last flee timestamp"); - if (!CheckLastFlee(angle, lastFleeAngle, lastFleeTimestamp)) { + auto& infoList = AI_VALUE_REF(std::list, "recently flee info"); + if (!CheckLastFlee(angle, infoList)) { continue; } bool strict = checkAngle.strict; @@ -1748,9 +1747,8 @@ Position MovementAction::BestPositionForRangedToFlee(Position pos, float radius) Position bestPos; for (CheckAngle &checkAngle : possibleAngles) { float angle = checkAngle.angle; - float lastFleeAngle = AI_VALUE(float, "last flee angle"); - uint32 lastFleeTimestamp = AI_VALUE(uint32, "last flee timestamp"); - if (!CheckLastFlee(angle, lastFleeAngle, lastFleeTimestamp)) { + auto& infoList = AI_VALUE_REF(std::list, "recently flee info"); + if (!CheckLastFlee(angle, infoList)) { continue; } bool strict = checkAngle.strict; @@ -1787,25 +1785,43 @@ bool MovementAction::FleePosition(Position pos, float radius) } if (bestPos != Position()) { if (MoveTo(bot->GetMapId(), bestPos.GetPositionX(), bestPos.GetPositionY(), bestPos.GetPositionZ(), false, false, true)) { - SET_AI_VALUE(float, "last flee angle", bot->GetAngle(&bestPos)); - SET_AI_VALUE(uint32, "last flee timestamp", getMSTime()); + auto& infoList = AI_VALUE_REF(std::list, "recently flee info"); + uint32 curTS = getMSTime(); + while (!infoList.empty()) { + if (infoList.size() > 10 || infoList.front().timestamp + 5000 < curTS) { + infoList.pop_front(); + } else { + break; + } + } + infoList.push_back({pos, radius, bot->GetAngle(&bestPos), curTS}); return true; } } return false; } -bool MovementAction::CheckLastFlee(float curAngle, float lastAngle, uint32 lastTS) +bool MovementAction::CheckLastFlee(float curAngle, std::list& infoList) { - // more than 5 sec - if (lastTS + 5000 < getMSTime()) { - return true; - } - float revAngle = fmod(lastAngle + M_PI, 2 * M_PI); + uint32 curTS = getMSTime(); curAngle = fmod(curAngle, 2 * M_PI); - // angle too close - if (fabs(revAngle - curAngle) < M_PI / 8) { - return false; + while (!infoList.empty()) { + if (infoList.size() > 10 || infoList.front().timestamp + 5000 < curTS) { + infoList.pop_front(); + } else { + break; + } + } + for (FleeInfo& info : infoList) { + // more than 5 sec + if (info.timestamp + 5000 < curTS) { + continue; + } + float revAngle = fmod(info.angle + M_PI, 2 * M_PI); + // angle too close + if (fabs(revAngle - curAngle) < M_PI / 8) { + return false; + } } return true; } diff --git a/src/strategy/actions/MovementActions.h b/src/strategy/actions/MovementActions.h index 0db8e7cd..c00ee5c8 100644 --- a/src/strategy/actions/MovementActions.h +++ b/src/strategy/actions/MovementActions.h @@ -13,6 +13,7 @@ class Player; class PlayerbotAI; class Unit; class WorldObject; +class Position; class MovementAction : public Action { @@ -44,7 +45,7 @@ class MovementAction : public Action Position BestPositionForMeleeToFlee(Position pos, float radius); Position BestPositionForRangedToFlee(Position pos, float radius); bool FleePosition(Position pos, float radius); - bool CheckLastFlee(float curAngle, float lastAngle, uint32 lastTS); + bool CheckLastFlee(float curAngle, std::list& infoList); protected: struct CheckAngle { float angle; diff --git a/src/strategy/actions/UseMeetingStoneAction.cpp b/src/strategy/actions/UseMeetingStoneAction.cpp index b0a68990..0d4be53e 100644 --- a/src/strategy/actions/UseMeetingStoneAction.cpp +++ b/src/strategy/actions/UseMeetingStoneAction.cpp @@ -83,6 +83,7 @@ bool SummonAction::Execute(Event event) if (master->GetSession()->GetSecurity() >= SEC_PLAYER) { // botAI->GetAiObjectContext()->GetValue("prioritized targets")->Set({}); + SET_AI_VALUE(std::list, "recently flee info", {}); return Teleport(master, bot); } diff --git a/src/strategy/values/ValueContext.h b/src/strategy/values/ValueContext.h index e202ff8d..2888b246 100644 --- a/src/strategy/values/ValueContext.h +++ b/src/strategy/values/ValueContext.h @@ -304,6 +304,7 @@ class ValueContext : public NamedObjectContext creators["disperse distance"] = &ValueContext::disperse_distance; creators["last flee angle"] = &ValueContext::last_flee_angle; creators["last flee timestamp"] = &ValueContext::last_flee_timestamp; + creators["recently flee info"] = &ValueContext::recently_flee_info; } private: @@ -511,6 +512,7 @@ class ValueContext : public NamedObjectContext static UntypedValue* disperse_distance(PlayerbotAI* ai) { return new DisperseDistanceValue(ai); } static UntypedValue* last_flee_angle(PlayerbotAI* ai) { return new LastFleeAngleValue(ai); } static UntypedValue* last_flee_timestamp(PlayerbotAI* ai) { return new LastFleeTimestampValue(ai); } + static UntypedValue* recently_flee_info(PlayerbotAI* ai) { return new RecentlyFleeInfo(ai); } }; #endif From ff7b58082d6c2fec86c5b46223f6e81e5842f63f Mon Sep 17 00:00:00 2001 From: Yunfan Li Date: Tue, 9 Jul 2024 17:29:54 +0800 Subject: [PATCH 11/13] [Combat formation] Flee optimize --- src/strategy/actions/MovementActions.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/strategy/actions/MovementActions.cpp b/src/strategy/actions/MovementActions.cpp index b1a1fbe3..f86d539e 100644 --- a/src/strategy/actions/MovementActions.cpp +++ b/src/strategy/actions/MovementActions.cpp @@ -1710,7 +1710,8 @@ Position MovementAction::BestPositionForMeleeToFlee(Position pos, float radius) bot->GetPositionY() + sin(angle) * fleeDis, bot->GetPositionZ()}; if (strict && currentTarget - && fleePos.GetExactDist(currentTarget) - currentTarget->GetCombatReach() > sPlayerbotAIConfig->tooCloseDistance) { + && fleePos.GetExactDist(currentTarget) - currentTarget->GetCombatReach() > sPlayerbotAIConfig->tooCloseDistance + && bot->IsWithinMeleeRange(currentTarget)) { continue; } if (pos.GetExactDist(fleePos) > farestDis) { @@ -1819,7 +1820,7 @@ bool MovementAction::CheckLastFlee(float curAngle, std::list& infoList } float revAngle = fmod(info.angle + M_PI, 2 * M_PI); // angle too close - if (fabs(revAngle - curAngle) < M_PI / 8) { + if (fabs(revAngle - curAngle) < M_PI / 4) { return false; } } From bd9434373815d36931e35340cbe3107fb3e5f8ad Mon Sep 17 00:00:00 2001 From: Yunfan Li Date: Tue, 9 Jul 2024 17:32:13 +0800 Subject: [PATCH 12/13] [Configuration] Make some configs deprecated --- conf/playerbots.conf.dist | 135 ++++++++++++++++++++------------------ src/PlayerbotAI.cpp | 22 +++---- src/PlayerbotAIConfig.cpp | 4 +- 3 files changed, 83 insertions(+), 78 deletions(-) diff --git a/conf/playerbots.conf.dist b/conf/playerbots.conf.dist index eb2fb3f1..3a3c2736 100644 --- a/conf/playerbots.conf.dist +++ b/conf/playerbots.conf.dist @@ -21,18 +21,9 @@ AiPlayerbot.BotAutologin = 0 # Default: 0 (disabled) AiPlayerbot.AllowPlayerBots = 0 -# Guild Task system -AiPlayerbot.EnableGuildTasks = 0 - # Enable LFG for random bots AiPlayerbot.RandomBotJoinLfg = 1 -# Enable dungeon suggestions for random bots -AiPlayerbot.RandomBotSuggestDungeons = 1 - -# Enable dungeon suggestions in lower case randomly -AiPlayerbot.SuggestDungeonsInLowerCaseRandomly = 0 - # Enable BG/Arena for random Bots AiPlayerbot.RandomBotJoinBG = 1 @@ -60,12 +51,6 @@ AiPlayerbot.RotationPoolSize = 500 AiPlayerbot.RandomBotAccountPrefix = "rndbot" AiPlayerbot.RandomBotAccountCount = 200 -# Random bot guild count -AiPlayerbot.RandomBotGuildCount = 20 - -# Delete all random bot guilds -AiPlayerbot.DeleteRandomBotGuilds = 0 - # Random bot arena team count AiPlayerbot.RandomBotArenaTeamCount = 20 @@ -78,18 +63,6 @@ AiPlayerbot.RandomGearLoweringChance = 0 # Chance random bot has max level on first randomize (default 0.15) AiPlayerbot.RandomBotMaxLevelChance = 0.15 -# Chance bot chooses RPG (Teleport to random camp for their level) instead of grinding -AiPlayerbot.RandomBotRpgChance = 0.20 #unused now - -# Set randombots movement speed to walking anywhere -AiPlayerbot.RandombotsWalkingRPG = 0 - -# Set randombots movement speed to walking only inside buildings -AiPlayerbot.RandombotsWalkingRPG.InDoors = 0 - -# Bots greet to the players -AiPlayerbot.EnableGreet = 0 - # Bots will be summoned to player when accept group invitation AiPlayerbot.SummonWhenGroup = 1 @@ -116,10 +89,6 @@ AiPlayerbot.KillXPRate = 1 # Need to reset rndbot after changing the setting (.playerbot rndbot reset) AiPlayerbot.DisableDeathKnightLogin = 0 -# Specify percent of active bots -# The default is 10. With 10% of all bots going active or inactive each minute. -AiPlayerbot.BotActiveAlone = 100 - # Set minimum level of randombots where gets enchants on items (Maxlevel + 1 to disable) # Default: 60 AiPlayerbot.MinEnchantingBotLevel = 60 @@ -296,7 +265,7 @@ AiPlayerbot.ReactDelay = 100 AiPlayerbot.PassiveDelay = 10000 # Minimum delay between repeating actions (chat messages, emotes etc) -AiPlayerbot.RepeatDelay = 5000 +AiPlayerbot.RepeatDelay = 2000 # Delay timers AiPlayerbot.ErrorDelay = 100 @@ -352,10 +321,6 @@ AiPlayerbot.AutoAvoidAoe = 1 # Default: 1 (enable) AiPlayerbot.TellWhenAvoidAoe = 1 -# Premade spell to avoid (undetected spells) -# spellid-radius, ... -AiPlayerbot.PremadeAvoidAoe = 62234-4 - # Random bot default strategies (applied after defaults) AiPlayerbot.RandomBotCombatStrategies = "+dps,+dps assist,-threat" # AiPlayerbot.RandomBotNonCombatStrategies = "+grind,+loot,+rpg,+custom::say" @@ -363,27 +328,6 @@ AiPlayerbot.RandomBotNonCombatStrategies = "" AiPlayerbot.CombatStrategies = "" AiPlayerbot.NonCombatStrategies = "" -# How often tasks are changed -AiPlayerbot.MinGuildTaskChangeTime = 172800 -AiPlayerbot.MaxGuildTaskChangeTime = 432000 - -# Mail spam interval -AiPlayerbot.MinGuildTaskAdvertisementTime = 300 -AiPlayerbot.MaxGuildTaskAdvertisementTime = 28800 - -# Delay before reward is sent -AiPlayerbot.MinGuildTaskRewardTime = 300 -AiPlayerbot.MaxGuildTaskRewardTime = 3600 - -# Cleanup of guild tasks interval -AiPlayerbot.GuildTaskAdvertCleanupTime = 300 - -# Specify max distance between victim and bot when creating guild kill task -AiPlayerbot.GuildTaskKillTaskDistance = 200 - -# Distance margin for facade calculations -AiPlayerbot.TargetPosRecalcDistance = 0.1 - # Maps where bots can be teleported to AiPlayerbot.RandomBotMaps = 0,1,530,571 @@ -417,12 +361,8 @@ AiPlayerbot.RandomBotCountChangeMaxInterval = 7200 AiPlayerbot.MinRandomBotInWorldTime = 3600 AiPlayerbot.MaxRandomBotInWorldTime = 43200 AiPlayerbot.MinRandomBotRandomizeTime = 302400 -AiPlayerbot.MaxRandomRandomizeTime = 1209600 +AiPlayerbot.MaxRandomBotRandomizeTime = 1209600 AiPlayerbot.RandomBotsPerInterval = 500 -AiPlayerbot.MinRandomBotsPriceChangeInterval = 7200 -AiPlayerbot.MaxRandomBotsPriceChangeInterval = 172800 -AiPlayerbot.MinRandomBotChangeStrategyTime = 180 -AiPlayerbot.MaxRandomBotChangeStrategyTime = 720 AiPlayerbot.MinRandomBotReviveTime = 60 AiPlayerbot.MaxRandomBotReviveTime = 300 AiPlayerbot.MinRandomBotTeleportInterval = 3600 @@ -444,9 +384,6 @@ AiPlayerbot.CommandServerPort = 8888 # Enables/Disables performance monitor AiPlayerbot.PerfMonEnabled = 0 -# Allow bots to be summoned near innkeepers -AiPlayerbot.SummonAtInnkeepersEnabled = 1 - # Custom config to allow logfiles to be created. # Example: AiPlayerbot.AllowedLogFiles = travelNodes.csv,travelPaths.csv,TravelNodeStore.h,bot_movement.csv,bot_location.csv AiPlayerbot.AllowedLogFiles = "" @@ -748,6 +685,74 @@ AiPlayerbot.RandomClassSpecIndex.11.1 = 1 AiPlayerbot.RandomClassSpecProb.11.2 = 40 AiPlayerbot.RandomClassSpecIndex.11.2 = 2 +############################################## +# Deprecated (temporary) # +############################################## +# Guild Task system +AiPlayerbot.EnableGuildTasks = 0 + +# Enable dungeon suggestions for random bots +AiPlayerbot.RandomBotSuggestDungeons = 1 + +# Enable dungeon suggestions in lower case randomly +AiPlayerbot.SuggestDungeonsInLowerCaseRandomly = 0 + +# Random bot guild count +AiPlayerbot.RandomBotGuildCount = 20 + +# Delete all random bot guilds +AiPlayerbot.DeleteRandomBotGuilds = 0 + +# Chance bot chooses RPG (Teleport to random camp for their level) instead of grinding +AiPlayerbot.RandomBotRpgChance = 0.20 #unused now + +# Set randombots movement speed to walking anywhere +AiPlayerbot.RandombotsWalkingRPG = 0 + +# Set randombots movement speed to walking only inside buildings +AiPlayerbot.RandombotsWalkingRPG.InDoors = 0 + +# Bots greet to the players +AiPlayerbot.EnableGreet = 0 + +# Specify percent of active bots +# The default is 10. With 10% of all bots going active or inactive each minute. +AiPlayerbot.BotActiveAlone = 100 + +# Premade spell to avoid (undetected spells) +# spellid-radius, ... +AiPlayerbot.PremadeAvoidAoe = 62234-4 + +AiPlayerbot.MinRandomBotsPriceChangeInterval = 7200 +AiPlayerbot.MaxRandomBotsPriceChangeInterval = 172800 +AiPlayerbot.MinRandomBotChangeStrategyTime = 180 +AiPlayerbot.MaxRandomBotChangeStrategyTime = 720 + + +# How often tasks are changed +AiPlayerbot.MinGuildTaskChangeTime = 172800 +AiPlayerbot.MaxGuildTaskChangeTime = 432000 + +# Mail spam interval +AiPlayerbot.MinGuildTaskAdvertisementTime = 300 +AiPlayerbot.MaxGuildTaskAdvertisementTime = 28800 + +# Delay before reward is sent +AiPlayerbot.MinGuildTaskRewardTime = 300 +AiPlayerbot.MaxGuildTaskRewardTime = 3600 + +# Cleanup of guild tasks interval +AiPlayerbot.GuildTaskAdvertCleanupTime = 300 + +# Specify max distance between victim and bot when creating guild kill task +AiPlayerbot.GuildTaskKillTaskDistance = 200 + +# Distance margin for facade calculations +AiPlayerbot.TargetPosRecalcDistance = 0.1 + +# Allow bots to be summoned near innkeepers +AiPlayerbot.SummonAtInnkeepersEnabled = 1 + ################################################################################## # # # Logging Stuff # diff --git a/src/PlayerbotAI.cpp b/src/PlayerbotAI.cpp index 25d92a1d..42ea3b7d 100644 --- a/src/PlayerbotAI.cpp +++ b/src/PlayerbotAI.cpp @@ -1900,19 +1900,19 @@ bool PlayerbotAI::TellMasterNoFacing(std::string const text, PlayerbotSecurityLe return false; time_t lastSaid = whispers[text]; - // Yunfan: Remove tell cooldown - // if (!lastSaid || (time(nullptr) - lastSaid) >= sPlayerbotAIConfig->repeatDelay / 1000) - // { - whispers[text] = time(nullptr); + + if (!lastSaid || (time(nullptr) - lastSaid) >= sPlayerbotAIConfig->repeatDelay / 1000) + { + whispers[text] = time(nullptr); - ChatMsg type = CHAT_MSG_WHISPER; - if (currentChat.second - time(nullptr) >= 1) - type = currentChat.first; + ChatMsg type = CHAT_MSG_WHISPER; + if (currentChat.second - time(nullptr) >= 1) + type = currentChat.first; - WorldPacket data; - ChatHandler::BuildChatPacket(data, type == CHAT_MSG_ADDON ? CHAT_MSG_PARTY : type, type == CHAT_MSG_ADDON ? LANG_ADDON : LANG_UNIVERSAL, bot, nullptr, text.c_str()); - master->SendDirectMessage(&data); - // } + WorldPacket data; + ChatHandler::BuildChatPacket(data, type == CHAT_MSG_ADDON ? CHAT_MSG_PARTY : type, type == CHAT_MSG_ADDON ? LANG_ADDON : LANG_UNIVERSAL, bot, nullptr, text.c_str()); + master->SendDirectMessage(&data); + } return true; } diff --git a/src/PlayerbotAIConfig.cpp b/src/PlayerbotAIConfig.cpp index d468533c..ed840e9c 100644 --- a/src/PlayerbotAIConfig.cpp +++ b/src/PlayerbotAIConfig.cpp @@ -58,7 +58,7 @@ bool PlayerbotAIConfig::Initialize() dispelAuraDuration = sConfigMgr->GetOption("AiPlayerbot.DispelAuraDuration", 7000); reactDelay = sConfigMgr->GetOption("AiPlayerbot.ReactDelay", 500); passiveDelay = sConfigMgr->GetOption("AiPlayerbot.PassiveDelay", 10000); - repeatDelay = sConfigMgr->GetOption("AiPlayerbot.RepeatDelay", 5000); + repeatDelay = sConfigMgr->GetOption("AiPlayerbot.RepeatDelay", 2000); errorDelay = sConfigMgr->GetOption("AiPlayerbot.ErrorDelay", 5000); rpgDelay = sConfigMgr->GetOption("AiPlayerbot.RpgDelay", 10000); sitDelay = sConfigMgr->GetOption("AiPlayerbot.SitDelay", 30000); @@ -123,7 +123,7 @@ bool PlayerbotAIConfig::Initialize() minRandomBotInWorldTime = sConfigMgr->GetOption("AiPlayerbot.MinRandomBotInWorldTime", 2 * HOUR); maxRandomBotInWorldTime = sConfigMgr->GetOption("AiPlayerbot.MaxRandomBotInWorldTime", 12 * HOUR); minRandomBotRandomizeTime = sConfigMgr->GetOption("AiPlayerbot.MinRandomBotRandomizeTime", 2 * HOUR); - maxRandomBotRandomizeTime = sConfigMgr->GetOption("AiPlayerbot.MaxRandomRandomizeTime", 14 * 24 * HOUR); + maxRandomBotRandomizeTime = sConfigMgr->GetOption("AiPlayerbot.MaxRandomBotRandomizeTime", 14 * 24 * HOUR); minRandomBotChangeStrategyTime = sConfigMgr->GetOption("AiPlayerbot.MinRandomBotChangeStrategyTime", 30 * MINUTE); maxRandomBotChangeStrategyTime = sConfigMgr->GetOption("AiPlayerbot.MaxRandomBotChangeStrategyTime", 2 * HOUR); minRandomBotReviveTime = sConfigMgr->GetOption("AiPlayerbot.MinRandomBotReviveTime", MINUTE); From b1f5c1313c296c970ba669fa15b821980e9999d7 Mon Sep 17 00:00:00 2001 From: Yunfan Li Date: Tue, 9 Jul 2024 17:33:09 +0800 Subject: [PATCH 13/13] [Strategy] Enable cat strategy, remove threat by default, reset strategy on talents change --- src/AiFactory.cpp | 46 ++++++++++++-------- src/strategy/actions/ChangeTalentsAction.cpp | 2 + src/strategy/druid/BearTankDruidStrategy.cpp | 2 +- src/strategy/druid/CasterDruidStrategy.cpp | 10 +++++ src/strategy/druid/CatDpsDruidStrategy.cpp | 2 +- 5 files changed, 41 insertions(+), 21 deletions(-) diff --git a/src/AiFactory.cpp b/src/AiFactory.cpp index 09401a82..d6baa1eb 100644 --- a/src/AiFactory.cpp +++ b/src/AiFactory.cpp @@ -279,7 +279,7 @@ void AiFactory::AddDefaultCombatStrategies(Player* player, PlayerbotAI* const fa { case CLASS_PRIEST: if (tab == 2) { - engine->addStrategies("dps", "shadow debuff", "shadow aoe", "threat", nullptr); + engine->addStrategies("dps", "shadow debuff", "shadow aoe", nullptr); } else if (tab == PRIEST_TAB_DISIPLINE) { engine->addStrategies("heal", nullptr); } else { @@ -290,11 +290,11 @@ void AiFactory::AddDefaultCombatStrategies(Player* player, PlayerbotAI* const fa break; case CLASS_MAGE: if (tab == 0) - engine->addStrategies("arcane", "arcane aoe", "threat", nullptr); + engine->addStrategies("arcane", "arcane aoe", nullptr); else if (tab == 1) - engine->addStrategies("fire", "fire aoe", "threat", nullptr); + engine->addStrategies("fire", "fire aoe", nullptr); else - engine->addStrategies("frost", "frost aoe", "threat", nullptr); + engine->addStrategies("frost", "frost aoe", nullptr); engine->addStrategies("dps", "dps assist", "cure", nullptr); break; @@ -302,17 +302,17 @@ void AiFactory::AddDefaultCombatStrategies(Player* player, PlayerbotAI* const fa if (tab == 2) engine->addStrategies("tank", "tank assist", "aoe", "mark rti", nullptr); else if (player->getLevel() < 36 || tab == 0) - engine->addStrategies("arms", "aoe", "dps assist", "threat", /*"behind",*/ nullptr); + engine->addStrategies("arms", "aoe", "dps assist",/*"behind",*/ nullptr); else - engine->addStrategies("fury", "aoe", "dps assist", "threat", /*"behind",*/ nullptr); + engine->addStrategies("fury", "aoe", "dps assist",/*"behind",*/ nullptr); break; case CLASS_SHAMAN: if (tab == 0) - engine->addStrategies("caster", "caster aoe", "bmana", "threat", nullptr); + engine->addStrategies("caster", "caster aoe", "bmana",nullptr); else if (tab == 2) engine->addStrategies("heal", "bmana", nullptr); else - engine->addStrategies("melee", "melee aoe", "bdps", "threat", nullptr); + engine->addStrategies("melee", "melee aoe", "bdps", nullptr); engine->addStrategies("dps assist", "cure", "totems", nullptr); break; @@ -328,38 +328,41 @@ void AiFactory::AddDefaultCombatStrategies(Player* player, PlayerbotAI* const fa case CLASS_DRUID: if (tab == 0) { - engine->addStrategies("caster", "cure", "caster aoe", "threat", "dps assist", nullptr); + engine->addStrategies("caster", "cure", "caster aoe", "dps assist", nullptr); engine->addStrategy("caster debuff"); } else if (tab == 2) engine->addStrategies("heal", "cure", "dps assist", nullptr); else { - engine->removeStrategy("flee"); - engine->addStrategies("bear", "tank assist", nullptr); + if (player->GetLevel() >= 20 && !player->HasAura(16931)/*thick hide*/) { + engine->addStrategies("cat", "dps assist", nullptr); + } else { + engine->addStrategies("bear", "tank assist", nullptr); + } } break; case CLASS_HUNTER: - engine->addStrategies("dps", "aoe", "bdps", "threat", "dps assist", nullptr); + engine->addStrategies("dps", "aoe", "bdps", "dps assist", nullptr); engine->addStrategy("dps debuff"); break; case CLASS_ROGUE: if (tab == ROGUE_TAB_ASSASSINATION) { - engine->addStrategies("melee", "threat", "dps assist", "aoe", /*"behind",*/ nullptr); + engine->addStrategies("melee", "dps assist", "aoe", /*"behind",*/ nullptr); } else { - engine->addStrategies("dps", "threat", "dps assist", "aoe", /*"behind",*/ nullptr); + engine->addStrategies("dps", "dps assist", "aoe", /*"behind",*/ nullptr); } break; case CLASS_WARLOCK: - engine->addStrategies("dps assist", "dps", "dps debuff", "aoe", "threat", nullptr); + engine->addStrategies("dps assist", "dps", "dps debuff", "aoe", nullptr); break; case CLASS_DEATH_KNIGHT: if (tab == 0) engine->addStrategies("blood", "tank assist", nullptr); else if (tab == 1) - engine->addStrategies("frost", "frost aoe", "dps assist", "threat", nullptr); + engine->addStrategies("frost", "frost aoe", "dps assist", nullptr); else - engine->addStrategies("unholy", "unholy aoe", "dps assist", "threat", nullptr); + engine->addStrategies("unholy", "unholy aoe", "dps assist", nullptr); break; } @@ -505,8 +508,13 @@ void AiFactory::AddDefaultNonCombatStrategies(Player* player, PlayerbotAI* const nonCombatEngine->addStrategies("dps assist", "cure", nullptr); break; case CLASS_DRUID: - if (tab == 1) - nonCombatEngine->addStrategy("tank assist"); + if (tab == 1) { + if (player->GetLevel() >= 20 && !player->HasAura(16931)/*thick hide*/) { + nonCombatEngine->addStrategy("dps assist"); + } else { + nonCombatEngine->addStrategy("tank assist"); + } + } else nonCombatEngine->addStrategies("dps assist", "cure", nullptr); break; diff --git a/src/strategy/actions/ChangeTalentsAction.cpp b/src/strategy/actions/ChangeTalentsAction.cpp index 69a6b19f..75de8e22 100644 --- a/src/strategy/actions/ChangeTalentsAction.cpp +++ b/src/strategy/actions/ChangeTalentsAction.cpp @@ -44,9 +44,11 @@ bool ChangeTalentsAction::Execute(Event event) } else if (param.find("spec ") != std::string::npos) { param = param.substr(5); out << SpecPick(param); + botAI->ResetStrategies(); } else if (param.find("apply ") != std::string::npos) { param = param.substr(6); out << SpecApply(param); + botAI->ResetStrategies(); } else { out << "Unknown command."; } diff --git a/src/strategy/druid/BearTankDruidStrategy.cpp b/src/strategy/druid/BearTankDruidStrategy.cpp index 2ddee32d..ab74c3f2 100644 --- a/src/strategy/druid/BearTankDruidStrategy.cpp +++ b/src/strategy/druid/BearTankDruidStrategy.cpp @@ -69,7 +69,7 @@ class BearTankDruidStrategyActionNodeFactory : public NamedObjectFactory