From 3d6d454337686e3a1a977858c14115f4824c3d16 Mon Sep 17 00:00:00 2001 From: kadeshar Date: Thu, 20 Mar 2025 10:53:16 +0100 Subject: [PATCH] Stay strategy improvement (#1072) * - Stay Strategy work in combat and with RTSC * - Fixed summon with stay strategy * - Added new stay strategy support for chat commands --- src/PlayerbotAI.cpp | 55 ++++++++++++------- src/PlayerbotAI.h | 2 + src/strategy/actions/ActionContext.h | 2 + src/strategy/actions/ChatShortcutActions.cpp | 43 ++++++++++++--- src/strategy/actions/ChatShortcutActions.h | 26 +++++---- src/strategy/actions/PositionAction.cpp | 22 ++++++++ src/strategy/actions/PositionAction.h | 7 +++ src/strategy/actions/ReachTargetActions.cpp | 12 ++++ src/strategy/actions/SeeSpellAction.cpp | 10 ++++ src/strategy/actions/StayActions.cpp | 12 ++++ .../actions/UseMeetingStoneAction.cpp | 11 ++++ src/strategy/generic/StayStrategy.cpp | 7 +++ src/strategy/generic/StayStrategy.h | 5 +- src/strategy/triggers/GenericTriggers.cpp | 26 ++++++++- src/strategy/triggers/GenericTriggers.h | 8 +++ src/strategy/triggers/TriggerContext.h | 2 + src/strategy/values/PositionValue.cpp | 22 ++++++++ src/strategy/values/PositionValue.h | 14 +++++ src/strategy/values/ValueContext.h | 2 + 19 files changed, 244 insertions(+), 44 deletions(-) diff --git a/src/PlayerbotAI.cpp b/src/PlayerbotAI.cpp index d6be9874..8c154b10 100644 --- a/src/PlayerbotAI.cpp +++ b/src/PlayerbotAI.cpp @@ -1105,9 +1105,9 @@ void PlayerbotAI::HandleBotOutgoingPacket(WorldPacket const& packet) } } - QueueChatResponse( - ChatQueuedReply{msgtype, guid1.GetCounter(), guid2.GetCounter(), message, chanName, - name, time(nullptr) + urand(inCombat ? 10 : 5, inCombat ? 25 : 15)}); + QueueChatResponse(ChatQueuedReply{msgtype, guid1.GetCounter(), guid2.GetCounter(), message, + chanName, name, + time(nullptr) + urand(inCombat ? 10 : 5, inCombat ? 25 : 15)}); GetAiObjectContext()->GetValue("last said", "chat")->Set(time(0) + urand(5, 25)); return; } @@ -1244,10 +1244,10 @@ void PlayerbotAI::ChangeEngine(BotState type) switch (type) { case BOT_STATE_COMBAT: - // LOG_DEBUG("playerbots", "=== {} COMBAT ===", bot->GetName().c_str()); + ChangeEngineOnCombat(); break; case BOT_STATE_NON_COMBAT: - // LOG_DEBUG("playerbots", "=== {} NON-COMBAT ===", bot->GetName().c_str()); + ChangeEngineOnNonCombat(); break; case BOT_STATE_DEAD: // LOG_DEBUG("playerbots", "=== {} DEAD ===", bot->GetName().c_str()); @@ -1258,6 +1258,23 @@ void PlayerbotAI::ChangeEngine(BotState type) } } +void PlayerbotAI::ChangeEngineOnCombat() +{ + if (HasStrategy("stay", BOT_STATE_COMBAT)) + { + aiObjectContext->GetValue("pos", "stay") + ->Set(PositionInfo(bot->GetPositionX(), bot->GetPositionY(), bot->GetPositionZ(), bot->GetMapId())); + } +} + +void PlayerbotAI::ChangeEngineOnNonCombat() +{ + if (HasStrategy("stay", BOT_STATE_NON_COMBAT)) + { + aiObjectContext->GetValue("pos", "stay")->Reset(); + } +} + void PlayerbotAI::DoNextAction(bool min) { if (!bot->IsInWorld() || bot->IsBeingTeleported() || (GetMaster() && GetMaster()->IsBeingTeleported())) @@ -2848,7 +2865,6 @@ bool PlayerbotAI::CanCastSpell(uint32 spellid, Unit* target, bool checkHasSpell, if (!target) target = bot; - if (Pet* pet = bot->GetPet()) if (pet->HasSpell(spellid)) return true; @@ -3041,8 +3057,7 @@ bool PlayerbotAI::CanCastSpell(uint32 spellid, GameObject* goTarget, bool checkH return false; } -bool PlayerbotAI::CanCastSpell(uint32 spellid, float x, float y, float z, bool checkHasSpell, - Item* itemTarget) +bool PlayerbotAI::CanCastSpell(uint32 spellid, float x, float y, float z, bool checkHasSpell, Item* itemTarget) { if (!spellid) return false; @@ -3070,7 +3085,7 @@ bool PlayerbotAI::CanCastSpell(uint32 spellid, float x, float y, float z, bool c Spell* spell = new Spell(bot, spellInfo, TRIGGERED_NONE); spell->m_targets.SetDst(x, y, z, 0.f); - + Item* item = itemTarget ? itemTarget : aiObjectContext->GetValue("item for spell", spellid)->Get(); spell->m_targets.SetItemTarget(item); @@ -4296,8 +4311,8 @@ bool PlayerbotAI::AllowActive(ActivityType activityType) mod = AutoScaleActivity(mod); } - uint32 ActivityNumber = GetFixedBotNumer(100, - sPlayerbotAIConfig->botActiveAlone * static_cast(mod) / 100 * 0.01f); + uint32 ActivityNumber = + GetFixedBotNumer(100, sPlayerbotAIConfig->botActiveAlone * static_cast(mod) / 100 * 0.01f); return ActivityNumber <= (sPlayerbotAIConfig->botActiveAlone * mod) / @@ -4356,7 +4371,8 @@ void PlayerbotAI::RemoveShapeshift() RemoveAura("moonkin form"); RemoveAura("travel form"); RemoveAura("cat form"); - RemoveAura("flight form"); bot->RemoveAura(33943); // The latter added for now as RemoveAura("flight form") currently does not work. + RemoveAura("flight form"); + bot->RemoveAura(33943); // The latter added for now as RemoveAura("flight form") currently does not work. RemoveAura("swift flight form"); RemoveAura("aquatic form"); RemoveAura("ghost wolf"); @@ -4943,11 +4959,9 @@ Item* PlayerbotAI::FindAmmo() const } // Search inventory for the correct ammo type - return FindItemInInventory([requiredAmmoType](ItemTemplate const* pItemProto) -> bool - { - return pItemProto->Class == ITEM_CLASS_PROJECTILE && - pItemProto->SubClass == requiredAmmoType; - }); + return FindItemInInventory( + [requiredAmmoType](ItemTemplate const* pItemProto) -> bool + { return pItemProto->Class == ITEM_CLASS_PROJECTILE && pItemProto->SubClass == requiredAmmoType; }); } return nullptr; // No ranged weapon equipped @@ -6178,9 +6192,10 @@ bool PlayerbotAI::IsHealingSpell(uint32 spellFamilyName, flag96 spellFalimyFlags return spellFalimyFlags & healingFlags; } - -SpellFamilyNames PlayerbotAI::Class2SpellFamilyName(uint8 cls) { - switch (cls) { +SpellFamilyNames PlayerbotAI::Class2SpellFamilyName(uint8 cls) +{ + switch (cls) + { case CLASS_WARRIOR: return SPELLFAMILY_WARRIOR; case CLASS_PALADIN: diff --git a/src/PlayerbotAI.h b/src/PlayerbotAI.h index 09a7f6fd..4e7c5a5c 100644 --- a/src/PlayerbotAI.h +++ b/src/PlayerbotAI.h @@ -392,6 +392,8 @@ public: void HandleMasterOutgoingPacket(WorldPacket const& packet); void HandleTeleportAck(); void ChangeEngine(BotState type); + void ChangeEngineOnCombat(); + void ChangeEngineOnNonCombat(); void DoNextAction(bool minimal = false); virtual bool DoSpecificAction(std::string const name, Event event = Event(), bool silent = false, std::string const qualifier = ""); diff --git a/src/strategy/actions/ActionContext.h b/src/strategy/actions/ActionContext.h index c69063d9..a56e711a 100644 --- a/src/strategy/actions/ActionContext.h +++ b/src/strategy/actions/ActionContext.h @@ -135,6 +135,7 @@ public: creators["move to loot"] = &ActionContext::move_to_loot; creators["open loot"] = &ActionContext::open_loot; creators["guard"] = &ActionContext::guard; + creators["return to stay position"] = &ActionContext::return_to_stay_position; creators["move out of enemy contact"] = &ActionContext::move_out_of_enemy_contact; creators["set facing"] = &ActionContext::set_facing; creators["set behind"] = &ActionContext::set_behind; @@ -271,6 +272,7 @@ private: static Action* drop_target(PlayerbotAI* botAI) { return new DropTargetAction(botAI); } static Action* attack_duel_opponent(PlayerbotAI* botAI) { return new AttackDuelOpponentAction(botAI); } static Action* guard(PlayerbotAI* botAI) { return new GuardAction(botAI); } + static Action* return_to_stay_position(PlayerbotAI* botAI) { return new ReturnToStayPositionAction(botAI); } static Action* open_loot(PlayerbotAI* botAI) { return new OpenLootAction(botAI); } static Action* move_to_loot(PlayerbotAI* botAI) { return new MoveToLootAction(botAI); } static Action* _return(PlayerbotAI* botAI) { return new ReturnAction(botAI); } diff --git a/src/strategy/actions/ChatShortcutActions.cpp b/src/strategy/actions/ChatShortcutActions.cpp index 30d4c2f8..f7e02163 100644 --- a/src/strategy/actions/ChatShortcutActions.cpp +++ b/src/strategy/actions/ChatShortcutActions.cpp @@ -10,7 +10,7 @@ #include "Playerbots.h" #include "PositionValue.h" -void ReturnPositionResetAction::ResetReturnPosition() +void PositionsResetAction::ResetReturnPosition() { PositionMap& posMap = context->GetValue("position")->Get(); PositionInfo pos = posMap["return"]; @@ -18,7 +18,7 @@ void ReturnPositionResetAction::ResetReturnPosition() posMap["return"] = pos; } -void ReturnPositionResetAction::SetReturnPosition(float x, float y, float z) +void PositionsResetAction::SetReturnPosition(float x, float y, float z) { PositionMap& posMap = context->GetValue("position")->Get(); PositionInfo pos = posMap["return"]; @@ -26,6 +26,22 @@ void ReturnPositionResetAction::SetReturnPosition(float x, float y, float z) posMap["return"] = pos; } +void PositionsResetAction::ResetStayPosition() +{ + PositionMap& posMap = context->GetValue("position")->Get(); + PositionInfo pos = posMap["stay"]; + pos.Reset(); + posMap["stay"] = pos; +} + +void PositionsResetAction::SetStayPosition(float x, float y, float z) +{ + PositionMap& posMap = context->GetValue("position")->Get(); + PositionInfo pos = posMap["stay"]; + pos.Set(x, y, z, botAI->GetBot()->GetMapId()); + posMap["stay"] = pos; +} + bool FollowChatShortcutAction::Execute(Event event) { Player* master = GetMaster(); @@ -34,7 +50,7 @@ bool FollowChatShortcutAction::Execute(Event event) // botAI->Reset(); botAI->ChangeStrategy("+follow,-passive,-grind,-move from group", BOT_STATE_NON_COMBAT); - botAI->ChangeStrategy("-follow,-passive,-grind,-move from group", BOT_STATE_COMBAT); + botAI->ChangeStrategy("-stay,-follow,-passive,-grind,-move from group", BOT_STATE_COMBAT); botAI->GetAiObjectContext()->GetValue("prioritized targets")->Reset(); PositionMap& posMap = context->GetValue("position")->Get(); @@ -42,6 +58,10 @@ bool FollowChatShortcutAction::Execute(Event event) pos.Reset(); posMap["return"] = pos; + pos = posMap["stay"]; + pos.Reset(); + posMap["stay"] = pos; + if (bot->IsInCombat()) { Formation* formation = AI_VALUE(Formation*, "formation"); @@ -103,9 +123,10 @@ bool StayChatShortcutAction::Execute(Event event) botAI->Reset(); botAI->ChangeStrategy("+stay,-passive,-move from group", BOT_STATE_NON_COMBAT); - botAI->ChangeStrategy("-follow,-passive,-move from group", BOT_STATE_COMBAT); + botAI->ChangeStrategy("+stay,-follow,-passive,-move from group", BOT_STATE_COMBAT); SetReturnPosition(bot->GetPositionX(), bot->GetPositionY(), bot->GetPositionZ()); + SetStayPosition(bot->GetPositionX(), bot->GetPositionY(), bot->GetPositionZ()); botAI->TellMaster("Staying"); return true; @@ -133,10 +154,11 @@ bool FleeChatShortcutAction::Execute(Event event) return false; botAI->Reset(); - botAI->ChangeStrategy("+follow,+passive", BOT_STATE_NON_COMBAT); - botAI->ChangeStrategy("+follow,+passive", BOT_STATE_COMBAT); + botAI->ChangeStrategy("+follow,-stay,+passive", BOT_STATE_NON_COMBAT); + botAI->ChangeStrategy("+follow,-stay,+passive", BOT_STATE_COMBAT); ResetReturnPosition(); + ResetStayPosition(); if (bot->GetMapId() != master->GetMapId() || bot->GetDistance(master) > sPlayerbotAIConfig->sightDistance) { @@ -155,10 +177,11 @@ bool GoawayChatShortcutAction::Execute(Event event) return false; botAI->Reset(); - botAI->ChangeStrategy("+runaway", BOT_STATE_NON_COMBAT); - botAI->ChangeStrategy("+runaway", BOT_STATE_COMBAT); + botAI->ChangeStrategy("+runaway,-stay", BOT_STATE_NON_COMBAT); + botAI->ChangeStrategy("+runaway,-stay", BOT_STATE_COMBAT); ResetReturnPosition(); + ResetStayPosition(); botAI->TellMaster("Running away"); return true; @@ -171,9 +194,10 @@ bool GrindChatShortcutAction::Execute(Event event) return false; botAI->Reset(); - botAI->ChangeStrategy("+grind,-passive", BOT_STATE_NON_COMBAT); + botAI->ChangeStrategy("+grind,-passive,-stay", BOT_STATE_NON_COMBAT); ResetReturnPosition(); + ResetStayPosition(); botAI->TellMaster("Grinding"); return true; @@ -193,6 +217,7 @@ bool TankAttackChatShortcutAction::Execute(Event event) botAI->ChangeStrategy("-passive", BOT_STATE_COMBAT); ResetReturnPosition(); + ResetStayPosition(); botAI->TellMaster("Attacking"); return true; diff --git a/src/strategy/actions/ChatShortcutActions.h b/src/strategy/actions/ChatShortcutActions.h index 3ee02407..90895431 100644 --- a/src/strategy/actions/ChatShortcutActions.h +++ b/src/strategy/actions/ChatShortcutActions.h @@ -10,13 +10,15 @@ class PlayerbotAI; -class ReturnPositionResetAction : public Action +class PositionsResetAction : public Action { public: - ReturnPositionResetAction(PlayerbotAI* botAI, std::string const name) : Action(botAI, name) {} + PositionsResetAction(PlayerbotAI* botAI, std::string const name) : Action(botAI, name) {} void ResetReturnPosition(); void SetReturnPosition(float x, float y, float z); + void ResetStayPosition(); + void SetStayPosition(float x, float y, float z); }; class FollowChatShortcutAction : public MovementAction @@ -27,10 +29,10 @@ public: bool Execute(Event event) override; }; -class StayChatShortcutAction : public ReturnPositionResetAction +class StayChatShortcutAction : public PositionsResetAction { public: - StayChatShortcutAction(PlayerbotAI* botAI) : ReturnPositionResetAction(botAI, "stay chat shortcut") {} + StayChatShortcutAction(PlayerbotAI* botAI) : PositionsResetAction(botAI, "stay chat shortcut") {} bool Execute(Event event) override; }; @@ -43,34 +45,34 @@ public: bool Execute(Event event) override; }; -class FleeChatShortcutAction : public ReturnPositionResetAction +class FleeChatShortcutAction : public PositionsResetAction { public: - FleeChatShortcutAction(PlayerbotAI* botAI) : ReturnPositionResetAction(botAI, "flee chat shortcut") {} + FleeChatShortcutAction(PlayerbotAI* botAI) : PositionsResetAction(botAI, "flee chat shortcut") {} bool Execute(Event event) override; }; -class GoawayChatShortcutAction : public ReturnPositionResetAction +class GoawayChatShortcutAction : public PositionsResetAction { public: - GoawayChatShortcutAction(PlayerbotAI* botAI) : ReturnPositionResetAction(botAI, "runaway chat shortcut") {} + GoawayChatShortcutAction(PlayerbotAI* botAI) : PositionsResetAction(botAI, "runaway chat shortcut") {} bool Execute(Event event) override; }; -class GrindChatShortcutAction : public ReturnPositionResetAction +class GrindChatShortcutAction : public PositionsResetAction { public: - GrindChatShortcutAction(PlayerbotAI* botAI) : ReturnPositionResetAction(botAI, "grind chat shortcut") {} + GrindChatShortcutAction(PlayerbotAI* botAI) : PositionsResetAction(botAI, "grind chat shortcut") {} bool Execute(Event event) override; }; -class TankAttackChatShortcutAction : public ReturnPositionResetAction +class TankAttackChatShortcutAction : public PositionsResetAction { public: - TankAttackChatShortcutAction(PlayerbotAI* botAI) : ReturnPositionResetAction(botAI, "tank attack chat shortcut") {} + TankAttackChatShortcutAction(PlayerbotAI* botAI) : PositionsResetAction(botAI, "tank attack chat shortcut") {} bool Execute(Event event) override; }; diff --git a/src/strategy/actions/PositionAction.cpp b/src/strategy/actions/PositionAction.cpp index df5fd500..3888f28f 100644 --- a/src/strategy/actions/PositionAction.cpp +++ b/src/strategy/actions/PositionAction.cpp @@ -159,3 +159,25 @@ bool ReturnAction::isUseful() PositionInfo pos = context->GetValue("position")->Get()[qualifier]; return pos.isSet() && AI_VALUE2(float, "distance", "position_random") > sPlayerbotAIConfig->followDistance; } + +bool ReturnToStayPositionAction::isPossible() +{ + PositionMap& posMap = AI_VALUE(PositionMap&, "position"); + PositionInfo stayPosition = posMap["stay"]; + if (stayPosition.isSet()) + { + const float distance = bot->GetDistance(stayPosition.x, stayPosition.y, stayPosition.z); + if (distance > sPlayerbotAIConfig->reactDistance) + { + botAI->TellMaster("The stay position is too far to return. I am going to stay where I am now"); + + // Set the stay position to current position + stayPosition.Set(bot->GetPositionX(), bot->GetPositionY(), bot->GetPositionZ(), bot->GetMapId()); + posMap["stay"] = stayPosition; + } + + return true; + } + + return false; +} diff --git a/src/strategy/actions/PositionAction.h b/src/strategy/actions/PositionAction.h index b53ba1b7..16a671de 100644 --- a/src/strategy/actions/PositionAction.h +++ b/src/strategy/actions/PositionAction.h @@ -40,6 +40,13 @@ public: GuardAction(PlayerbotAI* botAI) : MoveToPositionAction(botAI, "move to position", "guard") {} }; +class ReturnToStayPositionAction : public MoveToPositionAction +{ +public: + ReturnToStayPositionAction(PlayerbotAI* ai) : MoveToPositionAction(ai, "move to position", "stay") {} + virtual bool isPossible(); +}; + class SetReturnPositionAction : public Action { public: diff --git a/src/strategy/actions/ReachTargetActions.cpp b/src/strategy/actions/ReachTargetActions.cpp index 9184336c..f5e51011 100644 --- a/src/strategy/actions/ReachTargetActions.cpp +++ b/src/strategy/actions/ReachTargetActions.cpp @@ -14,6 +14,12 @@ bool ReachTargetAction::Execute(Event event) { return ReachCombatTo(AI_VALUE(Uni bool ReachTargetAction::isUseful() { + // do not move while staying + if (botAI->HasStrategy("stay", botAI->GetState())) + { + return false; + } + // do not move while casting if (bot->GetCurrentSpell(CURRENT_CHANNELED_SPELL) != nullptr) { @@ -30,6 +36,12 @@ std::string const ReachTargetAction::GetTargetName() { return "current target"; bool CastReachTargetSpellAction::isUseful() { + // do not move while staying + if (botAI->HasStrategy("stay", botAI->GetState())) + { + return false; + } + return sServerFacade->IsDistanceGreaterThan(AI_VALUE2(float, "distance", "current target"), (distance + sPlayerbotAIConfig->contactDistance)); } diff --git a/src/strategy/actions/SeeSpellAction.cpp b/src/strategy/actions/SeeSpellAction.cpp index dcce6952..0fd90bcc 100644 --- a/src/strategy/actions/SeeSpellAction.cpp +++ b/src/strategy/actions/SeeSpellAction.cpp @@ -11,6 +11,7 @@ #include "Playerbots.h" #include "RTSCValues.h" #include "RtscAction.h" +#include "PositionValue.h" Creature* SeeSpellAction::CreateWps(Player* wpOwner, float x, float y, float z, float o, uint32 entry, Creature* lastWp, bool important) @@ -123,6 +124,15 @@ bool SeeSpellAction::MoveToSpell(WorldPosition& spellPosition, bool inFormation) if (inFormation) SetFormationOffset(spellPosition); + if (botAI->HasStrategy("stay", botAI->GetState())) + { + PositionMap& posMap = AI_VALUE(PositionMap&, "position"); + PositionInfo stayPosition = posMap["stay"]; + + stayPosition.Set(spellPosition.getX(), spellPosition.getY(), spellPosition.getZ(), spellPosition.getMapId()); + posMap["stay"] = stayPosition; + } + if (bot->IsWithinLOS(spellPosition.getX(), spellPosition.getY(), spellPosition.getZ())) return MoveNear(spellPosition.getMapId(), spellPosition.getX(), spellPosition.getY(), spellPosition.getZ(), 0); diff --git a/src/strategy/actions/StayActions.cpp b/src/strategy/actions/StayActions.cpp index b041f4e2..cca32c1a 100644 --- a/src/strategy/actions/StayActions.cpp +++ b/src/strategy/actions/StayActions.cpp @@ -8,6 +8,7 @@ #include "Event.h" #include "LastMovementValue.h" #include "Playerbots.h" +#include "PositionValue.h" bool StayActionBase::Stay() { @@ -42,6 +43,17 @@ bool StayAction::Execute(Event event) { return Stay(); } bool StayAction::isUseful() { + // Check if the bots is in stay position + PositionInfo stayPosition = AI_VALUE(PositionMap&, "position")["stay"]; + if (stayPosition.isSet()) + { + const float distance = bot->GetDistance(stayPosition.x, stayPosition.y, stayPosition.z); + if (sPlayerbotAIConfig->followDistance) + { + return false; + } + } + // move from group takes priority over stay as it's added and removed automatically // (without removing/adding stay) if (botAI->HasStrategy("move from group", BOT_STATE_COMBAT) || diff --git a/src/strategy/actions/UseMeetingStoneAction.cpp b/src/strategy/actions/UseMeetingStoneAction.cpp index 071b5b44..82ab1f7b 100644 --- a/src/strategy/actions/UseMeetingStoneAction.cpp +++ b/src/strategy/actions/UseMeetingStoneAction.cpp @@ -11,6 +11,7 @@ #include "GridNotifiersImpl.h" #include "PlayerbotAIConfig.h" #include "Playerbots.h" +#include "PositionValue.h" bool UseMeetingStoneAction::Execute(Event event) { @@ -224,6 +225,16 @@ bool SummonAction::Teleport(Player* summoner, Player* player) player->GetMotionMaster()->Clear(); AI_VALUE(LastMovement&, "last movement").clear(); player->TeleportTo(mapId, x, y, z, 0); + + if (botAI->HasStrategy("stay", botAI->GetState())) + { + PositionMap& posMap = AI_VALUE(PositionMap&, "position"); + PositionInfo stayPosition = posMap["stay"]; + + stayPosition.Set(x,y, z, mapId); + posMap["stay"] = stayPosition; + } + return true; } } diff --git a/src/strategy/generic/StayStrategy.cpp b/src/strategy/generic/StayStrategy.cpp index 8809971a..b980275b 100644 --- a/src/strategy/generic/StayStrategy.cpp +++ b/src/strategy/generic/StayStrategy.cpp @@ -7,6 +7,13 @@ #include "Playerbots.h" +void StayStrategy::InitTriggers(std::vector& triggers) +{ + triggers.push_back(new TriggerNode( + "return to stay position", + NextAction::array(0, new NextAction("return to stay position", ACTION_MOVE), nullptr))); +} + NextAction** StayStrategy::getDefaultActions() { return NextAction::array(0, new NextAction("stay", 1.0f), nullptr); } void SitStrategy::InitTriggers(std::vector& triggers) diff --git a/src/strategy/generic/StayStrategy.h b/src/strategy/generic/StayStrategy.h index 0d9d6385..76674858 100644 --- a/src/strategy/generic/StayStrategy.h +++ b/src/strategy/generic/StayStrategy.h @@ -10,12 +10,13 @@ class PlayerbotAI; -class StayStrategy : public NonCombatStrategy +class StayStrategy : public Strategy { public: - StayStrategy(PlayerbotAI* botAI) : NonCombatStrategy(botAI) {} + StayStrategy(PlayerbotAI* botAI) : Strategy(botAI) {} std::string const getName() override { return "stay"; } + void InitTriggers(std::vector& triggers) override; NextAction** getDefaultActions() override; }; diff --git a/src/strategy/triggers/GenericTriggers.cpp b/src/strategy/triggers/GenericTriggers.cpp index bbda4c9c..4f0038f6 100644 --- a/src/strategy/triggers/GenericTriggers.cpp +++ b/src/strategy/triggers/GenericTriggers.cpp @@ -15,6 +15,7 @@ #include "ObjectGuid.h" #include "PlayerbotAIConfig.h" #include "Playerbots.h" +#include "PositionValue.h" #include "SharedDefines.h" #include "TemporarySummon.h" #include "ThreatMgr.h" @@ -507,11 +508,22 @@ bool IsBehindTargetTrigger::IsActive() bool IsNotBehindTargetTrigger::IsActive() { + if (botAI->HasStrategy("stay", botAI->GetState())) + { + return false; + } Unit* target = AI_VALUE(Unit*, "current target"); return target && !AI_VALUE2(bool, "behind", "current target"); } -bool IsNotFacingTargetTrigger::IsActive() { return !AI_VALUE2(bool, "facing", "current target"); } +bool IsNotFacingTargetTrigger::IsActive() +{ + if (botAI->HasStrategy("stay", botAI->GetState())) + { + return false; + } + return !AI_VALUE2(bool, "facing", "current target"); +} bool HasCcTargetTrigger::IsActive() { @@ -599,6 +611,18 @@ bool NewPlayerNearbyTrigger::IsActive() { return AI_VALUE(ObjectGuid, "new playe bool CollisionTrigger::IsActive() { return AI_VALUE2(bool, "collision", "self target"); } +bool ReturnToStayPositionTrigger::IsActive() +{ + PositionInfo stayPosition = AI_VALUE(PositionMap&, "position")["stay"]; + if (stayPosition.isSet()) + { + const float distance = bot->GetDistance(stayPosition.x, stayPosition.y, stayPosition.z); + return distance > sPlayerbotAIConfig->followDistance; + } + + return false; +} + bool GiveItemTrigger::IsActive() { return AI_VALUE2(Unit*, "party member without item", item) && AI_VALUE2(uint32, "item count", item); diff --git a/src/strategy/triggers/GenericTriggers.h b/src/strategy/triggers/GenericTriggers.h index dd4e4650..cee6b18a 100644 --- a/src/strategy/triggers/GenericTriggers.h +++ b/src/strategy/triggers/GenericTriggers.h @@ -827,6 +827,14 @@ public: SitTrigger(PlayerbotAI* botAI) : StayTimeTrigger(botAI, sPlayerbotAIConfig->sitDelay, "sit") {} }; +class ReturnToStayPositionTrigger : public Trigger +{ +public: + ReturnToStayPositionTrigger(PlayerbotAI* ai) : Trigger(ai, "return to stay position", 2) {} + + virtual bool IsActive() override; +}; + class ReturnTrigger : public StayTimeTrigger { public: diff --git a/src/strategy/triggers/TriggerContext.h b/src/strategy/triggers/TriggerContext.h index 17e236ec..1b2c86ff 100644 --- a/src/strategy/triggers/TriggerContext.h +++ b/src/strategy/triggers/TriggerContext.h @@ -30,6 +30,7 @@ public: { creators["return"] = &TriggerContext::_return; creators["sit"] = &TriggerContext::sit; + creators["return to stay position"] = &TriggerContext::return_to_stay_position; creators["collision"] = &TriggerContext::collision; creators["timer"] = &TriggerContext::Timer; @@ -228,6 +229,7 @@ private: static Trigger* give_water(PlayerbotAI* botAI) { return new GiveWaterTrigger(botAI); } static Trigger* no_rti(PlayerbotAI* botAI) { return new NoRtiTrigger(botAI); } static Trigger* _return(PlayerbotAI* botAI) { return new ReturnTrigger(botAI); } + static Trigger* return_to_stay_position(PlayerbotAI* ai) { return new ReturnToStayPositionTrigger(ai); } static Trigger* sit(PlayerbotAI* botAI) { return new SitTrigger(botAI); } static Trigger* far_from_rpg_target(PlayerbotAI* botAI) { return new FarFromRpgTargetTrigger(botAI); } static Trigger* near_rpg_target(PlayerbotAI* botAI) { return new NearRpgTargetTrigger(botAI); } diff --git a/src/strategy/values/PositionValue.cpp b/src/strategy/values/PositionValue.cpp index a914ef3f..e299c988 100644 --- a/src/strategy/values/PositionValue.cpp +++ b/src/strategy/values/PositionValue.cpp @@ -63,3 +63,25 @@ bool PositionValue::Load(std::string const text) } WorldPosition CurrentPositionValue::Calculate() { return WorldPosition(bot); } + +PositionInfo SinglePositionValue::Calculate() +{ + PositionMap& posMap = AI_VALUE(PositionMap&, "position"); + return posMap[getQualifier()]; +} + +void SinglePositionValue::Set(PositionInfo value) +{ + PositionMap& posMap = AI_VALUE(PositionMap&, "position"); + PositionInfo pos = posMap[getQualifier()]; + pos = value; + posMap[getQualifier()] = pos; +} + +void SinglePositionValue::Reset() +{ + PositionMap& posMap = AI_VALUE(PositionMap&, "position"); + PositionInfo pos = posMap[getQualifier()]; + pos.Reset(); + posMap[getQualifier()] = pos; +} diff --git a/src/strategy/values/PositionValue.h b/src/strategy/values/PositionValue.h index 42b0aaf8..89193246 100644 --- a/src/strategy/values/PositionValue.h +++ b/src/strategy/values/PositionValue.h @@ -6,6 +6,7 @@ #ifndef _PLAYERBOT_POSITIONVALUE_H #define _PLAYERBOT_POSITIONVALUE_H +#include "NamedObjectContext.h" #include "TravelMgr.h" #include "Value.h" @@ -15,6 +16,10 @@ class PositionInfo { public: PositionInfo() : valueSet(false), x(0), y(0), z(0), mapId(0) {} + PositionInfo(float x, float y, float z, uint32 mapId, bool valueSet = true) + : valueSet(valueSet), x(x), y(y), z(z), mapId(mapId) + { + } PositionInfo(PositionInfo const& other) : valueSet(other.valueSet), x(other.x), y(other.y), z(other.z), mapId(other.mapId) { @@ -72,4 +77,13 @@ public: WorldPosition Calculate() override; }; +class SinglePositionValue : public CalculatedValue, public Qualified +{ +public: + SinglePositionValue(PlayerbotAI* ai, std::string name = "pos") : CalculatedValue(ai, name), Qualified() {}; + virtual PositionInfo Calculate() override; + virtual void Set(PositionInfo value) override; + virtual void Reset() override; +}; + #endif diff --git a/src/strategy/values/ValueContext.h b/src/strategy/values/ValueContext.h index 88f4b224..c9dcdb7c 100644 --- a/src/strategy/values/ValueContext.h +++ b/src/strategy/values/ValueContext.h @@ -194,6 +194,7 @@ public: creators["rti cc"] = &ValueContext::rti_cc; creators["rti"] = &ValueContext::rti; creators["position"] = &ValueContext::position; + creators["pos"] = &ValueContext::pos; creators["current position"] = &ValueContext::current_position; creators["threat"] = &ValueContext::threat; @@ -342,6 +343,7 @@ private: static UntypedValue* attackers(PlayerbotAI* botAI) { return new AttackersValue(botAI); } static UntypedValue* position(PlayerbotAI* botAI) { return new PositionValue(botAI); } + static UntypedValue* pos(PlayerbotAI* ai) { return new SinglePositionValue(ai); } static UntypedValue* current_position(PlayerbotAI* botAI) { return new CurrentPositionValue(botAI); } static UntypedValue* rti(PlayerbotAI* botAI) { return new RtiValue(botAI); } static UntypedValue* rti_cc(PlayerbotAI* botAI) { return new RtiCcValue(botAI); }