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
This commit is contained in:
kadeshar
2025-03-20 10:53:16 +01:00
committed by GitHub
parent f039e88393
commit 3d6d454337
19 changed files with 244 additions and 44 deletions

View File

@@ -1105,9 +1105,9 @@ void PlayerbotAI::HandleBotOutgoingPacket(WorldPacket const& packet)
} }
} }
QueueChatResponse( QueueChatResponse(ChatQueuedReply{msgtype, guid1.GetCounter(), guid2.GetCounter(), message,
ChatQueuedReply{msgtype, guid1.GetCounter(), guid2.GetCounter(), message, chanName, chanName, name,
name, time(nullptr) + urand(inCombat ? 10 : 5, inCombat ? 25 : 15)}); time(nullptr) + urand(inCombat ? 10 : 5, inCombat ? 25 : 15)});
GetAiObjectContext()->GetValue<time_t>("last said", "chat")->Set(time(0) + urand(5, 25)); GetAiObjectContext()->GetValue<time_t>("last said", "chat")->Set(time(0) + urand(5, 25));
return; return;
} }
@@ -1244,10 +1244,10 @@ void PlayerbotAI::ChangeEngine(BotState type)
switch (type) switch (type)
{ {
case BOT_STATE_COMBAT: case BOT_STATE_COMBAT:
// LOG_DEBUG("playerbots", "=== {} COMBAT ===", bot->GetName().c_str()); ChangeEngineOnCombat();
break; break;
case BOT_STATE_NON_COMBAT: case BOT_STATE_NON_COMBAT:
// LOG_DEBUG("playerbots", "=== {} NON-COMBAT ===", bot->GetName().c_str()); ChangeEngineOnNonCombat();
break; break;
case BOT_STATE_DEAD: case BOT_STATE_DEAD:
// LOG_DEBUG("playerbots", "=== {} DEAD ===", bot->GetName().c_str()); // 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<PositionInfo>("pos", "stay")
->Set(PositionInfo(bot->GetPositionX(), bot->GetPositionY(), bot->GetPositionZ(), bot->GetMapId()));
}
}
void PlayerbotAI::ChangeEngineOnNonCombat()
{
if (HasStrategy("stay", BOT_STATE_NON_COMBAT))
{
aiObjectContext->GetValue<PositionInfo>("pos", "stay")->Reset();
}
}
void PlayerbotAI::DoNextAction(bool min) void PlayerbotAI::DoNextAction(bool min)
{ {
if (!bot->IsInWorld() || bot->IsBeingTeleported() || (GetMaster() && GetMaster()->IsBeingTeleported())) if (!bot->IsInWorld() || bot->IsBeingTeleported() || (GetMaster() && GetMaster()->IsBeingTeleported()))
@@ -2848,7 +2865,6 @@ bool PlayerbotAI::CanCastSpell(uint32 spellid, Unit* target, bool checkHasSpell,
if (!target) if (!target)
target = bot; target = bot;
if (Pet* pet = bot->GetPet()) if (Pet* pet = bot->GetPet())
if (pet->HasSpell(spellid)) if (pet->HasSpell(spellid))
return true; return true;
@@ -3041,8 +3057,7 @@ bool PlayerbotAI::CanCastSpell(uint32 spellid, GameObject* goTarget, bool checkH
return false; return false;
} }
bool PlayerbotAI::CanCastSpell(uint32 spellid, float x, float y, float z, bool checkHasSpell, bool PlayerbotAI::CanCastSpell(uint32 spellid, float x, float y, float z, bool checkHasSpell, Item* itemTarget)
Item* itemTarget)
{ {
if (!spellid) if (!spellid)
return false; return false;
@@ -4296,8 +4311,8 @@ bool PlayerbotAI::AllowActive(ActivityType activityType)
mod = AutoScaleActivity(mod); mod = AutoScaleActivity(mod);
} }
uint32 ActivityNumber = GetFixedBotNumer(100, uint32 ActivityNumber =
sPlayerbotAIConfig->botActiveAlone * static_cast<float>(mod) / 100 * 0.01f); GetFixedBotNumer(100, sPlayerbotAIConfig->botActiveAlone * static_cast<float>(mod) / 100 * 0.01f);
return ActivityNumber <= return ActivityNumber <=
(sPlayerbotAIConfig->botActiveAlone * mod) / (sPlayerbotAIConfig->botActiveAlone * mod) /
@@ -4356,7 +4371,8 @@ void PlayerbotAI::RemoveShapeshift()
RemoveAura("moonkin form"); RemoveAura("moonkin form");
RemoveAura("travel form"); RemoveAura("travel form");
RemoveAura("cat 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("swift flight form");
RemoveAura("aquatic form"); RemoveAura("aquatic form");
RemoveAura("ghost wolf"); RemoveAura("ghost wolf");
@@ -4943,11 +4959,9 @@ Item* PlayerbotAI::FindAmmo() const
} }
// Search inventory for the correct ammo type // Search inventory for the correct ammo type
return FindItemInInventory([requiredAmmoType](ItemTemplate const* pItemProto) -> bool return FindItemInInventory(
{ [requiredAmmoType](ItemTemplate const* pItemProto) -> bool
return pItemProto->Class == ITEM_CLASS_PROJECTILE && { return pItemProto->Class == ITEM_CLASS_PROJECTILE && pItemProto->SubClass == requiredAmmoType; });
pItemProto->SubClass == requiredAmmoType;
});
} }
return nullptr; // No ranged weapon equipped return nullptr; // No ranged weapon equipped
@@ -6178,9 +6192,10 @@ bool PlayerbotAI::IsHealingSpell(uint32 spellFamilyName, flag96 spellFalimyFlags
return spellFalimyFlags & healingFlags; return spellFalimyFlags & healingFlags;
} }
SpellFamilyNames PlayerbotAI::Class2SpellFamilyName(uint8 cls)
SpellFamilyNames PlayerbotAI::Class2SpellFamilyName(uint8 cls) { {
switch (cls) { switch (cls)
{
case CLASS_WARRIOR: case CLASS_WARRIOR:
return SPELLFAMILY_WARRIOR; return SPELLFAMILY_WARRIOR;
case CLASS_PALADIN: case CLASS_PALADIN:

View File

@@ -392,6 +392,8 @@ public:
void HandleMasterOutgoingPacket(WorldPacket const& packet); void HandleMasterOutgoingPacket(WorldPacket const& packet);
void HandleTeleportAck(); void HandleTeleportAck();
void ChangeEngine(BotState type); void ChangeEngine(BotState type);
void ChangeEngineOnCombat();
void ChangeEngineOnNonCombat();
void DoNextAction(bool minimal = false); void DoNextAction(bool minimal = false);
virtual bool DoSpecificAction(std::string const name, Event event = Event(), bool silent = false, virtual bool DoSpecificAction(std::string const name, Event event = Event(), bool silent = false,
std::string const qualifier = ""); std::string const qualifier = "");

View File

@@ -135,6 +135,7 @@ public:
creators["move to loot"] = &ActionContext::move_to_loot; creators["move to loot"] = &ActionContext::move_to_loot;
creators["open loot"] = &ActionContext::open_loot; creators["open loot"] = &ActionContext::open_loot;
creators["guard"] = &ActionContext::guard; 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["move out of enemy contact"] = &ActionContext::move_out_of_enemy_contact;
creators["set facing"] = &ActionContext::set_facing; creators["set facing"] = &ActionContext::set_facing;
creators["set behind"] = &ActionContext::set_behind; creators["set behind"] = &ActionContext::set_behind;
@@ -271,6 +272,7 @@ private:
static Action* drop_target(PlayerbotAI* botAI) { return new DropTargetAction(botAI); } static Action* drop_target(PlayerbotAI* botAI) { return new DropTargetAction(botAI); }
static Action* attack_duel_opponent(PlayerbotAI* botAI) { return new AttackDuelOpponentAction(botAI); } static Action* attack_duel_opponent(PlayerbotAI* botAI) { return new AttackDuelOpponentAction(botAI); }
static Action* guard(PlayerbotAI* botAI) { return new GuardAction(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* open_loot(PlayerbotAI* botAI) { return new OpenLootAction(botAI); }
static Action* move_to_loot(PlayerbotAI* botAI) { return new MoveToLootAction(botAI); } static Action* move_to_loot(PlayerbotAI* botAI) { return new MoveToLootAction(botAI); }
static Action* _return(PlayerbotAI* botAI) { return new ReturnAction(botAI); } static Action* _return(PlayerbotAI* botAI) { return new ReturnAction(botAI); }

View File

@@ -10,7 +10,7 @@
#include "Playerbots.h" #include "Playerbots.h"
#include "PositionValue.h" #include "PositionValue.h"
void ReturnPositionResetAction::ResetReturnPosition() void PositionsResetAction::ResetReturnPosition()
{ {
PositionMap& posMap = context->GetValue<PositionMap&>("position")->Get(); PositionMap& posMap = context->GetValue<PositionMap&>("position")->Get();
PositionInfo pos = posMap["return"]; PositionInfo pos = posMap["return"];
@@ -18,7 +18,7 @@ void ReturnPositionResetAction::ResetReturnPosition()
posMap["return"] = pos; 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<PositionMap&>("position")->Get(); PositionMap& posMap = context->GetValue<PositionMap&>("position")->Get();
PositionInfo pos = posMap["return"]; PositionInfo pos = posMap["return"];
@@ -26,6 +26,22 @@ void ReturnPositionResetAction::SetReturnPosition(float x, float y, float z)
posMap["return"] = pos; posMap["return"] = pos;
} }
void PositionsResetAction::ResetStayPosition()
{
PositionMap& posMap = context->GetValue<PositionMap&>("position")->Get();
PositionInfo pos = posMap["stay"];
pos.Reset();
posMap["stay"] = pos;
}
void PositionsResetAction::SetStayPosition(float x, float y, float z)
{
PositionMap& posMap = context->GetValue<PositionMap&>("position")->Get();
PositionInfo pos = posMap["stay"];
pos.Set(x, y, z, botAI->GetBot()->GetMapId());
posMap["stay"] = pos;
}
bool FollowChatShortcutAction::Execute(Event event) bool FollowChatShortcutAction::Execute(Event event)
{ {
Player* master = GetMaster(); Player* master = GetMaster();
@@ -34,7 +50,7 @@ bool FollowChatShortcutAction::Execute(Event event)
// botAI->Reset(); // botAI->Reset();
botAI->ChangeStrategy("+follow,-passive,-grind,-move from group", BOT_STATE_NON_COMBAT); 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<GuidVector>("prioritized targets")->Reset(); botAI->GetAiObjectContext()->GetValue<GuidVector>("prioritized targets")->Reset();
PositionMap& posMap = context->GetValue<PositionMap&>("position")->Get(); PositionMap& posMap = context->GetValue<PositionMap&>("position")->Get();
@@ -42,6 +58,10 @@ bool FollowChatShortcutAction::Execute(Event event)
pos.Reset(); pos.Reset();
posMap["return"] = pos; posMap["return"] = pos;
pos = posMap["stay"];
pos.Reset();
posMap["stay"] = pos;
if (bot->IsInCombat()) if (bot->IsInCombat())
{ {
Formation* formation = AI_VALUE(Formation*, "formation"); Formation* formation = AI_VALUE(Formation*, "formation");
@@ -103,9 +123,10 @@ bool StayChatShortcutAction::Execute(Event event)
botAI->Reset(); botAI->Reset();
botAI->ChangeStrategy("+stay,-passive,-move from group", BOT_STATE_NON_COMBAT); 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()); SetReturnPosition(bot->GetPositionX(), bot->GetPositionY(), bot->GetPositionZ());
SetStayPosition(bot->GetPositionX(), bot->GetPositionY(), bot->GetPositionZ());
botAI->TellMaster("Staying"); botAI->TellMaster("Staying");
return true; return true;
@@ -133,10 +154,11 @@ bool FleeChatShortcutAction::Execute(Event event)
return false; return false;
botAI->Reset(); botAI->Reset();
botAI->ChangeStrategy("+follow,+passive", BOT_STATE_NON_COMBAT); botAI->ChangeStrategy("+follow,-stay,+passive", BOT_STATE_NON_COMBAT);
botAI->ChangeStrategy("+follow,+passive", BOT_STATE_COMBAT); botAI->ChangeStrategy("+follow,-stay,+passive", BOT_STATE_COMBAT);
ResetReturnPosition(); ResetReturnPosition();
ResetStayPosition();
if (bot->GetMapId() != master->GetMapId() || bot->GetDistance(master) > sPlayerbotAIConfig->sightDistance) if (bot->GetMapId() != master->GetMapId() || bot->GetDistance(master) > sPlayerbotAIConfig->sightDistance)
{ {
@@ -155,10 +177,11 @@ bool GoawayChatShortcutAction::Execute(Event event)
return false; return false;
botAI->Reset(); botAI->Reset();
botAI->ChangeStrategy("+runaway", BOT_STATE_NON_COMBAT); botAI->ChangeStrategy("+runaway,-stay", BOT_STATE_NON_COMBAT);
botAI->ChangeStrategy("+runaway", BOT_STATE_COMBAT); botAI->ChangeStrategy("+runaway,-stay", BOT_STATE_COMBAT);
ResetReturnPosition(); ResetReturnPosition();
ResetStayPosition();
botAI->TellMaster("Running away"); botAI->TellMaster("Running away");
return true; return true;
@@ -171,9 +194,10 @@ bool GrindChatShortcutAction::Execute(Event event)
return false; return false;
botAI->Reset(); botAI->Reset();
botAI->ChangeStrategy("+grind,-passive", BOT_STATE_NON_COMBAT); botAI->ChangeStrategy("+grind,-passive,-stay", BOT_STATE_NON_COMBAT);
ResetReturnPosition(); ResetReturnPosition();
ResetStayPosition();
botAI->TellMaster("Grinding"); botAI->TellMaster("Grinding");
return true; return true;
@@ -193,6 +217,7 @@ bool TankAttackChatShortcutAction::Execute(Event event)
botAI->ChangeStrategy("-passive", BOT_STATE_COMBAT); botAI->ChangeStrategy("-passive", BOT_STATE_COMBAT);
ResetReturnPosition(); ResetReturnPosition();
ResetStayPosition();
botAI->TellMaster("Attacking"); botAI->TellMaster("Attacking");
return true; return true;

View File

@@ -10,13 +10,15 @@
class PlayerbotAI; class PlayerbotAI;
class ReturnPositionResetAction : public Action class PositionsResetAction : public Action
{ {
public: public:
ReturnPositionResetAction(PlayerbotAI* botAI, std::string const name) : Action(botAI, name) {} PositionsResetAction(PlayerbotAI* botAI, std::string const name) : Action(botAI, name) {}
void ResetReturnPosition(); void ResetReturnPosition();
void SetReturnPosition(float x, float y, float z); void SetReturnPosition(float x, float y, float z);
void ResetStayPosition();
void SetStayPosition(float x, float y, float z);
}; };
class FollowChatShortcutAction : public MovementAction class FollowChatShortcutAction : public MovementAction
@@ -27,10 +29,10 @@ public:
bool Execute(Event event) override; bool Execute(Event event) override;
}; };
class StayChatShortcutAction : public ReturnPositionResetAction class StayChatShortcutAction : public PositionsResetAction
{ {
public: public:
StayChatShortcutAction(PlayerbotAI* botAI) : ReturnPositionResetAction(botAI, "stay chat shortcut") {} StayChatShortcutAction(PlayerbotAI* botAI) : PositionsResetAction(botAI, "stay chat shortcut") {}
bool Execute(Event event) override; bool Execute(Event event) override;
}; };
@@ -43,34 +45,34 @@ public:
bool Execute(Event event) override; bool Execute(Event event) override;
}; };
class FleeChatShortcutAction : public ReturnPositionResetAction class FleeChatShortcutAction : public PositionsResetAction
{ {
public: public:
FleeChatShortcutAction(PlayerbotAI* botAI) : ReturnPositionResetAction(botAI, "flee chat shortcut") {} FleeChatShortcutAction(PlayerbotAI* botAI) : PositionsResetAction(botAI, "flee chat shortcut") {}
bool Execute(Event event) override; bool Execute(Event event) override;
}; };
class GoawayChatShortcutAction : public ReturnPositionResetAction class GoawayChatShortcutAction : public PositionsResetAction
{ {
public: public:
GoawayChatShortcutAction(PlayerbotAI* botAI) : ReturnPositionResetAction(botAI, "runaway chat shortcut") {} GoawayChatShortcutAction(PlayerbotAI* botAI) : PositionsResetAction(botAI, "runaway chat shortcut") {}
bool Execute(Event event) override; bool Execute(Event event) override;
}; };
class GrindChatShortcutAction : public ReturnPositionResetAction class GrindChatShortcutAction : public PositionsResetAction
{ {
public: public:
GrindChatShortcutAction(PlayerbotAI* botAI) : ReturnPositionResetAction(botAI, "grind chat shortcut") {} GrindChatShortcutAction(PlayerbotAI* botAI) : PositionsResetAction(botAI, "grind chat shortcut") {}
bool Execute(Event event) override; bool Execute(Event event) override;
}; };
class TankAttackChatShortcutAction : public ReturnPositionResetAction class TankAttackChatShortcutAction : public PositionsResetAction
{ {
public: public:
TankAttackChatShortcutAction(PlayerbotAI* botAI) : ReturnPositionResetAction(botAI, "tank attack chat shortcut") {} TankAttackChatShortcutAction(PlayerbotAI* botAI) : PositionsResetAction(botAI, "tank attack chat shortcut") {}
bool Execute(Event event) override; bool Execute(Event event) override;
}; };

View File

@@ -159,3 +159,25 @@ bool ReturnAction::isUseful()
PositionInfo pos = context->GetValue<PositionMap&>("position")->Get()[qualifier]; PositionInfo pos = context->GetValue<PositionMap&>("position")->Get()[qualifier];
return pos.isSet() && AI_VALUE2(float, "distance", "position_random") > sPlayerbotAIConfig->followDistance; 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;
}

View File

@@ -40,6 +40,13 @@ public:
GuardAction(PlayerbotAI* botAI) : MoveToPositionAction(botAI, "move to position", "guard") {} 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 class SetReturnPositionAction : public Action
{ {
public: public:

View File

@@ -14,6 +14,12 @@ bool ReachTargetAction::Execute(Event event) { return ReachCombatTo(AI_VALUE(Uni
bool ReachTargetAction::isUseful() bool ReachTargetAction::isUseful()
{ {
// do not move while staying
if (botAI->HasStrategy("stay", botAI->GetState()))
{
return false;
}
// do not move while casting // do not move while casting
if (bot->GetCurrentSpell(CURRENT_CHANNELED_SPELL) != nullptr) if (bot->GetCurrentSpell(CURRENT_CHANNELED_SPELL) != nullptr)
{ {
@@ -30,6 +36,12 @@ std::string const ReachTargetAction::GetTargetName() { return "current target";
bool CastReachTargetSpellAction::isUseful() 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"), return sServerFacade->IsDistanceGreaterThan(AI_VALUE2(float, "distance", "current target"),
(distance + sPlayerbotAIConfig->contactDistance)); (distance + sPlayerbotAIConfig->contactDistance));
} }

View File

@@ -11,6 +11,7 @@
#include "Playerbots.h" #include "Playerbots.h"
#include "RTSCValues.h" #include "RTSCValues.h"
#include "RtscAction.h" #include "RtscAction.h"
#include "PositionValue.h"
Creature* SeeSpellAction::CreateWps(Player* wpOwner, float x, float y, float z, float o, uint32 entry, Creature* lastWp, Creature* SeeSpellAction::CreateWps(Player* wpOwner, float x, float y, float z, float o, uint32 entry, Creature* lastWp,
bool important) bool important)
@@ -123,6 +124,15 @@ bool SeeSpellAction::MoveToSpell(WorldPosition& spellPosition, bool inFormation)
if (inFormation) if (inFormation)
SetFormationOffset(spellPosition); 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())) if (bot->IsWithinLOS(spellPosition.getX(), spellPosition.getY(), spellPosition.getZ()))
return MoveNear(spellPosition.getMapId(), spellPosition.getX(), spellPosition.getY(), spellPosition.getZ(), 0); return MoveNear(spellPosition.getMapId(), spellPosition.getX(), spellPosition.getY(), spellPosition.getZ(), 0);

View File

@@ -8,6 +8,7 @@
#include "Event.h" #include "Event.h"
#include "LastMovementValue.h" #include "LastMovementValue.h"
#include "Playerbots.h" #include "Playerbots.h"
#include "PositionValue.h"
bool StayActionBase::Stay() bool StayActionBase::Stay()
{ {
@@ -42,6 +43,17 @@ bool StayAction::Execute(Event event) { return Stay(); }
bool StayAction::isUseful() 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 // move from group takes priority over stay as it's added and removed automatically
// (without removing/adding stay) // (without removing/adding stay)
if (botAI->HasStrategy("move from group", BOT_STATE_COMBAT) || if (botAI->HasStrategy("move from group", BOT_STATE_COMBAT) ||

View File

@@ -11,6 +11,7 @@
#include "GridNotifiersImpl.h" #include "GridNotifiersImpl.h"
#include "PlayerbotAIConfig.h" #include "PlayerbotAIConfig.h"
#include "Playerbots.h" #include "Playerbots.h"
#include "PositionValue.h"
bool UseMeetingStoneAction::Execute(Event event) bool UseMeetingStoneAction::Execute(Event event)
{ {
@@ -224,6 +225,16 @@ bool SummonAction::Teleport(Player* summoner, Player* player)
player->GetMotionMaster()->Clear(); player->GetMotionMaster()->Clear();
AI_VALUE(LastMovement&, "last movement").clear(); AI_VALUE(LastMovement&, "last movement").clear();
player->TeleportTo(mapId, x, y, z, 0); 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; return true;
} }
} }

View File

@@ -7,6 +7,13 @@
#include "Playerbots.h" #include "Playerbots.h"
void StayStrategy::InitTriggers(std::vector<TriggerNode*>& 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); } NextAction** StayStrategy::getDefaultActions() { return NextAction::array(0, new NextAction("stay", 1.0f), nullptr); }
void SitStrategy::InitTriggers(std::vector<TriggerNode*>& triggers) void SitStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)

View File

@@ -10,12 +10,13 @@
class PlayerbotAI; class PlayerbotAI;
class StayStrategy : public NonCombatStrategy class StayStrategy : public Strategy
{ {
public: public:
StayStrategy(PlayerbotAI* botAI) : NonCombatStrategy(botAI) {} StayStrategy(PlayerbotAI* botAI) : Strategy(botAI) {}
std::string const getName() override { return "stay"; } std::string const getName() override { return "stay"; }
void InitTriggers(std::vector<TriggerNode*>& triggers) override;
NextAction** getDefaultActions() override; NextAction** getDefaultActions() override;
}; };

View File

@@ -15,6 +15,7 @@
#include "ObjectGuid.h" #include "ObjectGuid.h"
#include "PlayerbotAIConfig.h" #include "PlayerbotAIConfig.h"
#include "Playerbots.h" #include "Playerbots.h"
#include "PositionValue.h"
#include "SharedDefines.h" #include "SharedDefines.h"
#include "TemporarySummon.h" #include "TemporarySummon.h"
#include "ThreatMgr.h" #include "ThreatMgr.h"
@@ -507,11 +508,22 @@ bool IsBehindTargetTrigger::IsActive()
bool IsNotBehindTargetTrigger::IsActive() bool IsNotBehindTargetTrigger::IsActive()
{ {
if (botAI->HasStrategy("stay", botAI->GetState()))
{
return false;
}
Unit* target = AI_VALUE(Unit*, "current target"); Unit* target = AI_VALUE(Unit*, "current target");
return target && !AI_VALUE2(bool, "behind", "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() 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 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() bool GiveItemTrigger::IsActive()
{ {
return AI_VALUE2(Unit*, "party member without item", item) && AI_VALUE2(uint32, "item count", item); return AI_VALUE2(Unit*, "party member without item", item) && AI_VALUE2(uint32, "item count", item);

View File

@@ -827,6 +827,14 @@ public:
SitTrigger(PlayerbotAI* botAI) : StayTimeTrigger(botAI, sPlayerbotAIConfig->sitDelay, "sit") {} 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 class ReturnTrigger : public StayTimeTrigger
{ {
public: public:

View File

@@ -30,6 +30,7 @@ public:
{ {
creators["return"] = &TriggerContext::_return; creators["return"] = &TriggerContext::_return;
creators["sit"] = &TriggerContext::sit; creators["sit"] = &TriggerContext::sit;
creators["return to stay position"] = &TriggerContext::return_to_stay_position;
creators["collision"] = &TriggerContext::collision; creators["collision"] = &TriggerContext::collision;
creators["timer"] = &TriggerContext::Timer; creators["timer"] = &TriggerContext::Timer;
@@ -228,6 +229,7 @@ private:
static Trigger* give_water(PlayerbotAI* botAI) { return new GiveWaterTrigger(botAI); } static Trigger* give_water(PlayerbotAI* botAI) { return new GiveWaterTrigger(botAI); }
static Trigger* no_rti(PlayerbotAI* botAI) { return new NoRtiTrigger(botAI); } static Trigger* no_rti(PlayerbotAI* botAI) { return new NoRtiTrigger(botAI); }
static Trigger* _return(PlayerbotAI* botAI) { return new ReturnTrigger(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* sit(PlayerbotAI* botAI) { return new SitTrigger(botAI); }
static Trigger* far_from_rpg_target(PlayerbotAI* botAI) { return new FarFromRpgTargetTrigger(botAI); } static Trigger* far_from_rpg_target(PlayerbotAI* botAI) { return new FarFromRpgTargetTrigger(botAI); }
static Trigger* near_rpg_target(PlayerbotAI* botAI) { return new NearRpgTargetTrigger(botAI); } static Trigger* near_rpg_target(PlayerbotAI* botAI) { return new NearRpgTargetTrigger(botAI); }

View File

@@ -63,3 +63,25 @@ bool PositionValue::Load(std::string const text)
} }
WorldPosition CurrentPositionValue::Calculate() { return WorldPosition(bot); } 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;
}

View File

@@ -6,6 +6,7 @@
#ifndef _PLAYERBOT_POSITIONVALUE_H #ifndef _PLAYERBOT_POSITIONVALUE_H
#define _PLAYERBOT_POSITIONVALUE_H #define _PLAYERBOT_POSITIONVALUE_H
#include "NamedObjectContext.h"
#include "TravelMgr.h" #include "TravelMgr.h"
#include "Value.h" #include "Value.h"
@@ -15,6 +16,10 @@ class PositionInfo
{ {
public: public:
PositionInfo() : valueSet(false), x(0), y(0), z(0), mapId(0) {} 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) PositionInfo(PositionInfo const& other)
: valueSet(other.valueSet), x(other.x), y(other.y), z(other.z), mapId(other.mapId) : valueSet(other.valueSet), x(other.x), y(other.y), z(other.z), mapId(other.mapId)
{ {
@@ -72,4 +77,13 @@ public:
WorldPosition Calculate() override; WorldPosition Calculate() override;
}; };
class SinglePositionValue : public CalculatedValue<PositionInfo>, 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 #endif

View File

@@ -194,6 +194,7 @@ public:
creators["rti cc"] = &ValueContext::rti_cc; creators["rti cc"] = &ValueContext::rti_cc;
creators["rti"] = &ValueContext::rti; creators["rti"] = &ValueContext::rti;
creators["position"] = &ValueContext::position; creators["position"] = &ValueContext::position;
creators["pos"] = &ValueContext::pos;
creators["current position"] = &ValueContext::current_position; creators["current position"] = &ValueContext::current_position;
creators["threat"] = &ValueContext::threat; creators["threat"] = &ValueContext::threat;
@@ -342,6 +343,7 @@ private:
static UntypedValue* attackers(PlayerbotAI* botAI) { return new AttackersValue(botAI); } static UntypedValue* attackers(PlayerbotAI* botAI) { return new AttackersValue(botAI); }
static UntypedValue* position(PlayerbotAI* botAI) { return new PositionValue(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* current_position(PlayerbotAI* botAI) { return new CurrentPositionValue(botAI); }
static UntypedValue* rti(PlayerbotAI* botAI) { return new RtiValue(botAI); } static UntypedValue* rti(PlayerbotAI* botAI) { return new RtiValue(botAI); }
static UntypedValue* rti_cc(PlayerbotAI* botAI) { return new RtiCcValue(botAI); } static UntypedValue* rti_cc(PlayerbotAI* botAI) { return new RtiCcValue(botAI); }