Big update.

This commit is contained in:
UltraNix
2022-03-12 22:27:09 +01:00
parent b3d00ccb26
commit b952636f0d
843 changed files with 1534330 additions and 99 deletions

111
src/strategy/Action.cpp Normal file
View File

@@ -0,0 +1,111 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
*/
#include "Action.h"
#include "Playerbots.h"
uint32 NextAction::size(NextAction** actions)
{
if (!actions)
return 0;
uint32 size = 0;
for (size = 0; actions[size];)
++size;
return size;
}
NextAction** NextAction::clone(NextAction** actions)
{
if (!actions)
return nullptr;
uint32 size = NextAction::size(actions);
NextAction** res = new NextAction*[size + 1];
for (uint32 i = 0; i < size; i++)
res[i] = new NextAction(*actions[i]);
res[size] = nullptr;
return res;
}
NextAction** NextAction::merge(NextAction** left, NextAction** right)
{
uint32 leftSize = NextAction::size(left);
uint32 rightSize = NextAction::size(right);
NextAction** res = new NextAction*[leftSize + rightSize + 1];
for (uint32 i = 0; i < leftSize; i++)
res[i] = new NextAction(*left[i]);
for (uint32 i = 0; i < rightSize; i++)
res[leftSize + i] = new NextAction(*right[i]);
res[leftSize + rightSize] = nullptr;
NextAction::destroy(left);
NextAction::destroy(right);
return res;
}
NextAction** NextAction::array(uint32 nil, ...)
{
va_list vl;
va_start(vl, nil);
uint32 size = 0;
NextAction* cur = nullptr;
do
{
cur = va_arg(vl, NextAction*);
++size;
}
while (cur);
va_end(vl);
NextAction** res = new NextAction*[size];
va_start(vl, nil);
for (uint32 i = 0; i < size; i++)
res[i] = va_arg(vl, NextAction*);
va_end(vl);
return res;
}
void NextAction::destroy(NextAction** actions)
{
if (!actions)
return;
for (uint32 i=0; actions[i]; i++)
delete actions[i];
delete[] actions;
}
Value<Unit*>* Action::GetTargetValue()
{
return context->GetValue<Unit*>(GetTargetName());
}
Unit* Action::GetTarget()
{
return GetTargetValue()->Get();
}
ActionBasket::ActionBasket(ActionNode* action, float relevance, bool skipPrerequisites, Event event) :
action(action), relevance(relevance), skipPrerequisites(skipPrerequisites), event(event), created(time(nullptr))
{
}
bool ActionBasket::isExpired(time_t secs)
{
return time(nullptr) - created >= secs;
}

122
src/strategy/Action.h Normal file
View File

@@ -0,0 +1,122 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
*/
#ifndef _PLAYERBOT_ACTION_H
#define _PLAYERBOT_ACTION_H
#include "AiObject.h"
#include "Common.h"
#include "Event.h"
#include "Value.h"
class PlayerbotAI;
class Unit;
class NextAction
{
public:
NextAction(std::string const name, float relevance = 0.0f) : name(name), relevance(relevance) { }
NextAction(NextAction const& o) : name(o.name), relevance(o.relevance) { }
std::string const getName() { return name; }
float getRelevance() {return relevance;}
static uint32 size(NextAction** actions);
static NextAction** clone(NextAction** actions);
static NextAction** merge(NextAction** what, NextAction** with);
static NextAction** array(uint32 nil,...);
static void destroy(NextAction** actions);
private:
float relevance;
std::string const name;
};
class Action : public AiNamedObject
{
public:
enum class ActionThreatType
{
None = 0,
Single = 1,
Aoe = 2
};
Action(PlayerbotAI* botAI, std::string const name = "action") : verbose(false), AiNamedObject(botAI, name) { }
virtual ~Action(void) { }
virtual bool Execute(Event event) { return true; }
virtual bool isPossible() { return true; }
virtual bool isUseful() { return true; }
virtual NextAction** getPrerequisites() { return nullptr; }
virtual NextAction** getAlternatives() { return nullptr; }
virtual NextAction** getContinuers() { return nullptr; }
virtual ActionThreatType getThreatType() { return ActionThreatType::None; }
void Update() { }
void Reset() { }
virtual Unit* GetTarget();
virtual Value<Unit*>* GetTargetValue();
virtual std::string const GetTargetName() { return "self target"; }
void MakeVerbose() { verbose = true; }
void setRelevance(uint32 relevance1) { relevance = relevance1; };
virtual float getRelevance() { return relevance; }
protected:
bool verbose;
float relevance = 0;
};
class ActionNode
{
public:
ActionNode(std::string const name, NextAction** prerequisites = nullptr, NextAction** alternatives = nullptr, NextAction** continuers = nullptr) :
action(nullptr), name(name), prerequisites(prerequisites), alternatives(alternatives), continuers(continuers) { }
virtual ~ActionNode()
{
NextAction::destroy(prerequisites);
NextAction::destroy(alternatives);
NextAction::destroy(continuers);
}
Action* getAction() { return action; }
void setAction(Action* action) { this->action = action; }
std::string const getName() { return name; }
NextAction** getContinuers() { return NextAction::merge(NextAction::clone(continuers), action->getContinuers()); }
NextAction** getAlternatives() { return NextAction::merge(NextAction::clone(alternatives), action->getAlternatives()); }
NextAction** getPrerequisites() { return NextAction::merge(NextAction::clone(prerequisites), action->getPrerequisites()); }
private:
std::string const name;
Action* action;
NextAction** continuers;
NextAction** alternatives;
NextAction** prerequisites;
};
class ActionBasket
{
public:
ActionBasket(ActionNode* action, float relevance, bool skipPrerequisites, Event event);
virtual ~ActionBasket(void) { }
float getRelevance() {return relevance;}
ActionNode* getAction() {return action;}
Event getEvent() { return event; }
bool isSkipPrerequisites() { return skipPrerequisites; }
void AmendRelevance(float k) { relevance *= k; }
void setRelevance(float relevance) { this->relevance = relevance; }
bool isExpired(time_t secs);
private:
ActionNode* action;
float relevance;
bool skipPrerequisites;
Event event;
time_t created;
};
#endif

15
src/strategy/AiObject.cpp Normal file
View File

@@ -0,0 +1,15 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
*/
#include "AiObject.h"
#include "Playerbots.h"
AiObject::AiObject(PlayerbotAI* botAI) : PlayerbotAIAware(botAI), bot(botAI->GetBot()), context(botAI->GetAiObjectContext()), chat(botAI->GetChatHelper())
{
}
Player* AiObject::GetMaster()
{
return botAI->GetMaster();
}

497
src/strategy/AiObject.h Normal file
View File

@@ -0,0 +1,497 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
*/
#ifndef _PLAYERBOT_AIOBJECT_H
#define _PLAYERBOT_AIOBJECT_H
#include "Common.h"
#include "PlayerbotAIAware.h"
class AiObjectContext;
class ChatHelper;
class Player;
class PlayerbotAI;
class AiObject : public PlayerbotAIAware
{
public:
AiObject(PlayerbotAI* botAI);
protected:
Player* bot;
Player* GetMaster();
AiObjectContext* context;
ChatHelper* chat;
};
class AiNamedObject : public AiObject
{
public:
AiNamedObject(PlayerbotAI* botAI, std::string const name) : AiObject(botAI), name(name) { }
public:
virtual std::string const getName() { return name; }
protected:
std::string const name;
};
//
// TRIGGERS
//
#define NEXT_TRIGGERS(name, relevance) \
virtual NextAction* getNextAction() { return new NextAction(name, relevance); }
#define BEGIN_TRIGGER(clazz, super) \
class clazz : public super \
{ \
public: \
clazz(PlayerbotAI* botAI) : super(botAI) { } \
bool IsActive() override; \
#define END_TRIGGER() \
};
#define BUFF_TRIGGER(clazz, spell) \
class clazz : public BuffTrigger \
{ \
public: \
clazz(PlayerbotAI* botAI) : BuffTrigger(botAI, spell) { } \
}
#define BUFF_TRIGGER_A(clazz, spell) \
class clazz : public BuffTrigger \
{ \
public: \
clazz(PlayerbotAI* botAI) : BuffTrigger(botAI, spell) { } \
bool IsActive() override; \
}
#define BUFF_PARTY_TRIGGER(clazz, spell) \
class clazz : public BuffOnPartyTrigger \
{ \
public: \
clazz(PlayerbotAI* botAI) : BuffOnPartyTrigger(botAI, spell) { } \
}
#define BUFF_PARTY_TRIGGER_A(clazz, spell) \
class clazz : public BuffOnPartyTrigger \
{ \
public: \
clazz(PlayerbotAI* botAI) : BuffOnPartyTrigger(botAI, spell) { } \
bool IsActive() override; \
}
#define DEBUFF_TRIGGER(clazz, spell) \
class clazz : public DebuffTrigger \
{ \
public: \
clazz(PlayerbotAI* botAI) : DebuffTrigger(botAI, spell) { } \
}
#define DEBUFF_TRIGGER_A(clazz, spell) \
class clazz : public DebuffTrigger \
{ \
public: \
clazz(PlayerbotAI* botAI) : DebuffTrigger(botAI, spell) { } \
bool IsActive() override; \
}
#define DEBUFF_ENEMY_TRIGGER(clazz, spell) \
class clazz : public DebuffOnAttackerTrigger \
{ \
public: \
clazz(PlayerbotAI* botAI) : DebuffOnAttackerTrigger(botAI, spell) { } \
}
#define DEBUFF_ENEMY_TRIGGER_A(clazz, spell) \
class clazz : public DebuffOnAttackerTrigger \
{ \
public: \
clazz(PlayerbotAI* botAI) : DebuffOnAttackerTrigger(botAI, spell) { } \
bool IsActive() override; \
}
#define CURE_TRIGGER(clazz, spell, dispel) \
class clazz : public NeedCureTrigger \
{ \
public: \
clazz(PlayerbotAI* botAI) : NeedCureTrigger(botAI, spell, dispel) { } \
}
#define CURE_PARTY_TRIGGER(clazz, spell, dispel) \
class clazz : public PartyMemberNeedCureTrigger \
{ \
public: \
clazz(PlayerbotAI* botAI) : PartyMemberNeedCureTrigger(botAI, spell, dispel) { } \
}
#define CAN_CAST_TRIGGER(clazz, spell) \
class clazz : public SpellCanBeCastTrigger \
{ \
public: \
clazz(PlayerbotAI* botAI) : SpellCanBeCastTrigger(botAI, spell) { } \
}
#define CAN_CAST_TRIGGER_A(clazz, spell) \
class clazz : public SpellCanBeCastTrigger \
{ \
public: \
clazz(PlayerbotAI* botAI) : SpellCanBeCastTrigger(botAI, spell) { } \
bool IsActive() override; \
}
#define INTERRUPT_TRIGGER(clazz, spell) \
class clazz : public InterruptSpellTrigger \
{ \
public: \
clazz(PlayerbotAI* botAI) : InterruptSpellTrigger(botAI, spell) { } \
}
#define INTERRUPT_TRIGGER_A(clazz, spell) \
class clazz : public InterruptSpellTrigger \
{ \
public: \
clazz(PlayerbotAI* botAI) : InterruptSpellTrigger(botAI, spell) { } \
bool IsActive() override; \
}
#define HAS_AURA_TRIGGER(clazz, spell) \
class clazz : public HasAuraTrigger \
{ \
public: \
clazz(PlayerbotAI* botAI) : HasAuraTrigger(botAI, spell) { } \
}
#define HAS_AURA_TRIGGER_A(clazz, spell) \
class clazz : public HasAuraTrigger \
{ \
public: \
clazz(PlayerbotAI* botAI) : HasAuraTrigger(botAI, spell) { } \
bool IsActive() override; \
}
#define SNARE_TRIGGER(clazz, spell) \
class clazz : public SnareTargetTrigger \
{ \
public: \
clazz(PlayerbotAI* botAI) : SnareTargetTrigger(botAI, spell) { } \
}
#define SNARE_TRIGGER_A(clazz, spell) \
class clazz : public SnareTargetTrigger \
{ \
public: \
clazz(PlayerbotAI* botAI) : SnareTargetTrigger(botAI, spell) { } \
bool IsActive() override; \
}
#define PROTECT_TRIGGER(clazz, spell) \
class clazz : public ProtectPartyMemberTrigger \
{ \
public: \
clazz(PlayerbotAI* botAI) : ProtectPartyMemberTrigger(botAI) { } \
}
#define DEFLECT_TRIGGER(clazz, spell) \
class clazz : public DeflectSpellTrigger \
{ \
public: \
clazz(PlayerbotAI* botAI) : DeflectSpellTrigger(botAI, spell) { } \
}
#define BOOST_TRIGGER(clazz, spell) \
class clazz : public BoostTrigger \
{ \
public: \
clazz(PlayerbotAI* botAI) : BoostTrigger(botAI, spell) { } \
}
#define BOOST_TRIGGER_A(clazz, spell) \
class clazz : public BoostTrigger \
{ \
public: \
clazz(PlayerbotAI* botAI) : BoostTrigger(botAI, spell) { } \
bool IsActive() override; \
}
#define INTERRUPT_HEALER_TRIGGER(clazz, spell) \
class clazz : public InterruptEnemyHealerTrigger \
{ \
public: \
clazz(PlayerbotAI* botAI) : InterruptEnemyHealerTrigger(botAI, spell) { } \
}
#define INTERRUPT_HEALER_TRIGGER_A(clazz, spell) \
class clazz : public InterruptEnemyHealerTrigger \
{ \
public: \
clazz(PlayerbotAI* botAI) : InterruptEnemyHealerTrigger(botAI, spell) { } \
bool IsActive() override; \
}
#define CC_TRIGGER(clazz, spell) \
class clazz : public HasCcTargetTrigger \
{ \
public: \
clazz(PlayerbotAI* botAI) : HasCcTargetTrigger(botAI, spell) { } \
}
//
// ACTIONS
//
#define MELEE_ACTION(clazz, spell) \
class clazz : public CastMeleeSpellAction \
{ \
public: \
clazz(PlayerbotAI* botAI) : CastMeleeSpellAction(botAI, spell) { } \
}
#define MELEE_ACTION_U(clazz, spell, useful) \
class clazz : public CastMeleeSpellAction \
{ \
public: \
clazz(PlayerbotAI* botAI) : CastMeleeSpellAction(botAI, spell) { } \
bool isUseful() override { return useful; } \
}
#define SPELL_ACTION(clazz, spell) \
class clazz : public CastSpellAction \
{ \
public: \
clazz(PlayerbotAI* botAI) : CastSpellAction(botAI, spell) { } \
}
#define SPELL_ACTION_U(clazz, spell, useful) \
class clazz : public CastSpellAction \
{ \
public: \
clazz(PlayerbotAI* botAI) : CastSpellAction(botAI, spell) { } \
bool isUseful() override { return useful; } \
}
#define HEAL_ACTION(clazz, spell) \
class clazz : public CastHealingSpellAction \
{ \
public: \
clazz(PlayerbotAI* botAI) : CastHealingSpellAction(botAI, spell) { } \
}
#define HEAL_ACTION_U(clazz, spell, useful) \
class clazz : public CastHealingSpellAction \
{ \
public: \
clazz(PlayerbotAI* botAI) : CastHealingSpellAction(botAI, spell) { } \
bool isUseful() override { return useful; } \
}
#define HEAL_PARTY_ACTION(clazz, spell) \
class clazz : public HealPartyMemberAction \
{ \
public: \
clazz(PlayerbotAI* botAI) : HealPartyMemberAction(botAI, spell) { } \
}
#define AOE_HEAL_ACTION(clazz, spell) \
class clazz : public CastAoeHealSpellAction \
{ \
public: \
clazz(PlayerbotAI* botAI) : CastAoeHealSpellAction(botAI, spell) { } \
}
#define BUFF_ACTION(clazz, spell) \
class clazz : public CastBuffSpellAction \
{ \
public: \
clazz(PlayerbotAI* botAI) : CastBuffSpellAction(botAI, spell) { } \
}
#define BUFF_ACTION_U(clazz, spell, useful) \
class clazz : public CastBuffSpellAction \
{ \
public: \
clazz(PlayerbotAI* botAI) : CastBuffSpellAction(botAI, spell) { } \
bool isUseful() override { return useful; } \
}
#define BUFF_PARTY_ACTION(clazz, spell) \
class clazz : public BuffOnPartyAction \
{ \
public: \
clazz(PlayerbotAI* botAI) : BuffOnPartyAction(botAI, spell) { } \
}
#define CURE_ACTION(clazz, spell) \
class clazz : public CastCureSpellAction \
{ \
public: \
clazz(PlayerbotAI* botAI) : CastCureSpellAction(botAI, spell) { } \
}
#define CURE_PARTY_ACTION(clazz, spell, dispel) \
class clazz : public CurePartyMemberAction \
{ \
public: \
clazz(PlayerbotAI* botAI) : CurePartyMemberAction(botAI, spell, dispel) { } \
}
#define RESS_ACTION(clazz, spell) \
class clazz : public ResurrectPartyMemberAction \
{ \
public: \
clazz(PlayerbotAI* botAI) : ResurrectPartyMemberAction(botAI, spell) { } \
}
#define DEBUFF_ACTION(clazz, spell) \
class clazz : public CastDebuffSpellAction \
{ \
public: \
clazz(PlayerbotAI* botAI) : CastDebuffSpellAction(botAI, spell) { } \
}
#define DEBUFF_ACTION_U(clazz, spell, useful) \
class clazz : public CastDebuffSpellAction \
{ \
public: \
clazz(PlayerbotAI* botAI) : CastDebuffSpellAction(botAI, spell) { } \
bool isUseful() override { return useful; } \
}
#define DEBUFF_ACTION_R(clazz, spell, distance) \
class clazz : public CastDebuffSpellAction \
{ \
public: \
clazz(PlayerbotAI* botAI) : CastDebuffSpellAction(botAI, spell) \
{ \
range = distance; \
} \
}
#define DEBUFF_ENEMY_ACTION(clazz, spell) \
class clazz : public CastDebuffSpellOnAttackerAction \
{ \
public: \
clazz(PlayerbotAI* botAI) : CastDebuffSpellOnAttackerAction(botAI, spell) { } \
}
#define REACH_ACTION(clazz, spell, range) \
class clazz : public CastReachTargetSpellAction \
{ \
public: \
clazz(PlayerbotAI* botAI) : CastReachTargetSpellAction(botAI, spell, range) { } \
}
#define REACH_ACTION_U(clazz, spell, range, useful) \
class clazz : public CastReachTargetSpellAction \
{ \
public: \
clazz(PlayerbotAI* botAI) : CastReachTargetSpellAction(botAI, spell, range) { } \
bool isUseful() override { return useful; } \
}
#define ENEMY_HEALER_ACTION(clazz, spell) \
class clazz : public CastSpellOnEnemyHealerAction \
{ \
public: \
clazz(PlayerbotAI* botAI) : CastSpellOnEnemyHealerAction(botAI, spell) { } \
}
#define SNARE_ACTION(clazz, spell) \
class clazz : public CastSnareSpellAction \
{ \
public: \
clazz(PlayerbotAI* botAI) : CastSnareSpellAction(botAI, spell) { } \
}
#define CC_ACTION(clazz, spell) \
class clazz : public CastCrowdControlSpellAction \
{ \
public: \
clazz(PlayerbotAI* botAI) : CastCrowdControlSpellAction(botAI, spell) { } \
}
#define PROTECT_ACTION(clazz, spell) \
class clazz : public CastProtectSpellAction \
{ \
public: \
clazz(PlayerbotAI* botAI) : CastProtectSpellAction(botAI, spell) { } \
}
#define END_RANGED_SPELL_ACTION() \
};
#define BEGIN_SPELL_ACTION(clazz, name) \
class clazz : public CastSpellAction \
{ \
public: \
clazz(PlayerbotAI* botAI) : CastSpellAction(botAI, name) { } \
#define END_SPELL_ACTION() \
};
#define BEGIN_DEBUFF_ACTION(clazz, name) \
class clazz : public CastDebuffSpellAction \
{ \
public: \
clazz(PlayerbotAI* botAI) : CastDebuffSpellAction(botAI, name) { } \
#define BEGIN_RANGED_SPELL_ACTION(clazz, name) \
class clazz : public CastSpellAction \
{ \
public: \
clazz(PlayerbotAI* botAI) : CastSpellAction(botAI, name) { } \
#define BEGIN_MELEE_SPELL_ACTION(clazz, name) \
class clazz : public CastMeleeSpellAction \
{ \
public: \
clazz(PlayerbotAI* botAI) : CastMeleeSpellAction(botAI, name) { } \
#define END_RANGED_SPELL_ACTION() \
};
#define BEGIN_BUFF_ON_PARTY_ACTION(clazz, name) \
class clazz : public BuffOnPartyAction \
{ \
public: \
clazz(PlayerbotAI* botAI) : BuffOnPartyAction(botAI, name) { }
//
// Action node
//
// node_name , action, prerequisite
#define ACTION_NODE_P(name, spell, pre) \
static ActionNode* name(PlayerbotAI* botAI) \
{ \
return new ActionNode(spell, \
/*P*/ NextAction::array(0, new NextAction(pre), nullptr), \
/*A*/ nullptr, \
/*C*/ nullptr); \
}
// node_name , action, alternative
#define ACTION_NODE_A(name, spell, alt) \
static ActionNode* name(PlayerbotAI* botAI) \
{ \
return new ActionNode(spell, \
/*P*/ nullptr, \
/*A*/ NextAction::array(0, new NextAction(alt), nullptr), \
/*C*/ nullptr); \
}
// node_name , action, continuer
#define ACTION_NODE_C(name, spell, con) \
static ActionNode* name(PlayerbotAI* botAI) \
{ \
return new ActionNode(spell, \
/*P*/ nullptr, \
/*A*/ nullptr, \
/*C*/ NextAction::array(0, new NextAction(con), nullptr); \
}
#endif

View File

@@ -0,0 +1,162 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
*/
#include "AiObjectContext.h"
#include "StrategyContext.h"
#include "ActionContext.h"
#include "ChatActionContext.h"
#include "WorldPacketActionContext.h"
#include "ChatTriggerContext.h"
#include "TriggerContext.h"
#include "SharedValueContext.h"
#include "WorldPacketTriggerContext.h"
#include "ValueContext.h"
#include "Playerbots.h"
AiObjectContext::AiObjectContext(PlayerbotAI* botAI) : PlayerbotAIAware(botAI)
{
strategyContexts.Add(new StrategyContext());
strategyContexts.Add(new MovementStrategyContext());
strategyContexts.Add(new AssistStrategyContext());
strategyContexts.Add(new QuestStrategyContext());
actionContexts.Add(new ActionContext());
actionContexts.Add(new ChatActionContext());
actionContexts.Add(new WorldPacketActionContext());
triggerContexts.Add(new TriggerContext());
triggerContexts.Add(new ChatTriggerContext());
triggerContexts.Add(new WorldPacketTriggerContext());
valueContexts.Add(new ValueContext());
valueContexts.Add(sSharedValueContext);
}
void AiObjectContext::Update()
{
strategyContexts.Update();
triggerContexts.Update();
actionContexts.Update();
valueContexts.Update();
}
void AiObjectContext::Reset()
{
strategyContexts.Reset();
triggerContexts.Reset();
actionContexts.Reset();
valueContexts.Reset();
}
std::vector<std::string> AiObjectContext::Save()
{
std::vector<std::string> result;
std::set<std::string> names = valueContexts.GetCreated();
for (std::set<std::string>::iterator i = names.begin(); i != names.end(); ++i)
{
UntypedValue* value = GetUntypedValue(*i);
if (!value)
continue;
std::string const data = value->Save();
if (data == "?")
continue;
std::string const name = *i;
std::ostringstream out;
out << name;
out << ">" << data;
result.push_back(out.str());
}
return std::move(result);
}
void AiObjectContext::Load(std::vector<std::string> data)
{
for (std::vector<std::string>::iterator i = data.begin(); i != data.end(); ++i)
{
std::string const row = *i;
std::vector<std::string> parts = split(row, '>');
if (parts.size() != 2)
continue;
std::string const name = parts[0];
std::string const text = parts[1];
UntypedValue* value = GetUntypedValue(name);
if (!value)
continue;
value->Load(text);
}
}
Strategy* AiObjectContext::GetStrategy(std::string const name)
{
return strategyContexts.GetContextObject(name, botAI);
}
std::set<std::string> AiObjectContext::GetSiblingStrategy(std::string const name)
{
return strategyContexts.GetSiblings(name);
}
Trigger* AiObjectContext::GetTrigger(std::string const name)
{
return triggerContexts.GetContextObject(name, botAI);
}
Action* AiObjectContext::GetAction(std::string const name)
{
return actionContexts.GetContextObject(name, botAI);
}
UntypedValue* AiObjectContext::GetUntypedValue(std::string const name)
{
return valueContexts.GetContextObject(name, botAI);
}
std::set<std::string> AiObjectContext::GetValues()
{
return valueContexts.GetCreated();
}
std::set<std::string> AiObjectContext::GetSupportedStrategies()
{
return strategyContexts.supports();
}
std::set<std::string> AiObjectContext::GetSupportedActions()
{
return actionContexts.supports();
}
std::string const AiObjectContext::FormatValues()
{
std::ostringstream out;
std::set<std::string> names = valueContexts.GetCreated();
for (std::set<std::string>::iterator i = names.begin(); i != names.end(); ++i, out << "|")
{
UntypedValue* value = GetUntypedValue(*i);
if (!value)
continue;
std::string const text = value->Format();
if (text == "?")
continue;
out << "{" << *i << "=" << text << "}";
}
return out.str();
}
void AiObjectContext::AddShared(NamedObjectContext<UntypedValue>* sharedValues)
{
valueContexts.Add(sharedValues);
}

View File

@@ -0,0 +1,70 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
*/
#ifndef _PLAYERBOT_AIOBJECTCONTEXT_H
#define _PLAYERBOT_AIOBJECTCONTEXT_H
#include "Common.h"
#include "NamedObjectContext.h"
#include "PlayerbotAIAware.h"
#include "Strategy.h"
#include "Trigger.h"
#include "Value.h"
class PlayerbotAI;
class AiObjectContext : public PlayerbotAIAware
{
public:
AiObjectContext(PlayerbotAI* botAI);
virtual ~AiObjectContext() { }
virtual Strategy* GetStrategy(std::string const name);
virtual std::set<std::string> GetSiblingStrategy(std::string const name);
virtual Trigger* GetTrigger(std::string const name);
virtual Action* GetAction(std::string const name);
virtual UntypedValue* GetUntypedValue(std::string const name);
template<class T>
Value<T>* GetValue(std::string const name)
{
return dynamic_cast<Value<T>*>(GetUntypedValue(name));
}
template<class T>
Value<T>* GetValue(std::string const name, std::string const param)
{
return GetValue<T>((std::string(name) + "::" + param));
}
template<class T>
Value<T>* GetValue(std::string const name, int32 param)
{
std::ostringstream out;
out << param;
return GetValue<T>(name, out.str());
}
std::set<std::string> GetValues();
std::set<std::string> GetSupportedStrategies();
std::set<std::string> GetSupportedActions();
std::string const FormatValues();
virtual void Update();
virtual void Reset();
virtual void AddShared(NamedObjectContext<UntypedValue>* sharedValues);
std::vector<std::string> Save();
void Load(std::vector<std::string> data);
std::vector<std::string> performanceStack;
protected:
NamedObjectContextList<Strategy> strategyContexts;
NamedObjectContextList<Action> actionContexts;
NamedObjectContextList<Trigger> triggerContexts;
NamedObjectContextList<UntypedValue> valueContexts;
};
#endif

View File

@@ -0,0 +1,111 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
*/
#include "CustomStrategy.h"
#include "Playerbots.h"
#include <regex>
std::map<std::string, std::string> CustomStrategy::actionLinesCache;
NextAction* toNextAction(std::string const action)
{
std::vector<std::string> tokens = split(action, '!');
if (tokens.size() == 2 && !tokens[0].empty())
return new NextAction(tokens[0], atof(tokens[1].c_str()));
else if (tokens.size() == 1 && !tokens[0].empty())
return new NextAction(tokens[0], ACTION_NORMAL);
LOG_ERROR("playerbots", "Invalid action {}", action.c_str());
return nullptr;
}
NextAction** toNextActionArray(std::string const actions)
{
std::vector<std::string> tokens = split(actions, ',');
NextAction** res = new NextAction*[tokens.size() + 1];
uint32 index = 0;
for (std::vector<std::string>::iterator i = tokens.begin(); i != tokens.end(); ++i)
{
if (NextAction* na = toNextAction(*i))
res[index++] = na;
}
res[index++] = nullptr;
return res;
}
TriggerNode* toTriggerNode(std::string const actionLine)
{
std::vector<std::string> tokens = split(actionLine, '>');
if (tokens.size() == 2)
return new TriggerNode(tokens[0], toNextActionArray(tokens[1]));
LOG_ERROR("playerbots", "Invalid action line {}", actionLine.c_str());
return nullptr;
}
CustomStrategy::CustomStrategy(PlayerbotAI* botAI) : Strategy(botAI), Qualified()
{
}
void CustomStrategy::InitTriggers(std::vector<TriggerNode*> &triggers)
{
if (actionLines.empty())
{
if (actionLinesCache[qualifier].empty())
{
LoadActionLines((uint32)botAI->GetBot()->GetGUID().GetCounter());
if (actionLines.empty())
LoadActionLines(0);
}
else
{
std::vector<std::string> tokens = split(actionLinesCache[qualifier], '\n');
std::regex tpl("\\(nullptr,\\s*'.+',\\s*'(.+)'\\)(,|;)");
for (std::vector<std::string>::iterator i = tokens.begin(); i != tokens.end(); ++i)
{
std::string const line = *i;
for (std::sregex_iterator j = std::sregex_iterator(line.begin(), line.end(), tpl); j != std::sregex_iterator(); ++j)
{
std::smatch match = *j;
std::string const actionLine = match[1].str();
if (!actionLine.empty())
actionLines.push_back(actionLine);
}
}
}
}
for (std::vector<std::string>::iterator i = actionLines.begin(); i != actionLines.end(); ++i)
{
if (TriggerNode* tn = toTriggerNode(*i))
triggers.push_back(tn);
}
}
void CustomStrategy::LoadActionLines(uint32 owner)
{
PlayerbotsDatabasePreparedStatement* stmt = PlayerbotsDatabase.GetPreparedStatement(PLAYERBOTS_SEL_CUSTOM_STRATEGY_BY_OWNER_AND_NAME);
stmt->SetData(0, owner);
stmt->SetData(1, qualifier);
PreparedQueryResult result = PlayerbotsDatabase.Query(stmt);
if (result)
{
do
{
Field* fields = result->Fetch();
std::string const action = fields[0].Get<std::string>();
actionLines.push_back(action);
}
while (result->NextRow());
}
}
void CustomStrategy::Reset()
{
actionLines.clear();
actionLinesCache[qualifier].clear();
}

View File

@@ -0,0 +1,30 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
*/
#ifndef _PLAYERBOT_CUSTOMSTRATEGY_H
#define _PLAYERBOT_CUSTOMSTRATEGY_H
#include "Strategy.h"
#include <map>
class PlayerbotAI;
class CustomStrategy : public Strategy, public Qualified
{
public:
CustomStrategy(PlayerbotAI* botAI);
void InitTriggers(std::vector<TriggerNode*> &triggers) override;
std::string const getName() override { return std::string("custom::" + qualifier); }
void Reset();
static std::map<std::string, std::string> actionLinesCache;
private:
std::vector<std::string> actionLines;
void LoadActionLines(uint32 owner);
};
#endif

657
src/strategy/Engine.cpp Normal file
View File

@@ -0,0 +1,657 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
*/
#include "Engine.h"
#include "Action.h"
#include "Event.h"
#include "Queue.h"
#include "PerformanceMonitor.h"
#include "Playerbots.h"
Engine::Engine(PlayerbotAI* botAI, AiObjectContext* factory) : PlayerbotAIAware(botAI), aiObjectContext(factory)
{
lastRelevance = 0.0f;
testMode = false;
}
bool ActionExecutionListeners::Before(Action* action, Event event)
{
bool result = true;
for (std::list<ActionExecutionListener*>::iterator i = listeners.begin(); i != listeners.end(); i++)
{
result &= (*i)->Before(action, event);
}
return result;
}
void ActionExecutionListeners::After(Action* action, bool executed, Event event)
{
for (std::list<ActionExecutionListener*>::iterator i = listeners.begin(); i != listeners.end(); i++)
{
(*i)->After(action, executed, event);
}
}
bool ActionExecutionListeners::OverrideResult(Action* action, bool executed, Event event)
{
bool result = executed;
for (std::list<ActionExecutionListener*>::iterator i = listeners.begin(); i != listeners.end(); i++)
{
result = (*i)->OverrideResult(action, result, event);
}
return result;
}
bool ActionExecutionListeners::AllowExecution(Action* action, Event event)
{
bool result = true;
for (std::list<ActionExecutionListener*>::iterator i = listeners.begin(); i != listeners.end(); i++)
{
result &= (*i)->AllowExecution(action, event);
}
return result;
}
ActionExecutionListeners::~ActionExecutionListeners()
{
for (std::list<ActionExecutionListener*>::iterator i = listeners.begin(); i != listeners.end(); i++)
{
delete *i;
}
listeners.clear();
}
Engine::~Engine(void)
{
Reset();
strategies.clear();
}
void Engine::Reset()
{
ActionNode* action = nullptr;
do
{
action = queue.Pop();
if (!action)
break;
delete action;
} while (true);
for (std::vector<TriggerNode*>::iterator i = triggers.begin(); i != triggers.end(); i++)
{
TriggerNode* trigger = *i;
delete trigger;
}
triggers.clear();
for (std::vector<Multiplier*>::iterator i = multipliers.begin(); i != multipliers.end(); i++)
{
Multiplier* multiplier = *i;
delete multiplier;
}
multipliers.clear();
}
void Engine::Init()
{
Reset();
for (std::map<std::string, Strategy*>::iterator i = strategies.begin(); i != strategies.end(); i++)
{
Strategy* strategy = i->second;
strategy->InitMultipliers(multipliers);
strategy->InitTriggers(triggers);
Event emptyEvent;
MultiplyAndPush(strategy->getDefaultActions(), 0.0f, false, emptyEvent, "default");
}
if (testMode)
{
FILE* file = fopen("test.log", "w");
fprintf(file, "\n");
fclose(file);
}
}
bool Engine::DoNextAction(Unit* unit, uint32 depth, bool minimal)
{
LogAction("--- AI Tick ---");
if (sPlayerbotAIConfig->logValuesPerTick)
LogValues();
bool actionExecuted = false;
ActionBasket* basket = nullptr;
time_t currentTime = time(nullptr);
aiObjectContext->Update();
ProcessTriggers(minimal);
uint32 iterations = 0;
uint32 iterationsPerTick = queue.Size() * (minimal ? 2 : sPlayerbotAIConfig->iterationsPerTick);
do
{
basket = queue.Peek();
if (basket)
{
float relevance = basket->getRelevance(); // just for reference
bool skipPrerequisites = basket->isSkipPrerequisites();
if (minimal && (relevance < 100))
continue;
Event event = basket->getEvent();
// NOTE: queue.Pop() deletes basket
ActionNode* actionNode = queue.Pop();
Action* action = InitializeAction(actionNode);
if (action)
action->setRelevance(relevance);
if (!action)
{
LogAction("A:%s - UNKNOWN", actionNode->getName().c_str());
}
else if (action->isUseful())
{
for (std::vector<Multiplier*>::iterator i = multipliers.begin(); i!= multipliers.end(); i++)
{
Multiplier* multiplier = *i;
relevance *= multiplier->GetValue(action);
action->setRelevance(relevance);
if (!relevance)
{
LogAction("Multiplier %s made action %s useless", multiplier->getName().c_str(), action->getName().c_str());
break;
}
}
if (action->isPossible() && relevance)
{
if (!skipPrerequisites)
{
LogAction("A:%s - PREREQ", action->getName().c_str());
if (MultiplyAndPush(actionNode->getPrerequisites(), relevance + 0.02, false, event, "prereq"))
{
PushAgain(actionNode, relevance + 0.01, event);
continue;
}
}
PerformanceMonitorOperation* pmo = sPerformanceMonitor->start(PERF_MON_ACTION, action->getName(), &aiObjectContext->performanceStack);
actionExecuted = ListenAndExecute(action, event);
if (pmo)
pmo->finish();
if (actionExecuted)
{
LogAction("A:%s - OK", action->getName().c_str());
MultiplyAndPush(actionNode->getContinuers(), 0, false, event, "cont");
lastRelevance = relevance;
delete actionNode;
break;
}
else
{
LogAction("A:%s - FAILED", action->getName().c_str());
MultiplyAndPush(actionNode->getAlternatives(), relevance + 0.03, false, event, "alt");
}
}
else
{
LogAction("A:%s - IMPOSSIBLE", action->getName().c_str());
MultiplyAndPush(actionNode->getAlternatives(), relevance + 0.03, false, event, "alt");
}
}
else
{
lastRelevance = relevance;
LogAction("A:%s - USELESS", action->getName().c_str());
}
delete actionNode;
}
}
while (basket && ++iterations <= iterationsPerTick);
if (!basket)
{
lastRelevance = 0.0f;
PushDefaultActions();
if (queue.Peek() && depth < 2)
return DoNextAction(unit, depth + 1, minimal);
}
// MEMORY FIX TEST
/*
do
{
basket = queue.Peek();
if (basket)
{
// NOTE: queue.Pop() deletes basket
delete queue.Pop();
}
}
while (basket);
*/
if (time(nullptr) - currentTime > 1)
{
LogAction("too long execution");
}
if (!actionExecuted)
LogAction("no actions executed");
queue.RemoveExpired();
return actionExecuted;
}
ActionNode* Engine::CreateActionNode(std::string const name)
{
for (std::map<std::string, Strategy*>::iterator i = strategies.begin(); i != strategies.end(); i++)
{
if (ActionNode* node = i->second->GetAction(name))
return node;
}
return new ActionNode (name,
/*P*/ nullptr,
/*A*/ nullptr,
/*C*/ nullptr);
}
bool Engine::MultiplyAndPush(NextAction** actions, float forceRelevance, bool skipPrerequisites, Event event, char const* pushType)
{
bool pushed = false;
if (actions)
{
for (uint32 j = 0; actions[j]; j++)
{
if (NextAction* nextAction = actions[j])
{
ActionNode* action = CreateActionNode(nextAction->getName());
InitializeAction(action);
float k = nextAction->getRelevance();
if (forceRelevance > 0.0f)
{
k = forceRelevance;
}
if (k > 0)
{
LogAction("PUSH:%s - %f (%s)", action->getName().c_str(), k, pushType);
queue.Push(new ActionBasket(action, k, skipPrerequisites, event));
pushed = true;
}
else
{
delete action;
}
delete nextAction;
}
else
break;
}
delete[] actions;
}
return pushed;
}
ActionResult Engine::ExecuteAction(std::string const name, Event event, std::string const qualifier)
{
bool result = false;
ActionNode* actionNode = CreateActionNode(name);
if (!actionNode)
return ACTION_RESULT_UNKNOWN;
Action* action = InitializeAction(actionNode);
if (!action)
{
delete actionNode;
return ACTION_RESULT_UNKNOWN;
}
if (!qualifier.empty())
{
if (Qualified* q = dynamic_cast<Qualified*>(action))
q->Qualify(qualifier);
}
if (!action->isPossible())
{
delete actionNode;
return ACTION_RESULT_IMPOSSIBLE;
}
if (!action->isUseful())
{
delete actionNode;
return ACTION_RESULT_USELESS;
}
action->MakeVerbose();
result = ListenAndExecute(action, event);
MultiplyAndPush(action->getContinuers(), 0.0f, false, event, "default");
delete actionNode;
return result ? ACTION_RESULT_OK : ACTION_RESULT_FAILED;
}
void Engine::addStrategy(std::string const name)
{
removeStrategy(name);
if (Strategy* strategy = aiObjectContext->GetStrategy(name))
{
std::set<std::string> siblings = aiObjectContext->GetSiblingStrategy(name);
for (std::set<std::string>::iterator i = siblings.begin(); i != siblings.end(); i++)
removeStrategy(*i);
LogAction("S:+%s", strategy->getName().c_str());
strategies[strategy->getName()] = strategy;
}
Init();
}
void Engine::addStrategies(std::string first, ...)
{
addStrategy(first);
va_list vl;
va_start(vl, first);
const char* cur;
do
{
cur = va_arg(vl, const char*);
if (cur)
addStrategy(cur);
}
while (cur);
va_end(vl);
}
bool Engine::removeStrategy(std::string const name)
{
std::map<std::string, Strategy*>::iterator i = strategies.find(name);
if (i == strategies.end())
return false;
LogAction("S:-%s", name.c_str());
strategies.erase(i);
Init();
return true;
}
void Engine::removeAllStrategies()
{
strategies.clear();
Init();
}
void Engine::toggleStrategy(std::string const name)
{
if (!removeStrategy(name))
addStrategy(name);
}
bool Engine::HasStrategy(std::string const name)
{
return strategies.find(name) != strategies.end();
}
void Engine::ProcessTriggers(bool minimal)
{
std::map<Trigger*, Event> fires;
for (std::vector<TriggerNode*>::iterator i = triggers.begin(); i != triggers.end(); i++)
{
TriggerNode* node = *i;
if (!node)
continue;
Trigger* trigger = node->getTrigger();
if (!trigger)
{
trigger = aiObjectContext->GetTrigger(node->getName());
node->setTrigger(trigger);
}
if (!trigger)
continue;
if (testMode || trigger->needCheck())
{
if (minimal && node->getFirstRelevance() < 100)
continue;
PerformanceMonitorOperation* pmo = sPerformanceMonitor->start(PERF_MON_TRIGGER, trigger->getName(), &aiObjectContext->performanceStack);
Event event = trigger->Check();
if (pmo)
pmo->finish();
if (!event)
continue;
fires[trigger] = event;
LogAction("T:%s", trigger->getName().c_str());
}
}
for (std::vector<TriggerNode*>::iterator i = triggers.begin(); i != triggers.end(); i++)
{
TriggerNode* node = *i;
Trigger* trigger = node->getTrigger();
Event event = fires[trigger];
if (!event)
continue;
MultiplyAndPush(node->getHandlers(), 0.0f, false, event, "trigger");
}
for (std::vector<TriggerNode*>::iterator i = triggers.begin(); i != triggers.end(); i++)
{
if (Trigger* trigger = (*i)->getTrigger())
trigger->Reset();
}
}
void Engine::PushDefaultActions()
{
for (std::map<std::string, Strategy*>::iterator i = strategies.begin(); i != strategies.end(); i++)
{
Strategy* strategy = i->second;
Event emptyEvent;
MultiplyAndPush(strategy->getDefaultActions(), 0.0f, false, emptyEvent, "default");
}
}
std::string const Engine::ListStrategies()
{
std::string s = "Strategies: ";
if (strategies.empty())
return std::move(s);
for (std::map<std::string, Strategy*>::iterator i = strategies.begin(); i != strategies.end(); i++)
{
s.append(i->first);
s.append(", ");
}
return s.substr(0, s.length() - 2);
}
std::vector<std::string> Engine::GetStrategies()
{
std::vector<std::string> result;
for (std::map<std::string, Strategy*>::iterator i = strategies.begin(); i != strategies.end(); i++)
{
result.push_back(i->first);
}
return std::move(result);
}
void Engine::PushAgain(ActionNode* actionNode, float relevance, Event event)
{
NextAction** nextAction = new NextAction*[2];
nextAction[0] = new NextAction(actionNode->getName(), relevance);
nextAction[1] = nullptr;
MultiplyAndPush(nextAction, relevance, true, event, "again");
delete actionNode;
}
bool Engine::ContainsStrategy(StrategyType type)
{
for (std::map<std::string, Strategy*>::iterator i = strategies.begin(); i != strategies.end(); i++)
{
if (i->second->GetType() & type)
return true;
}
return false;
}
Action* Engine::InitializeAction(ActionNode* actionNode)
{
Action* action = actionNode->getAction();
if (!action)
{
action = aiObjectContext->GetAction(actionNode->getName());
actionNode->setAction(action);
}
return action;
}
bool Engine::ListenAndExecute(Action* action, Event event)
{
bool actionExecuted = false;
if (actionExecutionListeners.Before(action, event))
{
actionExecuted = actionExecutionListeners.AllowExecution(action, event) ? action->Execute(event) : true;
}
if (botAI->HasStrategy("debug", BOT_STATE_NON_COMBAT))
{
std::ostringstream out;
out << "do: ";
out << action->getName();
if (actionExecuted)
out << " 1 (";
else
out << " 0 (";
out << action->getRelevance() << ")";
if (!event.GetSource().empty())
out << " [" << event.GetSource() << "]";
botAI->TellMasterNoFacing(out);
}
actionExecuted = actionExecutionListeners.OverrideResult(action, actionExecuted, event);
actionExecutionListeners.After(action, actionExecuted, event);
return actionExecuted;
}
void Engine::LogAction(char const* format, ...)
{
char buf[1024];
va_list ap;
va_start(ap, format);
vsprintf(buf, format, ap);
va_end(ap);
lastAction += "|";
lastAction += buf;
if (lastAction.size() > 512)
{
lastAction = lastAction.substr(512);
size_t pos = lastAction.find("|");
lastAction = (pos == std::string::npos ? "" : lastAction.substr(pos));
}
if (testMode)
{
FILE* file = fopen("test.log", "a");
fprintf(file, "'{}'", buf);
fprintf(file, "\n");
fclose(file);
}
else
{
Player* bot = botAI->GetBot();
if (sPlayerbotAIConfig->logInGroupOnly && !bot->GetGroup())
return;
LOG_INFO("playerbots", "{} {}", bot->GetName().c_str(), buf);
}
}
void Engine::ChangeStrategy(std::string const names)
{
std::vector<std::string> splitted = split(names, ',');
for (std::vector<std::string>::iterator i = splitted.begin(); i != splitted.end(); i++)
{
char const* name = i->c_str();
switch (name[0])
{
case '+':
addStrategy(name+1);
break;
case '-':
removeStrategy(name+1);
break;
case '~':
toggleStrategy(name+1);
break;
case '?':
botAI->TellMaster(ListStrategies());
break;
}
}
}
void Engine::LogValues()
{
if (testMode)
return;
Player* bot = botAI->GetBot();
if (sPlayerbotAIConfig->logInGroupOnly && !bot->GetGroup())
return;
std::string const text = botAI->GetAiObjectContext()->FormatValues();
LOG_DEBUG("playerbots", "Values for {}: {}", bot->GetName().c_str(), text.c_str());
}

127
src/strategy/Engine.h Normal file
View File

@@ -0,0 +1,127 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
*/
#ifndef _PLAYERBOT_ENGINE_H
#define _PLAYERBOT_ENGINE_H
#include "Multiplier.h"
#include "Queue.h"
#include "PlayerbotAIAware.h"
#include "Strategy.h"
#include "Trigger.h"
#include <map>
class Action;
class ActionNode;
class AiObjectContext;
class Event;
class NextAction;
class PlayerbotAI;
enum ActionResult
{
ACTION_RESULT_UNKNOWN,
ACTION_RESULT_OK,
ACTION_RESULT_IMPOSSIBLE,
ACTION_RESULT_USELESS,
ACTION_RESULT_FAILED
};
class ActionExecutionListener
{
public:
virtual ~ActionExecutionListener() { };
virtual bool Before(Action* action, Event event) = 0;
virtual bool AllowExecution(Action* action, Event event) = 0;
virtual void After(Action* action, bool executed, Event event) = 0;
virtual bool OverrideResult(Action* action, bool executed, Event event) = 0;
};
class ActionExecutionListeners : public ActionExecutionListener
{
public:
virtual ~ActionExecutionListeners();
bool Before(Action* action, Event event) override;
bool AllowExecution(Action* action, Event event) override;
void After(Action* action, bool executed, Event event) override;
bool OverrideResult(Action* action, bool executed, Event event) override;
void Add(ActionExecutionListener* listener)
{
listeners.push_back(listener);
}
void Remove(ActionExecutionListener* listener)
{
listeners.remove(listener);
}
private:
std::list<ActionExecutionListener*> listeners;
};
class Engine : public PlayerbotAIAware
{
public:
Engine(PlayerbotAI* botAI, AiObjectContext* factory);
void Init();
void addStrategy(std::string const name);
void addStrategies(std::string first, ...);
bool removeStrategy(std::string const name);
bool HasStrategy(std::string const name);
void removeAllStrategies();
void toggleStrategy(std::string const name);
std::string const ListStrategies();
std::vector<std::string> GetStrategies();
bool ContainsStrategy(StrategyType type);
void ChangeStrategy(std::string const names);
std::string const GetLastAction() { return lastAction; }
virtual bool DoNextAction(Unit*, uint32 depth = 0, bool minimal = false);
ActionResult ExecuteAction(std::string const name, Event event = Event(), std::string const qualifier = "");
void AddActionExecutionListener(ActionExecutionListener* listener)
{
actionExecutionListeners.Add(listener);
}
void removeActionExecutionListener(ActionExecutionListener* listener)
{
actionExecutionListeners.Remove(listener);
}
virtual ~Engine(void);
bool testMode;
private:
bool MultiplyAndPush(NextAction** actions, float forceRelevance, bool skipPrerequisites, Event event, const char* pushType);
void Reset();
void ProcessTriggers(bool minimal);
void PushDefaultActions();
void PushAgain(ActionNode* actionNode, float relevance, Event event);
ActionNode* CreateActionNode(std::string const name);
Action* InitializeAction(ActionNode* actionNode);
bool ListenAndExecute(Action* action, Event event);
void LogAction(char const* format, ...);
void LogValues();
ActionExecutionListeners actionExecutionListeners;
protected:
Queue queue;
std::vector<TriggerNode*> triggers;
std::vector<Multiplier*> multipliers;
AiObjectContext* aiObjectContext;
std::map<std::string, Strategy*> strategies;
float lastRelevance;
std::string lastAction;
};
#endif

25
src/strategy/Event.cpp Normal file
View File

@@ -0,0 +1,25 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
*/
#include "Event.h"
#include "Playerbots.h"
Event::Event(std::string const source, ObjectGuid object, Player* owner) : source(source), owner(owner)
{
packet << object;
}
ObjectGuid Event::getObject()
{
if (packet.empty())
return ObjectGuid::Empty;
WorldPacket p(packet);
p.rpos(0);
ObjectGuid guid;
p >> guid;
return guid;
}

38
src/strategy/Event.h Normal file
View File

@@ -0,0 +1,38 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
*/
#ifndef _PLAYERBOT_EVENT_H
#define _PLAYERBOT_EVENT_H
#include "WorldPacket.h"
class ObjectGuid;
class Player;
class Event
{
public:
Event(Event const& other) : source(other.source), param(other.param), packet(other.packet), owner(other.owner) { }
Event() { }
Event(std::string const source) : source(source) { }
Event(std::string const source, std::string const param, Player* owner = nullptr) : source(source), param(param), owner(owner) { }
Event(std::string const source, WorldPacket& packet, Player* owner = nullptr) : source(source), packet(packet), owner(owner) { }
Event(std::string const source, ObjectGuid object, Player* owner = nullptr);
virtual ~Event() { }
std::string const GetSource() { return source; }
std::string const getParam() { return param; }
WorldPacket& getPacket() { return packet; }
ObjectGuid getObject();
Player* getOwner() { return owner; }
bool operator! () const { return source.empty(); }
protected:
std::string source;
std::string param;
WorldPacket packet;
Player* owner = nullptr;
};
#endif

View File

@@ -0,0 +1,64 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
*/
#include "ExternalEventHelper.h"
#include "Trigger.h"
#include "ChatHelper.h"
#include "Playerbots.h"
bool ExternalEventHelper::ParseChatCommand(std::string const command, Player* owner)
{
if (HandleCommand(command, "", owner))
return true;
size_t i = std::string::npos;
while (true)
{
size_t found = command.rfind(" ", i);
if (found == std::string::npos || !found)
break;
std::string const name = command.substr(0, found);
std::string const param = command.substr(found + 1);
i = found - 1;
if (HandleCommand(name, param, owner))
return true;
}
if (!ChatHelper::parseable(command))
return false;
HandleCommand("c", command, owner);
HandleCommand("t", command, owner);
return true;
}
void ExternalEventHelper::HandlePacket(std::map<uint16, std::string>& handlers, WorldPacket const& packet, Player* owner)
{
uint16 opcode = packet.GetOpcode();
std::string const name = handlers[opcode];
if (name.empty())
return;
Trigger* trigger = aiObjectContext->GetTrigger(name);
if (!trigger)
return;
WorldPacket p(packet);
trigger->ExternalEvent(p, owner);
}
bool ExternalEventHelper::HandleCommand(std::string const name, std::string const param, Player* owner)
{
Trigger* trigger = aiObjectContext->GetTrigger(name);
if (!trigger)
return false;
trigger->ExternalEvent(param, owner);
return true;
}

View File

@@ -0,0 +1,29 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
*/
#ifndef _PLAYERBOT_EXTERNALEVENTHELPER_H
#define _PLAYERBOT_EXTERNALEVENTHELPER_H
#include "Common.h"
#include <map>
class AiObjectContext;
class Player;
class WorldPacket;
class ExternalEventHelper
{
public:
ExternalEventHelper(AiObjectContext* aiObjectContext) : aiObjectContext(aiObjectContext) { }
bool ParseChatCommand(std::string const command, Player* owner = nullptr);
void HandlePacket(std::map<uint16, std::string>& handlers, WorldPacket const& packet, Player* owner = nullptr);
bool HandleCommand(std::string const name, std::string const param, Player* owner = nullptr);
private:
AiObjectContext* aiObjectContext;
};
#endif

View File

@@ -0,0 +1,87 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
*/
#include "ItemVisitors.h"
#include "Playerbots.h"
bool FindUsableItemVisitor::Visit(Item* item)
{
if (bot->CanUseItem(item->GetTemplate()) == EQUIP_ERR_OK)
return FindItemVisitor::Visit(item);
return true;
}
bool FindPotionVisitor::Accept(ItemTemplate const* proto)
{
if (proto->Class == ITEM_CLASS_CONSUMABLE && (proto->SubClass == ITEM_SUBCLASS_POTION || proto->SubClass == ITEM_SUBCLASS_FLASK))
{
for (uint8 j = 0; j < MAX_ITEM_PROTO_SPELLS; j++)
{
SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(proto->Spells[j].SpellId);
if (!spellInfo)
return false;
for (uint8 i = 0; i < 3; i++)
{
if (spellInfo->Effects[i].Effect == effectId)
return true;
}
}
}
return false;
}
bool FindMountVisitor::Accept(ItemTemplate const* proto)
{
for (uint8 j = 0; j < MAX_ITEM_PROTO_SPELLS; j++)
{
SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(proto->Spells[j].SpellId);
if (!spellInfo)
return false;
for (uint8 i = 0; i < 3; i++)
{
if (spellInfo->Effects[i].ApplyAuraName == SPELL_AURA_MOUNTED)
return true;
}
}
return false;
}
bool FindPetVisitor::Accept(ItemTemplate const* proto)
{
if (proto->Class == ITEM_CLASS_MISC)
{
for (uint8 j = 0; j < MAX_ITEM_PROTO_SPELLS; j++)
{
SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(proto->Spells[j].SpellId);
if (!spellInfo)
return false;
for (uint8 i = 0; i < 3; i++)
{
if (spellInfo->Effects[i].Effect == SPELL_EFFECT_SUMMON_PET)
return true;
}
}
}
return false;
}
FindItemUsageVisitor::FindItemUsageVisitor(Player* bot, ItemUsage usage) : FindUsableItemVisitor(bot), usage(usage)
{
context = GET_PLAYERBOT_AI(bot)->GetAiObjectContext();
};
bool FindItemUsageVisitor::Accept(ItemTemplate const* proto)
{
if (AI_VALUE2(ItemUsage, "item usage", proto->ItemId) == usage)
return true;
return false;
}

414
src/strategy/ItemVisitors.h Normal file
View File

@@ -0,0 +1,414 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
*/
#ifndef _PLAYERBOT_ITEMVISITORS_H
#define _PLAYERBOT_ITEMVISITORS_H
#include "ChatHelper.h"
#include "Common.h"
#include "Item.h"
#include "ItemUsageValue.h"
class AiObjectContext;
class Player;
char* strstri(char const* str1, char const* str2);
enum IterateItemsMask : uint32
{
ITERATE_ITEMS_IN_BAGS = 1,
ITERATE_ITEMS_IN_EQUIP = 2,
ITERATE_ITEMS_IN_BANK = 4,
ITERATE_ALL_ITEMS = 255
};
class IterateItemsVisitor
{
public:
IterateItemsVisitor() { }
virtual bool Visit(Item* item) = 0;
};
class FindItemVisitor : public IterateItemsVisitor
{
public:
FindItemVisitor() : IterateItemsVisitor(), result() { }
bool Visit(Item* item) override
{
if (!Accept(item->GetTemplate()))
return true;
result.push_back(item);
return true;
}
std::vector<Item*>& GetResult() { return result; }
protected:
virtual bool Accept(ItemTemplate const* proto) = 0;
private:
std::vector<Item*> result;
};
class FindUsableItemVisitor : public FindItemVisitor
{
public:
FindUsableItemVisitor(Player* bot) : FindItemVisitor(), bot(bot) { }
bool Visit(Item* item) override;
private:
Player* bot;
};
class FindItemsByQualityVisitor : public IterateItemsVisitor
{
public:
FindItemsByQualityVisitor(uint32 quality, uint32 count) : IterateItemsVisitor(), quality(quality), count(count) { }
bool Visit(Item* item) override
{
if (item->GetTemplate()->Quality != quality)
return true;
if (result.size() >= (size_t)count)
return false;
result.push_back(item);
return true;
}
std::vector<Item*>& GetResult()
{
return result;
}
private:
uint32 quality;
uint32 count;
std::vector<Item*> result;
};
class FindItemsToTradeByQualityVisitor : public FindItemsByQualityVisitor
{
public:
FindItemsToTradeByQualityVisitor(uint32 quality, uint32 count) : FindItemsByQualityVisitor(quality, count) { }
bool Visit(Item* item) override
{
if (item->IsSoulBound())
return true;
return FindItemsByQualityVisitor::Visit(item);
}
};
class FindItemsToTradeByClassVisitor : public IterateItemsVisitor
{
public:
FindItemsToTradeByClassVisitor(uint32 itemClass, uint32 itemSubClass, uint32 count)
: IterateItemsVisitor(), count(count), itemClass(itemClass), itemSubClass(itemSubClass) { }
bool Visit(Item* item) override
{
if (item->IsSoulBound())
return true;
if (item->GetTemplate()->Class != itemClass || item->GetTemplate()->SubClass != itemSubClass)
return true;
if (result.size() >= (size_t)count)
return false;
result.push_back(item);
return true;
}
std::vector<Item*>& GetResult()
{
return result;
}
private:
uint32 itemClass;
uint32 itemSubClass;
uint32 count;
std::vector<Item*> result;
};
class QueryItemCountVisitor : public IterateItemsVisitor
{
public:
QueryItemCountVisitor(uint32 itemId) : count(0), itemId(itemId) { }
bool Visit(Item* item) override
{
if (item->GetTemplate()->ItemId == itemId)
count += item->GetCount();
return true;
}
uint32 GetCount() { return count; }
protected:
uint32 count;
uint32 itemId;
};
class QueryNamedItemCountVisitor : public QueryItemCountVisitor
{
public:
QueryNamedItemCountVisitor(std::string const name) : QueryItemCountVisitor(0), name(name) { }
bool Visit(Item* item) override
{
ItemTemplate const* proto = item->GetTemplate();
if (proto && proto->Name1.c_str() && strstri(proto->Name1.c_str(), name.c_str()))
count += item->GetCount();
return true;
}
private:
std::string const name;
};
class FindNamedItemVisitor : public FindItemVisitor
{
public:
FindNamedItemVisitor(Player* bot, std::string const name) : FindItemVisitor(), name(name) { }
bool Accept(ItemTemplate const* proto) override
{
return proto && proto->Name1.c_str() && strstri(proto->Name1.c_str(), name.c_str());
}
private:
std::string const name;
};
class FindItemByIdVisitor : public FindItemVisitor
{
public:
FindItemByIdVisitor(uint32 id) : FindItemVisitor(), id(id) { }
bool Accept(ItemTemplate const* proto) override
{
return proto->ItemId == id;
}
private:
uint32 id;
};
class FindItemByIdsVisitor : public FindItemVisitor
{
public:
FindItemByIdsVisitor(ItemIds ids) : FindItemVisitor(), ids(ids) { }
bool Accept(ItemTemplate const* proto) override
{
return ids.find(proto->ItemId) != ids.end();
}
private:
ItemIds ids;
};
class ListItemsVisitor : public IterateItemsVisitor
{
public:
ListItemsVisitor() : IterateItemsVisitor() { }
std::map<uint32, uint32> items;
std::map<uint32, bool> soulbound;
bool Visit(Item* item) override
{
uint32 id = item->GetTemplate()->ItemId;
if (items.find(id) == items.end())
items[id] = 0;
items[id] += item->GetCount();
soulbound[id] = item->IsSoulBound();
return true;
}
};
class ItemCountByQuality : public IterateItemsVisitor
{
public:
ItemCountByQuality() : IterateItemsVisitor()
{
for (uint32 i = 0; i < MAX_ITEM_QUALITY; ++i)
count[i] = 0;
}
bool Visit(Item* item) override
{
++count[item->GetTemplate()->Quality];
return true;
}
public:
std::map<uint32, uint32> count;
};
class FindPotionVisitor : public FindUsableItemVisitor
{
public:
FindPotionVisitor(Player* bot, uint32 effectId) : FindUsableItemVisitor(bot), effectId(effectId) { }
bool Accept(ItemTemplate const* proto) override;
private:
uint32 effectId;
};
class FindFoodVisitor : public FindUsableItemVisitor
{
public:
FindFoodVisitor(Player* bot, uint32 spellCategory, bool conjured = false) : FindUsableItemVisitor(bot),
spellCategory(spellCategory), conjured(conjured) { }
bool Accept(ItemTemplate const* proto) override
{
return proto->Class == ITEM_CLASS_CONSUMABLE && (proto->SubClass == ITEM_SUBCLASS_CONSUMABLE || proto->SubClass == ITEM_SUBCLASS_FOOD) &&
proto->Spells[0].SpellCategory == spellCategory && (!conjured || proto->IsConjuredConsumable());
}
private:
uint32 spellCategory;
bool conjured;
};
class FindMountVisitor : public FindUsableItemVisitor
{
public:
FindMountVisitor(Player* bot) : FindUsableItemVisitor(bot) { }
bool Accept(ItemTemplate const* proto) override;
private:
uint32 effectId;
};
class FindPetVisitor : public FindUsableItemVisitor
{
public:
FindPetVisitor(Player* bot) : FindUsableItemVisitor(bot) { }
bool Accept(ItemTemplate const* proto) override;
};
class FindAmmoVisitor : public FindUsableItemVisitor
{
public:
FindAmmoVisitor(Player* bot, uint32 weaponType) : FindUsableItemVisitor(bot), weaponType(weaponType) { }
bool Accept(ItemTemplate const* proto)override
{
if (proto->Class == ITEM_CLASS_PROJECTILE)
{
uint32 subClass = 0;
switch (weaponType)
{
case ITEM_SUBCLASS_WEAPON_GUN:
subClass = ITEM_SUBCLASS_BULLET;
break;
case ITEM_SUBCLASS_WEAPON_BOW:
case ITEM_SUBCLASS_WEAPON_CROSSBOW:
subClass = ITEM_SUBCLASS_ARROW;
break;
}
if (!subClass)
return false;
if (proto->SubClass == subClass)
return true;
}
return false;
}
private:
uint32 weaponType;
};
class FindQuestItemVisitor : public FindUsableItemVisitor
{
public:
FindQuestItemVisitor(Player* bot) : FindUsableItemVisitor(bot) { }
bool Accept(ItemTemplate const* proto) override
{
if (proto->Class == ITEM_CLASS_QUEST)
{
return true;
}
return false;
}
};
class FindRecipeVisitor : public FindUsableItemVisitor
{
public:
FindRecipeVisitor(Player* bot, SkillType skill = SKILL_NONE) : FindUsableItemVisitor(bot), skill(skill) { };
bool Accept(ItemTemplate const* proto) override
{
if (proto->Class == ITEM_CLASS_RECIPE)
{
if (skill == SKILL_NONE)
return true;
switch (proto->SubClass)
{
case ITEM_SUBCLASS_LEATHERWORKING_PATTERN:
return skill == SKILL_LEATHERWORKING;
case ITEM_SUBCLASS_TAILORING_PATTERN:
return skill == SKILL_TAILORING;
case ITEM_SUBCLASS_ENGINEERING_SCHEMATIC:
return skill == SKILL_ENGINEERING;
case ITEM_SUBCLASS_BLACKSMITHING:
return skill == SKILL_BLACKSMITHING;
case ITEM_SUBCLASS_COOKING_RECIPE:
return skill == SKILL_COOKING;
case ITEM_SUBCLASS_ALCHEMY_RECIPE:
return skill == SKILL_ALCHEMY;
case ITEM_SUBCLASS_FIRST_AID_MANUAL:
return skill == SKILL_FIRST_AID;
case ITEM_SUBCLASS_ENCHANTING_FORMULA:
return skill == SKILL_ENCHANTING;
case ITEM_SUBCLASS_FISHING_MANUAL:
return skill == SKILL_FISHING;
}
}
return false;
}
private:
SkillType skill;
};
class FindItemUsageVisitor : public FindUsableItemVisitor
{
public:
FindItemUsageVisitor(Player* bot, ItemUsage usage = ITEM_USAGE_NONE);
bool Accept(ItemTemplate const* proto) override;
private:
AiObjectContext* context;
ItemUsage usage;
};
#endif

22
src/strategy/Multiplier.h Normal file
View File

@@ -0,0 +1,22 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
*/
#ifndef _PLAYERBOT_MULTIPLIER_H
#define _PLAYERBOT_MULTIPLIER_H
#include "AiObject.h"
class Action;
class PlayerbotAI;
class Multiplier : public AiNamedObject
{
public:
Multiplier(PlayerbotAI* botAI, std::string const name) : AiNamedObject(botAI, name) {}
virtual ~Multiplier() { }
virtual float GetValue(Action* action) { return 1.0f; }
};
#endif

View File

@@ -0,0 +1,33 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
*/
#include "NamedObjectContext.h"
#include "Playerbots.h"
void Qualified::Qualify(int qual)
{
std::ostringstream out;
out << qual;
qualifier = out.str();
}
std::string const Qualified::MultiQualify(std::vector<std::string> qualifiers)
{
std::ostringstream out;
for (auto& qualifier : qualifiers)
out << qualifier << (&qualifier != &qualifiers.back() ? " " : "");
return out.str();
}
std::vector<std::string> Qualified::getMultiQualifiers(std::string const qualifier1)
{
std::istringstream iss(qualifier1);
return { std::istream_iterator<std::string>{iss}, std::istream_iterator<std::string>{} };
}
int32 Qualified::getMultiQualifier(std::string const qualifier1, uint32 pos)
{
return std::stoi(getMultiQualifiers(qualifier1)[pos]);
}

View File

@@ -0,0 +1,282 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
*/
#ifndef _PLAYERBOT_NAMEDOBJECTCONEXT_H
#define _PLAYERBOT_NAMEDOBJECTCONEXT_H
#include "Common.h"
#include <map>
#include <set>
#include <list>
#include <vector>
class PlayerbotAI;
class Qualified
{
public:
Qualified() { };
Qualified(std::string const qualifier) : qualifier(qualifier) { }
Qualified(int32 qualifier1)
{
Qualify(qualifier1);
}
virtual void Qualify(int qual);
virtual void Qualify(std::string const qual)
{
qualifier = qual;
}
std::string const getQualifier() { return qualifier; }
static std::string const MultiQualify(std::vector<std::string> qualifiers);
static std::vector<std::string> getMultiQualifiers(std::string const qualifier1);
static int32 getMultiQualifier(std::string const qualifier1, uint32 pos);
protected:
std::string qualifier;
};
template <class T>
class NamedObjectFactory
{
protected:
typedef T*(*ActionCreator)(PlayerbotAI* botAI);
std::map<std::string, ActionCreator> creators;
public:
T* create(std::string name, PlayerbotAI* botAI)
{
size_t found = name.find("::");
std::string qualifier;
if (found != std::string::npos)
{
qualifier = name.substr(found + 2);
name = name.substr(0, found);
}
if (creators.find(name) == creators.end())
return nullptr;
ActionCreator creator = creators[name];
if (!creator)
return nullptr;
T* object = (*creator)(botAI);
Qualified* q = dynamic_cast<Qualified*>(object);
if (q && found != std::string::npos)
q->Qualify(qualifier);
return object;
}
std::set<std::string> supports()
{
std::set<std::string> keys;
for (typename std::map<std::string, ActionCreator>::iterator it = creators.begin(); it != creators.end(); it++)
keys.insert(it->first);
return std::move(keys);
}
};
template <class T>
class NamedObjectContext : public NamedObjectFactory<T>
{
public:
NamedObjectContext(bool shared = false, bool supportsSiblings = false) :
NamedObjectFactory<T>(), shared(shared), supportsSiblings(supportsSiblings) { }
virtual ~NamedObjectContext()
{
Clear();
}
T* create(std::string const name, PlayerbotAI* botAI)
{
if (created.find(name) == created.end())
return created[name] = NamedObjectFactory<T>::create(name, botAI);
return created[name];
}
void Clear()
{
for (typename std::map<std::string, T*>::iterator i = created.begin(); i != created.end(); i++)
{
if (i->second)
delete i->second;
}
created.clear();
}
void Update()
{
for (typename std::map<std::string, T*>::iterator i = created.begin(); i != created.end(); i++)
{
if (i->second)
i->second->Update();
}
}
void Reset()
{
for (typename std::map<std::string, T*>::iterator i = created.begin(); i != created.end(); i++)
{
if (i->second)
i->second->Reset();
}
}
bool IsShared() { return shared; }
bool IsSupportsSiblings() { return supportsSiblings; }
std::set<std::string> GetCreated()
{
std::set<std::string> keys;
for (typename std::map<std::string, T*>::iterator it = created.begin(); it != created.end(); it++)
keys.insert(it->first);
return std::move(keys);
}
protected:
std::map<std::string, T*> created;
bool shared;
bool supportsSiblings;
};
template <class T>
class NamedObjectContextList
{
public:
virtual ~NamedObjectContextList()
{
for (typename std::vector<NamedObjectContext<T>*>::iterator i = contexts.begin(); i != contexts.end(); i++)
{
NamedObjectContext<T>* context = *i;
if (!context->IsShared())
delete context;
}
}
void Add(NamedObjectContext<T>* context)
{
contexts.push_back(context);
}
T* GetContextObject(std::string const name, PlayerbotAI* botAI)
{
for (typename std::vector<NamedObjectContext<T>*>::iterator i = contexts.begin(); i != contexts.end(); i++)
{
if (T* object = (*i)->create(name, botAI))
return object;
}
return nullptr;
}
void Update()
{
for (typename std::vector<NamedObjectContext<T>*>::iterator i = contexts.begin(); i != contexts.end(); i++)
{
if (!(*i)->IsShared())
(*i)->Update();
}
}
void Reset()
{
for (typename std::vector<NamedObjectContext<T>*>::iterator i = contexts.begin(); i != contexts.end(); i++)
{
(*i)->Reset();
}
}
std::set<std::string> GetSiblings(std::string const name)
{
for (typename std::vector<NamedObjectContext<T>*>::iterator i = contexts.begin(); i != contexts.end(); i++)
{
if (!(*i)->IsSupportsSiblings())
continue;
std::set<std::string> supported = (*i)->supports();
std::set<std::string>::iterator found = supported.find(name);
if (found == supported.end())
continue;
supported.erase(found);
return supported;
}
return std::move(std::set<std::string>());
}
std::set<std::string> supports()
{
std::set<std::string> result;
for (typename std::vector<NamedObjectContext<T>*>::iterator i = contexts.begin(); i != contexts.end(); i++)
{
std::set<std::string> supported = (*i)->supports();
for (std::set<std::string>::iterator j = supported.begin(); j != supported.end(); j++)
result.insert(*j);
}
return std::move(result);
}
std::set<std::string> GetCreated()
{
std::set<std::string> result;
for (typename std::vector<NamedObjectContext<T>*>::iterator i = contexts.begin(); i != contexts.end(); i++)
{
std::set<std::string> createdKeys = (*i)->GetCreated();
for (std::set<std::string>::iterator j = createdKeys.begin(); j != createdKeys.end(); j++)
result.insert(*j);
}
return std::move(result);
}
private:
std::vector<NamedObjectContext<T>*> contexts;
};
template <class T>
class NamedObjectFactoryList
{
public:
virtual ~NamedObjectFactoryList()
{
for (typename std::list<NamedObjectFactory<T>*>::iterator i = factories.begin(); i != factories.end(); i++)
delete *i;
}
void Add(NamedObjectFactory<T>* context)
{
factories.push_front(context);
}
T* GetContextObject(std::string const name, PlayerbotAI* botAI)
{
for (typename std::list<NamedObjectFactory<T>*>::iterator i = factories.begin(); i != factories.end(); i++)
{
if (T* object = (*i)->create(name, botAI))
return object;
}
return nullptr;
}
private:
std::list<NamedObjectFactory<T>*> factories;
};
#endif

View File

@@ -0,0 +1,50 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
*/
#include "PassiveMultiplier.h"
#include "Action.h"
#include "AiObjectContext.h"
std::vector<std::string> PassiveMultiplier::allowedActions;
std::vector<std::string> PassiveMultiplier::allowedParts;
PassiveMultiplier::PassiveMultiplier(PlayerbotAI* botAI) : Multiplier(botAI, "passive")
{
if (allowedActions.empty())
{
allowedActions.push_back("co");
allowedActions.push_back("nc");
allowedActions.push_back("reset botAI");
allowedActions.push_back("check mount state");
}
if (allowedParts.empty())
{
allowedParts.push_back("follow");
allowedParts.push_back("stay");
allowedParts.push_back("chat shortcut");
}
}
float PassiveMultiplier::GetValue(Action* action)
{
if (!action)
return 1.0f;
std::string const name = action->getName();
for (std::vector<std::string>::iterator i = allowedActions.begin(); i != allowedActions.end(); i++)
{
if (name == *i)
return 1.0f;
}
for (std::vector<std::string>::iterator i = allowedParts.begin(); i != allowedParts.end(); i++)
{
if (name.find(*i) != std::string::npos)
return 1.0f;
}
return 0;
}

View File

@@ -0,0 +1,27 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
*/
#ifndef _PLAYERBOT_PASSIVEMULTIPLIER_H
#define _PLAYERBOT_PASSIVEMULTIPLIER_H
#include "Multiplier.h"
#include <vector>
class Action;
class PlayerbotAI;
class PassiveMultiplier : public Multiplier
{
public:
PassiveMultiplier(PlayerbotAI* botAI);
float GetValue(Action* action) override;
private:
static std::vector<std::string> allowedActions;
static std::vector<std::string> allowedParts;
};
#endif

106
src/strategy/Queue.cpp Normal file
View File

@@ -0,0 +1,106 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
*/
#include "Queue.h"
#include "AiObjectContext.h"
#include "Log.h"
#include "PlayerbotAIConfig.h"
void Queue::Push(ActionBasket *action)
{
if (action)
{
for (std::list<ActionBasket*>::iterator iter = actions.begin(); iter != actions.end(); iter++)
{
ActionBasket* basket = *iter;
if (action->getAction()->getName() == basket->getAction()->getName())
{
if (basket->getRelevance() < action->getRelevance())
basket->setRelevance(action->getRelevance());
if (ActionNode* actionNode = action->getAction())
delete actionNode;
delete action;
return;
}
}
actions.push_back(action);
}
}
ActionNode* Queue::Pop()
{
float max = -1;
ActionBasket* selection = nullptr;
for (std::list<ActionBasket*>::iterator iter = actions.begin(); iter != actions.end(); iter++)
{
ActionBasket* basket = *iter;
if (basket->getRelevance() > max)
{
max = basket->getRelevance();
selection = basket;
}
}
if (selection != nullptr)
{
ActionNode* action = selection->getAction();
actions.remove(selection);
delete selection;
return action;
}
return nullptr;
}
ActionBasket* Queue::Peek()
{
float max = -1;
ActionBasket* selection = nullptr;
for (std::list<ActionBasket*>::iterator iter = actions.begin(); iter != actions.end(); iter++)
{
ActionBasket* basket = *iter;
if (basket->getRelevance() > max)
{
max = basket->getRelevance();
selection = basket;
}
}
return selection;
}
uint32 Queue::Size()
{
return actions.size();
}
void Queue::RemoveExpired()
{
std::list<ActionBasket*> expired;
for (std::list<ActionBasket*>::iterator iter = actions.begin(); iter != actions.end(); iter++)
{
ActionBasket* basket = *iter;
if (sPlayerbotAIConfig->expireActionTime && basket->isExpired(sPlayerbotAIConfig->expireActionTime / 1000))
expired.push_back(basket);
}
for (std::list<ActionBasket*>::iterator iter = expired.begin(); iter != expired.end(); iter++)
{
ActionBasket* basket = *iter;
actions.remove(basket);
if (ActionNode* action = basket->getAction())
{
LOG_DEBUG("playerbots", "Action {} is expired", action->getName().c_str());
delete action;
}
delete basket;
}
}

27
src/strategy/Queue.h Normal file
View File

@@ -0,0 +1,27 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
*/
#ifndef _PLAYERBOT_QUEUE_H
#define _PLAYERBOT_QUEUE_H
#include "Common.h"
#include "Action.h"
class Queue
{
public:
Queue(void) { }
~Queue(void) { }
void Push(ActionBasket *action);
ActionNode* Pop();
ActionBasket* Peek();
uint32 Size();
void RemoveExpired();
private:
std::list<ActionBasket*> actions;
};
#endif

125
src/strategy/Strategy.cpp Normal file
View File

@@ -0,0 +1,125 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
*/
#include "Strategy.h"
#include "Playerbots.h"
class ActionNodeFactoryInternal : public NamedObjectFactory<ActionNode>
{
public:
ActionNodeFactoryInternal()
{
creators["melee"] = &melee;
creators["healthstone"] = &healthstone;
creators["be near"] = &follow_master_random;
creators["attack anything"] = &attack_anything;
creators["move random"] = &move_random;
creators["move to loot"] = &move_to_loot;
creators["food"] = &food;
creators["drink"] = &drink;
creators["mana potion"] = &mana_potion;
creators["healing potion"] = &healing_potion;
creators["flee"] = &flee;
}
private:
static ActionNode* melee(PlayerbotAI* botAI)
{
return new ActionNode ("melee",
/*P*/ nullptr,
/*A*/ nullptr,
/*C*/ nullptr);
}
static ActionNode* healthstone(PlayerbotAI* botAI)
{
return new ActionNode ("healthstone",
/*P*/ nullptr,
/*A*/ NextAction::array(0, new NextAction("healing potion"), nullptr),
/*C*/ nullptr);
}
static ActionNode* follow_master_random(PlayerbotAI* botAI)
{
return new ActionNode ("be near",
/*P*/ nullptr,
/*A*/ NextAction::array(0, new NextAction("follow"), nullptr),
/*C*/ nullptr);
}
static ActionNode* attack_anything(PlayerbotAI* botAI)
{
return new ActionNode ("attack anything",
/*P*/ nullptr,
/*A*/ nullptr,
/*C*/ nullptr);
}
static ActionNode* move_random(PlayerbotAI* botAI)
{
return new ActionNode ("move random",
/*P*/ nullptr,
/*A*/ NextAction::array(0, new NextAction("stay line"), nullptr),
/*C*/ nullptr);
}
static ActionNode* move_to_loot(PlayerbotAI* botAI)
{
return new ActionNode ("move to loot",
/*P*/ nullptr,
/*A*/ nullptr,
/*C*/ nullptr);
}
static ActionNode* food(PlayerbotAI* botAI)
{
return new ActionNode ("food",
/*P*/ nullptr,
/*A*/ nullptr,
/*C*/ nullptr);
}
static ActionNode* drink(PlayerbotAI* botAI)
{
return new ActionNode ("drink",
/*P*/ nullptr,
/*A*/ nullptr,
/*C*/ nullptr);
}
static ActionNode* mana_potion(PlayerbotAI* botAI)
{
return new ActionNode ("mana potion",
/*P*/ nullptr,
/*A*/ NextAction::array(0, new NextAction("drink"), nullptr),
/*C*/ nullptr);
}
static ActionNode* healing_potion(PlayerbotAI* botAI)
{
return new ActionNode ("healing potion",
/*P*/ nullptr,
/*A*/ NextAction::array(0, new NextAction("food"), nullptr),
/*C*/ nullptr);
}
static ActionNode* flee(PlayerbotAI* botAI)
{
return new ActionNode ("flee",
/*P*/ nullptr,
/*A*/ nullptr,
/*C*/ nullptr);
}
};
Strategy::Strategy(PlayerbotAI* botAI) : PlayerbotAIAware(botAI)
{
actionNodeFactories.Add(new ActionNodeFactoryInternal());
}
ActionNode* Strategy::GetAction(std::string const name)
{
return actionNodeFactories.GetContextObject(name, botAI);
}

59
src/strategy/Strategy.h Normal file
View File

@@ -0,0 +1,59 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
*/
#ifndef _PLAYERBOT_STRATEGY_H
#define _PLAYERBOT_STRATEGY_H
#include "Action.h"
#include "NamedObjectContext.h"
#include "Multiplier.h"
#include "Trigger.h"
#include "PlayerbotAIAware.h"
enum StrategyType : uint32
{
STRATEGY_TYPE_GENERIC = 0,
STRATEGY_TYPE_COMBAT = 1,
STRATEGY_TYPE_NONCOMBAT = 2,
STRATEGY_TYPE_TANK = 4,
STRATEGY_TYPE_DPS = 8,
STRATEGY_TYPE_HEAL = 16,
STRATEGY_TYPE_RANGED = 32,
STRATEGY_TYPE_MELEE = 64
};
enum ActionPriority
{
ACTION_IDLE = 0,
ACTION_NORMAL = 10,
ACTION_HIGH = 20,
ACTION_MOVE = 30,
ACTION_INTERRUPT = 40,
ACTION_DISPEL = 50,
ACTION_LIGHT_HEAL = 60,
ACTION_MEDIUM_HEAL = 70,
ACTION_CRITICAL_HEAL = 80,
ACTION_EMERGENCY = 90
};
class Strategy : public PlayerbotAIAware
{
public:
Strategy(PlayerbotAI* botAI);
virtual ~Strategy() { }
virtual NextAction** getDefaultActions() { return nullptr; }
virtual void InitTriggers(std::vector<TriggerNode*> &triggers) { }
virtual void InitMultipliers(std::vector<Multiplier*> &multipliers) { }
virtual std::string const getName() = 0;
virtual uint32 GetType() const { return STRATEGY_TYPE_GENERIC; }
virtual ActionNode* GetAction(std::string const name);
void Update() { }
void Reset() { }
protected:
NamedObjectFactoryList<ActionNode> actionNodeFactories;
};
#endif

View File

@@ -0,0 +1,221 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
*/
#ifndef _PLAYERBOT_STRATEGYCONTEXT_H
#define _PLAYERBOT_STRATEGYCONTEXT_H
#include "CustomStrategy.h"
#include "NamedObjectContext.h"
#include "AttackEnemyPlayersStrategy.h"
#include "BattlegroundStrategy.h"
#include "CastTimeStrategy.h"
#include "ChatCommandHandlerStrategy.h"
#include "ConserveManaStrategy.h"
#include "DeadStrategy.h"
#include "DebugStrategy.h"
#include "DpsAssistStrategy.h"
#include "DuelStrategy.h"
#include "EmoteStrategy.h"
#include "FleeStrategy.h"
#include "FollowMasterStrategy.h"
#include "GrindingStrategy.h"
#include "GroupStrategy.h"
#include "GuardStrategy.h"
#include "GuildStrategy.h"
#include "KiteStrategy.h"
#include "LfgStrategy.h"
#include "LootNonCombatStrategy.h"
#include "MaintenanceStrategy.h"
#include "MarkRtiStrategy.h"
#include "MeleeCombatStrategy.h"
#include "NonCombatStrategy.h"
#include "QuestStrategies.h"
#include "PassiveStrategy.h"
#include "PullStrategy.h"
#include "RacialsStrategy.h"
#include "RangedCombatStrategy.h"
#include "ReturnStrategy.h"
#include "RpgStrategy.h"
#include "RTSCStrategy.h"
#include "RunawayStrategy.h"
#include "StayStrategy.h"
#include "TankAssistStrategy.h"
#include "TellTargetStrategy.h"
#include "ThreatStrategy.h"
#include "TravelStrategy.h"
#include "UseFoodStrategy.h"
#include "UsePotionsStrategy.h"
#include "WorldPacketHandlerStrategy.h"
class StrategyContext : public NamedObjectContext<Strategy>
{
public:
StrategyContext()
{
creators["racials"] = &StrategyContext::racials;
creators["loot"] = &StrategyContext::loot;
creators["gather"] = &StrategyContext::gather;
creators["emote"] = &StrategyContext::emote;
creators["passive"] = &StrategyContext::passive;
creators["conserve mana"] = &StrategyContext::conserve_mana;
creators["food"] = &StrategyContext::food;
creators["chat"] = &StrategyContext::chat;
creators["default"] = &StrategyContext::world_packet;
creators["ready check"] = &StrategyContext::ready_check;
creators["dead"] = &StrategyContext::dead;
creators["flee"] = &StrategyContext::flee;
creators["duel"] = &StrategyContext::duel;
creators["start duel"] = &StrategyContext::start_duel;
creators["kite"] = &StrategyContext::kite;
creators["potions"] = &StrategyContext::potions;
creators["cast time"] = &StrategyContext::cast_time;
creators["threat"] = &StrategyContext::threat;
creators["tell target"] = &StrategyContext::tell_target;
creators["pvp"] = &StrategyContext::pvp;
creators["return"] = &StrategyContext::_return;
creators["lfg"] = &StrategyContext::lfg;
creators["custom"] = &StrategyContext::custom;
creators["reveal"] = &StrategyContext::reveal;
creators["collision"] = &StrategyContext::collision;
creators["rpg"] = &StrategyContext::rpg;
creators["travel"] = &StrategyContext::travel;
creators["explore"] = &StrategyContext::explore;
creators["map"] = &StrategyContext::map;
creators["map full"] = &StrategyContext::map_full;
creators["sit"] = &StrategyContext::sit;
creators["mark rti"] = &StrategyContext::mark_rti;
creators["ads"] = &StrategyContext::possible_adds;
creators["close"] = &StrategyContext::close;
creators["ranged"] = &StrategyContext::ranged;
creators["behind"] = &StrategyContext::behind;
creators["bg"] = &StrategyContext::bg;
creators["Battleground"] = &StrategyContext::Battleground;
creators["warsong"] = &StrategyContext::warsong;
creators["alterac"] = &StrategyContext::alterac;
creators["arathi"] = &StrategyContext::arathi;
creators["eye"] = &StrategyContext::eye;
creators["isle"] = &StrategyContext::isle;
creators["arena"] = &StrategyContext::arena;
creators["mount"] = &StrategyContext::mount;
creators["rtsc"] = &StrategyContext::rtsc;
creators["attack tagged"] = &StrategyContext::attack_tagged;
creators["debug"] = &StrategyContext::debug;
creators["debug move"] = &StrategyContext::debug_move;
creators["debug rpg"] = &StrategyContext::debug_rpg;
creators["debug spell"] = &StrategyContext::debug_spell;
creators["maintenance"] = &StrategyContext::maintenance;
creators["group"] = &StrategyContext::group;
creators["guild"] = &StrategyContext::guild;
creators["grind"] = &StrategyContext::grind;
}
private:
static Strategy* behind(PlayerbotAI* botAI) { return new SetBehindCombatStrategy(botAI); }
static Strategy* ranged(PlayerbotAI* botAI) { return new RangedCombatStrategy(botAI); }
static Strategy* close(PlayerbotAI* botAI) { return new MeleeCombatStrategy(botAI); }
static Strategy* mark_rti(PlayerbotAI* botAI) { return new MarkRtiStrategy(botAI); }
static Strategy* tell_target(PlayerbotAI* botAI) { return new TellTargetStrategy(botAI); }
static Strategy* threat(PlayerbotAI* botAI) { return new ThreatStrategy(botAI); }
static Strategy* cast_time(PlayerbotAI* botAI) { return new CastTimeStrategy(botAI); }
static Strategy* potions(PlayerbotAI* botAI) { return new UsePotionsStrategy(botAI); }
static Strategy* kite(PlayerbotAI* botAI) { return new KiteStrategy(botAI); }
static Strategy* duel(PlayerbotAI* botAI) { return new DuelStrategy(botAI); }
static Strategy* start_duel(PlayerbotAI* botAI) { return new StartDuelStrategy(botAI); }
static Strategy* flee(PlayerbotAI* botAI) { return new FleeStrategy(botAI); }
static Strategy* dead(PlayerbotAI* botAI) { return new DeadStrategy(botAI); }
static Strategy* racials(PlayerbotAI* botAI) { return new RacialsStrategy(botAI); }
static Strategy* loot(PlayerbotAI* botAI) { return new LootNonCombatStrategy(botAI); }
static Strategy* gather(PlayerbotAI* botAI) { return new GatherStrategy(botAI); }
static Strategy* emote(PlayerbotAI* botAI) { return new EmoteStrategy(botAI); }
static Strategy* passive(PlayerbotAI* botAI) { return new PassiveStrategy(botAI); }
static Strategy* conserve_mana(PlayerbotAI* botAI) { return new ConserveManaStrategy(botAI); }
static Strategy* food(PlayerbotAI* botAI) { return new UseFoodStrategy(botAI); }
static Strategy* chat(PlayerbotAI* botAI) { return new ChatCommandHandlerStrategy(botAI); }
static Strategy* world_packet(PlayerbotAI* botAI) { return new WorldPacketHandlerStrategy(botAI); }
static Strategy* ready_check(PlayerbotAI* botAI) { return new ReadyCheckStrategy(botAI); }
static Strategy* pvp(PlayerbotAI* botAI) { return new AttackEnemyPlayersStrategy(botAI); }
static Strategy* _return(PlayerbotAI* botAI) { return new ReturnStrategy(botAI); }
static Strategy* lfg(PlayerbotAI* botAI) { return new LfgStrategy(botAI); }
static Strategy* custom(PlayerbotAI* botAI) { return new CustomStrategy(botAI); }
static Strategy* reveal(PlayerbotAI* botAI) { return new RevealStrategy(botAI); }
static Strategy* collision(PlayerbotAI* botAI) { return new CollisionStrategy(botAI); }
static Strategy* rpg(PlayerbotAI* botAI) { return new RpgStrategy(botAI); }
static Strategy* travel(PlayerbotAI* botAI) { return new TravelStrategy(botAI); }
static Strategy* explore(PlayerbotAI* botAI) { return new ExploreStrategy(botAI); }
static Strategy* map(PlayerbotAI* botAI) { return new MapStrategy(botAI); }
static Strategy* map_full(PlayerbotAI* botAI) { return new MapFullStrategy(botAI); }
static Strategy* sit(PlayerbotAI* botAI) { return new SitStrategy(botAI); }
static Strategy* possible_adds(PlayerbotAI* botAI) { return new PossibleAddsStrategy(botAI); }
static Strategy* mount(PlayerbotAI* botAI) { return new MountStrategy(botAI); }
static Strategy* bg(PlayerbotAI* botAI) { return new BGStrategy(botAI); }
static Strategy* Battleground(PlayerbotAI* botAI) { return new BattlegroundStrategy(botAI); }
static Strategy* warsong(PlayerbotAI* botAI) { return new WarsongStrategy(botAI); }
static Strategy* alterac(PlayerbotAI* botAI) { return new AlteracStrategy(botAI); }
static Strategy* arathi(PlayerbotAI* botAI) { return new ArathiStrategy(botAI); }
static Strategy* eye(PlayerbotAI* botAI) { return new EyeStrategy(botAI); }
static Strategy* isle(PlayerbotAI* botAI) { return new IsleStrategy(botAI); }
static Strategy* arena(PlayerbotAI* botAI) { return new ArenaStrategy(botAI); }
static Strategy* rtsc(PlayerbotAI* botAI) { return new RTSCStrategy(botAI); }
static Strategy* attack_tagged(PlayerbotAI* botAI) { return new AttackTaggedStrategy(botAI); }
static Strategy* debug(PlayerbotAI* botAI) { return new DebugStrategy(botAI); }
static Strategy* debug_move(PlayerbotAI* botAI) { return new DebugMoveStrategy(botAI); }
static Strategy* debug_rpg(PlayerbotAI* botAI) { return new DebugRpgStrategy(botAI); }
static Strategy* debug_spell(PlayerbotAI* botAI) { return new DebugSpellStrategy(botAI); }
static Strategy* maintenance(PlayerbotAI* botAI) { return new MaintenanceStrategy(botAI); }
static Strategy* group(PlayerbotAI* botAI) { return new GroupStrategy(botAI); }
static Strategy* guild (PlayerbotAI* botAI) { return new GuildStrategy(botAI); }
static Strategy* grind(PlayerbotAI* botAI) { return new GrindingStrategy(botAI); }
};
class MovementStrategyContext : public NamedObjectContext<Strategy>
{
public:
MovementStrategyContext() : NamedObjectContext<Strategy>(false, true)
{
creators["follow"] = &MovementStrategyContext::follow_master;
creators["stay"] = &MovementStrategyContext::stay;
creators["runaway"] = &MovementStrategyContext::runaway;
creators["flee from adds"] = &MovementStrategyContext::flee_from_adds;
creators["guard"] = &MovementStrategyContext::guard;
}
private:
static Strategy* guard(PlayerbotAI* botAI) { return new GuardStrategy(botAI); }
static Strategy* follow_master(PlayerbotAI* botAI) { return new FollowMasterStrategy(botAI); }
static Strategy* stay(PlayerbotAI* botAI) { return new StayStrategy(botAI); }
static Strategy* runaway(PlayerbotAI* botAI) { return new RunawayStrategy(botAI); }
static Strategy* flee_from_adds(PlayerbotAI* botAI) { return new FleeFromAddsStrategy(botAI); }
};
class AssistStrategyContext : public NamedObjectContext<Strategy>
{
public:
AssistStrategyContext() : NamedObjectContext<Strategy>(false, true)
{
creators["dps assist"] = &AssistStrategyContext::dps_assist;
creators["dps aoe"] = &AssistStrategyContext::dps_aoe;
creators["tank assist"] = &AssistStrategyContext::tank_assist;
}
private:
static Strategy* dps_assist(PlayerbotAI* botAI) { return new DpsAssistStrategy(botAI); }
static Strategy* dps_aoe(PlayerbotAI* botAI) { return new DpsAoeStrategy(botAI); }
static Strategy* tank_assist(PlayerbotAI* botAI) { return new TankAssistStrategy(botAI); }
};
class QuestStrategyContext : public NamedObjectContext<Strategy>
{
public:
QuestStrategyContext() : NamedObjectContext<Strategy>(false, true)
{
creators["quest"] = &QuestStrategyContext::quest;
creators["accept all quests"] = &QuestStrategyContext::accept_all_quests;
}
private:
static Strategy* quest(PlayerbotAI* botAI) { return new DefaultQuestStrategy(botAI); }
static Strategy* accept_all_quests(PlayerbotAI* botAI) { return new AcceptAllQuestsStrategy(botAI); }
};
#endif

49
src/strategy/Trigger.cpp Normal file
View File

@@ -0,0 +1,49 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
*/
#include "Trigger.h"
#include "Event.h"
#include "Playerbots.h"
Trigger::Trigger(PlayerbotAI* botAI, std::string const name, int32 checkInterval) :
AiNamedObject(botAI, name), checkInterval(checkInterval), lastCheckTime(time(nullptr) - rand() % checkInterval)
{
}
Event Trigger::Check()
{
if (IsActive())
{
Event event(getName());
return event;
}
Event event;
return event;
}
Value<Unit*>* Trigger::GetTargetValue()
{
return context->GetValue<Unit*>(GetTargetName());
}
Unit* Trigger::GetTarget()
{
return GetTargetValue()->Get();
}
bool Trigger::needCheck()
{
if (checkInterval < 2)
return true;
time_t now = time(nullptr);
if (!lastCheckTime || now - lastCheckTime >= checkInterval)
{
lastCheckTime = now;
return true;
}
return false;
}

69
src/strategy/Trigger.h Normal file
View File

@@ -0,0 +1,69 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
*/
#ifndef _PLAYERBOT_TRIGGER_H
#define _PLAYERBOT_TRIGGER_H
#include "Common.h"
#include "Action.h"
class PlayerbotAI;
class Unit;
class Trigger : public AiNamedObject
{
public:
Trigger(PlayerbotAI* botAI, std::string const name = "trigger", int32 checkInterval = 1);
virtual ~Trigger() { }
virtual Event Check();
virtual void ExternalEvent(std::string const param, Player* owner = nullptr) { }
virtual void ExternalEvent(WorldPacket& packet, Player* owner = nullptr) { }
virtual bool IsActive() { return false; }
virtual NextAction** getHandlers() { return nullptr; }
void Update() { }
virtual void Reset() { }
virtual Unit* GetTarget();
virtual Value<Unit*>* GetTargetValue();
virtual std::string const GetTargetName() { return "self target"; }
bool needCheck();
protected:
int32 checkInterval;
time_t lastCheckTime;
};
class TriggerNode
{
public:
TriggerNode(std::string const name, NextAction** handlers = nullptr) : name(name), handlers(handlers), trigger(nullptr) { }
virtual ~TriggerNode()
{
NextAction::destroy(handlers);
}
Trigger* getTrigger() { return trigger; }
void setTrigger(Trigger* trigger) { this->trigger = trigger; }
std::string const getName() { return name; }
NextAction** getHandlers()
{
return NextAction::merge(NextAction::clone(handlers), trigger->getHandlers());
}
float getFirstRelevance()
{
return handlers[0] ? handlers[0]->getRelevance() : -1;
}
private:
Trigger* trigger;
NextAction** handlers;
std::string const name;
};
#endif

111
src/strategy/Value.cpp Normal file
View File

@@ -0,0 +1,111 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
*/
#include "Value.h"
#include "PerformanceMonitor.h"
#include "Playerbots.h"
UnitCalculatedValue::UnitCalculatedValue(PlayerbotAI* botAI, std::string const name, int32 checkInterval) : CalculatedValue<Unit*>(botAI, name, checkInterval)
{
lastCheckTime = time(nullptr) - checkInterval / 2;
}
std::string const UnitCalculatedValue::Format()
{
Unit* unit = Calculate();
return unit ? unit->GetName() : "<none>";
}
std::string const UnitManualSetValue::Format()
{
Unit* unit = Get();
return unit ? unit->GetName() : "<none>";
}
std::string const Uint8CalculatedValue::Format()
{
std::ostringstream out;
out << Calculate();
return out.str();
}
std::string const Uint32CalculatedValue::Format()
{
std::ostringstream out;
out << Calculate();
return out.str();
}
std::string const FloatCalculatedValue::Format()
{
std::ostringstream out;
out << Calculate();
return out.str();
}
CDPairCalculatedValue::CDPairCalculatedValue(PlayerbotAI* botAI, std::string const name, int32 checkInterval) :
CalculatedValue<CreatureData const*>(botAI, name, checkInterval)
{
lastCheckTime = time(nullptr) - checkInterval / 2;
}
std::string const CDPairCalculatedValue::Format()
{
CreatureData const* creatureData = Calculate();
CreatureTemplate const* bmTemplate = sObjectMgr->GetCreatureTemplate(creatureData->id1);
return bmTemplate ? bmTemplate->Name : "<none>";
}
CDPairListCalculatedValue::CDPairListCalculatedValue(PlayerbotAI* botAI, std::string const name, int32 checkInterval) :
CalculatedValue<std::vector<CreatureData const*>>(botAI, name, checkInterval)
{
lastCheckTime = time(nullptr) - checkInterval / 2;
}
std::string const CDPairListCalculatedValue::Format()
{
std::ostringstream out; out << "{";
std::vector<CreatureData const*> cdPairs = Calculate();
for (CreatureData const* cdPair : cdPairs)
{
out << cdPair->id1 << ",";
}
out << "}";
return out.str();
}
ObjectGuidCalculatedValue::ObjectGuidCalculatedValue(PlayerbotAI* botAI, std::string const name, int32 checkInterval) :
CalculatedValue<ObjectGuid>(botAI, name, checkInterval)
{
lastCheckTime = time(nullptr) - checkInterval / 2;
}
std::string const ObjectGuidCalculatedValue::Format()
{
ObjectGuid guid = Calculate();
return guid ? std::to_string(guid.GetRawValue()) : "<none>";
}
ObjectGuidListCalculatedValue::ObjectGuidListCalculatedValue(PlayerbotAI* botAI, std::string const name, int32 checkInterval) :
CalculatedValue<GuidVector>(botAI, name, checkInterval)
{
lastCheckTime = time(nullptr) - checkInterval / 2;
}
std::string const ObjectGuidListCalculatedValue::Format()
{
std::ostringstream out;
out << "{";
GuidVector guids = Calculate();
for (GuidVector::iterator i = guids.begin(); i != guids.end(); ++i)
{
ObjectGuid guid = *i;
out << guid.GetRawValue() << ",";
}
out << "}";
return out.str();
}

319
src/strategy/Value.h Normal file
View File

@@ -0,0 +1,319 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
*/
#ifndef _PLAYERBOT_VALUE_H
#define _PLAYERBOT_VALUE_H
#include "AiObject.h"
#include "ObjectGuid.h"
#include "PerformanceMonitor.h"
#include <time.h>
class PlayerbotAI;
class Unit;
struct CreatureData;
class UntypedValue : public AiNamedObject
{
public:
UntypedValue(PlayerbotAI* botAI, std::string const name) : AiNamedObject(botAI, name) { }
virtual void Update() { }
virtual void Reset() { }
virtual std::string const Format() { return "?"; }
virtual std::string const Save() { return "?"; }
virtual bool Load(std::string const value) { return false; }
};
template<class T>
class Value
{
public:
virtual ~Value() { }
virtual T Get() = 0;
virtual T LazyGet() = 0;
virtual void Reset() { }
virtual void Set(T value) = 0;
operator T() { return Get(); }
};
template<class T>
class CalculatedValue : public UntypedValue, public Value<T>
{
public:
CalculatedValue(PlayerbotAI* botAI, std::string const name = "value", uint32 checkInterval = 1) : UntypedValue(botAI, name),
checkInterval(checkInterval), lastCheckTime(0) { }
virtual ~CalculatedValue() { }
T Get() override
{
time_t now = time(nullptr);
if (!lastCheckTime || checkInterval < 2 || now - lastCheckTime >= checkInterval / 2)
{
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;
}
T LazyGet() override
{
if (!lastCheckTime)
return Get();
return value;
}
void Set(T val) override { value = val; }
void Update() override {}
void Reset() override { lastCheckTime = 0; }
protected:
virtual T Calculate() = 0;
uint32 checkInterval;
time_t lastCheckTime;
T value;
};
template <class T>
class SingleCalculatedValue : public CalculatedValue<T>
{
public:
SingleCalculatedValue(PlayerbotAI* botAI, std::string const name = "value") : CalculatedValue<T>(botAI, name)
{
this->Reset();
}
T Get() override
{
time_t now = time(0);
if (!this->lastCheckTime)
{
this->lastCheckTime = now;
PerformanceMonitorOperation* pmo = sPerformanceMonitor->start(PERF_MON_VALUE, this->getName(), this->context ? &this->context->performanceStack : nullptr);
this->value = this->Calculate();
if (pmo)
pmo->finish();
}
return this->value;
}
};
template<class T>
class MemoryCalculatedValue : public CalculatedValue<T>
{
public:
MemoryCalculatedValue(PlayerbotAI* botAI, std::string const name = "value", int32 checkInterval = 1) : CalculatedValue<T>(botAI, name, checkInterval)
{
lastChangeTime = time(0);
}
virtual bool EqualToLast(T value) = 0;
virtual bool CanCheckChange()
{
return time(0) - lastChangeTime < minChangeInterval || EqualToLast(this->value);
}
virtual bool UpdateChange()
{
if (CanCheckChange())
return false;
lastChangeTime = time(0);
lastValue = this->value;
return true;
}
void Set(T value) override
{
CalculatedValue<T>::Set(this->value);
UpdateChange();
}
T Get() override
{
this->value = CalculatedValue<T>::Get();
UpdateChange();
return this->value;
}
T LazyGet() override
{
return this->value;
}
time_t LastChangeOn()
{
Get();
UpdateChange();
return lastChangeTime;
}
uint32 LastChangeDelay() { return time(0) - LastChangeOn(); }
void Reset() override
{
CalculatedValue<T>::Reset();
lastChangeTime = time(0);
}
protected:
T lastValue;
uint32 minChangeInterval = 0;
time_t lastChangeTime;
};
template<class T>
class LogCalculatedValue : public MemoryCalculatedValue<T>
{
public:
LogCalculatedValue(PlayerbotAI* botAI, std::string const name = "value", int32 checkInterval = 1) : MemoryCalculatedValue<T>(botAI, name, checkInterval) { }
bool UpdateChange() override
{
if (MemoryCalculatedValue<T>::UpdateChange())
return false;
valueLog.push_back(std::make_pair(this->value, time(0)));
if (valueLog.size() > logLength)
valueLog.pop_front();
return true;
}
std::list<std::pair<T, time_t>> ValueLog() { return valueLog; }
void Reset() override
{
MemoryCalculatedValue<T>::Reset();
valueLog.clear();
}
protected:
std::list<std::pair<T, time_t>> valueLog;
uint8 logLength = 10;
};
class Uint8CalculatedValue : public CalculatedValue<uint8>
{
public:
Uint8CalculatedValue(PlayerbotAI* botAI, std::string const name = "value", uint32 checkInterval = 1) :
CalculatedValue<uint8>(botAI, name, checkInterval) { }
std::string const Format() override;
};
class Uint32CalculatedValue : public CalculatedValue<uint32>
{
public:
Uint32CalculatedValue(PlayerbotAI* botAI, std::string const name = "value", int checkInterval = 1) :
CalculatedValue<uint32>(botAI, name, checkInterval) { }
std::string const Format() override;
};
class FloatCalculatedValue : public CalculatedValue<float>
{
public:
FloatCalculatedValue(PlayerbotAI* botAI, std::string const name = "value", int checkInterval = 1) :
CalculatedValue<float>(botAI, name, checkInterval) { }
std::string const Format() override;
};
class BoolCalculatedValue : public CalculatedValue<bool>
{
public:
BoolCalculatedValue(PlayerbotAI* botAI, std::string const name = "value", int checkInterval = 1) :
CalculatedValue<bool>(botAI, name, checkInterval) { }
std::string const Format() override
{
return Calculate() ? "true" : "false";
}
};
class UnitCalculatedValue : public CalculatedValue<Unit*>
{
public:
UnitCalculatedValue(PlayerbotAI* botAI, std::string const name = "value", int32 checkInterval = 1);
std::string const Format() override;
};
class CDPairCalculatedValue : public CalculatedValue<CreatureData const*>
{
public:
CDPairCalculatedValue(PlayerbotAI* botAI, std::string const name = "value", int32 checkInterval = 1);
std::string const Format() override;
};
class CDPairListCalculatedValue : public CalculatedValue<std::vector<CreatureData const*>>
{
public:
CDPairListCalculatedValue(PlayerbotAI* botAI, std::string const name = "value", int32 checkInterval = 1);
std::string const Format() override;
};
class ObjectGuidCalculatedValue : public CalculatedValue<ObjectGuid>
{
public:
ObjectGuidCalculatedValue(PlayerbotAI* botAI, std::string const name = "value", int32 checkInterval = 1);
std::string const Format() override;
};
class ObjectGuidListCalculatedValue : public CalculatedValue<GuidVector>
{
public:
ObjectGuidListCalculatedValue(PlayerbotAI* botAI, std::string const name = "value", int32 checkInterval = 1);
std::string const Format() override;
};
template<class T>
class ManualSetValue : public UntypedValue, public Value<T>
{
public:
ManualSetValue(PlayerbotAI* botAI, T defaultValue, std::string const name = "value") :
UntypedValue(botAI, name), value(defaultValue), defaultValue(defaultValue) { }
virtual ~ManualSetValue() { }
T Get() override { return value; }
T LazyGet() override { return value; }
void Set(T val) override { value = val; }
void Update() override {}
void Reset() override
{
value = defaultValue;
}
protected:
T value;
T defaultValue;
};
class UnitManualSetValue : public ManualSetValue<Unit*>
{
public:
UnitManualSetValue(PlayerbotAI* botAI, Unit* defaultValue, std::string const name = "value") :
ManualSetValue<Unit*>(botAI, defaultValue, name) { }
std::string const Format() override;
};
#endif

View File

@@ -0,0 +1,24 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
*/
#include "AcceptBattlegroundInvitationAction.h"
#include "Event.h"
#include "Playerbots.h"
bool AcceptBgInvitationAction::Execute(Event event)
{
uint8 type = 0; // arenatype if arena
uint8 unk2 = 0; // unk, can be 0x0 (may be if was invited?) and 0x1
uint32 bgTypeId_ = BATTLEGROUND_WS; // type id from dbc
uint16 unk = 0x1F90; // 0x1F90 constant?*/
uint8 action = 1;
WorldPacket packet(CMSG_BATTLEFIELD_PORT, 20);
packet << type << unk2 << (uint32)bgTypeId_ << unk << action;
//packet << bgTypeId_ << action;
bot->GetSession()->HandleBattleFieldPortOpcode(packet);
botAI->ResetStrategies();
return true;
}

View File

@@ -0,0 +1,20 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
*/
#ifndef _PLAYERBOT_ACCEPTBATTLEGROUNDINVITATIONACTION_H
#define _PLAYERBOT_ACCEPTBATTLEGROUNDINVITATIONACTION_H
#include "Action.h"
class PlayerbotAI;
class AcceptBgInvitationAction : public Action
{
public:
AcceptBgInvitationAction(PlayerbotAI* botAI) : Action(botAI, "accept bg invitatio") { }
bool Execute(Event event) override;
};
#endif

View File

@@ -0,0 +1,24 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
*/
#include "AcceptDuelAction.h"
#include "Event.h"
#include "Playerbots.h"
bool AcceptDuelAction::Execute(Event event)
{
WorldPacket p(event.getPacket());
ObjectGuid flagGuid;
p >> flagGuid;
ObjectGuid playerGuid;
p >> playerGuid;
WorldPacket packet(CMSG_DUEL_ACCEPTED, 8);
packet << flagGuid;
bot->GetSession()->HandleDuelAcceptedOpcode(packet);
botAI->ResetStrategies();
return true;
}

View File

@@ -0,0 +1,20 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
*/
#ifndef _PLAYERBOT_ACCEPTDUELACTION_H
#define _PLAYERBOT_ACCEPTDUELACTION_H
#include "Action.h"
class PlayerbotAI;
class AcceptDuelAction : public Action
{
public:
AcceptDuelAction(PlayerbotAI* botAI) : Action(botAI, "accept duel") { }
bool Execute(Event event) override;
};
#endif

View File

@@ -0,0 +1,45 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
*/
#include "AcceptInvitationAction.h"
#include "Event.h"
#include "Playerbots.h"
#include "PlayerbotSecurity.h"
bool AcceptInvitationAction::Execute(Event event)
{
Group* grp = bot->GetGroupInvite();
if (!grp)
return false;
Player* inviter = ObjectAccessor::FindPlayer(grp->GetLeaderGUID());
if (!inviter)
return false;
if (!botAI->GetSecurity()->CheckLevelFor(PLAYERBOT_SECURITY_INVITE, false, inviter))
{
WorldPacket data(SMSG_GROUP_DECLINE, 10);
data << bot->GetName();
inviter->SendDirectMessage(&data);
bot->UninviteFromGroup();
return false;
}
WorldPacket p;
uint32 roles_mask = 0;
p << roles_mask;
bot->GetSession()->HandleGroupAcceptOpcode(p);
if (sRandomPlayerbotMgr->IsRandomBot(bot))
botAI->SetMaster(inviter);
//else
//sPlayerbotDbStore->Save(botAI);
botAI->ResetStrategies();
botAI->ChangeStrategy("+follow,-lfg,-bg", BOT_STATE_NON_COMBAT);
botAI->Reset();
botAI->TellMaster("Hello");
return true;
}

View File

@@ -0,0 +1,20 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
*/
#ifndef _PLAYERBOT_ACCEPTINVITATIONACTION_H
#define _PLAYERBOT_ACCEPTINVITATIONACTION_H
#include "Action.h"
class PlayerbotAI;
class AcceptInvitationAction : public Action
{
public:
AcceptInvitationAction(PlayerbotAI* botAI) : Action(botAI, "accept invitation") { }
bool Execute(Event event) override;
};
#endif

View File

@@ -0,0 +1,128 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
*/
#include "AcceptQuestAction.h"
#include "Event.h"
#include "Playerbots.h"
void AcceptAllQuestsAction::ProcessQuest(Quest const* quest, WorldObject* questGiver)
{
AcceptQuest(quest, questGiver->GetGUID());
bot->PlayDistanceSound(620);
}
bool AcceptQuestAction::Execute(Event event)
{
Player* master = GetMaster();
if (!master)
return false;
Player* bot = botAI->GetBot();
ObjectGuid guid;
uint32 quest = 0;
std::string const text = event.getParam();
PlayerbotChatHandler ch(master);
quest = ch.extractQuestId(text);
if (event.getPacket().empty())
{
GuidVector npcs = AI_VALUE(GuidVector, "nearest npcs");
for (GuidVector::iterator i = npcs.begin(); i != npcs.end(); i++)
{
Unit* unit = botAI->GetUnit(*i);
if (unit && quest && unit->hasQuest(quest))
{
guid = unit->GetGUID();
break;
}
if (unit && text == "*" && bot->GetDistance(unit) <= INTERACTION_DISTANCE)
QuestAction::ProcessQuests(unit);
}
GuidVector gos = AI_VALUE(GuidVector, "nearest game objects");
for (GuidVector::iterator i = gos.begin(); i != gos.end(); i++)
{
GameObject* go = botAI->GetGameObject(*i);
if (go && quest && go->hasQuest(quest))
{
guid = go->GetGUID();
break;
}
if (go && text == "*" && bot->GetDistance(go) <= INTERACTION_DISTANCE)
QuestAction::ProcessQuests(go);
}
}
else
{
WorldPacket& p = event.getPacket();
p.rpos(0);
p >> guid >> quest;
}
if (!quest || !guid)
return false;
Quest const* qInfo = sObjectMgr->GetQuestTemplate(quest);
if (!qInfo)
return false;
return AcceptQuest(qInfo, guid);
}
bool AcceptQuestShareAction::Execute(Event event)
{
Player* master = GetMaster();
Player* bot = botAI->GetBot();
WorldPacket& p = event.getPacket();
p.rpos(0);
uint32 quest;
p >> quest;
Quest const* qInfo = sObjectMgr->GetQuestTemplate(quest);
if (!qInfo || !bot->GetDivider())
return false;
quest = qInfo->GetQuestId();
if (!bot->CanTakeQuest(qInfo, false))
{
// can't take quest
bot->SetDivider(ObjectGuid::Empty);
botAI->TellError("I can't take this quest");
return false;
}
if (!bot->GetDivider().IsEmpty())
{
// send msg to quest giving player
master->SendPushToPartyResponse(bot, QUEST_PARTY_MSG_ACCEPT_QUEST);
bot->SetDivider(ObjectGuid::Empty);
}
if (bot->CanAddQuest( qInfo, false))
{
bot->AddQuest(qInfo, master);
if (bot->CanCompleteQuest(quest))
bot->CompleteQuest(quest);
// Runsttren: did not add typeid switch from WorldSession::HandleQuestgiverAcceptQuestOpcode!
// I think it's not needed, cause typeid should be TYPEID_PLAYER - and this one is not handled
// there and there is no default case also.
if (qInfo->GetSrcSpell() > 0)
{
bot->CastSpell( bot, qInfo->GetSrcSpell(), true);
}
botAI->TellMaster("Quest accepted");
return true;
}
return false;
}

View File

@@ -0,0 +1,37 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
*/
#ifndef _PLAYERBOT_ACCEPTQUESTACTION_H
#define _PLAYERBOT_ACCEPTQUESTACTION_H
#include "QuestAction.h"
class Quest;
class PlayerbotAI;
class WorldObject;
class AcceptAllQuestsAction : public QuestAction
{
public:
AcceptAllQuestsAction(PlayerbotAI* botAI, std::string const name = "accept all quests") : QuestAction(botAI, name) { }
protected:
void ProcessQuest(Quest const* quest, WorldObject* questGiver) override;
};
class AcceptQuestAction : public AcceptAllQuestsAction
{
public:
AcceptQuestAction(PlayerbotAI* botAI) : AcceptAllQuestsAction(botAI, "accept quest") { }
bool Execute(Event event) override;
};
class AcceptQuestShareAction : public Action
{
public:
AcceptQuestShareAction(PlayerbotAI* botAI) : Action(botAI, "accept quest share") { }
bool Execute(Event event) override;
};
#endif

View File

@@ -0,0 +1,27 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
*/
#include "AcceptResurrectAction.h"
#include "Event.h"
#include "Playerbots.h"
bool AcceptResurrectAction::Execute(Event event)
{
if (bot->IsAlive())
return false;
WorldPacket p(event.getPacket());
p.rpos(0);
ObjectGuid guid;
p >> guid;
WorldPacket packet(CMSG_RESURRECT_RESPONSE, 8 + 1);
packet << guid;
packet << uint8(1); // accept
bot->GetSession()->HandleResurrectResponseOpcode(packet); // queue the packet to get around race condition
botAI->ChangeEngine(BOT_STATE_NON_COMBAT);
return true;
}

View File

@@ -0,0 +1,20 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
*/
#ifndef _PLAYERBOT_ACCEPTRESURRECTACTION_H
#define _PLAYERBOT_ACCEPTRESURRECTACTION_H
#include "Action.h"
class PlayerbotAI;
class AcceptResurrectAction : public Action
{
public:
AcceptResurrectAction(PlayerbotAI* botAI) : Action(botAI, "accept resurrect") { }
bool Execute(Event event) override;
};
#endif

View File

@@ -0,0 +1,382 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
*/
#ifndef _PLAYERBOT_ACTIONCONTEXT_H
#define _PLAYERBOT_ACTIONCONTEXT_H
#include "AddLootAction.h"
#include "AttackAction.h"
#include "AutoLearnSpellAction.h"
#include "BattlegroundTactics.h"
#include "BattlegroundJoinAction.h"
#include "BuyAction.h"
#include "CastCustomSpellAction.h"
#include "ChangeStrategyAction.h"
#include "ChangeTalentsAction.h"
#include "CheckMailAction.h"
#include "CheckValuesAction.h"
#include "ChooseTargetActions.h"
#include "ChooseTravelTargetAction.h"
#include "ChooseRpgTargetAction.h"
#include "CombatActions.h"
#include "DelayAction.h"
#include "DestroyItemAction.h"
#include "EmoteAction.h"
#include "GenericActions.h"
#include "GenericSpellActions.h"
#include "GiveItemAction.h"
#include "GreetAction.h"
#include "GuildAcceptAction.h"
#include "GuildCreateActions.h"
#include "GuildManagementActions.h"
#include "ImbueAction.h"
#include "InviteToGroupAction.h"
#include "LeaveGroupAction.h"
#include "FollowActions.h"
#include "LootAction.h"
#include "MovementActions.h"
#include "MoveToRpgTargetAction.h"
#include "MoveToTravelTargetAction.h"
#include "NonCombatActions.h"
#include "OutfitAction.h"
#include "PositionAction.h"
#include "RandomBotUpdateAction.h"
#include "ReachTargetActions.h"
#include "ReleaseSpiritAction.h"
#include "RemoveAuraAction.h"
#include "ResetInstancesAction.h"
#include "RevealGatheringItemAction.h"
#include "RpgAction.h"
#include "RpgSubActions.h"
#include "RtiAction.h"
#include "SayAction.h"
#include "StayActions.h"
#include "SuggestWhatToDoAction.h"
#include "TravelAction.h"
#include "XpGainAction.h"
#include "VehicleActions.h"
#include "WorldBuffAction.h"
class PlayerbotAI;
class ActionContext : public NamedObjectContext<Action>
{
public:
ActionContext()
{
creators["mark rti"] = &ActionContext::mark_rti;
creators["set return position"] = &ActionContext::set_return_position;
creators["rpg"] = &ActionContext::rpg;
creators["crpg"] = &ActionContext::crpg;
creators["choose rpg target"] = &ActionContext::choose_rpg_target;
creators["move to rpg target"] = &ActionContext::move_to_rpg_target;
creators["travel"] = &ActionContext::travel;
creators["choose travel target"] = &ActionContext::choose_travel_target;
creators["move to travel target"] = &ActionContext::move_to_travel_target;
creators["move out of collision"] = &ActionContext::move_out_of_collision;
creators["move out of collision"] = &ActionContext::move_out_of_collision;
creators["move random"] = &ActionContext::move_random;
creators["attack"] = &ActionContext::melee;
creators["melee"] = &ActionContext::melee;
creators["switch to melee"] = &ActionContext::switch_to_melee;
creators["switch to ranged"] = &ActionContext::switch_to_ranged;
creators["reach spell"] = &ActionContext::ReachSpell;
creators["reach melee"] = &ActionContext::ReachMelee;
creators["reach party member to heal"] = &ActionContext::reach_party_member_to_heal;
creators["flee"] = &ActionContext::flee;
creators["flee with pet"] = &ActionContext::flee_with_pet;
creators["gift of the naaru"] = &ActionContext::gift_of_the_naaru;
creators["shoot"] = &ActionContext::shoot;
creators["lifeblood"] = &ActionContext::lifeblood;
creators["arcane torrent"] = &ActionContext::arcane_torrent;
creators["end pull"] = &ActionContext::end_pull;
creators["healthstone"] = &ActionContext::healthstone;
creators["healing potion"] = &ActionContext::healing_potion;
creators["mana potion"] = &ActionContext::mana_potion;
creators["food"] = &ActionContext::food;
creators["drink"] = &ActionContext::drink;
creators["tank assist"] = &ActionContext::tank_assist;
creators["dps assist"] = &ActionContext::dps_assist;
creators["dps aoe"] = &ActionContext::dps_aoe;
creators["attack rti target"] = &ActionContext::attack_rti_target;
creators["loot"] = &ActionContext::loot;
creators["add loot"] = &ActionContext::add_loot;
creators["add gathering loot"] = &ActionContext::add_gathering_loot;
creators["add all loot"] = &ActionContext::add_all_loot;
creators["release loot"] = &ActionContext::release_loot;
creators["shoot"] = &ActionContext::shoot;
creators["follow"] = &ActionContext::follow;
creators["flee to master"] = &ActionContext::flee_to_master;
creators["runaway"] = &ActionContext::runaway;
creators["stay"] = &ActionContext::stay;
creators["sit"] = &ActionContext::sit;
creators["attack anything"] = &ActionContext::attack_anything;
creators["attack least hp target"] = &ActionContext::attack_least_hp_target;
creators["attack enemy player"] = &ActionContext::attack_enemy_player;
creators["emote"] = &ActionContext::emote;
creators["talk"] = &ActionContext::talk;
creators["suggest what to do"] = &ActionContext::suggest_what_to_do;
creators["suggest trade"] = &ActionContext::suggest_trade;
creators["return"] = &ActionContext::_return;
creators["move to loot"] = &ActionContext::move_to_loot;
creators["open loot"] = &ActionContext::open_loot;
creators["guard"] = &ActionContext::guard;
creators["move out of enemy contact"] = &ActionContext::move_out_of_enemy_contact;
creators["set facing"] = &ActionContext::set_facing;
creators["set behind"] = &ActionContext::set_behind;
creators["attack duel opponent"] = &ActionContext::attack_duel_opponent;
creators["drop target"] = &ActionContext::drop_target;
creators["check mail"] = &ActionContext::check_mail;
creators["say"] = &ActionContext::say;
creators["reveal gathering item"] = &ActionContext::reveal_gathering_item;
creators["outfit"] = &ActionContext::outfit;
creators["random bot update"] = &ActionContext::random_bot_update;
creators["delay"] = &ActionContext::delay;
creators["greet"] = &ActionContext::greet;
creators["check values"] = &ActionContext::check_values;
creators["ra"] = &ActionContext::ra;
creators["give food"] = &ActionContext::give_food;
creators["give water"] = &ActionContext::give_water;
creators["apply poison"] = &ActionContext::apply_poison;
creators["apply stone"] = &ActionContext::apply_stone;
creators["apply oil"] = &ActionContext::apply_oil;
creators["try emergency"] = &ActionContext::try_emergency;
creators["mount"] = &ActionContext::mount;
creators["war stomp"] = &ActionContext::war_stomp;
creators["auto talents"] = &ActionContext::auto_talents;
creators["auto learn spell"] = &ActionContext::auto_learn_spell;
creators["xp gain"] = &ActionContext::xp_gain;
creators["invite nearby"] = &ActionContext::invite_nearby;
creators["invite guild"] = &ActionContext::invite_guild;
creators["leave far away"] = &ActionContext::leave_far_away;
creators["move to dark portal"] = &ActionContext::move_to_dark_portal;
creators["move from dark portal"] = &ActionContext::move_from_dark_portal;
creators["use dark portal azeroth"] = &ActionContext::use_dark_portal_azeroth;
creators["world buff"] = &ActionContext::world_buff;
creators["hearthstone"] = &ActionContext::hearthstone;
creators["cast random spell"] = &ActionContext::cast_random_spell;
creators["free bg join"] = &ActionContext::free_bg_join;
creators["use random recipe"] = &ActionContext::use_random_recipe;
creators["use random quest item"] = &ActionContext::use_random_quest_item;
creators["craft random item"] = &ActionContext::craft_random_item;
creators["smart destroy item"] = &ActionContext::smart_destroy_item;
creators["disenchant random item"] = &ActionContext::disenchant_random_item;
creators["enchant random item"] = &ActionContext::enchant_random_item;
creators["reset instances"] = &ActionContext::reset_instances;
creators["buy petition"] = &ActionContext::buy_petition;
creators["offer petition"] = &ActionContext::offer_petition;
creators["offer petition nearby"] = &ActionContext::offer_petition_nearby;
creators["turn in petition"] = &ActionContext::turn_in_petition;
creators["buy tabard"] = &ActionContext::buy_tabard;
creators["guild manage nearby"] = &ActionContext::guild_manage_nearby;
// BG Tactics
creators["bg tactics"] = &ActionContext::bg_tactics;
creators["bg move to start"] = &ActionContext::bg_move_to_start;
creators["bg move to objective"] = &ActionContext::bg_move_to_objective;
creators["bg select objective"] = &ActionContext::bg_select_objective;
creators["bg check objective"] = &ActionContext::bg_check_objective;
creators["bg attack fc"] = &ActionContext::bg_attack_fc;
creators["bg protect fc"] = &ActionContext::bg_protect_fc;
creators["bg use buff"] = &ActionContext::bg_use_buff;
creators["attack enemy flag carrier"] = &ActionContext::attack_enemy_fc;
creators["bg check flag"] = &ActionContext::bg_check_flag;
// Vehicles
creators["enter vehicle"] = &ActionContext::enter_vehicle;
creators["leave vehicle"] = &ActionContext::leave_vehicle;
creators["hurl boulder"] = &ActionContext::hurl_boulder;
creators["ram"] = &ActionContext::ram;
creators["steam rush"] = &ActionContext::steam_rush;
creators["steam blast"] = &ActionContext::steam_blast;
creators["napalm"] = &ActionContext::napalm;
creators["fire cannon"] = &ActionContext::fire_cannon;
creators["incendiary rocket"] = &ActionContext::incendiary_rocket;
creators["rocket blast"] = &ActionContext::rocket_blast;
creators["blade salvo"] = &ActionContext::blade_salvo;
creators["glaive throw"] = &ActionContext::glaive_throw;
//Rpg
creators["rpg stay"] = &ActionContext::rpg_stay;
creators["rpg work"] = &ActionContext::rpg_work;
creators["rpg emote"] = &ActionContext::rpg_emote;
creators["rpg cancel"] = &ActionContext::rpg_cancel;
creators["rpg taxi"] = &ActionContext::rpg_taxi;
creators["rpg discover"] = &ActionContext::rpg_discover;
creators["rpg start quest"] = &ActionContext::rpg_start_quest;
creators["rpg end quest"] = &ActionContext::rpg_end_quest;
creators["rpg buy"] = &ActionContext::rpg_buy;
creators["rpg sell"] = &ActionContext::rpg_sell;
creators["rpg repair"] = &ActionContext::rpg_repair;
creators["rpg train"] = &ActionContext::rpg_train;
creators["rpg heal"] = &ActionContext::rpg_heal;
creators["rpg home bind"] = &ActionContext::rpg_home_bind;
creators["rpg queue bg"] = &ActionContext::rpg_queue_bg;
creators["rpg buy petition"] = &ActionContext::rpg_buy_petition;
creators["rpg use"] = &ActionContext::rpg_use;
creators["rpg spell"] = &ActionContext::rpg_spell;
creators["rpg craft"] = &ActionContext::rpg_craft;
creators["rpg trade useful"] = &ActionContext::rpg_trade_useful;
creators["rpg duel"] = &ActionContext::rpg_duel;
}
private:
static Action* give_water(PlayerbotAI* botAI) { return new GiveWaterAction(botAI); }
static Action* give_food(PlayerbotAI* botAI) { return new GiveFoodAction(botAI); }
static Action* ra(PlayerbotAI* botAI) { return new RemoveAuraAction(botAI); }
static Action* mark_rti(PlayerbotAI* botAI) { return new MarkRtiAction(botAI); }
static Action* set_return_position(PlayerbotAI* botAI) { return new SetReturnPositionAction(botAI); }
static Action* rpg(PlayerbotAI* botAI) { return new RpgAction(botAI); }
static Action* crpg(PlayerbotAI* botAI) { return new CRpgAction(botAI); }
static Action* choose_rpg_target(PlayerbotAI* botAI) { return new ChooseRpgTargetAction(botAI); }
static Action* move_to_rpg_target(PlayerbotAI* botAI) { return new MoveToRpgTargetAction(botAI); }
static Action* travel(PlayerbotAI* botAI) { return new TravelAction(botAI); }
static Action* choose_travel_target(PlayerbotAI* botAI) { return new ChooseTravelTargetAction(botAI); }
static Action* move_to_travel_target(PlayerbotAI* botAI) { return new MoveToTravelTargetAction(botAI); }
static Action* move_out_of_collision(PlayerbotAI* botAI) { return new MoveOutOfCollisionAction(botAI); }
static Action* move_random(PlayerbotAI* botAI) { return new MoveRandomAction(botAI); }
static Action* check_values(PlayerbotAI* botAI) { return new CheckValuesAction(botAI); }
static Action* greet(PlayerbotAI* botAI) { return new GreetAction(botAI); }
static Action* check_mail(PlayerbotAI* botAI) { return new CheckMailAction(botAI); }
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* 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); }
static Action* shoot(PlayerbotAI* botAI) { return new CastShootAction(botAI); }
static Action* melee(PlayerbotAI* botAI) { return new MeleeAction(botAI); }
static Action* switch_to_melee(PlayerbotAI* botAI) { return new SwitchToMeleeAction(botAI); }
static Action* switch_to_ranged(PlayerbotAI* botAI) { return new SwitchToRangedAction(botAI); }
static Action* ReachSpell(PlayerbotAI* botAI) { return new ReachSpellAction(botAI); }
static Action* ReachMelee(PlayerbotAI* botAI) { return new ReachMeleeAction(botAI); }
static Action* reach_party_member_to_heal(PlayerbotAI* botAI) { return new ReachPartyMemberToHealAction(botAI); }
static Action* flee(PlayerbotAI* botAI) { return new FleeAction(botAI); }
static Action* flee_with_pet(PlayerbotAI* botAI) { return new FleeWithPetAction(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); }
static Action* mana_tap(PlayerbotAI* botAI) { return new CastManaTapAction(botAI); }
static Action* end_pull(PlayerbotAI* botAI) { return new ChangeCombatStrategyAction(botAI, "-pull"); }
static Action* emote(PlayerbotAI* botAI) { return new EmoteAction(botAI); }
static Action* talk(PlayerbotAI* botAI) { return new TalkAction(botAI); }
static Action* suggest_what_to_do(PlayerbotAI* botAI) { return new SuggestWhatToDoAction(botAI); }
static Action* suggest_trade(PlayerbotAI* botAI) { return new SuggestTradeAction(botAI); }
static Action* attack_anything(PlayerbotAI* botAI) { return new AttackAnythingAction(botAI); }
static Action* attack_least_hp_target(PlayerbotAI* botAI) { return new AttackLeastHpTargetAction(botAI); }
static Action* attack_enemy_player(PlayerbotAI* botAI) { return new AttackEnemyPlayerAction(botAI); }
static Action* stay(PlayerbotAI* botAI) { return new StayAction(botAI); }
static Action* sit(PlayerbotAI* botAI) { return new SitAction(botAI); }
static Action* runaway(PlayerbotAI* botAI) { return new RunAwayAction(botAI); }
static Action* follow(PlayerbotAI* botAI) { return new FollowAction(botAI); }
static Action* flee_to_master(PlayerbotAI* botAI) { return new FleeToMasterAction(botAI); }
static Action* add_gathering_loot(PlayerbotAI* botAI) { return new AddGatheringLootAction(botAI); }
static Action* add_loot(PlayerbotAI* botAI) { return new AddLootAction(botAI); }
static Action* add_all_loot(PlayerbotAI* botAI) { return new AddAllLootAction(botAI); }
static Action* loot(PlayerbotAI* botAI) { return new LootAction(botAI); }
static Action* release_loot(PlayerbotAI* botAI) { return new ReleaseLootAction(botAI); }
static Action* dps_assist(PlayerbotAI* botAI) { return new DpsAssistAction(botAI); }
static Action* dps_aoe(PlayerbotAI* botAI) { return new DpsAoeAction(botAI); }
static Action* attack_rti_target(PlayerbotAI* botAI) { return new AttackRtiTargetAction(botAI); }
static Action* tank_assist(PlayerbotAI* botAI) { return new TankAssistAction(botAI); }
static Action* drink(PlayerbotAI* botAI) { return new DrinkAction(botAI); }
static Action* food(PlayerbotAI* botAI) { return new EatAction(botAI); }
static Action* mana_potion(PlayerbotAI* botAI) { return new UseManaPotion(botAI); }
static Action* healing_potion(PlayerbotAI* botAI) { return new UseHealingPotion(botAI); }
static Action* healthstone(PlayerbotAI* botAI) { return new UseItemAction(botAI, "healthstone"); }
static Action* move_out_of_enemy_contact(PlayerbotAI* botAI) { return new MoveOutOfEnemyContactAction(botAI); }
static Action* set_facing(PlayerbotAI* botAI) { return new SetFacingTargetAction(botAI); }
static Action* set_behind(PlayerbotAI* botAI) { return new SetBehindTargetAction(botAI); }
static Action* say(PlayerbotAI* botAI) { return new SayAction(botAI); }
static Action* reveal_gathering_item(PlayerbotAI* botAI) { return new RevealGatheringItemAction(botAI); }
static Action* outfit(PlayerbotAI* botAI) { return new OutfitAction(botAI); }
static Action* random_bot_update(PlayerbotAI* botAI) { return new RandomBotUpdateAction(botAI); }
static Action* delay(PlayerbotAI* botAI) { return new DelayAction(botAI); }
static Action* apply_poison(PlayerbotAI* botAI) { return new ImbueWithPoisonAction(botAI); }
static Action* apply_oil(PlayerbotAI* botAI) { return new ImbueWithOilAction(botAI); }
static Action* apply_stone(PlayerbotAI* botAI) { return new ImbueWithStoneAction(botAI); }
static Action* try_emergency(PlayerbotAI* botAI) { return new TryEmergencyAction(botAI); }
static Action* mount(PlayerbotAI* botAI) { return new CastSpellAction(botAI, "mount"); }
static Action* war_stomp(PlayerbotAI* botAI) { return new CastWarStompAction(botAI); }
static Action* auto_talents(PlayerbotAI* botAI) { return new AutoSetTalentsAction(botAI); }
static Action* auto_learn_spell(PlayerbotAI* botAI) { return new AutoLearnSpellAction(botAI); }
static Action* xp_gain(PlayerbotAI* botAI) { return new XpGainAction(botAI); }
static Action* invite_nearby(PlayerbotAI* botAI) { return new InviteNearbyToGroupAction(botAI); }
static Action* invite_guild(PlayerbotAI* botAI) { return new InviteGuildToGroupAction(botAI); }
static Action* leave_far_away(PlayerbotAI* botAI) { return new LeaveFarAwayAction(botAI); }
static Action* move_to_dark_portal(PlayerbotAI* botAI) { return new MoveToDarkPortalAction(botAI); }
static Action* use_dark_portal_azeroth(PlayerbotAI* botAI) { return new DarkPortalAzerothAction(botAI); }
static Action* move_from_dark_portal(PlayerbotAI* botAI) { return new MoveFromDarkPortalAction(botAI); }
static Action* world_buff(PlayerbotAI* botAI) { return new WorldBuffAction(botAI); }
static Action* hearthstone(PlayerbotAI* botAI) { return new UseHearthStone(botAI); }
static Action* cast_random_spell(PlayerbotAI* botAI) { return new CastRandomSpellAction(botAI); }
static Action* free_bg_join(PlayerbotAI* botAI) { return new FreeBGJoinAction(botAI); }
static Action* use_random_recipe(PlayerbotAI* botAI) { return new UseRandomRecipe(botAI); }
static Action* use_random_quest_item(PlayerbotAI* botAI) { return new UseRandomQuestItem(botAI); }
static Action* craft_random_item(PlayerbotAI* botAI) { return new CraftRandomItemAction(botAI); }
static Action* smart_destroy_item(PlayerbotAI* botAI) { return new SmartDestroyItemAction(botAI); }
static Action* disenchant_random_item(PlayerbotAI* botAI) { return new DisEnchantRandomItemAction(botAI); }
static Action* enchant_random_item(PlayerbotAI* botAI) { return new EnchantRandomItemAction(botAI); }
static Action* reset_instances(PlayerbotAI* botAI) { return new ResetInstancesAction(botAI); }
static Action* buy_petition(PlayerbotAI* botAI) { return new BuyPetitionAction(botAI); }
static Action* offer_petition(PlayerbotAI* botAI) { return new PetitionOfferAction(botAI); }
static Action* offer_petition_nearby(PlayerbotAI* botAI) { return new PetitionOfferNearbyAction(botAI); }
static Action* turn_in_petition(PlayerbotAI* botAI) { return new PetitionTurnInAction(botAI); }
static Action* buy_tabard(PlayerbotAI* botAI) { return new BuyTabardAction(botAI); }
static Action* guild_manage_nearby(PlayerbotAI* botAI) { return new GuildManageNearbyAction(botAI); }
// BG Tactics
static Action* bg_tactics(PlayerbotAI* botAI) { return new BGTactics(botAI); }
static Action* bg_move_to_start(PlayerbotAI* botAI) { return new BGTactics(botAI, "move to start"); }
static Action* bg_move_to_objective(PlayerbotAI* botAI) { return new BGTactics(botAI, "move to objective"); }
static Action* bg_select_objective(PlayerbotAI* botAI) { return new BGTactics(botAI, "select objective"); }
static Action* bg_check_objective(PlayerbotAI* botAI) { return new BGTactics(botAI, "check objective"); }
static Action* bg_attack_fc(PlayerbotAI* botAI) { return new BGTactics(botAI, "attack fc"); }
static Action* bg_protect_fc(PlayerbotAI* botAI) { return new BGTactics(botAI, "protect fc"); }
static Action* attack_enemy_fc(PlayerbotAI* botAI) { return new AttackEnemyFlagCarrierAction(botAI); }
static Action* bg_use_buff(PlayerbotAI* botAI) { return new BGTactics(botAI, "use buff"); }
static Action* bg_check_flag(PlayerbotAI* botAI) { return new BGTactics(botAI, "check flag"); }
// Vehicles
static Action* enter_vehicle(PlayerbotAI* botAI) { return new EnterVehicleAction(botAI); }
static Action* leave_vehicle(PlayerbotAI* botAI) { return new LeaveVehicleAction(botAI); }
static Action* hurl_boulder(PlayerbotAI* botAI) { return new CastHurlBoulderAction(botAI); }
static Action* ram(PlayerbotAI* botAI) { return new CastRamAction(botAI); }
static Action* steam_blast(PlayerbotAI* botAI) { return new CastSteamBlastAction(botAI); }
static Action* steam_rush(PlayerbotAI* botAI) { return new CastSteamRushAction(botAI); }
static Action* napalm(PlayerbotAI* botAI) { return new CastNapalmAction(botAI); }
static Action* fire_cannon(PlayerbotAI* botAI) { return new CastFireCannonAction(botAI); }
static Action* incendiary_rocket(PlayerbotAI* botAI) { return new CastIncendiaryRocketAction(botAI); }
static Action* rocket_blast(PlayerbotAI* botAI) { return new CastRocketBlastAction(botAI); }
static Action* glaive_throw(PlayerbotAI* botAI) { return new CastGlaiveThrowAction(botAI); }
static Action* blade_salvo(PlayerbotAI* botAI) { return new CastBladeSalvoAction(botAI); }
//Rpg
static Action* rpg_stay(PlayerbotAI* botAI) { return new RpgStayAction(botAI); }
static Action* rpg_work(PlayerbotAI* botAI) { return new RpgWorkAction(botAI); }
static Action* rpg_emote(PlayerbotAI* botAI) { return new RpgEmoteAction(botAI); }
static Action* rpg_cancel(PlayerbotAI* botAI) { return new RpgCancelAction(botAI); }
static Action* rpg_taxi(PlayerbotAI* botAI) { return new RpgTaxiAction(botAI); }
static Action* rpg_discover(PlayerbotAI* botAI) { return new RpgDiscoverAction(botAI); }
static Action* rpg_start_quest(PlayerbotAI* botAI) { return new RpgStartQuestAction(botAI); }
static Action* rpg_end_quest(PlayerbotAI* botAI) { return new RpgEndQuestAction(botAI); }
static Action* rpg_buy(PlayerbotAI* botAI) { return new RpgBuyAction(botAI); }
static Action* rpg_sell(PlayerbotAI* botAI) { return new RpgSellAction(botAI); }
static Action* rpg_repair(PlayerbotAI* botAI) { return new RpgRepairAction(botAI); }
static Action* rpg_train(PlayerbotAI* botAI) { return new RpgTrainAction(botAI); }
static Action* rpg_heal(PlayerbotAI* botAI) { return new RpgHealAction(botAI); }
static Action* rpg_home_bind(PlayerbotAI* botAI) { return new RpgHomeBindAction(botAI); }
static Action* rpg_queue_bg(PlayerbotAI* botAI) { return new RpgQueueBgAction(botAI); }
static Action* rpg_buy_petition(PlayerbotAI* botAI) { return new RpgBuyPetitionAction(botAI); }
static Action* rpg_use(PlayerbotAI* botAI) { return new RpgUseAction(botAI); }
static Action* rpg_spell(PlayerbotAI* botAI) { return new RpgSpellAction(botAI); }
static Action* rpg_craft(PlayerbotAI* botAI) { return new RpgCraftAction(botAI); }
static Action* rpg_trade_useful(PlayerbotAI* botAI) { return new RpgTradeUsefulAction(botAI); }
static Action* rpg_duel(PlayerbotAI* botAI) { return new RpgDuelAction(botAI); }
};
#endif

View File

@@ -0,0 +1,86 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
*/
#include "AddLootAction.h"
#include "CellImpl.h"
#include "Event.h"
#include "GridNotifiers.h"
#include "GridNotifiersImpl.h"
#include "LootObjectStack.h"
#include "Playerbots.h"
#include "ServerFacade.h"
bool AddLootAction::Execute(Event event)
{
ObjectGuid guid = event.getObject();
if (!guid)
return false;
return AI_VALUE(LootObjectStack*, "available loot")->Add(guid);
}
bool AddAllLootAction::Execute(Event event)
{
bool added = false;
GuidVector gos = context->GetValue<GuidVector>("nearest game objects")->Get();
for (GuidVector::iterator i = gos.begin(); i != gos.end(); i++)
added |= AddLoot(*i);
GuidVector corpses = context->GetValue<GuidVector>("nearest corpses")->Get();
for (GuidVector::iterator i = corpses.begin(); i != corpses.end(); i++)
added |= AddLoot(*i);
return added;
}
bool AddLootAction::isUseful()
{
return true;
}
bool AddAllLootAction::isUseful()
{
return true;
}
bool AddAllLootAction::AddLoot(ObjectGuid guid)
{
return AI_VALUE(LootObjectStack*, "available loot")->Add(guid);
}
bool AddGatheringLootAction::AddLoot(ObjectGuid guid)
{
LootObject loot(bot, guid);
WorldObject* wo = loot.GetWorldObject(bot);
if (loot.IsEmpty() || !wo)
return false;
if (!bot->IsWithinLOSInMap(wo))
return false;
if (loot.skillId == SKILL_NONE)
return false;
if (!loot.IsLootPossible(bot))
return false;
if (sServerFacade->IsDistanceGreaterThan(sServerFacade->GetDistance2d(bot, wo), INTERACTION_DISTANCE))
{
std::list<Unit*> targets;
Acore::AnyUnfriendlyUnitInObjectRangeCheck u_check(bot, bot, sPlayerbotAIConfig->lootDistance);
Acore::UnitListSearcher<Acore::AnyUnfriendlyUnitInObjectRangeCheck> searcher(bot, targets, u_check);
Cell::VisitAllObjects(bot, searcher, sPlayerbotAIConfig->lootDistance * 1.5f);
if (!targets.empty())
{
std::ostringstream out;
out << "Kill that " << targets.front()->GetName() << " so I can loot freely";
botAI->TellError(out.str());
return false;
}
}
return AddAllLootAction::AddLoot(guid);
}

View File

@@ -0,0 +1,41 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
*/
#ifndef _PLAYERBOT_ADDLOOTACTION_H
#define _PLAYERBOT_ADDLOOTACTION_H
#include "Action.h"
class ObjectGuid;
class PlayerbotAI;
class AddLootAction : public Action
{
public:
AddLootAction(PlayerbotAI* botAI) : Action(botAI, "add loot") { }
bool Execute(Event event) override;
bool isUseful() override;
};
class AddAllLootAction : public Action
{
public:
AddAllLootAction(PlayerbotAI* botAI, std::string const name = "add all loot") : Action(botAI, name) { }
bool Execute(Event event) override;
bool isUseful() override;
protected:
virtual bool AddLoot(ObjectGuid guid);
};
class AddGatheringLootAction : public AddAllLootAction
{
public:
AddGatheringLootAction(PlayerbotAI* botAI) : AddAllLootAction(botAI, "add gathering loot") { }
protected:
bool AddLoot(ObjectGuid guid) override;
};
#endif

View File

@@ -0,0 +1,72 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
*/
#include "AreaTriggerAction.h"
#include "Event.h"
#include "LastMovementValue.h"
#include "Playerbots.h"
#include "Transport.h"
bool ReachAreaTriggerAction::Execute(Event event)
{
if (botAI->IsRealPlayer()) //Do not trigger own area trigger.
return false;
uint32 triggerId;
WorldPacket p(event.getPacket());
p.rpos(0);
p >> triggerId;
AreaTrigger const* at = sObjectMgr->GetAreaTrigger(triggerId);
if (!at)
return false;
if (!sObjectMgr->GetAreaTriggerTeleport(triggerId))
{
WorldPacket p1(CMSG_AREATRIGGER);
p1 << triggerId;
p1.rpos(0);
bot->GetSession()->HandleAreaTriggerOpcode(p1);
return true;
}
if (bot->GetMapId() != at->map || sqrt(bot->GetDistance(at->x, at->y, at->z)) > sPlayerbotAIConfig->sightDistance)
{
botAI->TellError("I won't follow: too far away");
return true;
}
bot->GetMotionMaster()->MovePoint(at->map, at->x, at->y, at->z);
float distance = sqrt(bot->GetDistance(at->x, at->y, at->z));
float delay = 1000.0f * distance / bot->GetSpeed(MOVE_RUN) + sPlayerbotAIConfig->reactDelay;
botAI->TellError("Wait for me");
botAI->SetNextCheckDelay(delay);
context->GetValue<LastMovement&>("last area trigger")->Get().lastAreaTrigger = triggerId;
return true;
}
bool AreaTriggerAction::Execute(Event event)
{
LastMovement& movement = context->GetValue<LastMovement&>("last area trigger")->Get();
uint32 triggerId = movement.lastAreaTrigger;
movement.lastAreaTrigger = 0;
if (!sObjectMgr->GetAreaTrigger(triggerId))
return false;
if (!sObjectMgr->GetAreaTriggerTeleport(triggerId))
return true;
WorldPacket p(CMSG_AREATRIGGER);
p << triggerId;
p.rpos(0);
bot->GetSession()->HandleAreaTriggerOpcode(p);
botAI->TellMaster("Hello");
return true;
}

View File

@@ -0,0 +1,28 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
*/
#ifndef _PLAYERBOT_AREATRIGGERACTION_H
#define _PLAYERBOT_AREATRIGGERACTION_H
#include "MovementActions.h"
class PlayerbotAI;
class ReachAreaTriggerAction : public MovementAction
{
public:
ReachAreaTriggerAction(PlayerbotAI* botAI) : MovementAction(botAI, "reach area trigger") { }
bool Execute(Event event) override;
};
class AreaTriggerAction : public MovementAction
{
public:
AreaTriggerAction(PlayerbotAI* botAI) : MovementAction(botAI, "area trigger") { }
bool Execute(Event event) override;
};
#endif

View File

@@ -0,0 +1,53 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
*/
#include "ArenaTeamActions.h"
#include "ArenaTeamMgr.h"
#include "Playerbots.h"
bool ArenaTeamAcceptAction::Execute(Event event)
{
WorldPacket p(event.getPacket());
p.rpos(0);
Player* inviter = nullptr;
std::string Invitedname;
p >> Invitedname;
if (normalizePlayerName(Invitedname))
inviter = ObjectAccessor::FindPlayerByName(Invitedname.c_str());
if (!inviter)
return false;
ArenaTeam* at = sArenaTeamMgr->GetArenaTeamById(bot->GetArenaTeamIdInvited());
if (!at)
return false;
bool accept = true;
if (bot->GetArenaTeamId(at->GetSlot()))
{
// bot is already in an arena team
bot->Say("Sorry, I am already in such team", LANG_UNIVERSAL);
accept = false;
}
if (accept)
{
WorldPacket data(CMSG_ARENA_TEAM_ACCEPT);
bot->GetSession()->HandleArenaTeamAcceptOpcode(data);
bot->Say("Thanks for the invite!", LANG_UNIVERSAL);
LOG_INFO("playerbots", "Bot {} <{}> accepts Arena Team invite", bot->GetGUID().ToString().c_str(), bot->GetName().c_str());
return true;
}
else
{
WorldPacket data(CMSG_ARENA_TEAM_DECLINE);
bot->GetSession()->HandleArenaTeamDeclineOpcode(data);
LOG_INFO("playerbots", "Bot {} <{}> declines Arena Team invite", bot->GetGUID().ToString().c_str(), bot->GetName().c_str());
return false;
}
return false;
}

View File

@@ -0,0 +1,20 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
*/
#ifndef _PLAYERBOT_ARENATEAMACTION_H
#define _PLAYERBOT_ARENATEAMACTION_H
#include "Action.h"
class PlayerbotAI;
class ArenaTeamAcceptAction : public Action
{
public:
ArenaTeamAcceptAction(PlayerbotAI* botAI) : Action(botAI, "arena team accept") { }
bool Execute(Event event) override;
};
#endif

View File

@@ -0,0 +1,140 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
*/
#include "AttackAction.h"
#include "Event.h"
#include "LootObjectStack.h"
#include "Playerbots.h"
#include "ServerFacade.h"
#include "CreatureAI.h"
bool AttackAction::Execute(Event event)
{
Unit* target = GetTarget();
if (!target)
return false;
return Attack(target);
}
bool AttackMyTargetAction::Execute(Event event)
{
Player* master = GetMaster();
if (!master)
return false;
ObjectGuid guid = master->GetTarget();
if (!guid)
{
if (verbose)
botAI->TellError("You have no target");
return false;
}
bool result = Attack(botAI->GetUnit(guid));
if (result)
context->GetValue<ObjectGuid>("pull target")->Set(guid);
return result;
}
bool AttackAction::Attack(Unit* target)
{
if (bot->GetMotionMaster()->GetCurrentMovementGeneratorType() == FLIGHT_MOTION_TYPE || bot->HasUnitState(UNIT_STATE_IN_FLIGHT))
{
if (verbose)
botAI->TellError("I cannot attack in flight");
return false;
}
if (!target)
{
if (verbose)
botAI->TellError("I have no target");
return false;
}
std::ostringstream msg;
msg << target->GetName();
if (bot->IsFriendlyTo(target))
{
msg << " is friendly to me";
if (verbose)
botAI->TellError(msg.str());
return false;
}
if (!bot->IsWithinLOSInMap(target))
{
msg << " is not on my sight";
if (verbose)
botAI->TellError(msg.str());
}
if (target->isDead())
{
msg << " is dead";
if (verbose)
botAI->TellError(msg.str());
return false;
}
if (bot->IsMounted() && bot->IsWithinLOSInMap(target) && sServerFacade->GetDistance2d(bot, target) < 40.0f)
{
WorldPacket emptyPacket;
bot->GetSession()->HandleCancelMountAuraOpcode(emptyPacket);
}
ObjectGuid guid = target->GetGUID();
bot->SetTarget(target->GetGUID());
Unit* oldTarget = context->GetValue<Unit*>("current target")->Get();
context->GetValue<Unit*>("old target")->Set(oldTarget);
context->GetValue<Unit*>("current target")->Set(target);
context->GetValue<LootObjectStack*>("available loot")->Get()->Add(guid);
if (Pet* pet = bot->GetPet())
{
if (CreatureAI* creatureAI = ((Creature*)pet)->AI())
{
pet->SetReactState(REACT_PASSIVE);
pet->GetCharmInfo()->SetCommandState(COMMAND_ATTACK);
creatureAI->AttackStart(target);
}
}
if (!urand(0, 300) && botAI->HasStrategy("emote", BOT_STATE_NON_COMBAT))
{
std::vector<uint32> sounds;
sounds.push_back(TEXT_EMOTE_OPENFIRE);
sounds.push_back(305);
sounds.push_back(307);
botAI->PlayEmote(sounds[urand(0, sounds.size() - 1)]);
}
if (IsMovingAllowed() && !bot->HasInArc(CAST_ANGLE_IN_FRONT, target, sPlayerbotAIConfig->sightDistance))
bot->SetFacingToObject(target);
bool attacked = bot->Attack(target, !botAI->IsRanged(bot));
botAI->ChangeEngine(BOT_STATE_COMBAT);
return attacked;
}
bool AttackDuelOpponentAction::isUseful()
{
return AI_VALUE(Unit*, "duel target");
}
bool AttackDuelOpponentAction::Execute(Event event)
{
return Attack(AI_VALUE(Unit*, "duel target"));
}

View File

@@ -0,0 +1,41 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
*/
#ifndef _PLAYERBOT_ATTACKACTION_H
#define _PLAYERBOT_ATTACKACTION_H
#include "MovementActions.h"
class PlayerbotAI;
class AttackAction : public MovementAction
{
public:
AttackAction(PlayerbotAI* botAI, std::string const name) : MovementAction(botAI, name) { }
bool Execute(Event event) override;
protected:
bool Attack(Unit* target);
};
class AttackMyTargetAction : public AttackAction
{
public:
AttackMyTargetAction(PlayerbotAI* botAI, std::string const name = "attack my target") : AttackAction(botAI, name) { }
bool Execute(Event event) override;
};
class AttackDuelOpponentAction : public AttackAction
{
public:
AttackDuelOpponentAction(PlayerbotAI* botAI, std::string const name = "attack duel opponent") : AttackAction(botAI, name) { }
public:
bool Execute(Event event) override;
bool isUseful() override;
};
#endif

View File

@@ -0,0 +1,165 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
*/
#include "AutoLearnSpellAction.h"
#include "Event.h"
#include "Playerbots.h"
bool AutoLearnSpellAction::Execute(Event event)
{
std::string const param = event.getParam();
std::ostringstream out;
LearnSpells(&out);
if (!out.str().empty())
{
std::string const temp = out.str();
out.seekp(0);
out << "Learned spells: ";
out << temp;
out.seekp(-2, out.cur);
out << ".";
botAI->TellMaster(out);
}
return true;
}
void AutoLearnSpellAction::LearnSpells(std::ostringstream* out)
{
if (sPlayerbotAIConfig->autoLearnTrainerSpells)// || (!botAI->GetMaster() && sRandomPlayerbotMgr->IsRandomBot(bot)))
LearnTrainerSpells(out);
if (sPlayerbotAIConfig->autoLearnQuestSpells)// || (!botAI->GetMaster() && sRandomPlayerbotMgr->IsRandomBot(bot)))
LearnQuestSpells(out);
}
void AutoLearnSpellAction::LearnTrainerSpells(std::ostringstream* out)
{
bot->LearnDefaultSkills();
bot->LearnCustomSpells();
CreatureTemplateContainer const* creatures = sObjectMgr->GetCreatureTemplates();
for (CreatureTemplateContainer::const_iterator itr = creatures->begin(); itr != creatures->end(); ++itr)
{
if (itr->second.trainer_type != TRAINER_TYPE_CLASS && itr->second.trainer_type != TRAINER_TYPE_TRADESKILLS)
continue;
if (itr->second.trainer_type == TRAINER_TYPE_CLASS && itr->second.trainer_class != bot->getClass())
continue;
TrainerSpellData const* trainer_spells = sObjectMgr->GetNpcTrainerSpells(itr->second.Entry);
if (!trainer_spells)
continue;
for (TrainerSpellMap::const_iterator iter = trainer_spells->spellList.begin(); iter != trainer_spells->spellList.end(); ++iter)
{
TrainerSpell const* tSpell = &iter->second;
if (!tSpell)
continue;
TrainerSpellState state = bot->GetTrainerSpellState(tSpell);
if (state != TRAINER_SPELL_GREEN)
continue;
if (itr->second.trainer_type == TRAINER_TYPE_TRADESKILLS)
{
SpellInfo const* spell = sSpellMgr->GetSpellInfo(tSpell->spell);
if (spell)
{
std::string const SpellName = spell->SpellName[0];
if (spell->Effects[EFFECT_1].Effect == SPELL_EFFECT_SKILL_STEP)
{
uint32 skill = spell->Effects[EFFECT_1].MiscValue;
if (skill)
{
SkillLineEntry const* pSkill = sSkillLineStore.LookupEntry(skill);
if (pSkill)
{
if (SpellName.find("Apprentice") != std::string::npos && pSkill->categoryId == SKILL_CATEGORY_PROFESSION || pSkill->categoryId == SKILL_CATEGORY_SECONDARY)
continue;
}
}
}
}
}
LearnSpell(tSpell->spell, out);
}
}
}
void AutoLearnSpellAction::LearnQuestSpells(std::ostringstream* out)
{
//CreatureTemplate const* co = sCreatureStorage.LookupEntry<CreatureTemplate>(id);
ObjectMgr::QuestMap const& questTemplates = sObjectMgr->GetQuestTemplates();
for (ObjectMgr::QuestMap::const_iterator i = questTemplates.begin(); i != questTemplates.end(); ++i)
{
uint32 questId = i->first;
Quest const* quest = i->second;
if (!quest->GetRequiredClasses() || quest->IsRepeatable() || quest->GetMinLevel() < 10)
continue;
if (!bot->SatisfyQuestClass(quest, false) || quest->GetMinLevel() > bot->getLevel() || !bot->SatisfyQuestRace(quest, false))
continue;
if (quest->GetRewSpellCast() > 0)
{
LearnSpell(quest->GetRewSpellCast(), out);
}
else if (quest->GetRewSpell() > 0)
{
LearnSpell(quest->GetRewSpell(), out);
}
}
}
std::string const FormatSpell(SpellInfo const* sInfo)
{
std::ostringstream out;
std::string const rank = sInfo->Rank[0];
if (rank.empty())
out << "|cffffffff|Hspell:" << sInfo->Id << "|h[" << sInfo->SpellName[LOCALE_enUS] << "]|h|r";
else
out << "|cffffffff|Hspell:" << sInfo->Id << "|h[" << sInfo->SpellName[LOCALE_enUS] << " " << rank << "]|h|r";
return out.str();
}
void AutoLearnSpellAction::LearnSpell(uint32 spellId, std::ostringstream* out)
{
SpellInfo const* proto = sSpellMgr->GetSpellInfo(spellId);
if (!proto)
return;
bool learned = false;
for (uint8 j = 0; j < 3; ++j)
{
if (proto->Effects[j].Effect == SPELL_EFFECT_LEARN_SPELL)
{
uint32 learnedSpell = proto->Effects[j].TriggerSpell;
if (!bot->HasSpell(learnedSpell))
{
bot->learnSpell(learnedSpell);
*out << FormatSpell(sSpellMgr->GetSpellInfo(learnedSpell)) << ", ";
}
learned = true;
}
}
if (!learned)
{
if (!bot->HasSpell(spellId))
{
bot->learnSpell(spellId);
*out << FormatSpell(proto) << ", ";
}
}
}

View File

@@ -0,0 +1,26 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
*/
#ifndef _PLAYERBOT_AUTOLEARNSPELLACTION_H
#define _PLAYERBOT_AUTOLEARNSPELLACTION_H
#include "Action.h"
class PlayerbotAI;
class AutoLearnSpellAction : public Action
{
public:
AutoLearnSpellAction(PlayerbotAI* botAI, std::string const name = "auto learn spell") : Action(botAI, name) { }
bool Execute(Event event);
private:
void LearnSpells(std::ostringstream* out);
void LearnTrainerSpells(std::ostringstream* out);
void LearnQuestSpells(std::ostringstream* out);
void LearnSpell(uint32 spellId, std::ostringstream* out);
};
#endif

View File

@@ -0,0 +1,171 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
*/
#include "BankAction.h"
#include "Event.h"
#include "ItemCountValue.h"
#include "Playerbots.h"
bool BankAction::Execute(Event event)
{
std::string const text = event.getParam();
GuidVector npcs = AI_VALUE(GuidVector, "nearest npcs");
for (GuidVector::iterator i = npcs.begin(); i != npcs.end(); i++)
{
Unit* npc = botAI->GetUnit(*i);
if (!npc || !npc->HasFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_BANKER))
continue;
return ExecuteBank(text, npc);
}
botAI->TellError("Cannot find banker nearby");
return false;
}
bool BankAction::ExecuteBank(std::string const text, Unit* bank)
{
if (text.empty() || text == "?")
{
ListItems();
return true;
}
bool result = false;
if (text[0] == '-')
{
std::vector<Item*> found = parseItems(text.substr(1), ITERATE_ITEMS_IN_BANK);
for (std::vector<Item*>::iterator i = found.begin(); i != found.end(); i++)
{
Item* item = *i;
result &= Withdraw(item->GetTemplate()->ItemId);
}
}
else
{
std::vector<Item*> found = parseItems(text, ITERATE_ITEMS_IN_BAGS);
if (found.empty())
return false;
for (std::vector<Item*>::iterator i = found.begin(); i != found.end(); i++)
{
Item* item = *i;
if (!item)
continue;
result &= Deposit(item);
}
}
return result;
}
bool BankAction::Withdraw(uint32 itemid)
{
Item* pItem = FindItemInBank(itemid);
if (!pItem)
return false;
ItemPosCountVec dest;
InventoryResult msg = bot->CanStoreItem(NULL_BAG, NULL_SLOT, dest, pItem, false);
if (msg != EQUIP_ERR_OK)
{
bot->SendEquipError(msg, pItem, nullptr);
return false;
}
bot->RemoveItem(pItem->GetBagSlot(), pItem->GetSlot(), true);
bot->StoreItem(dest, pItem, true);
std::ostringstream out;
out << "got " << chat->FormatItem(pItem->GetTemplate(), pItem->GetCount()) << " from bank";
botAI->TellMaster(out.str());
return true;
}
bool BankAction::Deposit(Item* pItem)
{
std::ostringstream out;
ItemPosCountVec dest;
InventoryResult msg = bot->CanBankItem(NULL_BAG, NULL_SLOT, dest, pItem, false);
if (msg != EQUIP_ERR_OK)
{
bot->SendEquipError(msg, pItem, nullptr);
return false;
}
bot->RemoveItem(pItem->GetBagSlot(), pItem->GetSlot(), true);
bot->BankItem(dest, pItem, true);
out << "put " << chat->FormatItem(pItem->GetTemplate(), pItem->GetCount()) << " to bank";
botAI->TellMaster(out.str());
return true;
}
void BankAction::ListItems()
{
botAI->TellMaster("=== Bank ===");
std::map<uint32, uint32> items;
std::map<uint32, bool> soulbound;
for (uint32 i = BANK_SLOT_ITEM_START; i < BANK_SLOT_ITEM_END; ++i)
if (Item* pItem = bot->GetItemByPos(INVENTORY_SLOT_BAG_0, i))
if (pItem)
{
items[pItem->GetTemplate()->ItemId] += pItem->GetCount();
soulbound[pItem->GetTemplate()->ItemId] = pItem->IsSoulBound();
}
for (uint32 i = BANK_SLOT_BAG_START; i < BANK_SLOT_BAG_END; ++i)
if (Bag* pBag = (Bag*)bot->GetItemByPos(INVENTORY_SLOT_BAG_0, i))
if (pBag)
for (uint32 j = 0; j < pBag->GetBagSize(); ++j)
if (Item* pItem = pBag->GetItemByPos(j))
if (pItem)
{
items[pItem->GetTemplate()->ItemId] += pItem->GetCount();
soulbound[pItem->GetTemplate()->ItemId] = pItem->IsSoulBound();
}
TellItems(items, soulbound);
}
Item* BankAction::FindItemInBank(uint32 ItemId)
{
for (uint8 slot = BANK_SLOT_ITEM_START; slot < BANK_SLOT_ITEM_END; slot++)
{
if (Item* const pItem = bot->GetItemByPos(INVENTORY_SLOT_BAG_0, slot))
{
ItemTemplate const* const pItemProto = pItem->GetTemplate();
if (!pItemProto)
continue;
if (pItemProto->ItemId == ItemId) // have required item
return pItem;
}
}
for (uint8 bag = BANK_SLOT_BAG_START; bag < BANK_SLOT_BAG_END; ++bag)
{
Bag const* const pBag = (Bag *) bot->GetItemByPos(INVENTORY_SLOT_BAG_0, bag);
if (pBag)
for (uint8 slot = 0; slot < pBag->GetBagSize(); ++slot)
{
Item* const pItem = bot->GetItemByPos(bag, slot);
if (pItem)
{
ItemTemplate const* const pItemProto = pItem->GetTemplate();
if (!pItemProto)
continue;
if (pItemProto->ItemId == ItemId)
return pItem;
}
}
}
return nullptr;
}

View File

@@ -0,0 +1,28 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
*/
#ifndef _PLAYERBOT_BANKACTION_H
#define _PLAYERBOT_BANKACTION_H
#include "InventoryAction.h"
class Item;
class PlayerbotAI;
class BankAction : public InventoryAction
{
public:
BankAction(PlayerbotAI* botAI) : InventoryAction(botAI, "bank") { }
bool Execute(Event event) override;
private:
bool ExecuteBank(std::string const text, Unit* bank);
void ListItems();
bool Withdraw(uint32 itemid);
bool Deposit(Item* pItem);
Item* FindItemInBank(uint32 ItemId);
};
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,69 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
*/
#ifndef _PLAYERBOT_BATTLEGROUNDJOINACTION_H
#define _PLAYERBOT_BATTLEGROUNDJOINACTION_H
#include "Action.h"
#include "DBCEnums.h"
class PlayerbotAI;
struct CreatureData;
enum ArenaType : uint8;
enum BattlegroundQueueTypeId : uint8;
class BGJoinAction : public Action
{
public:
BGJoinAction(PlayerbotAI* botAI, std::string const name = "bg join") : Action(botAI, name) { }
bool isUseful() override;
bool canJoinBg(BattlegroundQueueTypeId queueTypeId, BattlegroundBracketId bracketId);
virtual bool shouldJoinBg(BattlegroundQueueTypeId queueTypeId, BattlegroundBracketId bracketId);
bool Execute(Event event) override;
virtual bool gatherArenaTeam(ArenaType type);
protected:
bool JoinQueue(uint32 type);
std::vector<uint32> bgList;
std::vector<uint32> ratedList;
};
class FreeBGJoinAction : public BGJoinAction
{
public:
FreeBGJoinAction(PlayerbotAI* botAI, std::string const name = "free bg join") : BGJoinAction(botAI, name) { }
bool shouldJoinBg(BattlegroundQueueTypeId queueTypeId, BattlegroundBracketId bracketId) override;
};
class BGLeaveAction : public Action
{
public:
BGLeaveAction(PlayerbotAI* botAI, std::string const name = "bg leave") : Action(botAI) { }
bool Execute(Event event) override;
};
class BGStatusAction : public Action
{
public:
BGStatusAction(PlayerbotAI* botAI) : Action(botAI, "bg status") { }
bool Execute(Event event) override;
bool isUseful() override;
};
class BGStatusCheckAction : public Action
{
public:
BGStatusCheckAction(PlayerbotAI* botAI, std::string const name = "bg status check") : Action(botAI, name) { }
bool Execute(Event event) override;
bool isUseful() override;
};
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,73 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
*/
#ifndef _PLAYERBOT_BATTLEGROUNDTACTICSACTION_H
#define _PLAYERBOT_BATTLEGROUNDTACTICSACTION_H
#include "MovementActions.h"
class Battleground;
class PlayerbotAI;
struct Position;
#define SPELL_CAPTURE_BANNER 21651
typedef void(*BattleBotWaypointFunc)();
struct BattleBotWaypoint
{
BattleBotWaypoint(float x_, float y_, float z_, BattleBotWaypointFunc func) : x(x_), y(y_), z(z_), pFunc(func) { };
float x = 0.0f;
float y = 0.0f;
float z = 0.0f;
BattleBotWaypointFunc pFunc = nullptr;
};
typedef std::vector<BattleBotWaypoint> BattleBotPath;
extern std::vector<BattleBotPath*> const vPaths_WS;
extern std::vector<BattleBotPath*> const vPaths_AB;
extern std::vector<BattleBotPath*> const vPaths_AV;
extern std::vector<BattleBotPath*> const vPaths_EY;
extern std::vector<BattleBotPath*> const vPaths_IC;
class BGTactics : public MovementAction
{
public:
BGTactics(PlayerbotAI* botAI, std::string const name = "bg tactics") : MovementAction(botAI, name) { }
bool Execute(Event event) override;
private:
bool moveToStart(bool force = false);
bool selectObjective(bool reset = false);
bool moveToObjective();
bool selectObjectiveWp(std::vector<BattleBotPath*> const& vPaths);
bool moveToObjectiveWp(BattleBotPath* const& currentPath, uint32 currentPoint, bool reverse = false);
bool startNewPathBegin(std::vector<BattleBotPath*> const& vPaths);
bool startNewPathFree(std::vector<BattleBotPath*> const& vPaths);
bool resetObjective();
bool wsgPaths();
bool atFlag(std::vector<BattleBotPath*> const& vPaths, std::vector<uint32> const& vFlagIds);
bool flagTaken();
bool teamFlagTaken();
bool protectFC();
bool useBuff();
uint32 getDefendersCount(Position point, float range, bool combat = true);
bool IsLockedInsideKeep();
};
class ArenaTactics : public MovementAction
{
public:
ArenaTactics(PlayerbotAI* botAI, std::string const name = "arena tactics") : MovementAction(botAI, name) { }
bool Execute(Event event) override;
private:
bool moveToCenter(Battleground *bg);
};
#endif

View File

@@ -0,0 +1,113 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
*/
#include "BuffAction.h"
#include "Event.h"
#include "ItemCountValue.h"
#include "Playerbots.h"
class FindBuffVisitor : public IterateItemsVisitor
{
public:
FindBuffVisitor(Player* bot) : IterateItemsVisitor(), bot(bot)
{
}
bool Visit(Item* item) override
{
if (bot->CanUseItem(item->GetTemplate()) != EQUIP_ERR_OK)
return true;
ItemTemplate const* proto = item->GetTemplate();
if (proto->Class != ITEM_CLASS_CONSUMABLE)
return true;
if (proto->SubClass != ITEM_SUBCLASS_ELIXIR && proto->SubClass != ITEM_SUBCLASS_FLASK && proto->SubClass != ITEM_SUBCLASS_SCROLL &&
proto->SubClass != ITEM_SUBCLASS_FOOD && proto->SubClass != ITEM_SUBCLASS_CONSUMABLE_OTHER && proto->SubClass != ITEM_SUBCLASS_ITEM_ENHANCEMENT)
return true;
for (uint8 i = 0; i < MAX_ITEM_PROTO_SPELLS; i++)
{
uint32 spellId = proto->Spells[i].SpellId;
if (!spellId)
continue;
if (bot->HasAura(spellId))
return true;
Item* itemForSpell = *GET_PLAYERBOT_AI(bot)->GetAiObjectContext()->GetValue<Item*>("item for spell", spellId);
if (itemForSpell && itemForSpell->GetEnchantmentId(TEMP_ENCHANTMENT_SLOT))
return true;
if (items.find(proto->SubClass) == items.end())
items[proto->SubClass] = std::vector<Item*>();
items[proto->SubClass].push_back(item);
break;
}
return true;
}
std::map<uint32, std::vector<Item*> > items;
private:
Player* bot;
};
void BuffAction::TellHeader(uint32 subClass)
{
switch (subClass)
{
case ITEM_SUBCLASS_ELIXIR:
botAI->TellMaster("--- Elixir ---");
return;
case ITEM_SUBCLASS_FLASK:
botAI->TellMaster("--- Flask ---");
return;
case ITEM_SUBCLASS_SCROLL:
botAI->TellMaster("--- Scroll ---");
return;
case ITEM_SUBCLASS_FOOD:
botAI->TellMaster("--- Food ---");
return;
case ITEM_SUBCLASS_ITEM_ENHANCEMENT:
botAI->TellMaster("--- Enchant ---");
return;
}
}
bool BuffAction::Execute(Event event)
{
std::string const text = event.getParam();
FindBuffVisitor visitor(bot);
IterateItems(&visitor);
uint32 oldSubClass = -1;
for (std::map<uint32, std::vector<Item*> >::iterator i = visitor.items.begin(); i != visitor.items.end(); ++i)
{
std::vector<Item*> items = i->second;
uint32 subClass = i->first;
if (oldSubClass != subClass)
{
if (!items.empty())
TellHeader(subClass);
oldSubClass = subClass;
}
for (std::vector<Item*>::iterator j = items.begin(); j != items.end(); ++j)
{
Item* item = *j;
std::ostringstream out;
out << chat->FormatItem(item->GetTemplate(), item->GetCount());
botAI->TellMaster(out);
}
}
return true;
}

View File

@@ -0,0 +1,23 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
*/
#ifndef _PLAYERBOT_BUFFACTION_H
#define _PLAYERBOT_BUFFACTION_H
#include "InventoryAction.h"
class PlayerbotAI;
class BuffAction : public InventoryAction
{
public:
BuffAction(PlayerbotAI* botAI) : InventoryAction(botAI, "buff") { }
bool Execute(Event event) override;
private:
void TellHeader(uint32 subClass);
};
#endif

View File

@@ -0,0 +1,184 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
*/
#include "BuyAction.h"
#include "BudgetValues.h"
#include "Event.h"
#include "ItemVisitors.h"
#include "ItemCountValue.h"
#include "ItemUsageValue.h"
#include "Playerbots.h"
bool BuyAction::Execute(Event event)
{
bool buyUseful = false;
ItemIds itemIds;
std::string const link = event.getParam();
if (link == "vendor")
buyUseful = true;
else
{
itemIds = chat->parseItems(link);
}
GuidVector vendors = botAI->GetAiObjectContext()->GetValue<GuidVector>("nearest npcs")->Get();
bool vendored = false;
bool result = false;
for (GuidVector::iterator i = vendors.begin(); i != vendors.end(); ++i)
{
ObjectGuid vendorguid = *i;
Creature* pCreature = bot->GetNPCIfCanInteractWith(vendorguid, UNIT_NPC_FLAG_VENDOR);
if (!pCreature)
continue;
vendored = true;
if (buyUseful)
{
// Items are evaluated from high-level to low level.
// For each item the bot checks again if an item is usefull.
// Bot will buy until no usefull items are left.
VendorItemData const* tItems = pCreature->GetVendorItems();
if (!tItems)
continue;
VendorItemList m_items_sorted = tItems->m_items;
m_items_sorted.erase(std::remove_if(m_items_sorted.begin(), m_items_sorted.end(), [](VendorItem* i)
{
ItemTemplate const* proto = sObjectMgr->GetItemTemplate(i->item);
return !proto;
}), m_items_sorted.end());
if (m_items_sorted.empty())
continue;
std::sort(m_items_sorted.begin(), m_items_sorted.end(), [](VendorItem* i, VendorItem* j) { return sObjectMgr->GetItemTemplate(i->item)->ItemLevel > sObjectMgr->GetItemTemplate(j->item)->ItemLevel; });
for (auto& tItem : m_items_sorted)
{
for (uint32 i = 0; i < 10; i++) // Buy 10 times or until no longer usefull/possible
{
ItemUsage usage = AI_VALUE2(ItemUsage, "item usage", tItem->item);
ItemTemplate const* proto = sObjectMgr->GetItemTemplate(tItem->item);
uint32 price = proto->BuyPrice;
// reputation discount
price = uint32(floor(price * bot->GetReputationPriceDiscount(pCreature)));
NeedMoneyFor needMoneyFor = NeedMoneyFor::none;
switch (usage)
{
case ITEM_USAGE_REPLACE:
case ITEM_USAGE_EQUIP:
needMoneyFor = NeedMoneyFor::gear;
break;
case ITEM_USAGE_AMMO:
needMoneyFor = NeedMoneyFor::ammo;
break;
case ITEM_USAGE_QUEST:
needMoneyFor = NeedMoneyFor::anything;
break;
case ITEM_USAGE_USE:
needMoneyFor = NeedMoneyFor::consumables;
break;
case ITEM_USAGE_SKILL:
needMoneyFor = NeedMoneyFor::tradeskill;
break;
}
if (needMoneyFor == NeedMoneyFor::none)
break;
if (AI_VALUE2(uint32, "free money for", uint32(needMoneyFor)) < price)
break;
if (!BuyItem(tItems, vendorguid, proto))
break;
if (usage == ITEM_USAGE_REPLACE || usage == ITEM_USAGE_EQUIP) // Equip upgrades and stop buying this time.
{
botAI->DoSpecificAction("equip upgrades");
break;
}
}
}
}
else
{
if (itemIds.empty())
return false;
for (ItemIds::iterator i = itemIds.begin(); i != itemIds.end(); i++)
{
uint32 itemId = *i;
ItemTemplate const* proto = sObjectMgr->GetItemTemplate(itemId);
if (!proto)
continue;
result |= BuyItem(pCreature->GetVendorItems(), vendorguid, proto);
if (!result)
{
std::ostringstream out;
out << "Nobody sells " << ChatHelper::FormatItem(proto) << " nearby";
botAI->TellMaster(out.str());
}
}
}
}
if (!vendored)
{
botAI->TellError("There are no vendors nearby");
return false;
}
return true;
}
bool BuyAction::BuyItem(VendorItemData const* tItems, ObjectGuid vendorguid, ItemTemplate const* proto)
{
uint32 oldCount = AI_VALUE2(uint32, "item count", proto->Name1);
if (!tItems)
return false;
uint32 itemId = proto->ItemId;
for (uint32 slot = 0; slot < tItems->GetItemCount(); slot++)
{
if (tItems->GetItem(slot)->item == itemId)
{
uint32 botMoney = bot->GetMoney();
if (botAI->HasCheat(BotCheatMask::gold))
{
bot->SetMoney(10000000);
}
bot->BuyItemFromVendorSlot(vendorguid, slot, itemId, 1, NULL_BAG, NULL_SLOT);
if (botAI->HasCheat(BotCheatMask::gold))
{
bot->SetMoney(botMoney);
}
if (oldCount < AI_VALUE2(uint32, "item count", proto->Name1)) // BuyItem Always returns false (unless unique) so we have to check the item counts.
{
std::ostringstream out;
out << "Buying " << ChatHelper::FormatItem(proto);
botAI->TellMaster(out.str());
return true;
}
return false;
}
}
return false;
}

View File

@@ -0,0 +1,33 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
*/
#ifndef _PLAYERBOT_BUYACTION_H
#define _PLAYERBOT_BUYACTION_H
#include "InventoryAction.h"
class FindItemVisitor;
class ObjectGuid;
class Item;
class Player;
class PlayerbotAI;
struct ItemTemplate;
struct VendorItemData;
class BuyAction : public InventoryAction
{
public:
BuyAction(PlayerbotAI* botAI) : InventoryAction(botAI, "buy") { }
bool Execute(Event event) override;
private:
bool BuyItem(VendorItemData const* tItems, ObjectGuid vendorguid, ItemTemplate const* proto);
bool TradeItem(FindItemVisitor* visitor, int8 slot);
bool TradeItem(Item const* item, int8 slot);
};
#endif

View File

@@ -0,0 +1,343 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
*/
#include "CastCustomSpellAction.h"
#include "ChatHelper.h"
#include "Event.h"
#include "ItemUsageValue.h"
#include "Playerbots.h"
uint32 FindLastSeparator(std::string const text, std::string const sep)
{
uint32 pos = text.rfind(sep);
if (pos == std::string::npos)
return pos;
uint32 lastLinkBegin = text.rfind("|H");
uint32 lastLinkEnd = text.find("|h|r", lastLinkBegin + 1);
if (pos >= lastLinkBegin && pos <= lastLinkEnd)
pos = text.find_last_of(sep, lastLinkBegin);
return pos;
}
static inline void ltrim(std::string& s)
{
s.erase(s.begin(), std::find_if(s.begin(), s.end(), [](unsigned char ch) { return !std::isspace(ch); }));
}
bool CastCustomSpellAction::Execute(Event event)
{
// only allow proper vehicle seats
if (botAI->IsInVehicle() && !botAI->IsInVehicle(false, false, true))
return false;
Player* master = GetMaster();
Unit* target = nullptr;
std::string text = event.getParam();
GuidVector gos = chat->parseGameobjects(text);
if (!gos.empty())
{
for (auto go : gos)
{
if (!target)
target = botAI->GetUnit(go);
chat->eraseAllSubStr(text, chat->FormatWorldobject(botAI->GetUnit(go)));
}
ltrim(text);
}
if (!target)
if (master && master->GetTarget())
target = botAI->GetUnit(master->GetTarget());
if (!target)
target = bot;
if (!master) // Use self as master for permissions.
master = bot;
Item* itemTarget = nullptr;
uint32 pos = FindLastSeparator(text, " ");
uint32 castCount = 1;
if (pos != std::string::npos)
{
std::string const param = text.substr(pos + 1);
std::vector<Item*> items = InventoryAction::parseItems(param, ITERATE_ITEMS_IN_BAGS);
if (!items.empty())
itemTarget = *items.begin();
else
{
castCount = atoi(param.c_str());
if (castCount > 0)
text = text.substr(0, pos);
}
}
uint32 spell = AI_VALUE2(uint32, "spell id", text);
std::ostringstream msg;
if (!spell)
{
msg << "Unknown spell " << text;
botAI->TellError(msg.str());
return false;
}
SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spell);
if (!spellInfo)
{
msg << "Unknown spell " << text;
botAI->TellError(msg.str());
return false;
}
if (target != bot && !bot->HasInArc(CAST_ANGLE_IN_FRONT, target, sPlayerbotAIConfig->sightDistance))
{
bot->SetFacingToObject(target);
botAI->SetNextCheckDelay(sPlayerbotAIConfig->globalCoolDown);
msg << "cast " << text;
botAI->HandleCommand(CHAT_MSG_WHISPER, msg.str(), master);
return true;
}
std::ostringstream spellName;
spellName << ChatHelper::FormatSpell(spellInfo) << " on ";
if (bot->GetTrader())
spellName << "trade item";
else if (itemTarget)
spellName << chat->FormatItem(itemTarget->GetTemplate());
else if (target == bot)
spellName << "self";
else
spellName << target->GetName();
if (!bot->GetTrader() && !botAI->CanCastSpell(spell, target, true, itemTarget))
{
msg << "Cannot cast " << spellName.str();
botAI->TellError(msg.str());
return false;
}
bool result = spell ? botAI->CastSpell(spell, target, itemTarget) : botAI->CastSpell(text, target, itemTarget);
if (result)
{
msg << "Casting " << spellName.str();
if (castCount > 1)
{
std::ostringstream cmd;
cmd << castString(target) << " " << text << " " << (castCount - 1);
botAI->HandleCommand(CHAT_MSG_WHISPER, cmd.str(), master);
msg << "|cffffff00(x" << (castCount-1) << " left)|r";
}
botAI->TellMasterNoFacing(msg.str());
}
else
{
msg << "Cast " << spellName.str() << " is failed";
botAI->TellError(msg.str());
}
return result;
}
bool CastCustomNcSpellAction::isUseful()
{
return !bot->IsInCombat();
}
std::string const CastCustomNcSpellAction::castString(WorldObject* target)
{
return "castnc " + chat->FormatWorldobject(target);
}
bool CastRandomSpellAction::AcceptSpell(SpellInfo const* spellInfo)
{
bool isTradeSkill = spellInfo->Effects[EFFECT_0].Effect == SPELL_EFFECT_CREATE_ITEM && spellInfo->ReagentCount[EFFECT_0] > 0 && spellInfo->SchoolMask == 1;
return !isTradeSkill && spellInfo->GetRecoveryTime() < MINUTE * IN_MILLISECONDS;
}
bool CastRandomSpellAction::Execute(Event event)
{
std::vector<std::pair<uint32, std::string>> spellMap = GetSpellList();
Player* master = GetMaster();
Unit* target = nullptr;
GameObject* got = nullptr;
std::string name = event.getParam();
if (name.empty())
name = getName();
GuidVector wos = chat->parseGameobjects(name);
for (auto wo : wos)
{
target = botAI->GetUnit(wo);
got = botAI->GetGameObject(wo);
if (got || target)
break;
}
if (!got && !target && bot->GetTarget())
{
target = botAI->GetUnit(bot->GetTarget());
got = botAI->GetGameObject(bot->GetTarget());
}
if (!got && !target)
if (master && master->GetTarget())
target = botAI->GetUnit(master->GetTarget());
if (!got && !target)
target = bot;
std::vector<std::pair<uint32, std::pair<uint32, WorldObject*>>> spellList;
for (auto& spell : spellMap)
{
uint32 spellId = spell.first;
SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellId);
if (!spellInfo)
continue;
if (!AcceptSpell(spellInfo))
continue;
if (bot->HasSpell(spellId))
{
uint32 spellPriority = GetSpellPriority(spellInfo);
if (target && botAI->CanCastSpell(spellId, target, true))
spellList.push_back(std::make_pair(spellId, std::make_pair(spellPriority, target)));
if (got && botAI->CanCastSpell(spellId, got->GetPositionX(), got->GetPositionY(), got->GetPositionZ(), true))
spellList.push_back(std::make_pair(spellId, std::make_pair(spellPriority, got)));
if (botAI->CanCastSpell(spellId, bot, true))
spellList.push_back(std::make_pair(spellId, std::make_pair(spellPriority, bot)));
}
}
if (spellList.empty())
return false;
bool isCast = false;
std::sort(spellList.begin(), spellList.end(), [](std::pair<uint32, std::pair<uint32, WorldObject*>> i, std::pair<uint32, std::pair<uint32, WorldObject*>> j)
{
return i.first > j.first;
});
uint32 rndBound = spellList.size() / 4;
rndBound = std::min(rndBound, (uint32)10);
rndBound = std::max(rndBound, (uint32)0);
for (uint32 i = 0; i < 5; i++)
{
uint32 rnd = urand(0, rndBound);
auto spell = spellList[rnd];
uint32 spellId = spell.first;
WorldObject* wo = spell.second.second;
bool isCast = castSpell(spellId, wo);
if (isCast)
{
if (MultiCast && ((wo && bot->HasInArc(CAST_ANGLE_IN_FRONT, wo, sPlayerbotAIConfig->sightDistance))))
{
std::ostringstream cmd;
cmd << "castnc " << chat->FormatWorldobject(wo) + " " << spellId << " " << 19;
botAI->HandleCommand(CHAT_MSG_WHISPER, cmd.str(), bot);
}
return true;
}
}
return false;
}
bool CraftRandomItemAction::AcceptSpell(SpellInfo const* spellInfo)
{
return spellInfo->Effects[EFFECT_0].Effect == SPELL_EFFECT_CREATE_ITEM && spellInfo->ReagentCount[EFFECT_0] > 0 && spellInfo->SchoolMask == 0;
}
uint32 CraftRandomItemAction::GetSpellPriority(SpellInfo const* spellInfo)
{
if (spellInfo->Effects[EFFECT_0].Effect != SPELL_EFFECT_CREATE_ITEM)
{
uint32 newItemId = spellInfo->Effects[EFFECT_0].ItemType;
if (newItemId)
{
ItemUsage usage = AI_VALUE2(ItemUsage, "item usage", newItemId);
if (usage == ITEM_USAGE_REPLACE || usage == ITEM_USAGE_EQUIP || usage == ITEM_USAGE_AMMO || usage == ITEM_USAGE_QUEST || usage == ITEM_USAGE_SKILL || usage == ITEM_USAGE_USE)
return 10;
}
if (ItemUsageValue::SpellGivesSkillUp(spellInfo->Id, bot))
return 8;
}
return 1;
}
bool CastRandomSpellAction::castSpell(uint32 spellId, WorldObject* wo)
{
if (wo->GetGUID().IsUnit())
return botAI->CastSpell(spellId, (Unit*)(wo));
else
return botAI->CastSpell(spellId, wo->GetPositionX(), wo->GetPositionY(), wo->GetPositionZ());
}
bool DisEnchantRandomItemAction::Execute(Event event)
{
std::vector<Item*> items = AI_VALUE2(std::vector<Item*>, "inventory items", "usage " + std::to_string(ITEM_USAGE_DISENCHANT));
std::reverse(items.begin(), items.end());
for (auto& item: items)
{
if(CastCustomSpellAction::Execute(Event("disenchant random item", "13262 "+ chat->FormatQItem(item->GetEntry()))))
return true;
}
return false;
}
bool DisEnchantRandomItemAction::isUseful()
{
return botAI->HasSkill(SKILL_ENCHANTING) && !bot->IsInCombat() && AI_VALUE2(uint32, "item count", "usage " + std::to_string(ITEM_USAGE_DISENCHANT)) > 0;
}
bool EnchantRandomItemAction::isUseful()
{
return botAI->HasSkill(SKILL_ENCHANTING) && !bot->IsInCombat();
}
bool EnchantRandomItemAction::AcceptSpell(SpellInfo const* spellInfo)
{
return spellInfo->Effects[EFFECT_0].Effect == SPELL_EFFECT_ENCHANT_ITEM && spellInfo->ReagentCount[EFFECT_0] > 0;
}
uint32 EnchantRandomItemAction::GetSpellPriority(SpellInfo const* spellInfo)
{
if (spellInfo->Effects[EFFECT_0].Effect == SPELL_EFFECT_ENCHANT_ITEM)
{
if (AI_VALUE2(Item*, "item for spell", spellInfo->Id) && ItemUsageValue::SpellGivesSkillUp(spellInfo->Id, bot))
return 10;
}
return 1;
}

View File

@@ -0,0 +1,81 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
*/
#ifndef _PLAYERBOT_CASTCUSTOMSPELLACTION_H
#define _PLAYERBOT_CASTCUSTOMSPELLACTION_H
#include "ListSpellsAction.h"
class PlayerbotAI;
class SpellInfo;
class WorldObject;
class CastCustomSpellAction : public InventoryAction
{
public:
CastCustomSpellAction(PlayerbotAI* botAI, std::string const name = "cast custom spell") : InventoryAction(botAI, name) { }
bool Execute(Event event) override;
virtual std::string const castString(WorldObject* target) { return "cast"; }
protected:
bool ncCast = false;
};
class CastCustomNcSpellAction : public CastCustomSpellAction
{
public:
CastCustomNcSpellAction(PlayerbotAI* botAI, std::string const name = "cast custom nc spell") : CastCustomSpellAction(botAI, name) { }
bool isUseful() override;
std::string const castString(WorldObject* target) override;
};
class CastRandomSpellAction : public ListSpellsAction
{
public:
CastRandomSpellAction(PlayerbotAI* botAI, std::string const name = "cast random spell") : ListSpellsAction(botAI, name) { }
bool isUseful() override { return false; }
virtual bool AcceptSpell(SpellInfo const* spellInfo);
virtual uint32 GetSpellPriority(SpellInfo const* spellInfo) { return 1; }
virtual bool castSpell(uint32 spellId, WorldObject* wo);
bool Execute(Event event) override;
protected:
bool MultiCast = false;
};
class CraftRandomItemAction : public CastRandomSpellAction
{
public:
CraftRandomItemAction(PlayerbotAI* botAI) : CastRandomSpellAction(botAI, "craft random item")
{
MultiCast = true;
}
bool AcceptSpell(SpellInfo const* spellInfo) override;
uint32 GetSpellPriority(SpellInfo const* spellInfo) override;
};
class DisEnchantRandomItemAction : public CastCustomSpellAction
{
public:
DisEnchantRandomItemAction(PlayerbotAI* botAI) : CastCustomSpellAction(botAI, "disenchant random item") {}
bool isUseful() override;
bool Execute(Event event) override;
};
class EnchantRandomItemAction : public CastRandomSpellAction
{
public:
EnchantRandomItemAction(PlayerbotAI* botAI) : CastRandomSpellAction(botAI, "enchant random item") { }
bool isUseful() override;
bool AcceptSpell(SpellInfo const* spellInfo) override;
uint32 GetSpellPriority(SpellInfo const* spellInfo) override;
};
#endif

View File

@@ -0,0 +1,29 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
*/
#include "ChangeChatAction.h"
#include "Event.h"
#include "Playerbots.h"
bool ChangeChatAction::Execute(Event event)
{
std::string const text = event.getParam();
ChatMsg parsed = chat->parseChat(text);
if (parsed == CHAT_MSG_SYSTEM)
{
std::ostringstream out;
out << "Current chat is " << chat->FormatChat(*context->GetValue<ChatMsg>("chat"));
botAI->TellMaster(out);
}
else
{
context->GetValue<ChatMsg>("chat")->Set(parsed);
std::ostringstream out;
out << "Chat set to " << chat->FormatChat(parsed);
botAI->TellMaster(out);
}
return true;
}

View File

@@ -0,0 +1,20 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
*/
#ifndef _PLAYERBOT_CHANGECHATACTION_H
#define _PLAYERBOT_CHANGECHATACTION_H
#include "Action.h"
class PlayerbotAI;
class ChangeChatAction : public Action
{
public:
ChangeChatAction(PlayerbotAI* botAI) : Action(botAI, "chat") { }
bool Execute(Event event) override;
};
#endif

View File

@@ -0,0 +1,78 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
*/
#include "ChangeStrategyAction.h"
#include "Event.h"
#include "Playerbots.h"
#include "PlayerbotDbStore.h"
bool ChangeCombatStrategyAction::Execute(Event event)
{
std::string const text = event.getParam();
botAI->ChangeStrategy(text.empty() ? getName() : text, BOT_STATE_COMBAT);
if (event.GetSource() == "co")
{
std::vector<std::string> splitted = split(text, ',');
for (std::vector<std::string>::iterator i = splitted.begin(); i != splitted.end(); i++)
{
const char* name = i->c_str();
switch (name[0])
{
case '+':
case '-':
case '~':
sPlayerbotDbStore->Save(botAI);
break;
case '?':
break;
}
}
}
return true;
}
bool ChangeNonCombatStrategyAction::Execute(Event event)
{
std::string const text = event.getParam();
uint32 account = bot->GetSession()->GetAccountId();
if (sPlayerbotAIConfig->IsInRandomAccountList(account) && botAI->GetMaster() && botAI->GetMaster()->GetSession()->GetSecurity() < SEC_GAMEMASTER)
{
if (text.find("loot") != std::string::npos || text.find("gather") != std::string::npos)
{
botAI->TellError("You can change any strategy except loot and gather");
return false;
}
}
botAI->ChangeStrategy(text, BOT_STATE_NON_COMBAT);
if (event.GetSource() == "nc")
{
std::vector<std::string> splitted = split(text, ',');
for (std::vector<std::string>::iterator i = splitted.begin(); i != splitted.end(); i++)
{
const char* name = i->c_str();
switch (name[0])
{
case '+':
case '-':
case '~':
sPlayerbotDbStore->Save(botAI);
break;
case '?':
break;
}
}
}
return true;
}
bool ChangeDeadStrategyAction::Execute(Event event)
{
std::string const text = event.getParam();
botAI->ChangeStrategy(text, BOT_STATE_DEAD);
return true;
}

View File

@@ -0,0 +1,36 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
*/
#ifndef _PLAYERBOT_CHANGESTRATEGYACTION_H
#define _PLAYERBOT_CHANGESTRATEGYACTION_H
#include "Action.h"
class PlayerbotAI;
class ChangeCombatStrategyAction : public Action
{
public:
ChangeCombatStrategyAction(PlayerbotAI* botAI, std::string const name = "co") : Action(botAI, name) { }
bool Execute(Event event) override;
};
class ChangeNonCombatStrategyAction : public Action
{
public:
ChangeNonCombatStrategyAction(PlayerbotAI* botAI) : Action(botAI, "nc") { }
bool Execute(Event event) override;
};
class ChangeDeadStrategyAction : public Action
{
public:
ChangeDeadStrategyAction(PlayerbotAI* botAI) : Action(botAI, "de") { }
bool Execute(Event event) override;
};
#endif

View File

@@ -0,0 +1,338 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
*/
#include "ChangeTalentsAction.h"
#include "ChatHelper.h"
#include "Event.h"
#include "Playerbots.h"
bool ChangeTalentsAction::Execute(Event event)
{
std::string param = event.getParam();
std::ostringstream out;
TalentSpec botSpec(bot);
if (!param.empty())
{
if (param.find("auto") != std::string::npos)
{
AutoSelectTalents(&out);
}
else if (param.find("list ") != std::string::npos)
{
listPremadePaths(getPremadePaths(param.substr(5)), &out);
}
else if (param.find("list") != std::string::npos)
{
listPremadePaths(getPremadePaths(""), &out);
}
else
{
bool crop = false;
bool shift = false;
if (param.find("do ") != std::string::npos)
{
crop = true;
param = param.substr(3);
}
else if (param.find("shift ") != std::string::npos)
{
shift = true;
param = param.substr(6);
}
out << "Apply talents [" << param << "] ";
if (botSpec.CheckTalentLink(param, &out))
{
TalentSpec newSpec(bot, param);
std::string const specLink = newSpec.GetTalentLink();
if (crop)
{
newSpec.CropTalents(bot->getLevel());
out << "becomes: " << newSpec.GetTalentLink();
}
if (shift)
{
TalentSpec botSpec(bot);
newSpec.ShiftTalents(&botSpec, bot->getLevel());
out << "becomes: " << newSpec.GetTalentLink();
}
if (newSpec.CheckTalents(bot->getLevel(), &out))
{
newSpec.ApplyTalents(bot, &out);
sRandomPlayerbotMgr->SetValue(bot->GetGUID().GetCounter(), "specNo", 0);
sRandomPlayerbotMgr->SetValue(bot->GetGUID().GetCounter(), "specLink", 1, specLink);
}
}
else
{
std::vector<TalentPath*> paths = getPremadePaths(param);
if (paths.size() > 0)
{
out.str("");
out.clear();
if (paths.size() > 1 && sPlayerbotAIConfig->autoPickTalents != "full")
{
out << "Found multiple specs: ";
listPremadePaths(paths, &out);
}
else
{
if (paths.size() > 1)
out << "Found " << paths.size() << " possible specs to choose from. ";
TalentPath* path = PickPremadePath(paths, sRandomPlayerbotMgr->IsRandomBot(bot));
TalentSpec newSpec = *GetBestPremadeSpec(path->id);
std::string const specLink = newSpec.GetTalentLink();
newSpec.CropTalents(bot->getLevel());
newSpec.ApplyTalents(bot, &out);
if (newSpec.GetTalentPoints() > 0)
{
out << "Apply spec " << "|h|cffffffff" << path->name << " " << newSpec.FormatSpec(bot);
sRandomPlayerbotMgr->SetValue(bot->GetGUID().GetCounter(), "specNo", path->id + 1);
sRandomPlayerbotMgr->SetValue(bot->GetGUID().GetCounter(), "specLink", 0);
}
}
}
}
}
}
else
{
uint32 specId = sRandomPlayerbotMgr->GetValue(bot->GetGUID().GetCounter(), "specNo") - 1;
std::string specName = "";
TalentPath* specPath;
if (specId)
{
specPath = getPremadePath(specId);
if (specPath->id == specId)
specName = specPath->name;
}
out << "My current talent spec is: " << "|h|cffffffff";
if (specName != "")
out << specName << " (" << botSpec.FormatSpec(bot) << ")";
else
out << chat->FormatClass(bot, botSpec.highestTree());
out << " Link: ";
out << botSpec.GetTalentLink();
}
botAI->TellMaster(out);
return true;
}
std::vector<TalentPath*> ChangeTalentsAction::getPremadePaths(std::string const findName)
{
std::vector<TalentPath*> ret;
for (auto& path : sPlayerbotAIConfig->classSpecs[bot->getClass()].talentPath)
{
if (findName.empty() || path.name.find(findName) != std::string::npos)
{
ret.push_back(&path);
}
}
return std::move(ret);
}
std::vector<TalentPath*> ChangeTalentsAction::getPremadePaths(TalentSpec* oldSpec)
{
std::vector<TalentPath*> ret;
for (auto& path : sPlayerbotAIConfig->classSpecs[bot->getClass()].talentPath)
{
TalentSpec newSpec = *GetBestPremadeSpec(path.id);
newSpec.CropTalents(bot->getLevel());
if (oldSpec->isEarlierVersionOf(newSpec))
{
ret.push_back(&path);
}
}
return std::move(ret);
}
TalentPath* ChangeTalentsAction::getPremadePath(uint32 id)
{
for (auto& path : sPlayerbotAIConfig->classSpecs[bot->getClass()].talentPath)
{
if (id == path.id)
{
return &path;
}
}
return &sPlayerbotAIConfig->classSpecs[bot->getClass()].talentPath[0];
}
void ChangeTalentsAction::listPremadePaths(std::vector<TalentPath*> paths, std::ostringstream* out)
{
if (paths.size() == 0)
{
*out << "No predefined talents found..";
}
*out << "|h|cffffffff";
for (auto path : paths)
{
*out << path->name << " (" << path->talentSpec.back().FormatSpec(bot) << "), ";
}
out->seekp(-2, out->cur);
*out << ".";
}
TalentPath* ChangeTalentsAction::PickPremadePath(std::vector<TalentPath*> paths, bool useProbability)
{
uint32 totProbability = 0;
uint32 curProbability = 0;
if (paths.size() == 1)
return paths[0];
for (auto path : paths)
{
totProbability += useProbability ? path->probability : 1;
}
totProbability = urand(0, totProbability);
for (auto path : paths)
{
curProbability += (useProbability ? path->probability : 1);
if (curProbability >= totProbability)
return path;
}
return paths[0];
}
bool ChangeTalentsAction::AutoSelectTalents(std::ostringstream* out)
{
// Does the bot have talentpoints?
if (bot->getLevel() < 10)
{
*out << "No free talent points.";
return false;
}
uint32 specNo = sRandomPlayerbotMgr->GetValue(bot->GetGUID().GetCounter(), "specNo");
uint32 specId = specNo - 1;
std::string specLink = sRandomPlayerbotMgr->GetData(bot->GetGUID().GetCounter(), "specLink");
//Continue the current spec
if (specNo > 0)
{
TalentSpec newSpec = *GetBestPremadeSpec(specId);
newSpec.CropTalents(bot->getLevel());
newSpec.ApplyTalents(bot, out);
if (newSpec.GetTalentPoints() > 0)
{
*out << "Upgrading spec " << "|h|cffffffff" << getPremadePath(specId)->name << "" << newSpec.FormatSpec(bot);
}
}
else if (!specLink.empty())
{
TalentSpec newSpec(bot, specLink);
newSpec.CropTalents(bot->getLevel());
newSpec.ApplyTalents(bot, out);
if (newSpec.GetTalentPoints() > 0)
{
*out << "Upgrading saved spec "
<< "|h|cffffffff" << chat->FormatClass(bot, newSpec.highestTree()) << " (" << newSpec.FormatSpec(bot) << ")";
}
}
//Spec was not found or not sufficient
if (bot->GetFreeTalentPoints() > 0 || (!specNo && specLink.empty()))
{
TalentSpec oldSpec(bot);
std::vector<TalentPath*> paths = getPremadePaths(&oldSpec);
if (paths.size() == 0) //No spec like the old one found. Pick any.
{
if (bot->CalculateTalentsPoints() > 0)
*out << "No specs like the current spec found. ";
paths = getPremadePaths("");
}
if (paths.size() == 0)
{
*out << "No predefined talents found for this class.";
specId = -1;
// specLink = "";
}
else if (paths.size() > 1 && sPlayerbotAIConfig->autoPickTalents != "full" && !sRandomPlayerbotMgr->IsRandomBot(bot))
{
*out << "Found multiple specs: ";
listPremadePaths(paths, out);
}
else
{
specId = PickPremadePath(paths, sRandomPlayerbotMgr->IsRandomBot(bot))->id;
TalentSpec newSpec = *GetBestPremadeSpec(specId);
specLink = newSpec.GetTalentLink();
newSpec.CropTalents(bot->getLevel());
newSpec.ApplyTalents(bot, out);
if (paths.size() > 1)
*out << "Found " << paths.size() << " possible specs to choose from. ";
*out << "Apply spec " << "|h|cffffffff" << getPremadePath(specId)->name << " " << newSpec.FormatSpec(bot);
}
}
sRandomPlayerbotMgr->SetValue(bot->GetGUID().GetCounter(), "specNo", specId + 1);
if (!specLink.empty() && specId == -1)
sRandomPlayerbotMgr->SetValue(bot->GetGUID().GetCounter(), "specLink", 1, specLink);
else
sRandomPlayerbotMgr->SetValue(bot->GetGUID().GetCounter(), "specLink", 0);
return (specNo == 0) ? false : true;
}
//Returns a pre-made talentspec that best suits the bots current talents.
TalentSpec* ChangeTalentsAction::GetBestPremadeSpec(uint32 specId)
{
TalentPath* path = getPremadePath(specId);
for (auto& spec : path->talentSpec)
{
if (spec.points >= bot->CalculateTalentsPoints())
return &spec;
}
if (path->talentSpec.size())
return &path->talentSpec.back();
return &sPlayerbotAIConfig->classSpecs[bot->getClassMask()].baseSpec;
}
bool AutoSetTalentsAction::Execute(Event event)
{
std::ostringstream out;
if (sPlayerbotAIConfig->autoPickTalents == "no" && !sRandomPlayerbotMgr->IsRandomBot(bot))
return false;
if (bot->GetFreeTalentPoints() <= 0)
return false;
AutoSelectTalents(&out);
botAI->TellMaster(out);
return true;
}

View File

@@ -0,0 +1,38 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
*/
#ifndef _PLAYERBOT_CHANGETALENTSACTION_H
#define _PLAYERBOT_CHANGETALENTSACTION_H
#include "Action.h"
#include "Talentspec.h"
class PlayerbotAI;
class ChangeTalentsAction : public Action
{
public:
ChangeTalentsAction(PlayerbotAI* botAI, std::string const name = "talents") : Action(botAI, name) { }
bool Execute(Event event);
bool AutoSelectTalents(std::ostringstream* out);
private:
std::vector<TalentPath*> getPremadePaths(std::string const findName);
std::vector<TalentPath*> getPremadePaths(TalentSpec* oldSpec);
TalentPath* getPremadePath(uint32 id);
void listPremadePaths(std::vector<TalentPath*> paths, std::ostringstream* out);
TalentPath* PickPremadePath(std::vector<TalentPath*> paths, bool useProbability);
TalentSpec* GetBestPremadeSpec(uint32 spec);
};
class AutoSetTalentsAction : public ChangeTalentsAction
{
public:
AutoSetTalentsAction(PlayerbotAI* botAI) : ChangeTalentsAction(botAI, "auto talents") { }
bool Execute(Event event) override;
};
#endif

View File

@@ -0,0 +1,255 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
*/
#ifndef _PLAYERBOT_CHATACTIONCONTEXTACTION_H
#define _PLAYERBOT_CHATACTIONCONTEXTACTION_H
#include "AddLootAction.h"
#include "AttackAction.h"
#include "BankAction.h"
#include "BuffAction.h"
#include "BuyAction.h"
#include "CastCustomSpellAction.h"
#include "ChangeChatAction.h"
#include "ChangeTalentsAction.h"
#include "ChangeStrategyAction.h"
#include "ChatShortcutActions.h"
#include "CheatAction.h"
#include "CustomStrategyEditAction.h"
#include "DebugAction.h"
#include "DestroyItemAction.h"
#include "DropQuestAction.h"
#include "EquipAction.h"
#include "FlagAction.h"
#include "GoAction.h"
#include "GossipHelloAction.h"
#include "GuildBankAction.h"
#include "GuildManagementActions.h"
#include "HelpAction.h"
#include "HireAction.h"
#include "InviteToGroupAction.h"
#include "LeaveGroupAction.h"
#include "ListQuestsActions.h"
#include "ListSpellsAction.h"
#include "LogLevelAction.h"
#include "LootStrategyAction.h"
#include "MailAction.h"
#include "QueryItemUsageAction.h"
#include "QueryQuestAction.h"
#include "PassLeadershipToMasterAction.h"
#include "PositionAction.h"
#include "RangeAction.h"
#include "ReleaseSpiritAction.h"
#include "RepairAllAction.h"
#include "ResetAiAction.h"
#include "ReviveFromCorpseAction.h"
#include "RewardAction.h"
#include "RtiAction.h"
#include "RTSCAction.h"
#include "SaveManaAction.h"
#include "SellAction.h"
#include "SetCraftAction.h"
#include "SendMailAction.h"
#include "SetHomeAction.h"
#include "ShareQuestAction.h"
#include "SkipSpellsListAction.h"
#include "StatsAction.h"
#include "TaxiAction.h"
#include "TeleportAction.h"
#include "TellCastFailedAction.h"
#include "TellItemCountAction.h"
#include "TellLosAction.h"
#include "TellReputationAction.h"
#include "TellTargetAction.h"
#include "TradeAction.h"
#include "TrainerAction.h"
#include "UnequipAction.h"
#include "UseItemAction.h"
#include "UseMeetingStoneAction.h"
#include "WhoAction.h"
#include "WtsAction.h"
#include "NamedObjectContext.h"
#include "Formations.h"
#include "Stances.h"
class ChatActionContext : public NamedObjectContext<Action>
{
public:
ChatActionContext()
{
creators["range"] = &ChatActionContext::range;
creators["stats"] = &ChatActionContext::stats;
creators["quests"] = &ChatActionContext::quests;
creators["leave"] = &ChatActionContext::leave;
creators["reputation"] = &ChatActionContext::reputation;
creators["log"] = &ChatActionContext::log;
creators["los"] = &ChatActionContext::los;
creators["drop"] = &ChatActionContext::drop;
creators["clean quest log"] = &ChatActionContext::clean_quest_log;
creators["share"] = &ChatActionContext::share;
creators["query quest"] = &ChatActionContext::query_quest;
creators["query item usage"] = &ChatActionContext::query_item_usage;
creators["ll"] = &ChatActionContext::ll;
creators["ss"] = &ChatActionContext::ss;
creators["add all loot"] = &ChatActionContext::add_all_loot;
creators["release"] = &ChatActionContext::release;
creators["repop"] = &ChatActionContext::repop;
creators["teleport"] = &ChatActionContext::teleport;
creators["taxi"] = &ChatActionContext::taxi;
creators["repair"] = &ChatActionContext::repair;
creators["use"] = &ChatActionContext::use;
creators["item count"] = &ChatActionContext::item_count;
creators["equip"] = &ChatActionContext::equip;
creators["equip upgrades"] = &ChatActionContext::equip_upgrades;
creators["unequip"] = &ChatActionContext::unequip;
creators["sell"] = &ChatActionContext::sell;
creators["buy"] = &ChatActionContext::buy;
creators["reward"] = &ChatActionContext::reward;
creators["trade"] = &ChatActionContext::trade;
creators["talents"] = &ChatActionContext::talents;
creators["spells"] = &ChatActionContext::spells;
creators["co"] = &ChatActionContext::co;
creators["nc"] = &ChatActionContext::nc;
creators["de"] = &ChatActionContext::dead;
creators["trainer"] = &ChatActionContext::trainer;
creators["attack my target"] = &ChatActionContext::attack_my_target;
creators["chat"] = &ChatActionContext::chat;
creators["home"] = &ChatActionContext::home;
creators["destroy"] = &ChatActionContext::destroy;
creators["reset botAI"] = &ChatActionContext::reset_ai;
creators["buff"] = &ChatActionContext::buff;
creators["help"] = &ChatActionContext::help;
creators["gb"] = &ChatActionContext::gb;
creators["bank"] = &ChatActionContext::bank;
creators["follow chat shortcut"] = &ChatActionContext::follow_chat_shortcut;
creators["stay chat shortcut"] = &ChatActionContext::stay_chat_shortcut;
creators["flee chat shortcut"] = &ChatActionContext::flee_chat_shortcut;
creators["runaway chat shortcut"] = &ChatActionContext::runaway_chat_shortcut;
creators["grind chat shortcut"] = &ChatActionContext::grind_chat_shortcut;
creators["tank attack chat shortcut"] = &ChatActionContext::tank_attack_chat_shortcut;
creators["gossip hello"] = &ChatActionContext::gossip_hello;
creators["cast custom spell"] = &ChatActionContext::cast_custom_spell;
creators["cast custom nc spell"] = &ChatActionContext::cast_custom_nc_spell;
creators["invite"] = &ChatActionContext::invite;
creators["spell"] = &ChatActionContext::spell;
creators["rti"] = &ChatActionContext::rti;
creators["spirit healer"] = &ChatActionContext::spirit_healer;
creators["position"] = &ChatActionContext::position;
creators["tell target"] = &ChatActionContext::tell_target;
creators["summon"] = &ChatActionContext::summon;
creators["who"] = &ChatActionContext::who;
creators["save mana"] = &ChatActionContext::save_mana;
creators["max dps chat shortcut"] = &ChatActionContext::max_dps_chat_shortcut;
creators["tell attackers"] = &ChatActionContext::tell_attackers;
creators["formation"] = &ChatActionContext::formation;
creators["stance"] = &ChatActionContext::stance;
creators["sendmail"] = &ChatActionContext::sendmail;
creators["mail"] = &ChatActionContext::mail;
creators["go"] = &ChatActionContext::go;
creators["debug"] = &ChatActionContext::debug;
creators["cdebug"] = &ChatActionContext::debug;
creators["cs"] = &ChatActionContext::cs;
creators["wts"] = &ChatActionContext::wts;
creators["hire"] = &ChatActionContext::hire;
creators["craft"] = &ChatActionContext::craft;
creators["flag"] = &ChatActionContext::flag;
creators["give leader"] = &ChatActionContext::give_leader;
creators["cheat"] = &ChatActionContext::cheat;
creators["ginvite"] = &ChatActionContext::ginvite;
creators["guild promote"] = &ChatActionContext::guild_promote;
creators["guild demote"] = &ChatActionContext::guild_demote;
creators["guild remove"] = &ChatActionContext::guild_remove;
creators["guild leave"] = &ChatActionContext::guild_leave;
creators["rtsc"] = &ChatActionContext::rtsc;
}
private:
static Action* range(PlayerbotAI* botAI) { return new RangeAction(botAI); }
static Action* flag(PlayerbotAI* botAI) { return new FlagAction(botAI); }
static Action* craft(PlayerbotAI* botAI) { return new SetCraftAction(botAI); }
static Action* hire(PlayerbotAI* botAI) { return new HireAction(botAI); }
static Action* wts(PlayerbotAI* botAI) { return new WtsAction(botAI); }
static Action* cs(PlayerbotAI* botAI) { return new CustomStrategyEditAction(botAI); }
static Action* debug(PlayerbotAI* botAI) { return new DebugAction(botAI); }
static Action* mail(PlayerbotAI* botAI) { return new MailAction(botAI); }
static Action* go(PlayerbotAI* botAI) { return new GoAction(botAI); }
static Action* sendmail(PlayerbotAI* botAI) { return new SendMailAction(botAI); }
static Action* formation(PlayerbotAI* botAI) { return new SetFormationAction(botAI); }
static Action* stance(PlayerbotAI* botAI) { return new SetStanceAction(botAI); }
static Action* tell_attackers(PlayerbotAI* botAI) { return new TellAttackersAction(botAI); }
static Action* max_dps_chat_shortcut(PlayerbotAI* botAI) { return new MaxDpsChatShortcutAction(botAI); }
static Action* save_mana(PlayerbotAI* botAI) { return new SaveManaAction(botAI); }
static Action* who(PlayerbotAI* botAI) { return new WhoAction(botAI); }
static Action* summon(PlayerbotAI* botAI) { return new SummonAction(botAI); }
static Action* tell_target(PlayerbotAI* botAI) { return new TellTargetAction(botAI); }
static Action* position(PlayerbotAI* botAI) { return new PositionAction(botAI); }
static Action* spirit_healer(PlayerbotAI* botAI) { return new SpiritHealerAction(botAI); }
static Action* rti(PlayerbotAI* botAI) { return new RtiAction(botAI); }
static Action* invite(PlayerbotAI* botAI) { return new InviteToGroupAction(botAI); }
static Action* spell(PlayerbotAI* botAI) { return new TellSpellAction(botAI); }
static Action* cast_custom_spell(PlayerbotAI* botAI) { return new CastCustomSpellAction(botAI); }
static Action* cast_custom_nc_spell(PlayerbotAI* botAI) { return new CastCustomNcSpellAction(botAI); }
static Action* tank_attack_chat_shortcut(PlayerbotAI* botAI) { return new TankAttackChatShortcutAction(botAI); }
static Action* grind_chat_shortcut(PlayerbotAI* botAI) { return new GrindChatShortcutAction(botAI); }
static Action* flee_chat_shortcut(PlayerbotAI* botAI) { return new FleeChatShortcutAction(botAI); }
static Action* runaway_chat_shortcut(PlayerbotAI* botAI) { return new GoawayChatShortcutAction(botAI); }
static Action* stay_chat_shortcut(PlayerbotAI* botAI) { return new StayChatShortcutAction(botAI); }
static Action* follow_chat_shortcut(PlayerbotAI* botAI) { return new FollowChatShortcutAction(botAI); }
static Action* gb(PlayerbotAI* botAI) { return new GuildBankAction(botAI); }
static Action* bank(PlayerbotAI* botAI) { return new BankAction(botAI); }
static Action* help(PlayerbotAI* botAI) { return new HelpAction(botAI); }
static Action* buff(PlayerbotAI* botAI) { return new BuffAction(botAI); }
static Action* destroy(PlayerbotAI* botAI) { return new DestroyItemAction(botAI); }
static Action* home(PlayerbotAI* botAI) { return new SetHomeAction(botAI); }
static Action* chat(PlayerbotAI* botAI) { return new ChangeChatAction(botAI); }
static Action* attack_my_target(PlayerbotAI* botAI) { return new AttackMyTargetAction(botAI); }
static Action* trainer(PlayerbotAI* botAI) { return new TrainerAction(botAI); }
static Action* co(PlayerbotAI* botAI) { return new ChangeCombatStrategyAction(botAI); }
static Action* nc(PlayerbotAI* botAI) { return new ChangeNonCombatStrategyAction(botAI); }
static Action* dead(PlayerbotAI* botAI) { return new ChangeDeadStrategyAction(botAI); }
static Action* spells(PlayerbotAI* botAI) { return new ListSpellsAction(botAI); }
static Action* talents(PlayerbotAI* botAI) { return new ChangeTalentsAction(botAI); }
static Action* equip(PlayerbotAI* botAI) { return new EquipAction(botAI); }
static Action* equip_upgrades(PlayerbotAI* botAI) { return new EquipUpgradesAction(botAI); }
static Action* unequip(PlayerbotAI* botAI) { return new UnequipAction(botAI); }
static Action* sell(PlayerbotAI* botAI) { return new SellAction(botAI); }
static Action* buy(PlayerbotAI* botAI) { return new BuyAction(botAI); }
static Action* reward(PlayerbotAI* botAI) { return new RewardAction(botAI); }
static Action* trade(PlayerbotAI* botAI) { return new TradeAction(botAI); }
static Action* item_count(PlayerbotAI* botAI) { return new TellItemCountAction(botAI); }
static Action* use(PlayerbotAI* botAI) { return new UseItemAction(botAI); }
static Action* repair(PlayerbotAI* botAI) { return new RepairAllAction(botAI); }
static Action* taxi(PlayerbotAI* botAI) { return new TaxiAction(botAI); }
static Action* teleport(PlayerbotAI* botAI) { return new TeleportAction(botAI); }
static Action* release(PlayerbotAI* botAI) { return new ReleaseSpiritAction(botAI); }
static Action* repop(PlayerbotAI* botAI) { return new RepopAction(botAI); }
static Action* query_item_usage(PlayerbotAI* botAI) { return new QueryItemUsageAction(botAI); }
static Action* query_quest(PlayerbotAI* botAI) { return new QueryQuestAction(botAI); }
static Action* drop(PlayerbotAI* botAI) { return new DropQuestAction(botAI); }
static Action* clean_quest_log(PlayerbotAI* botAI) { return new CleanQuestLogAction(botAI); }
static Action* share(PlayerbotAI* botAI) { return new ShareQuestAction(botAI); }
static Action* stats(PlayerbotAI* botAI) { return new StatsAction(botAI); }
static Action* quests(PlayerbotAI* botAI) { return new ListQuestsAction(botAI); }
static Action* leave(PlayerbotAI* botAI) { return new LeaveGroupAction(botAI); }
static Action* reputation(PlayerbotAI* botAI) { return new TellReputationAction(botAI); }
static Action* log(PlayerbotAI* botAI) { return new LogLevelAction(botAI); }
static Action* los(PlayerbotAI* botAI) { return new TellLosAction(botAI); }
static Action* ll(PlayerbotAI* botAI) { return new LootStrategyAction(botAI); }
static Action* ss(PlayerbotAI* botAI) { return new SkipSpellsListAction(botAI); }
static Action* add_all_loot(PlayerbotAI* botAI) { return new AddAllLootAction(botAI); }
static Action* reset_ai(PlayerbotAI* botAI) { return new ResetAiAction(botAI); }
static Action* gossip_hello(PlayerbotAI* botAI) { return new GossipHelloAction(botAI); }
static Action* give_leader(PlayerbotAI* botAI) { return new GiveLeaderAction(botAI); }
static Action* cheat(PlayerbotAI* botAI) { return new CheatAction(botAI); }
static Action* ginvite(PlayerbotAI* botAI) { return new GuildInviteAction(botAI); }
static Action* guild_promote(PlayerbotAI* botAI) { return new GuildPromoteAction(botAI); }
static Action* guild_demote(PlayerbotAI* botAI) { return new GuildDemoteAction(botAI); }
static Action* guild_remove(PlayerbotAI* botAI) { return new GuildRemoveAction(botAI); }
static Action* guild_leave(PlayerbotAI* botAI) { return new GuildLeaveAction(botAI); }
static Action* rtsc(PlayerbotAI* botAI) { return new RTSCAction(botAI); }
};
#endif

View File

@@ -0,0 +1,190 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
*/
#include "ChatShortcutActions.h"
#include "Event.h"
#include "Formations.h"
#include "PositionValue.h"
#include "Playerbots.h"
void ReturnPositionResetAction::ResetReturnPosition()
{
PositionMap& posMap = context->GetValue<PositionMap&>("position")->Get();
PositionInfo pos = posMap["return"];
pos.Reset();
posMap["return"] = pos;
}
void ReturnPositionResetAction::SetReturnPosition(float x, float y, float z)
{
PositionMap& posMap = context->GetValue<PositionMap&>("position")->Get();
PositionInfo pos = posMap["return"];
pos.Set(x, y, z, botAI->GetBot()->GetMapId());
posMap["return"] = pos;
}
bool FollowChatShortcutAction::Execute(Event event)
{
Player* master = GetMaster();
if (!master)
return false;
botAI->Reset();
botAI->ChangeStrategy("+follow,-passive", BOT_STATE_NON_COMBAT);
botAI->ChangeStrategy("-follow,-passive", BOT_STATE_COMBAT);
PositionMap& posMap = context->GetValue<PositionMap&>("position")->Get();
PositionInfo pos = posMap["return"];
pos.Reset();
posMap["return"] = pos;
if (bot->IsInCombat())
{
Formation* formation = AI_VALUE(Formation*, "formation");
std::string const target = formation->GetTargetName();
bool moved = false;
if (!target.empty())
{
moved = Follow(AI_VALUE(Unit*, target));
}
else
{
WorldLocation loc = formation->GetLocation();
if (Formation::IsNullLocation(loc) || loc.GetMapId() == -1)
return false;
moved = MoveTo(loc.GetMapId(), loc.GetPositionX(), loc.GetPositionY(), loc.GetPositionZ());
}
if (moved)
{
botAI->TellMaster("Following");
return true;
}
}
/* Default mechanics takes care of this now.
if (bot->GetMapId() != master->GetMapId() || (master && bot->GetDistance(master) > sPlayerbotAIConfig->sightDistance))
{
if (bot->isDead())
{
bot->ResurrectPlayer(1.0f, false);
botAI->TellMasterNoFacing("Back from the grave!");
}
else
botAI->TellMaster("You are too far away from me! I will there soon.");
bot->TeleportTo(master->GetMapId(), master->GetPositionX(), master->GetPositionY(), master->GetPositionZ(), master->GetOrientation());
return true;
}
*/
botAI->TellMaster("Following");
return true;
}
bool StayChatShortcutAction::Execute(Event event)
{
Player* master = GetMaster();
if (!master)
return false;
botAI->Reset();
botAI->ChangeStrategy("+stay,-passive", BOT_STATE_NON_COMBAT);
botAI->ChangeStrategy("-follow,-passive", BOT_STATE_COMBAT);
SetReturnPosition(bot->GetPositionX(), bot->GetPositionY(), bot->GetPositionZ());
botAI->TellMaster("Staying");
return true;
}
bool FleeChatShortcutAction::Execute(Event event)
{
Player* master = GetMaster();
if (!master)
return false;
botAI->Reset();
botAI->ChangeStrategy("+follow,+passive", BOT_STATE_NON_COMBAT);
botAI->ChangeStrategy("+follow,+passive", BOT_STATE_COMBAT);
ResetReturnPosition();
if (bot->GetMapId() != master->GetMapId() || bot->GetDistance(master) > sPlayerbotAIConfig->sightDistance)
{
botAI->TellError("I will not flee with you - too far away");
return true;
}
botAI->TellMaster("Fleeing");
return true;
}
bool GoawayChatShortcutAction::Execute(Event event)
{
Player* master = GetMaster();
if (!master)
return false;
botAI->Reset();
botAI->ChangeStrategy("+runaway", BOT_STATE_NON_COMBAT);
botAI->ChangeStrategy("+runaway", BOT_STATE_COMBAT);
ResetReturnPosition();
botAI->TellMaster("Running away");
return true;
}
bool GrindChatShortcutAction::Execute(Event event)
{
Player* master = GetMaster();
if (!master)
return false;
botAI->Reset();
botAI->ChangeStrategy("+grind,-passive", BOT_STATE_NON_COMBAT);
ResetReturnPosition();
botAI->TellMaster("Grinding");
return true;
}
bool TankAttackChatShortcutAction::Execute(Event event)
{
Player* master = GetMaster();
if (!master)
return false;
if (!botAI->IsTank(bot))
return false;
botAI->Reset();
botAI->ChangeStrategy("-passive", BOT_STATE_NON_COMBAT);
botAI->ChangeStrategy("-passive", BOT_STATE_COMBAT);
ResetReturnPosition();
botAI->TellMaster("Attacking");
return true;
}
bool MaxDpsChatShortcutAction::Execute(Event event)
{
Player* master = GetMaster();
if (!master)
return false;
if (!botAI->ContainsStrategy(STRATEGY_TYPE_DPS))
return false;
botAI->Reset();
botAI->ChangeStrategy("-threat,-conserve mana,-cast time,+dps debuff,+boost", BOT_STATE_COMBAT);
botAI->TellMaster("Max DPS!");
return true;
}

View File

@@ -0,0 +1,77 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
*/
#ifndef _PLAYERBOT_CHATSHORTCUTACTION_H
#define _PLAYERBOT_CHATSHORTCUTACTION_H
#include "MovementActions.h"
class PlayerbotAI;
class ReturnPositionResetAction : public Action
{
public:
ReturnPositionResetAction(PlayerbotAI* botAI, std::string const name) : Action(botAI, name) { }
void ResetReturnPosition();
void SetReturnPosition(float x, float y, float z);
};
class FollowChatShortcutAction : public MovementAction
{
public:
FollowChatShortcutAction(PlayerbotAI* botAI) : MovementAction(botAI, "follow chat shortcut") { }
bool Execute(Event event) override;
};
class StayChatShortcutAction : public ReturnPositionResetAction
{
public:
StayChatShortcutAction(PlayerbotAI* botAI) : ReturnPositionResetAction(botAI, "stay chat shortcut") { }
bool Execute(Event event) override;
};
class FleeChatShortcutAction : public ReturnPositionResetAction
{
public:
FleeChatShortcutAction(PlayerbotAI* botAI) : ReturnPositionResetAction(botAI, "flee chat shortcut") { }
bool Execute(Event event) override;
};
class GoawayChatShortcutAction : public ReturnPositionResetAction
{
public:
GoawayChatShortcutAction(PlayerbotAI* botAI) : ReturnPositionResetAction(botAI, "runaway chat shortcut") { }
bool Execute(Event event) override;
};
class GrindChatShortcutAction : public ReturnPositionResetAction
{
public:
GrindChatShortcutAction(PlayerbotAI* botAI) : ReturnPositionResetAction(botAI, "grind chat shortcut") { }
bool Execute(Event event) override;
};
class TankAttackChatShortcutAction : public ReturnPositionResetAction
{
public:
TankAttackChatShortcutAction(PlayerbotAI* botAI) : ReturnPositionResetAction(botAI, "tank attack chat shortcut") { }
bool Execute(Event event) override;
};
class MaxDpsChatShortcutAction : public Action
{
public:
MaxDpsChatShortcutAction(PlayerbotAI* botAI) : Action(botAI, "max dps chat shortcut") { }
bool Execute(Event event) override;
};
#endif

View File

@@ -0,0 +1,89 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
*/
#include "CheatAction.h"
#include "Playerbots.h"
bool CheatAction::Execute(Event event)
{
std::string const param = event.getParam();
uint32 cheatMask = (uint32)botAI->GetCheat();
std::vector<std::string> splitted = split(param, ',');
for (std::vector<std::string>::iterator i = splitted.begin(); i != splitted.end(); i++)
{
const char* name = i->c_str();
switch (name[0])
{
case '+':
cheatMask |= (uint32)GetCheatMask(name + 1);
break;
case '-':
cheatMask ^= (uint32)GetCheatMask(name + 1);
break;
case '?':
ListCheats();
return true;
}
}
botAI->SetCheat(BotCheatMask(cheatMask));
return true;
}
BotCheatMask CheatAction::GetCheatMask(std::string const cheat)
{
if (cheat=="taxi")
return BotCheatMask::taxi;
if (cheat == "gold")
return BotCheatMask::gold;
if (cheat == "health")
return BotCheatMask::health;
if (cheat == "mana")
return BotCheatMask::mana;
if (cheat == "power")
return BotCheatMask::power;
return BotCheatMask::none;
}
std::string const CheatAction::GetCheatName(BotCheatMask cheatMask)
{
switch (cheatMask)
{
case BotCheatMask::taxi:
return "taxi";
case BotCheatMask::gold:
return "gold";
case BotCheatMask::health:
return "health";
case BotCheatMask::mana:
return "mana";
case BotCheatMask::power:
return "power";
default:
return "none";
}
}
void CheatAction::ListCheats()
{
std::ostringstream out;
for (int i = 0; i < log2((uint32)BotCheatMask::maxMask); i++)
{
BotCheatMask cheatMask = BotCheatMask(1 << i);
if ((uint32)cheatMask & (uint32)sPlayerbotAIConfig->botCheatMask)
out << "[conf:" << GetCheatName(BotCheatMask(cheatMask)) << "]";
else if (botAI->HasCheat(cheatMask))
out << "[" << GetCheatName(BotCheatMask(cheatMask)) << "]";
}
botAI->TellMasterNoFacing(out);
}

View File

@@ -0,0 +1,22 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
*/
#include "Action.h"
class PlayerbotAI;
enum class BotCheatMask : uint32;
class CheatAction : public Action
{
public:
CheatAction(PlayerbotAI* botAI) : Action(botAI, "cheat") { }
bool Execute(Event event) override;
private:
static BotCheatMask GetCheatMask(std::string const cheat);
static std::string const GetCheatName(BotCheatMask cheatMask);
void ListCheats();
};

View File

@@ -0,0 +1,101 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
*/
#include "CheckMailAction.h"
#include "Event.h"
#include "GuildTaskMgr.h"
#include "Playerbots.h"
bool CheckMailAction::Execute(Event event)
{
WorldPacket p;
bot->GetSession()->HandleQueryNextMailTime(p);
CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction();
std::vector<uint32> ids;
for (PlayerMails::const_iterator i = bot->GetMails().begin(); i != bot->GetMails().end(); ++i)
{
Mail* mail = *i;
if (!mail || mail->state == MAIL_STATE_DELETED)
continue;
Player* owner = ObjectAccessor::FindConnectedPlayer(ObjectGuid::Create<HighGuid::Player>(mail->sender));
if (!owner)
continue;
uint32 account = owner->GetSession()->GetAccountId();
if (sPlayerbotAIConfig->IsInRandomAccountList(account))
continue;
ProcessMail(mail, owner, trans);
ids.push_back(mail->messageID);
mail->state = MAIL_STATE_DELETED;
}
for (uint32 id : ids)
{
bot->SendMailResult(id, MAIL_DELETED, MAIL_OK);
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_MAIL_BY_ID);
stmt->SetData(0, id);
trans->Append(stmt);
stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_MAIL_ITEM_BY_ID);
stmt->SetData(0, id);
trans->Append(stmt);
bot->RemoveMail(id);
}
CharacterDatabase.CommitTransaction(trans);
return true;
}
bool CheckMailAction::isUseful()
{
if (botAI->GetMaster() || !bot->GetMailSize() || bot->InBattleground())
return false;
return true;
}
void CheckMailAction::ProcessMail(Mail* mail, Player* owner, CharacterDatabaseTransaction trans)
{
if (mail->items.empty())
{
return;
}
if (mail->subject.find("Item(s) you asked for") != std::string::npos)
return;
for (MailItemInfoVec::iterator i = mail->items.begin(); i != mail->items.end(); ++i)
{
Item *item = bot->GetMItem(i->item_guid);
if (!item)
continue;
if (!sGuildTaskMgr->CheckItemTask(i->item_template, item->GetCount(), owner, bot, true))
{
std::ostringstream body;
body << "Hello, " << owner->GetName() << ",\n";
body << "\n";
body << "Here are the item(s) you've sent me by mistake";
body << "\n";
body << "Thanks,\n";
body << bot->GetName() << "\n";
MailDraft draft("Item(s) you've sent me", body.str());
draft.AddItem(item);
bot->RemoveMItem(i->item_guid);
draft.SendMailTo(trans, MailReceiver(owner), MailSender(bot));
return;
}
bot->RemoveMItem(i->item_guid);
item->DestroyForPlayer(bot);
}
}

View File

@@ -0,0 +1,27 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
*/
#ifndef _PLAYERBOT_CHECKMAILACTION_H
#define _PLAYERBOT_CHECKMAILACTION_H
#include "Action.h"
#include "DatabaseEnvFwd.h"
class PlayerbotAI;
struct Mail;
class CheckMailAction : public Action
{
public:
CheckMailAction(PlayerbotAI* botAI) : Action(botAI, "check mail") { }
bool Execute(Event event) override;
bool isUseful() override;
private:
void ProcessMail(Mail* mail, Player* owner, CharacterDatabaseTransaction trans);
};
#endif

View File

@@ -0,0 +1,274 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
*/
#include "CheckMountStateAction.h"
#include "BattlegroundWS.h"
#include "Event.h"
#include "Playerbots.h"
#include "ServerFacade.h"
#include "SpellAuraEffects.h"
bool CheckMountStateAction::Execute(Event event)
{
bool noattackers = AI_VALUE2(bool, "combat", "self target") ? (AI_VALUE(uint8, "attacker count") > 0 ? false : true) : true;
bool enemy = AI_VALUE(Unit*, "enemy player target");
bool dps = (AI_VALUE(Unit*, "dps target") || AI_VALUE(Unit*, "grind target"));
bool fartarget = (enemy && sServerFacade->IsDistanceGreaterThan(AI_VALUE2(float, "distance", "enemy player target"), 40.0f)) ||
(dps && sServerFacade->IsDistanceGreaterThan(AI_VALUE2(float, "distance", "dps target"), 50.0f));
bool attackdistance = false;
bool chasedistance = false;
float attack_distance = 35.0f;
switch (bot->getClass())
{
case CLASS_WARRIOR:
case CLASS_PALADIN:
attack_distance = 10.0f;
break;
case CLASS_ROGUE:
attack_distance = 40.0f;
break;
}
if (enemy)
attack_distance /= 2;
if (dps || enemy)
{
attackdistance = (enemy || dps) && sServerFacade->IsDistanceLessThan(AI_VALUE2(float, "distance", "current target"), attack_distance);
chasedistance = enemy && sServerFacade->IsDistanceGreaterThan(AI_VALUE2(float, "distance", "enemy player target"), 45.0f) && AI_VALUE2(bool, "moving", "enemy player target");
}
Player* master = GetMaster();
if (master != nullptr && !bot->InBattleground())
{
if (!bot->GetGroup() || bot->GetGroup()->GetLeaderGUID() != master->GetGUID())
return false;
bool farFromMaster = sServerFacade->GetDistance2d(bot, master) > sPlayerbotAIConfig->sightDistance;
if (master->IsMounted() && !bot->IsMounted() && noattackers)
{
return Mount();
}
if (!bot->IsMounted() && (chasedistance || (farFromMaster && botAI->HasStrategy("follow", BOT_STATE_NON_COMBAT))) && !bot->IsInCombat() && !dps)
return Mount();
if (!bot->IsFlying() && ((!farFromMaster && !master->IsMounted()) || attackdistance) && bot->IsMounted())
{
WorldPacket emptyPacket;
bot->GetSession()->HandleCancelMountAuraOpcode(emptyPacket);
return true;
}
return false;
}
if (bot->InBattleground() && !attackdistance && (noattackers || fartarget) && !bot->IsInCombat() && !bot->IsMounted())
{
if (bot->GetBattlegroundTypeId() == BATTLEGROUND_WS)
{
BattlegroundWS *bg = (BattlegroundWS*)botAI->GetBot()->GetBattleground();
if (bot->HasAura(23333) || bot->HasAura(23335))
{
return false;
}
}
return Mount();
}
if (!bot->InBattleground())
{
if (AI_VALUE(GuidPosition, "rpg target"))
{
if (sServerFacade->IsDistanceGreaterThan(AI_VALUE2(float, "distance", "rpg target"), sPlayerbotAIConfig->farDistance) && noattackers && !dps && !enemy)
return Mount();
}
if (((!AI_VALUE(GuidVector, "possible rpg targets").empty()) && noattackers && !dps && !enemy) && urand(0, 100) > 50)
return Mount();
}
if (!bot->IsMounted() && !attackdistance && (fartarget || chasedistance))
return Mount();
if (!bot->IsFlying() && attackdistance && bot->IsMounted() && (enemy || dps || (!noattackers && bot->IsInCombat())))
{
WorldPacket emptyPacket;
bot->GetSession()->HandleCancelMountAuraOpcode(emptyPacket);
return true;
}
return false;
}
bool CheckMountStateAction::isUseful()
{
// do not use on vehicle
if (botAI->IsInVehicle())
return false;
if (bot->isDead())
return false;
bool isOutdoor = bot->IsOutdoors();
if (!isOutdoor)
return false;
if (bot->HasUnitState(UNIT_STATE_IN_FLIGHT))
return false;
if (bot->InArena())
return false;
if (!GET_PLAYERBOT_AI(bot)->HasStrategy("mount", BOT_STATE_NON_COMBAT) && !bot->IsMounted())
return false;
bool firstmount = bot->getLevel() >= 20;
if (!firstmount)
return false;
// Do not use with BG Flags
if (bot->HasAura(23333) || bot->HasAura(23335) || bot->HasAura(34976))
{
return false;
}
// Only mount if BG starts in less than 30 sec
if (bot->InBattleground())
{
if (Battleground* bg = bot->GetBattleground())
if (bg->GetStatus() == STATUS_WAIT_JOIN)
{
if (bg->GetStartDelayTime() > BG_START_DELAY_30S)
return false;
}
}
return true;
}
bool CheckMountStateAction::Mount()
{
uint32 secondmount = 40;
if (bot->isMoving())
{
bot->StopMoving();
bot->GetMotionMaster()->Clear();
bot->GetMotionMaster()->MoveIdle();
}
Player* master = GetMaster();
botAI->RemoveShapeshift();
int32 masterSpeed = 59;
SpellInfo const* masterSpell = nullptr;
if (master && !master->GetAuraEffectsByType(SPELL_AURA_MOUNTED).empty() && !bot->InBattleground())
{
masterSpell = master->GetAuraEffectsByType(SPELL_AURA_MOUNTED).front()->GetSpellInfo();
masterSpeed = std::max(masterSpell->Effects[1].BasePoints, masterSpell->Effects[2].BasePoints);
}
else
{
masterSpeed = 59;
for (PlayerSpellMap::iterator itr = bot->GetSpellMap().begin(); itr != bot->GetSpellMap().end(); ++itr)
{
uint32 spellId = itr->first;
SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellId);
if (!spellInfo || spellInfo->Effects[0].ApplyAuraName != SPELL_AURA_MOUNTED)
continue;
if (itr->second->State == PLAYERSPELL_REMOVED || !itr->second->Active || spellInfo->IsPassive())
continue;
int32 effect = std::max(spellInfo->Effects[1].BasePoints, spellInfo->Effects[2].BasePoints);
if (effect > masterSpeed)
masterSpeed = effect;
}
}
if (bot->GetPureSkillValue(SKILL_RIDING) <= 75 && bot->getLevel() < secondmount)
masterSpeed = 59;
if (bot->InBattleground() && masterSpeed > 99)
masterSpeed = 99;
bool hasSwiftMount = false;
//std::map<int32, std::vector<uint32> > spells;
std::map<uint32, std::map<int32, std::vector<uint32>>> allSpells;
for (PlayerSpellMap::iterator itr = bot->GetSpellMap().begin(); itr != bot->GetSpellMap().end(); ++itr)
{
uint32 spellId = itr->first;
SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellId);
if (!spellInfo || spellInfo->Effects[0].ApplyAuraName != SPELL_AURA_MOUNTED)
continue;
if (itr->second->State == PLAYERSPELL_REMOVED || !itr->second->Active || spellInfo->IsPassive())
continue;
int32 effect = std::max(spellInfo->Effects[1].BasePoints, spellInfo->Effects[2].BasePoints);
//if (effect < masterSpeed)
//continue;
uint32 index = (spellInfo->Effects[1].ApplyAuraName == SPELL_AURA_MOD_INCREASE_MOUNTED_FLIGHT_SPEED ||
spellInfo->Effects[2].ApplyAuraName == SPELL_AURA_MOD_INCREASE_MOUNTED_FLIGHT_SPEED) ? 1 : 0;
if (index == 0 && std::max(spellInfo->Effects[EFFECT_1].BasePoints, spellInfo->Effects[EFFECT_2].BasePoints) > 59)
hasSwiftMount = true;
if (index == 1 && std::max(spellInfo->Effects[EFFECT_1].BasePoints, spellInfo->Effects[EFFECT_2].BasePoints) > 149)
hasSwiftMount = true;
allSpells[index][effect].push_back(spellId);
}
int32 masterMountType = 0;
if (masterSpell)
{
masterMountType = (masterSpell->Effects[1].ApplyAuraName == SPELL_AURA_MOD_INCREASE_MOUNTED_FLIGHT_SPEED ||
masterSpell->Effects[2].ApplyAuraName == SPELL_AURA_MOD_INCREASE_MOUNTED_FLIGHT_SPEED) ? 1 : 0;
}
std::map<int32, std::vector<uint32>>& spells = allSpells[masterMountType];
if (hasSwiftMount)
{
for (auto i : spells)
{
std::vector<uint32> ids = i.second;
for (auto itr : ids)
{
SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(itr);
if (!spellInfo)
continue;
if (masterMountType == 0 && masterSpeed > 59 && std::max(spellInfo->Effects[EFFECT_1].BasePoints, spellInfo->Effects[EFFECT_2].BasePoints) < 99)
spells[59].clear();
if (masterMountType == 1 && masterSpeed > 149 && std::max(spellInfo->Effects[EFFECT_1].BasePoints, spellInfo->Effects[EFFECT_2].BasePoints) < 279)
spells[149].clear();
}
}
}
for (std::map<int32, std::vector<uint32>>::iterator i = spells.begin(); i != spells.end(); ++i)
{
std::vector<uint32>& ids = i->second;
uint32 index = urand(0, ids.size() - 1);
if (index >= ids.size())
continue;
botAI->CastSpell(ids[index], bot);
return true;
}
std::vector<Item*> items = AI_VALUE2(std::vector<Item*>, "inventory items", "mount");
if (!items.empty())
return UseItemAuto(*items.begin());
return false;
}

View File

@@ -0,0 +1,23 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
*/
#ifndef _PLAYERBOT_CHECKMOUNTSTATEACTION_H
#define _PLAYERBOT_CHECKMOUNTSTATEACTION_H
#include "UseItemAction.h"
class PlayerbotAI;
class CheckMountStateAction : public UseItemAction
{
public:
CheckMountStateAction(PlayerbotAI* botAI) : UseItemAction(botAI, "check mount state", true) { }
bool Execute(Event event) override;
bool isUseful() override;
bool isPossible() override { return true; }
bool Mount();
};
#endif

View File

@@ -0,0 +1,34 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
*/
#include "CheckValuesAction.h"
#include "Event.h"
#include "Playerbots.h"
#include "ServerFacade.h"
CheckValuesAction::CheckValuesAction(PlayerbotAI* botAI) : Action(botAI, "check values")
{
}
bool CheckValuesAction::Execute(Event event)
{
if (botAI->HasStrategy("debug move", BOT_STATE_NON_COMBAT))
{
botAI->Ping(bot->GetPositionX(), bot->GetPositionY());
}
if (botAI->HasStrategy("map", BOT_STATE_NON_COMBAT) || botAI->HasStrategy("map full", BOT_STATE_NON_COMBAT))
{
sTravelNodeMap->manageNodes(bot, botAI->HasStrategy("map full", BOT_STATE_NON_COMBAT));
}
GuidVector possible_targets = *context->GetValue<GuidVector>("possible targets");
GuidVector all_targets = *context->GetValue<GuidVector>("all targets");
GuidVector npcs = *context->GetValue<GuidVector>("nearest npcs");
GuidVector corpses = *context->GetValue<GuidVector>("nearest corpses");
GuidVector gos = *context->GetValue<GuidVector>("nearest game objects");
GuidVector nfp = *context->GetValue<GuidVector>("nearest friendly players");
return true;
}

View File

@@ -0,0 +1,20 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
*/
#ifndef _PLAYERBOT_CHECKVALUESACTION_H
#define _PLAYERBOT_CHECKVALUESACTION_H
#include "Action.h"
class PlayerbotAI;
class CheckValuesAction : public Action
{
public:
CheckValuesAction(PlayerbotAI* botAI);
bool Execute(Event event) override;
};
#endif

View File

@@ -0,0 +1,316 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
*/
#include "ChooseRpgTargetAction.h"
#include "BattlegroundMgr.h"
#include "BudgetValues.h"
#include "ChatHelper.h"
#include "Event.h"
#include "Formations.h"
#include "GuildCreateActions.h"
#include "PossibleRpgTargetsValue.h"
#include "Playerbots.h"
#include <random>
bool ChooseRpgTargetAction::HasSameTarget(ObjectGuid guid, uint32 max, GuidVector const& nearGuids)
{
if (botAI->HasRealPlayerMaster())
return false;
uint32 num = 0;
for (auto& i : nearGuids)
{
Player* player = ObjectAccessor::FindPlayer(i);
if (!player)
continue;
PlayerbotAI* botAI = GET_PLAYERBOT_AI(player);
if (!botAI)
continue;
if (!botAI->AllowActivity(GRIND_ACTIVITY))
continue;
if (PAI_VALUE(GuidPosition, "rpg target") != guid)
continue;
num++;
if (num >= max)
break;
}
return num > 0;
}
float ChooseRpgTargetAction::getMaxRelevance(GuidPosition guidP)
{
GuidPosition currentRpgTarget = AI_VALUE(GuidPosition, "rpg target");
SET_AI_VALUE(GuidPosition, "rpg target", guidP);
Strategy* rpgStrategy = botAI->GetAiObjectContext()->GetStrategy("rpg");
std::vector<TriggerNode*> triggerNodes;
rpgStrategy->InitTriggers(triggerNodes);
float maxRelevance = 0.0f;
for (auto& triggerNode : triggerNodes)
{
Trigger* trigger = context->GetTrigger(triggerNode->getName());
if (trigger)
{
triggerNode->setTrigger(trigger);
if (triggerNode->getFirstRelevance() < maxRelevance || triggerNode->getFirstRelevance() > 2.0f)
continue;
trigger = triggerNode->getTrigger();
if (!trigger->IsActive())
continue;
maxRelevance = triggerNode->getFirstRelevance();
}
}
SET_AI_VALUE(GuidPosition, "rpg target", currentRpgTarget);
for (std::vector<TriggerNode*>::iterator i = triggerNodes.begin(); i != triggerNodes.end(); i++)
{
TriggerNode* trigger = *i;
delete trigger;
}
triggerNodes.clear();
return (maxRelevance - 1.0) * 1000.0f;
}
bool ChooseRpgTargetAction::Execute(Event event)
{
TravelTarget* travelTarget = AI_VALUE(TravelTarget*, "travel target");
uint32 num = 0;
std::unordered_map<ObjectGuid, uint32> targets;
GuidVector possibleTargets = AI_VALUE(GuidVector, "possible rpg targets");
GuidVector possibleObjects = AI_VALUE(GuidVector, "nearest game objects no los");
GuidVector possiblePlayers = AI_VALUE(GuidVector, "nearest friendly players");
GuidSet& ignoreList = AI_VALUE(GuidSet&, "ignore rpg target");
for (auto target : possibleTargets)
targets[target] = 0.0f;
for (auto target : possibleObjects)
targets[target] = 0.0f;
for (auto target : possiblePlayers)
targets[target] = 0.0f;
if (targets.empty())
{
return false;
}
if (urand(0, 9))
{
for (auto target : ignoreList)
targets.erase(target);
}
SET_AI_VALUE(std::string, "next rpg action", this->getName());
bool hasGoodRelevance = false;
for (auto& target : targets)
{
Unit* unit = ObjectAccessor::GetUnit(*bot, target.first);
if (!unit)
continue;
GuidPosition guidP(unit);
if (!guidP)
continue;
float priority = 1;
if (guidP.GetWorldObject() && !isFollowValid(bot, guidP.GetWorldObject()))
continue;
if (guidP.IsGameObject())
{
GameObject* go = guidP.GetGameObject();
if (!go || !go->isSpawned() || go->GetGoState() != GO_STATE_READY)
continue;
}
else if (guidP.IsPlayer())
{
Player* player = guidP.GetPlayer();
if (!player)
continue;
if (GET_PLAYERBOT_AI(player))
{
GuidPosition guidPP = PAI_VALUE(GuidPosition, "rpg target");
if (guidPP.IsPlayer())
{
continue;
}
}
}
if (possiblePlayers.size() > 200 || HasSameTarget(guidP, urand(5, 15), possiblePlayers))
continue;
float relevance = getMaxRelevance(guidP);
if (!hasGoodRelevance || relevance > 1)
target.second = relevance;
if (target.second > 1)
hasGoodRelevance = true;
}
SET_AI_VALUE(std::string, "next rpg action", "");
for (auto it = begin(targets); it != end(targets);)
{
if (it->second == 0 || (hasGoodRelevance && it->second <= 1.0))
{
it = targets.erase(it);
}
else
++it;
}
if (targets.empty())
{
LOG_INFO("playerbots", "{} can't choose RPG target: all {} are not available", bot->GetName().c_str(), possibleTargets.size());
RESET_AI_VALUE(GuidSet&, "ignore rpg target");
RESET_AI_VALUE(GuidPosition, "rpg target");
return false;
}
std::vector<GuidPosition> guidps;
std::vector<int32> relevances;
for (auto& target : targets)
{
Unit* unit = ObjectAccessor::GetUnit(*bot, target.first);
if (!unit)
continue;
GuidPosition guidP(unit);
if (!guidP)
continue;
guidps.push_back(guidP);
relevances.push_back(target.second);
}
std::mt19937 gen(time(0));
sTravelMgr->weighted_shuffle(guidps.begin(), guidps.end(), relevances.begin(), relevances.end(), gen);
GuidPosition guidP(guidps.front());
if (!guidP)
{
RESET_AI_VALUE(GuidPosition, "rpg target");
return false;
}
if (botAI->HasStrategy("debug", BOT_STATE_NON_COMBAT) && guidP.GetWorldObject())
{
std::ostringstream out;
out << "found: ";
out << chat->FormatWorldobject(guidP.GetWorldObject());
out << " " << relevances.front();
botAI->TellMasterNoFacing(out);
}
SET_AI_VALUE(GuidPosition, "rpg target", guidP);
return true;
}
bool ChooseRpgTargetAction::isUseful()
{
if (!botAI->AllowActivity(RPG_ACTIVITY))
return false;
if (AI_VALUE(GuidPosition, "rpg target"))
return false;
TravelTarget* travelTarget = AI_VALUE(TravelTarget*, "travel target");
if (travelTarget->isTraveling() && isFollowValid(bot, *travelTarget->getPosition()))
return false;
if (AI_VALUE(GuidVector, "possible rpg targets").empty())
return false;
if (!AI_VALUE(bool, "can move around"))
return false;
return true;
}
bool ChooseRpgTargetAction::isFollowValid(Player* bot, WorldObject* target)
{
if (!target)
return false;
return isFollowValid(bot, WorldPosition(target));
}
bool ChooseRpgTargetAction::isFollowValid(Player* bot, WorldPosition pos)
{
PlayerbotAI* botAI = GET_PLAYERBOT_AI(bot);
Player* master = botAI->GetGroupMaster();
Player* realMaster = botAI->GetMaster();
AiObjectContext* context = botAI->GetAiObjectContext();
bool inDungeon = false;
if (botAI->HasActivePlayerMaster())
{
if (realMaster->IsInWorld() && realMaster->GetMap()->IsDungeon() && bot->GetMapId() == realMaster->GetMapId())
inDungeon = true;
if (realMaster && realMaster->IsInWorld() && realMaster->GetMap()->IsDungeon() && (realMaster->GetMapId() != pos.getMapId()))
return false;
}
if (!master || bot == master)
return true;
if (!botAI->HasStrategy("follow", BOT_STATE_NON_COMBAT))
return true;
if (sqrt(bot->GetDistance(master)) > sPlayerbotAIConfig->rpgDistance * 2)
return false;
Formation* formation = AI_VALUE(Formation*, "formation");
float distance = master->GetDistance2d(pos.getX(), pos.getY());
if (!botAI->HasActivePlayerMaster() && distance < 50.0f)
{
Player* player = master;
if (!master->isMoving() || PAI_VALUE(WorldPosition, "last long move").distance(pos) < sPlayerbotAIConfig->reactDistance)
return true;
}
if (inDungeon && realMaster == master && distance > 5.0f)
return false;
if (!master->isMoving() && distance < 25.0f)
return true;
if (distance < formation->GetMaxDistance())
return true;
return false;
}

View File

@@ -0,0 +1,42 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
*/
#ifndef _PLAYERBOT_CHOOSERPGTARGETACTION_H
#define _PLAYERBOT_CHOOSERPGTARGETACTION_H
#include "ObjectGuid.h"
#include "RpgAction.h"
class GuidPosition;
class Player;
class PlayerbotAI;
class WorldObject;
class WorldPosition;
class ChooseRpgTargetAction : public Action
{
public:
ChooseRpgTargetAction(PlayerbotAI* botAI, std::string const name = "choose rpg target") : Action(botAI, name) { }
bool Execute(Event event) override;
bool isUseful() override;
static bool isFollowValid(Player* bot, WorldObject* target);
static bool isFollowValid(Player* bot, WorldPosition pos);
private:
float getMaxRelevance(GuidPosition guidP);
bool HasSameTarget(ObjectGuid guid, uint32 max, GuidVector const& nearGuids);
};
class ClearRpgTargetAction : public ChooseRpgTargetAction
{
public:
ClearRpgTargetAction(PlayerbotAI* botAI) : ChooseRpgTargetAction(botAI, "clear rpg target") { }
bool Execute(Event event) override;
bool isUseful() override;
};
#endif

View File

@@ -0,0 +1,141 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
*/
#include "ChooseTargetActions.h"
#include "ChooseRpgTargetAction.h"
#include "Event.h"
#include "LootObjectStack.h"
#include "PossibleRpgTargetsValue.h"
#include "Playerbots.h"
#include "ServerFacade.h"
bool AttackEnemyPlayerAction::isUseful()
{
// if carry flag, do not start fight
if (bot->HasAura(23333) || bot->HasAura(23335) || bot->HasAura(34976))
return false;
return !sPlayerbotAIConfig->IsInPvpProhibitedZone(bot->GetAreaId());
}
bool AttackEnemyFlagCarrierAction::isUseful()
{
Unit* target = context->GetValue<Unit*>("enemy flag carrier")->Get();
return target && sServerFacade->IsDistanceLessOrEqualThan(sServerFacade->GetDistance2d(bot, target), 75.0f) && (bot->HasAura(23333) || bot->HasAura(23335) || bot->HasAura(34976));
}
bool AttackAnythingAction::isUseful()
{
if (!botAI->AllowActivity(GRIND_ACTIVITY)) //Bot not allowed to be active
return false;
if (!AI_VALUE(bool, "can move around"))
return false;
if (context->GetValue<TravelTarget*>("travel target")->Get()->isTraveling() && ChooseRpgTargetAction::isFollowValid(bot, *context->GetValue<TravelTarget*>("travel target")->Get()->getPosition())) //Bot is traveling
return false;
Unit* target = GetTarget();
if (!target)
return false;
std::string const name = std::string(target->GetName());
if (!name.empty() && name.find("Dummy") != std::string::npos) // Target is not a targetdummy
return false;
if (!ChooseRpgTargetAction::isFollowValid(bot, target)) //Do not grind mobs far away from master.
return false;
return true;
}
bool DropTargetAction::Execute(Event event)
{
Unit* target = context->GetValue<Unit*>("current target")->Get();
if (target && target->isDead())
{
ObjectGuid guid = target->GetGUID();
if (guid)
context->GetValue<LootObjectStack*>("available loot")->Get()->Add(guid);
}
ObjectGuid pullTarget = context->GetValue<ObjectGuid>("pull target")->Get();
GuidVector possible = botAI->GetAiObjectContext()->GetValue<GuidVector>("possible targets no los")->Get();
if (pullTarget && find(possible.begin(), possible.end(), pullTarget) == possible.end())
{
context->GetValue<ObjectGuid>("pull target")->Set(ObjectGuid::Empty);
}
context->GetValue<Unit*>("current target")->Set(nullptr);
bot->SetTarget(ObjectGuid::Empty);
botAI->ChangeEngine(BOT_STATE_NON_COMBAT);
botAI->InterruptSpell();
bot->AttackStop();
if (Pet* pet = bot->GetPet())
{
if (CreatureAI* creatureAI = ((Creature*)pet)->AI())
{
pet->SetReactState(REACT_PASSIVE);
pet->GetCharmInfo()->SetCommandState(COMMAND_FOLLOW);
pet->AttackStop();
}
}
if (!urand(0, 50) && botAI->HasStrategy("emote", BOT_STATE_NON_COMBAT))
{
std::vector<uint32> sounds;
if (target && target->isDead())
{
sounds.push_back(TEXT_EMOTE_CHEER);
sounds.push_back(TEXT_EMOTE_CONGRATULATE);
}
else
{
sounds.push_back(304); // guard
sounds.push_back(325); // stay
}
if (!sounds.empty())
botAI->PlayEmote(sounds[urand(0, sounds.size() - 1)]);
}
return true;
}
bool AttackAnythingAction::Execute(Event event)
{
bool result = AttackAction::Execute(event);
if (result)
{
if (Unit* grindTarget = GetTarget())
{
if (char const* grindName = grindTarget->GetName().c_str())
{
context->GetValue<ObjectGuid>("pull target")->Set(grindTarget->GetGUID());
bot->GetMotionMaster()->Clear();
bot->StopMoving();
}
}
}
return result;
}
bool AttackAnythingAction::isPossible()
{
return AttackAction::isPossible() && GetTarget();
}
bool DpsAssistAction::isUseful()
{
// if carry flag, do not start fight
if (bot->HasAura(23333) || bot->HasAura(23335) || bot->HasAura(34976))
return false;
return true;
}

View File

@@ -0,0 +1,90 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
*/
#ifndef _PLAYERBOT_CHOOSETARGETACTIONS_H
#define _PLAYERBOT_CHOOSETARGETACTIONS_H
#include "AttackAction.h"
class PlayerbotAI;
class DpsAoeAction : public AttackAction
{
public:
DpsAoeAction(PlayerbotAI* botAI) : AttackAction(botAI, "dps aoe") { }
std::string const GetTargetName() override { return "dps aoe target"; }
};
class DpsAssistAction : public AttackAction
{
public:
DpsAssistAction(PlayerbotAI* botAI) : AttackAction(botAI, "dps assist") { }
std::string const GetTargetName() override { return "dps target"; }
bool isUseful() override;
};
class TankAssistAction : public AttackAction
{
public:
TankAssistAction(PlayerbotAI* botAI) : AttackAction(botAI, "tank assist") { }
std::string const GetTargetName() override { return "tank target"; }
};
class AttackAnythingAction : public AttackAction
{
public:
AttackAnythingAction(PlayerbotAI* botAI) : AttackAction(botAI, "attack anything") { }
std::string const GetTargetName() override { return "grind target"; }
bool Execute(Event event) override;
bool isUseful() override;
bool isPossible() override;
};
class AttackLeastHpTargetAction : public AttackAction
{
public:
AttackLeastHpTargetAction(PlayerbotAI* botAI) : AttackAction(botAI, "attack least hp target") { }
std::string const GetTargetName() override { return "least hp target"; }
};
class AttackEnemyPlayerAction : public AttackAction
{
public:
AttackEnemyPlayerAction(PlayerbotAI* botAI) : AttackAction(botAI, "attack enemy player") { }
std::string const GetTargetName() override { return "enemy player target"; }
bool isUseful() override;
};
class AttackRtiTargetAction : public AttackAction
{
public:
AttackRtiTargetAction(PlayerbotAI* botAI) : AttackAction(botAI, "attack rti target") { }
std::string const GetTargetName() override { return "rti target"; }
};
class AttackEnemyFlagCarrierAction : public AttackAction
{
public:
AttackEnemyFlagCarrierAction(PlayerbotAI* botAI) : AttackAction(botAI, "attack enemy flag carrier") { }
std::string const GetTargetName() override { return "enemy flag carrier"; }
bool isUseful() override;
};
class DropTargetAction : public Action
{
public:
DropTargetAction(PlayerbotAI* botAI) : Action(botAI, "drop target") { }
bool Execute(Event event) override;
};
#endif

View File

@@ -0,0 +1,857 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
*/
#include "ChooseTravelTargetAction.h"
#include "ChatHelper.h"
#include "LootObjectStack.h"
#include "Playerbots.h"
bool ChooseTravelTargetAction::Execute(Event event)
{
//Get the current travel target. This target is no longer active.
TravelTarget* oldTarget = context->GetValue<TravelTarget*>("travel target")->Get();
//Select a new target to travel to.
TravelTarget newTarget = TravelTarget(botAI);
getNewTarget(&newTarget, oldTarget);
//If the new target is not active we failed.
if (!newTarget.isActive())
return false;
setNewTarget(&newTarget, oldTarget);
return true;
}
//Select a new travel target.
//Currently this selectes mostly based on priority (current quest > new quest).
//This works fine because destinations can be full (max 15 bots per quest giver, max 1 bot per quest mob).
//
//Eventually we want to rewrite this to be more intelligent.
void ChooseTravelTargetAction::getNewTarget(TravelTarget* newTarget, TravelTarget* oldTarget)
{
bool foundTarget = false;
foundTarget = SetGroupTarget(newTarget); //Join groups members
//Enpty bags/repair
if (!foundTarget && urand(1, 100) > 10) //90% chance
if (AI_VALUE2(bool, "group or", "should sell,can sell,following party,near leader") || AI_VALUE2(bool, "group or", "should repair,can repair,following party,near leader"))
foundTarget = SetRpgTarget(newTarget); //Go to town to sell items or repair
//Rpg in city
if (!foundTarget && urand(1, 100) > 90) //10% chance
foundTarget = SetNpcFlagTarget(newTarget, { UNIT_NPC_FLAG_BANKER,UNIT_NPC_FLAG_BATTLEMASTER,UNIT_NPC_FLAG_AUCTIONEER });
//Grind for money
if (!foundTarget && AI_VALUE(bool, "should get money"))
if (urand(1, 100) > 66)
{
foundTarget = SetQuestTarget(newTarget, true); //Turn in quests for money.
if (!foundTarget)
foundTarget = SetQuestTarget(newTarget); //Do low level quests
}
else if (urand(1, 100) > 50)
foundTarget = SetGrindTarget(newTarget); //Go grind mobs for money
else
foundTarget = SetNewQuestTarget(newTarget); //Find a low level quest to do
//Continue
if (!foundTarget && urand(1, 100) > 10) //90% chance
foundTarget = SetCurrentTarget(newTarget, oldTarget); //Extend current target.
//Dungeon in group
if (!foundTarget && urand(1, 100) > 50) //50% chance
if (AI_VALUE(bool, "can fight boss"))
foundTarget = SetBossTarget(newTarget); //Go fight a (dungeon boss)
if (!foundTarget && urand(1, 100) > 5) //95% chance
foundTarget = SetQuestTarget(newTarget); //Do a target of an active quest.
if (!foundTarget && urand(1, 100) > 5)
foundTarget = SetNewQuestTarget(newTarget); //Find a new quest to do.
if (!foundTarget && botAI->HasStrategy("explore", BOT_STATE_NON_COMBAT)) //Explore a unexplored sub-zone.
foundTarget = SetExploreTarget(newTarget);
// if (!foundTarget)
//foundTarget = SetRpgTarget(target);
if (!foundTarget)
SetNullTarget(newTarget); //Idle a bit.
}
void ChooseTravelTargetAction::setNewTarget(TravelTarget* newTarget, TravelTarget* oldTarget)
{
//Tell the master where we are going.
if (!bot->GetGroup() || (botAI->GetGroupMaster() == bot))
ReportTravelTarget(newTarget, oldTarget);
//If we are heading to a creature/npc clear it from the ignore list.
if (oldTarget && oldTarget == newTarget && newTarget->getEntry())
{
GuidSet& ignoreList = context->GetValue<GuidSet&>("ignore rpg target")->Get();
for (ObjectGuid const guid : ignoreList)
{
if (guid.GetEntry() == newTarget->getEntry())
{
ignoreList.erase(guid);
}
}
context->GetValue<GuidSet&>("ignore rpg target")->Set(ignoreList);
}
//Actually apply the new target to the travel target used by the bot.
oldTarget->copyTarget(newTarget);
//If we are idling but have a master. Idle only 10 seconds.
if (botAI->GetMaster() && oldTarget->isActive() && oldTarget->getDestination()->getName() == "NullTravelDestination")
oldTarget->setExpireIn(10 * IN_MILLISECONDS);
else if (oldTarget->isForced()) // Make sure travel goes into cooldown after getting to the destination.
oldTarget->setExpireIn(HOUR * IN_MILLISECONDS);
//Clear rpg and pull/grind target. We want to travel, not hang around some more.
RESET_AI_VALUE(GuidPosition, "rpg target");
RESET_AI_VALUE(ObjectGuid, "pull target");
}
//Tell the master what travel target we are moving towards.
//This should at some point be rewritten to be denser or perhaps logic moved to ->getTitle()
void ChooseTravelTargetAction::ReportTravelTarget(TravelTarget* newTarget, TravelTarget* oldTarget)
{
TravelDestination* destination = newTarget->getDestination();
TravelDestination* oldDestination = oldTarget->getDestination();
std::ostringstream out;
if (newTarget->isForced())
out << "(Forced) ";
if (destination->getName() == "QuestRelationTravelDestination" || destination->getName() == "QuestObjectiveTravelDestination")
{
QuestTravelDestination* QuestDestination = (QuestTravelDestination*)destination;
Quest const* quest = QuestDestination->GetQuestTemplate();
WorldPosition botLocation(bot);
CreatureTemplate const* cInfo = nullptr;
GameObjectTemplate const* gInfo = nullptr;
if (destination->getEntry() > 0)
cInfo = sObjectMgr->GetCreatureTemplate(destination->getEntry());
else
gInfo = sObjectMgr->GetGameObjectTemplate(destination->getEntry() * -1);
std::string Sub;
if (newTarget->isGroupCopy())
out << "Following group ";
else if (oldDestination && oldDestination == destination)
out << "Continuing ";
else
out << "Traveling ";
out << round(newTarget->getDestination()->distanceTo(&botLocation)) << "y";
out << " for " << chat->FormatQuest(quest);
out << " to " << QuestDestination->getTitle();
botAI->TellMaster(out);
}
else if (destination->getName() == "RpgTravelDestination")
{
RpgTravelDestination* RpgDestination = (RpgTravelDestination*)destination;
WorldPosition botLocation(bot);
if (newTarget->isGroupCopy())
out << "Following group ";
else if (oldDestination && oldDestination == destination)
out << "Continuing ";
else
out << "Traveling ";
out << round(newTarget->getDestination()->distanceTo(&botLocation)) << "y";
out << " for ";
if (AI_VALUE2(bool, "group or", "should sell,can sell"))
out << "selling items";
else if (AI_VALUE2(bool, "group or", "should repair,can repair"))
out << "repairing";
else
out << "rpg";
out << " to " << RpgDestination->getTitle();
botAI->TellMaster(out);
}
else if (destination->getName() == "ExploreTravelDestination")
{
ExploreTravelDestination* ExploreDestination = (ExploreTravelDestination*)destination;
WorldPosition botLocation(bot);
if (newTarget->isGroupCopy())
out << "Following group ";
else if (oldDestination && oldDestination == destination)
out << "Continuing ";
else
out << "Traveling ";
out << round(newTarget->getDestination()->distanceTo(&botLocation)) << "y";
out << " for exploration";
out << " to " << ExploreDestination->getTitle();
botAI->TellMaster(out);
}
else if (destination->getName() == "GrindTravelDestination")
{
GrindTravelDestination* GrindDestination = (GrindTravelDestination*)destination;
WorldPosition botLocation(bot);
if (newTarget->isGroupCopy())
out << "Following group ";
else if (oldDestination && oldDestination == destination)
out << "Continuing ";
else
out << "Traveling ";
out << round(newTarget->getDestination()->distanceTo(&botLocation)) << "y";
out << " for grinding money";
out << " to " << GrindDestination->getTitle();
botAI->TellMaster(out);
}
else if (destination->getName() == "BossTravelDestination")
{
BossTravelDestination* BossDestination = (BossTravelDestination*)destination;
WorldPosition botLocation(bot);
if (newTarget->isGroupCopy())
out << "Following group ";
else if (oldDestination && oldDestination == destination)
out << "Continuing ";
else
out << "Traveling ";
out << round(newTarget->getDestination()->distanceTo(&botLocation)) << "y";
out << " for good loot";
out << " to " << BossDestination->getTitle();
botAI->TellMaster(out);
}
else if (destination->getName() == "NullTravelDestination")
{
if (!oldTarget->getDestination() || oldTarget->getDestination()->getName() != "NullTravelDestination")
{
botAI->TellMaster("No where to travel. Idling a bit.");
}
}
}
bool ChooseTravelTargetAction::getBestDestination(std::vector<TravelDestination*>* activeDestinations, std::vector<WorldPosition*>* activePoints)
{
if (activeDestinations->empty() || activePoints->empty()) //No targets or no points.
return false;
WorldPosition botLocation(bot);
std::vector<WorldPosition*> availablePoints = sTravelMgr->getNextPoint(&botLocation, *activePoints); //Pick a good point.
if (availablePoints.empty()) //No points available.
return false;
TravelDestination* targetDestination;
for (auto activeTarget : *activeDestinations) //Pick the destination that has this point.
if (activeTarget->distanceTo(availablePoints.front()) == 0)
targetDestination = activeTarget;
if (!targetDestination)
return false;
activeDestinations->clear();
activePoints->clear();
activeDestinations->push_back(targetDestination);
activePoints->push_back(availablePoints.front());
return true;
}
bool ChooseTravelTargetAction::SetGroupTarget(TravelTarget* target)
{
std::vector<TravelDestination*> activeDestinations;
std::vector<WorldPosition*> activePoints;
GuidList groupPlayers;
Group* group = bot->GetGroup();
if (group)
{
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
{
if (ref->GetSource() != bot)
{
if (ref->getSubGroup() != bot->GetSubGroup())
{
groupPlayers.push_back(ref->GetSource()->GetGUID());
}
else
{
groupPlayers.push_front(ref->GetSource()->GetGUID());
}
}
}
}
//Find targets of the group.
for (auto& member : groupPlayers)
{
Player* player = ObjectAccessor::FindPlayer(member);
if (!player)
continue;
PlayerbotAI* playerBotAI = GET_PLAYERBOT_AI(player);
if (!playerBotAI)
continue;
if (!playerBotAI->GetAiObjectContext())
continue;
TravelTarget* groupTarget = playerBotAI->GetAiObjectContext()->GetValue<TravelTarget*>("travel target")->Get();
if (groupTarget->isGroupCopy())
continue;
if (!groupTarget->isActive())
continue;
if (!groupTarget->getDestination()->isActive(bot) || groupTarget->getDestination()->getName() == "RpgTravelDestination")
continue;
activeDestinations.push_back(groupTarget->getDestination());
activePoints.push_back(groupTarget->getPosition());
}
if (!getBestDestination(&activeDestinations, &activePoints))
return false;
target->setTarget(activeDestinations.front(), activePoints.front(), true);
return target->isActive();
}
bool ChooseTravelTargetAction::SetCurrentTarget(TravelTarget* target, TravelTarget* oldTarget)
{
TravelDestination* oldDestination = oldTarget->getDestination();
if (oldTarget->isMaxRetry(false))
return false;
if (!oldDestination) //Does this target have a destination?
return false;
if (!oldDestination->isActive(bot)) //Is the destination still valid?
return false;
WorldPosition botLocation(bot);
std::vector<WorldPosition*> availablePoints = oldDestination->nextPoint(&botLocation);
if (availablePoints.empty())
return false;
target->setTarget(oldTarget->getDestination(), availablePoints.front());
target->setStatus(TRAVEL_STATUS_TRAVEL);
target->setRetry(false, oldTarget->getRetryCount(false) + 1);
return target->isActive();
}
bool ChooseTravelTargetAction::SetQuestTarget(TravelTarget* target, bool onlyCompleted)
{
std::vector<TravelDestination*> activeDestinations;
std::vector<WorldPosition*> activePoints;
QuestStatusMap& questMap = bot->getQuestStatusMap();
WorldPosition botLocation(bot);
//Find destinations related to the active quests.
for (auto& quest : questMap)
{
if (bot->IsQuestRewarded(quest.first))
continue;
uint32 questId = quest.first;
QuestStatusData* questStatus = &quest.second;
if (onlyCompleted && sObjectMgr->GetQuestTemplate(questId) && !bot->CanRewardQuest(sObjectMgr->GetQuestTemplate(questId), false))
continue;
std::vector<TravelDestination*> questDestinations = sTravelMgr->getQuestTravelDestinations(bot, questId, botAI->HasRealPlayerMaster(), false, 5000);
std::vector<WorldPosition*> questPoints;
for (auto& questDestination : questDestinations)
{
std::vector<WorldPosition*> destinationPoints = questDestination->getPoints();
if (!destinationPoints.empty())
questPoints.insert(questPoints.end(), destinationPoints.begin(), destinationPoints.end());
}
if (getBestDestination(&questDestinations, &questPoints))
{
activeDestinations.push_back(questDestinations.front());
activePoints.push_back(questPoints.front());
}
}
if (!getBestDestination(&activeDestinations, &activePoints))
return false;
target->setTarget(activeDestinations.front(), activePoints.front());
return target->isActive();
}
bool ChooseTravelTargetAction::SetNewQuestTarget(TravelTarget* target)
{
std::vector<TravelDestination*> activeDestinations;
std::vector<WorldPosition*> activePoints;
WorldPosition botLocation(bot);
//Find quest givers.
std::vector<TravelDestination*> TravelDestinations = sTravelMgr->getQuestTravelDestinations(bot, -1, botAI->HasRealPlayerMaster());
activeDestinations.insert(activeDestinations.end(), TravelDestinations.begin(), TravelDestinations.end());
//Pick one good point per destination.
for (auto& activeTarget : activeDestinations)
{
std::vector<WorldPosition*> points = activeTarget->nextPoint(&botLocation);
if (!points.empty())
activePoints.push_back(points.front());
}
if (!getBestDestination(&activeDestinations, &activePoints))
return false;
target->setTarget(activeDestinations.front(), activePoints.front());
return target->isActive();
}
bool ChooseTravelTargetAction::SetRpgTarget(TravelTarget* target)
{
std::vector<TravelDestination*> activeDestinations;
std::vector<WorldPosition*> activePoints;
WorldPosition botLocation(bot);
//Find rpg npcs
std::vector<TravelDestination*> TravelDestinations = sTravelMgr->getRpgTravelDestinations(bot, botAI->HasRealPlayerMaster());
activeDestinations.insert(activeDestinations.end(), TravelDestinations.begin(), TravelDestinations.end());
//Pick one good point per destination.
for (auto& activeTarget : activeDestinations)
{
std::vector<WorldPosition*> points = activeTarget->nextPoint(&botLocation);
if (!points.empty())
activePoints.push_back(points.front());
}
if (!getBestDestination(&activeDestinations, &activePoints))
return false;
target->setTarget(activeDestinations.front(), activePoints.front());
return target->isActive();
}
bool ChooseTravelTargetAction::SetGrindTarget(TravelTarget* target)
{
std::vector<TravelDestination*> activeDestinations;
std::vector<WorldPosition*> activePoints;
WorldPosition botLocation(bot);
//Find grind mobs.
std::vector<TravelDestination*> TravelDestinations = sTravelMgr->getGrindTravelDestinations(bot, botAI->HasRealPlayerMaster());
activeDestinations.insert(activeDestinations.end(), TravelDestinations.begin(), TravelDestinations.end());
//Pick one good point per destination.
for (auto& activeTarget : activeDestinations)
{
std::vector<WorldPosition*> points = activeTarget->nextPoint(&botLocation);
if (!points.empty())
activePoints.push_back(points.front());
}
if (!getBestDestination(&activeDestinations, &activePoints))
return false;
target->setTarget(activeDestinations.front(), activePoints.front());
return target->isActive();
}
bool ChooseTravelTargetAction::SetBossTarget(TravelTarget* target)
{
std::vector<TravelDestination*> activeDestinations;
std::vector<WorldPosition*> activePoints;
WorldPosition botLocation(bot);
//Find boss mobs.
std::vector<TravelDestination*> TravelDestinations = sTravelMgr->getBossTravelDestinations(bot, botAI->HasRealPlayerMaster());
activeDestinations.insert(activeDestinations.end(), TravelDestinations.begin(), TravelDestinations.end());
//Pick one good point per destination.
for (auto& activeTarget : activeDestinations)
{
std::vector<WorldPosition*> points = activeTarget->nextPoint(&botLocation);
if (!points.empty())
activePoints.push_back(points.front());
}
if (!getBestDestination(&activeDestinations, &activePoints))
return false;
target->setTarget(activeDestinations.front(), activePoints.front());
return target->isActive();
}
bool ChooseTravelTargetAction::SetExploreTarget(TravelTarget* target)
{
std::vector<TravelDestination*> activeDestinations;
std::vector<WorldPosition*> activePoints;
WorldPosition botLocation(bot);
//Find quest givers.
std::vector<TravelDestination*> TravelDestinations = sTravelMgr->getExploreTravelDestinations(bot, true, true);
activeDestinations.insert(activeDestinations.end(), TravelDestinations.begin(), TravelDestinations.end());
/*
//Pick one good point per destination.
for (auto& activeTarget : activeDestinations)
{
//271 south shore
//35 booty bay
//380 The Barrens The Crossroads
if(((ExploreTravelDestination * )activeTarget)->getAreaId() == 380)
{
activePoints.push_back(activeTarget->getPoints(true)[0]);
}
}
*/
if (activePoints.empty())
{
TravelDestinations = sTravelMgr->getExploreTravelDestinations(bot, botAI->HasRealPlayerMaster());
for (auto& activeTarget : activeDestinations)
{
std::vector<WorldPosition*> points = activeTarget->nextPoint(&botLocation);
if (!points.empty())
{
activePoints.push_back(points.front());
}
}
}
if (!getBestDestination(&activeDestinations, &activePoints))
return false;
target->setTarget(activeDestinations.front(), activePoints.front());
return target->isActive();
}
char* strstri(char const* haystack, char const* needle);
bool ChooseTravelTargetAction::SetNpcFlagTarget(TravelTarget* target, std::vector<NPCFlags> flags, std::string const name, std::vector<uint32> items)
{
WorldPosition botPos(bot);
std::vector<TravelDestination*> dests;
for (auto& d : sTravelMgr->getRpgTravelDestinations(bot, true, true))
{
if (!d->getEntry())
continue;
CreatureTemplate const* cInfo = sObjectMgr->GetCreatureTemplate(d->getEntry());
if (!cInfo)
continue;
bool foundFlag = false;
for (auto flag : flags)
if (cInfo->npcflag & flag)
{
foundFlag = true;
break;
}
if (!foundFlag)
continue;
if (!name.empty() && !strstri(cInfo->Name.c_str(), name.c_str()) && !strstri(cInfo->SubName.c_str(), name.c_str()))
continue;
if (!items.empty())
{
bool foundItem = false;
VendorItemData const* vItems = sObjectMgr->GetNpcVendorItemList(d->getEntry());
if (vItems)
{
for (auto item : items)
{
for (auto vitem : vItems->m_items)
{
if (vitem->item == item)
{
foundItem = true;
break;
}
}
}
}
if (!foundItem)
continue;
}
FactionTemplateEntry const* factionEntry = sFactionTemplateStore.LookupEntry(cInfo->faction);
ReputationRank reaction = Unit::GetFactionReactionTo(botAI->GetBot()->GetFactionTemplateEntry(), factionEntry);
if (reaction < REP_NEUTRAL)
continue;
dests.push_back(d);
}
if (!dests.empty())
{
TravelDestination* dest = *std::min_element(dests.begin(), dests.end(), [botPos](TravelDestination* i, TravelDestination* j)
{
return i->distanceTo(const_cast<WorldPosition*>(&botPos)) < j->distanceTo(const_cast<WorldPosition*>(&botPos));
});
std::vector<WorldPosition*> points = dest->nextPoint(const_cast<WorldPosition*>(&botPos), true);
if (points.empty())
return false;
target->setTarget(dest, points.front());
target->setForced(true);
return true;
}
return false;
}
std::vector<TravelDestination*> TravelMgr::getBossTravelDestinations(Player* bot, bool ignoreFull, bool ignoreInactive, float maxDistance)
{
WorldPosition botLocation(bot);
std::vector<TravelDestination*> retTravelLocations;
for (auto& dest : bossMobs)
{
if (!ignoreInactive && !dest->isActive(bot))
continue;
if (dest->isFull(ignoreFull))
continue;
if (maxDistance > 0 && dest->distanceTo(&botLocation) > maxDistance)
continue;
retTravelLocations.push_back(dest);
}
return retTravelLocations;
}
bool ChooseTravelTargetAction::SetNullTarget(TravelTarget* target)
{
target->setTarget(sTravelMgr->nullTravelDestination, sTravelMgr->nullWorldPosition, true);
return true;
}
std::vector<std::string> split(std::string const s, char delim);
char* strstri(char const* haystack, char const* needle);
TravelDestination* ChooseTravelTargetAction::FindDestination(Player* bot, std::string const name)
{
PlayerbotAI* botAI = GET_PLAYERBOT_AI(bot);
AiObjectContext* context = botAI->GetAiObjectContext();
std::vector<TravelDestination*> dests;
//Zones
for (auto& d : sTravelMgr->getExploreTravelDestinations(bot, true, true))
{
if (strstri(d->getTitle().c_str(), name.c_str()))
dests.push_back(d);
}
//Npcs
for (auto& d : sTravelMgr->getRpgTravelDestinations(bot, true, true))
{
if (strstri(d->getTitle().c_str(), name.c_str()))
dests.push_back(d);
}
//Mobs
for (auto& d : sTravelMgr->getGrindTravelDestinations(bot, true, true))
{
if (strstri(d->getTitle().c_str(), name.c_str()))
dests.push_back(d);
}
//Bosses
for (auto& d : sTravelMgr->getBossTravelDestinations(bot, true, true))
{
if (strstri(d->getTitle().c_str(), name.c_str()))
dests.push_back(d);
}
WorldPosition botPos(bot);
if (dests.empty())
return nullptr;
TravelDestination* dest = *std::min_element(dests.begin(), dests.end(), [botPos](TravelDestination* i, TravelDestination* j)
{
return i->distanceTo(const_cast<WorldPosition*>(&botPos)) < j->distanceTo(const_cast<WorldPosition*>(&botPos));
});
return dest;
};
bool ChooseTravelTargetAction::isUseful()
{
if (!botAI->AllowActivity(TRAVEL_ACTIVITY))
return false;
return !context->GetValue<TravelTarget*>("travel target")->Get()->isActive() && !context->GetValue<LootObject>("loot target")->Get().IsLootPossible(bot) && !bot->IsInCombat();
}
bool ChooseTravelTargetAction::needForQuest(Unit* target)
{
bool justCheck = (bot->GetGUID() == target->GetGUID());
QuestStatusMap& questMap = bot->getQuestStatusMap();
for (auto& quest : questMap)
{
Quest const* questTemplate = sObjectMgr->GetQuestTemplate(quest.first);
if (!questTemplate)
continue;
uint32 questId = questTemplate->GetQuestId();
if (!questId)
continue;
QuestStatus status = bot->GetQuestStatus(questId);
if ((status == QUEST_STATUS_COMPLETE && !bot->GetQuestRewardStatus(questId)))
{
if (!justCheck && !target->hasInvolvedQuest(questId))
continue;
return true;
}
else if (status == QUEST_STATUS_INCOMPLETE)
{
QuestStatusData questStatus = quest.second;
if (questTemplate->GetQuestLevel() > bot->getLevel())
continue;
for (int j = 0; j < QUEST_OBJECTIVES_COUNT; j++)
{
int32 entry = questTemplate->RequiredNpcOrGo[j];
if (entry && entry > 0)
{
int required = questTemplate->RequiredNpcOrGoCount[j];
int available = questStatus.CreatureOrGOCount[j];
if(required && available < required && (target->GetEntry() == entry || justCheck))
return true;
}
if (justCheck)
{
int32 itemId = questTemplate->RequiredItemId[j];
if (itemId && itemId > 0)
{
int required = questTemplate->RequiredItemCount[j];
int available = questStatus.ItemCount[j];
if (required && available < required)
return true;
}
}
}
if (!justCheck)
{
if (CreatureTemplate const* data = sObjectMgr->GetCreatureTemplate(target->GetEntry()))
{
if (uint32 lootId = data->lootid)
{
if (LootTemplates_Creature.HaveQuestLootForPlayer(lootId, bot))
return true;
}
}
}
}
}
return false;
}
bool ChooseTravelTargetAction::needItemForQuest(uint32 itemId, const Quest* questTemplate, const QuestStatusData* questStatus)
{
for (uint32 i = 0; i < QUEST_OBJECTIVES_COUNT; i++)
{
if (questTemplate->RequiredItemId[i] != itemId)
continue;
uint32 required = questTemplate->RequiredItemCount[i];
uint32 available = questStatus->ItemCount[i];
if (!required)
continue;
return available < required;
}
return false;
}

View File

@@ -0,0 +1,50 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
*/
#ifndef _PLAYERBOT_CHOOSETRAVELTARGETACTION_H
#define _PLAYERBOT_CHOOSETRAVELTARGETACTION_H
#include "MovementActions.h"
#include "TravelMgr.h"
class Quest;
class PlayerbotAI;
class Unit;
struct QuestStatusData;
class ChooseTravelTargetAction : public MovementAction
{
public:
ChooseTravelTargetAction(PlayerbotAI* botAI, std::string const name = "choose travel target") : MovementAction(botAI, name) { }
bool Execute(Event event) override;
bool isUseful() override;
static TravelDestination* FindDestination(Player* bot, std::string const name);
protected:
void getNewTarget(TravelTarget* newTarget, TravelTarget* oldTarget);
void setNewTarget(TravelTarget* newTarget, TravelTarget* oldTarget);
void ReportTravelTarget(TravelTarget* newTarget, TravelTarget* oldTarget);
bool getBestDestination(std::vector<TravelDestination*>* activeDestinations, std::vector<WorldPosition*>* activePoints);
bool SetGroupTarget(TravelTarget* target);
bool SetCurrentTarget(TravelTarget* target, TravelTarget* oldTarget);
bool SetQuestTarget(TravelTarget* target, bool onlyCompleted = false);
bool SetNewQuestTarget(TravelTarget* target);
bool SetRpgTarget(TravelTarget* target);
bool SetGrindTarget(TravelTarget* target);
bool SetBossTarget(TravelTarget* target);
bool SetExploreTarget(TravelTarget* target);
bool SetNpcFlagTarget(TravelTarget* target, std::vector<NPCFlags> flags, std::string const name = "", std::vector<uint32> items = { });
bool SetNullTarget(TravelTarget* target);
private:
virtual bool needForQuest(Unit* target);
virtual bool needItemForQuest(uint32 itemId, Quest const* questTemplate, QuestStatusData const* questStatus);
};
#endif

View File

@@ -0,0 +1,47 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
*/
#include "CombatActions.h"
#include "Event.h"
#include "LastMovementValue.h"
#include "Playerbots.h"
#include "ServerFacade.h"
bool SwitchToMeleeAction::Execute(Event event)
{
//botAI->TellMasterNoFacing("Switching to melee!");
return ChangeCombatStrategyAction::Execute(event);
}
bool SwitchToMeleeAction::isUseful()
{
if (bot->getClass() == CLASS_HUNTER)
{
Unit* target = AI_VALUE(Unit*, "current target");
time_t lastFlee = AI_VALUE(LastMovement&, "last movement").lastFlee;
return botAI->HasStrategy("ranged", BOT_STATE_COMBAT) && ((bot->IsInCombat() && target && (target->GetVictim() == bot && (!bot->GetGroup() || lastFlee) &&
sServerFacade->IsDistanceLessOrEqualThan(AI_VALUE2(float, "distance", "current target"), 8.0f))) || (!bot->IsInCombat()));
}
return botAI->HasStrategy("ranged", BOT_STATE_COMBAT);
}
bool SwitchToRangedAction::Execute(Event event)
{
//botAI->TellMasterNoFacing("Switching to ranged!");
return ChangeCombatStrategyAction::Execute(event);
}
bool SwitchToRangedAction::isUseful()
{
if (bot->getClass() == CLASS_HUNTER)
{
Unit* target = AI_VALUE(Unit*, "current target");
bool hasAmmo = AI_VALUE2(uint32, "item count", "ammo");
return botAI->HasStrategy("close", BOT_STATE_COMBAT) && hasAmmo && ((bot->IsInCombat() && target && ((target->GetVictim() != bot || target->GetTarget() != bot->GetGUID()) ||
sServerFacade->IsDistanceGreaterThan(AI_VALUE2(float, "distance", "current target"), 8.0f))) || (!bot->IsInCombat()));
}
return botAI->HasStrategy("close", BOT_STATE_COMBAT);
}

View File

@@ -0,0 +1,30 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
*/
#ifndef _PLAYERBOT_COMBATACTIONS_H
#define _PLAYERBOT_COMBATACTIONS_H
#include "ChangeStrategyAction.h"
class PlayerbotAI;
class SwitchToMeleeAction : public ChangeCombatStrategyAction
{
public:
SwitchToMeleeAction(PlayerbotAI* botAI) : ChangeCombatStrategyAction(botAI, "-ranged,+close") { }
bool Execute(Event event) override;
bool isUseful() override;
};
class SwitchToRangedAction : public ChangeCombatStrategyAction
{
public:
SwitchToRangedAction(PlayerbotAI* botAI) : ChangeCombatStrategyAction(botAI, "-close,+ranged") { }
bool Execute(Event event) override;
bool isUseful() override;
};
#endif

View File

@@ -0,0 +1,141 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
*/
#include "CustomStrategyEditAction.h"
#include "CustomStrategy.h"
#include "Event.h"
#include "Playerbots.h"
bool CustomStrategyEditAction::Execute(Event event)
{
std::string text = event.getParam();
uint32 pos = text.find(" ");
if (pos == std::string::npos)
return PrintHelp();
std::string const name = text.substr(0, pos);
text = text.substr(pos + 1);
pos = text.find(" ");
if (pos == std::string::npos)
pos = text.size();
std::string const idx = text.substr(0, pos);
text = pos >= text.size() ? "" : text.substr(pos + 1);
return idx == "?" ? Print(name) : Edit(name, atoi(idx.c_str()), text);
}
bool CustomStrategyEditAction::PrintHelp()
{
botAI->TellMaster("=== Custom strategies ===");
uint32 owner = botAI->GetBot()->GetGUID().GetCounter();
PlayerbotsDatabasePreparedStatement* stmt = PlayerbotsDatabase.GetPreparedStatement(PLAYERBOTS_SEL_CUSTOM_STRATEGY_BY_OWNER);
stmt->SetData(0, owner);
if (PreparedQueryResult result = PlayerbotsDatabase.Query(stmt))
{
do
{
Field* fields = result->Fetch();
std::string const name = fields[0].Get<std::string>();
botAI->TellMaster(name);
}
while (result->NextRow());
}
botAI->TellMaster("Usage: cs <name> <idx> <command>");
return false;
}
bool CustomStrategyEditAction::Print(std::string const name)
{
std::ostringstream out;
out << "=== " << name << " ===";
botAI->TellMaster(out.str());
uint32 owner = botAI->GetBot()->GetGUID().GetCounter();
PlayerbotsDatabasePreparedStatement* stmt = PlayerbotsDatabase.GetPreparedStatement(PLAYERBOTS_SEL_CUSTOM_STRATEGY_BY_OWNER_AND_NAME);
stmt->SetData(0, owner);
stmt->SetData(1, name);
if (PreparedQueryResult result = PlayerbotsDatabase.Query(stmt))
{
do
{
Field* fields = result->Fetch();
uint32 idx = fields[0].Get<uint32>();
std::string const action = fields[1].Get<std::string>();
PrintActionLine(idx, action);
}
while (result->NextRow());
}
return true;
}
bool CustomStrategyEditAction::Edit(std::string const name, uint32 idx, std::string const command)
{
uint32 owner = botAI->GetBot()->GetGUID().GetCounter();
PlayerbotsDatabasePreparedStatement* stmt = PlayerbotsDatabase.GetPreparedStatement(PLAYERBOTS_SEL_CUSTOM_STRATEGY_BY_OWNER_AND_NAME_AND_IDX);
stmt->SetData(0, owner);
stmt->SetData(1, name);
stmt->SetData(2, idx);
if (PreparedQueryResult result = PlayerbotsDatabase.Query(stmt))
{
if (command.empty())
{
stmt = PlayerbotsDatabase.GetPreparedStatement(PLAYERBOTS_DEL_CUSTOM_STRATEGY);
stmt->SetData(0, name);
stmt->SetData(1, owner);
stmt->SetData(2, idx);
PlayerbotsDatabase.Execute(stmt);
}
else
{
stmt = PlayerbotsDatabase.GetPreparedStatement(PLAYERBOTS_UPD_CUSTOM_STRATEGY);
stmt->SetData(0, command);
stmt->SetData(1, name);
stmt->SetData(2, owner);
stmt->SetData(3, idx);
PlayerbotsDatabase.Execute(stmt);
}
}
else
{
stmt = PlayerbotsDatabase.GetPreparedStatement(PLAYERBOTS_INS_CUSTOM_STRATEGY);
stmt->SetData(0, name);
stmt->SetData(1, owner);
stmt->SetData(2, idx);
stmt->SetData(3, command);
PlayerbotsDatabase.Execute(stmt);
}
PrintActionLine(idx, command);
std::ostringstream ss;
ss << "custom::" << name;
if (Strategy* strategy = botAI->GetAiObjectContext()->GetStrategy(ss.str()))
{
if (CustomStrategy* cs = dynamic_cast<CustomStrategy*>(strategy))
{
cs->Reset();
botAI->ReInitCurrentEngine();
}
}
return true;
}
bool CustomStrategyEditAction::PrintActionLine(uint32 idx, std::string const command)
{
std::ostringstream out;
out << "#" << idx << " " << command;
botAI->TellMaster(out.str());
return true;
}

View File

@@ -0,0 +1,26 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
*/
#ifndef _PLAYERBOT_CUSTOMSTRATEGYEDITACTION_H
#define _PLAYERBOT_CUSTOMSTRATEGYEDITACTION_H
#include "Action.h"
class PlayerbotAI;
class CustomStrategyEditAction : public Action
{
public:
CustomStrategyEditAction(PlayerbotAI* botAI) : Action(botAI, "cs") { }
bool Execute(Event event) override;
private:
bool PrintHelp();
bool PrintActionLine(uint32 idx, std::string const command);
bool Print(std::string const name);
bool Edit(std::string const name, uint32 idx, std::string const command);
};
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,26 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
*/
#ifndef _PLAYERBOT_DEBUGACTION_H
#define _PLAYERBOT_DEBUGACTION_H
#include "Action.h"
#include "ObjectGuid.h"
#include "TravelMgr.h"
class PlayerbotAI;
class Unit;
class DebugAction : public Action
{
public:
DebugAction(PlayerbotAI* botAI) : Action(botAI, "Debug") { }
bool Execute(Event event) override;
void FakeSpell(uint32 spellId, Unit* truecaster, Unit* caster, ObjectGuid target = ObjectGuid::Empty, GuidVector otherTargets = {}, GuidVector missTargets = {}, WorldPosition source = WorldPosition(), WorldPosition dest = WorldPosition(), bool forceDest = false);
void addAura(uint32 spellId, Unit* target);
};
#endif

View File

@@ -0,0 +1,21 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
*/
#include "DelayAction.h"
#include "Event.h"
#include "Playerbots.h"
bool DelayAction::Execute(Event event)
{
uint32 delay = sPlayerbotAIConfig->passiveDelay + sPlayerbotAIConfig->globalCoolDown;
botAI->SetNextCheckDelay(delay);
return true;
}
bool DelayAction::isUseful()
{
return !botAI->AllowActivity(ALL_ACTIVITY);
}

View File

@@ -0,0 +1,21 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
*/
#ifndef _PLAYERBOT_DELAYACTION_H
#define _PLAYERBOT_DELAYACTION_H
#include "Action.h"
class PlayerbotAI;
class DelayAction : public Action
{
public:
DelayAction(PlayerbotAI* botAI) : Action(botAI, "delay") { }
bool Execute(Event event) override;
bool isUseful() override;
};
#endif

View File

@@ -0,0 +1,82 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
*/
#include "DestroyItemAction.h"
#include "Event.h"
#include "ItemCountValue.h"
#include "Playerbots.h"
bool DestroyItemAction::Execute(Event event)
{
std::string const text = event.getParam();
ItemIds ids = chat->parseItems(text);
for (ItemIds::iterator i = ids.begin(); i != ids.end(); i++)
{
FindItemByIdVisitor visitor(*i);
DestroyItem(&visitor);
}
return true;
}
void DestroyItemAction::DestroyItem(FindItemVisitor* visitor)
{
IterateItems(visitor);
std::vector<Item*> items = visitor->GetResult();
for (Item* item : items)
{
bot->DestroyItem(item->GetBagSlot(),item->GetSlot(), true);
std::ostringstream out;
out << chat->FormatItem(item->GetTemplate()) << " destroyed";
botAI->TellMaster(out);
}
}
bool SmartDestroyItemAction::isUseful()
{
return !botAI->HasActivePlayerMaster();
}
bool SmartDestroyItemAction::Execute(Event event)
{
uint8 bagSpace = AI_VALUE(uint8, "bag space");
if (bagSpace < 90)
return false;
std::vector<uint32> bestToDestroy = { ITEM_USAGE_NONE }; //First destroy anything useless.
if (!AI_VALUE(bool, "can sell") && AI_VALUE(bool, "should get money")) // We need money so quest items are less important since they can't directly be sold.
bestToDestroy.push_back(ITEM_USAGE_QUEST);
else // We don't need money so destroy the cheapest stuff.
{
bestToDestroy.push_back(ITEM_USAGE_VENDOR);
bestToDestroy.push_back(ITEM_USAGE_AH);
}
// If we still need room
bestToDestroy.push_back(ITEM_USAGE_SKILL); // Items that might help tradeskill are more important than above but still expenable.
bestToDestroy.push_back(ITEM_USAGE_USE); // These are more likely to be usefull 'soon' but still expenable.
for (auto& usage : bestToDestroy)
{
std::vector<Item*> items = AI_VALUE2(std::vector<Item*>, "inventory items", "usage " + std::to_string(usage));
std::reverse(items.begin(), items.end());
for (auto& item : items)
{
FindItemByIdVisitor visitor(item->GetTemplate()->ItemId);
DestroyItem(&visitor);
bagSpace = AI_VALUE(uint8, "bag space");
if (bagSpace < 90)
return true;
}
}
return false;
}

View File

@@ -0,0 +1,33 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
*/
#ifndef _PLAYERBOT_DESTROYITEMACTION_H
#define _PLAYERBOT_DESTROYITEMACTION_H
#include "InventoryAction.h"
class FindItemVisitor;
class PlayerbotAI;
class DestroyItemAction : public InventoryAction
{
public:
DestroyItemAction(PlayerbotAI* botAI, std::string const name = "destroy") : InventoryAction(botAI, name) { }
bool Execute(Event event) override;
protected:
void DestroyItem(FindItemVisitor* visitor);
};
class SmartDestroyItemAction : public DestroyItemAction
{
public:
SmartDestroyItemAction(PlayerbotAI* botAI) : DestroyItemAction(botAI, "smart destroy") { }
bool Execute(Event event) override;
bool isUseful() override;
};
#endif

View File

@@ -0,0 +1,183 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
*/
#include "DropQuestAction.h"
#include "ChatHelper.h"
#include "Event.h"
#include "Playerbots.h"
bool DropQuestAction::Execute(Event event)
{
std::string const link = event.getParam();
Player* master = GetMaster();
if (!master)
return false;
PlayerbotChatHandler handler(master);
uint32 entry = handler.extractQuestId(link);
// remove all quest entries for 'entry' from quest log
for (uint8 slot = 0; slot < MAX_QUEST_LOG_SIZE; ++slot)
{
uint32 logQuest = bot->GetQuestSlotQuestId(slot);
Quest const* quest = sObjectMgr->GetQuestTemplate(logQuest);
if (!quest)
continue;
if (logQuest == entry || link.find(quest->GetTitle()) != std::string::npos)
{
bot->SetQuestSlot(slot, 0);
// we ignore unequippable quest items in this case, its' still be equipped
bot->TakeQuestSourceItem(logQuest, false);
entry = logQuest;
break;
}
}
if (!entry)
return false;
bot->RemoveRewardedQuest(entry);
bot->RemoveActiveQuest(entry, false);
botAI->TellMaster("Quest removed");
return true;
}
bool CleanQuestLogAction::Execute(Event event)
{
std::string const link = event.getParam();
if (botAI->HasActivePlayerMaster())
return false;
uint8 totalQuests = 0;
DropQuestType(totalQuests); //Count the total quests
if (MAX_QUEST_LOG_SIZE - totalQuests > 6)
return true;
if (AI_VALUE(bool, "can fight equal")) // Only drop gray quests when able to fight proper lvl quests.
{
DropQuestType(totalQuests, MAX_QUEST_LOG_SIZE - 6); // Drop gray/red quests.
DropQuestType(totalQuests, MAX_QUEST_LOG_SIZE - 6, false, true); // Drop gray/red quests with progress.
DropQuestType(totalQuests, MAX_QUEST_LOG_SIZE - 6, false, true, true); // Drop gray/red completed quests.
}
if (MAX_QUEST_LOG_SIZE - totalQuests > 4)
return true;
DropQuestType(totalQuests, MAX_QUEST_LOG_SIZE - 4, true); //Drop quests without progress.
if (MAX_QUEST_LOG_SIZE - totalQuests > 2)
return true;
DropQuestType(totalQuests, MAX_QUEST_LOG_SIZE - 2, true, true); //Drop quests with progress.
if (MAX_QUEST_LOG_SIZE - totalQuests > 0)
return true;
DropQuestType(totalQuests, MAX_QUEST_LOG_SIZE - 1, true, true, true); //Drop completed quests.
if (MAX_QUEST_LOG_SIZE - totalQuests > 0)
return true;
return false;
}
void CleanQuestLogAction::DropQuestType(uint8& numQuest, uint8 wantNum, bool isGreen, bool hasProgress, bool isComplete)
{
for (uint8 slot = 0; slot < MAX_QUEST_LOG_SIZE; ++slot)
{
uint32 questId = bot->GetQuestSlotQuestId(slot);
if (!questId)
continue;
Quest const* quest = sObjectMgr->GetQuestTemplate(questId);
if (!quest)
continue;
if (quest->GetRequiredClasses() && (quest->GetRewSpellCast() || quest->GetRewSpell())) //Do not drop class specific quests that learn spells.
continue;
if (quest->GetRequiredClasses() && (quest->GetRewSpellCast() || quest->GetRewSpell())) // Do not drop class specific quests that learn spells.
continue;
if (wantNum == 100)
numQuest++;
int32 lowLevelDiff = sWorld->getIntConfig(CONFIG_QUEST_LOW_LEVEL_HIDE_DIFF);
if (lowLevelDiff < 0 || bot->getLevel() <= bot->GetQuestLevel(quest) + uint32(lowLevelDiff)) // Quest is not gray
{
if (bot->getLevel() + 5 > bot->GetQuestLevel(quest)) // Quest is not red
if (!isGreen)
continue;
}
else // Quest is gray
{
if (isGreen)
continue;
}
if (HasProgress(bot, quest) && !hasProgress)
continue;
if (bot->GetQuestStatus(questId) == QUEST_STATUS_COMPLETE && !isComplete)
continue;
if (numQuest <= wantNum && bot->GetQuestStatus(questId) != QUEST_STATUS_FAILED) // Always drop failed quests
continue;
//Drop quest.
bot->SetQuestSlot(slot, 0);
//We ignore unequippable quest items in this case, its' still be equipped
bot->TakeQuestSourceItem(questId, false);
bot->SetQuestStatus(questId, QUEST_STATUS_NONE);
bot->RemoveRewardedQuest(questId);
numQuest--;
botAI->TellMaster("Quest removed" + chat->FormatQuest(quest));
}
}
bool CleanQuestLogAction::HasProgress(Player* bot, Quest const* quest)
{
uint32 questId = quest->GetQuestId();
if (bot->GetQuestStatus(questId) == QUEST_STATUS_COMPLETE)
return true;
QuestStatusData questStatus = bot->getQuestStatusMap()[questId];
for (uint32 i = 0; i < QUEST_OBJECTIVES_COUNT; i++)
{
if (!quest->ObjectiveText[i].empty())
return true;
if (quest->RequiredItemId[i])
{
int required = quest->RequiredItemCount[i];
int available = questStatus.ItemCount[i];
if (available > 0 && required > 0)
return true;
}
if (quest->RequiredNpcOrGo[i])
{
int required = quest->RequiredNpcOrGoCount[i];
int available = questStatus.CreatureOrGOCount[i];
if (available > 0 && required > 0)
return true;
}
}
return false;
}

View File

@@ -0,0 +1,34 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
*/
#ifndef _PLAYERBOT_DROPQUESTACTION_H
#define _PLAYERBOT_DROPQUESTACTION_H
#include "Action.h"
class Player;
class PlayerbotAI;
class Quest;
class DropQuestAction : public Action
{
public:
DropQuestAction(PlayerbotAI* botAI) : Action(botAI, "drop quest") { }
bool Execute(Event event) override;
};
class CleanQuestLogAction : public Action
{
public:
CleanQuestLogAction(PlayerbotAI* botAI) : Action(botAI, "clean quest log") { }
bool Execute(Event event) override;
void DropQuestType(uint8& numQuest, uint8 wantNum = 100, bool isGreen = false, bool hasProgress = false, bool isComplete = false);
static bool HasProgress(Player* bot, Quest const* quest);
};
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,53 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
*/
#ifndef _PLAYERBOT_EMOTEACTION_H
#define _PLAYERBOT_EMOTEACTION_H
#include "Action.h"
#include "NamedObjectContext.h"
#include <map>
class Player;
class PlayerbotAI;
class Unit;
enum TextEmotes : uint32;
class EmoteActionBase : public Action
{
public:
EmoteActionBase(PlayerbotAI* botAI, std::string const name);
static uint32 GetNumberOfEmoteVariants(TextEmotes emote, uint8 race, uint8 gender);
protected:
bool Emote(Unit* target, uint32 type, bool textEmote = false);
bool ReceiveEmote(Player* source, uint32 emote, bool verbal = false);
Unit* GetTarget();
void InitEmotes();
static std::map<std::string, uint32> emotes;
static std::map<std::string, uint32> textEmotes;
};
class EmoteAction : public EmoteActionBase, public Qualified
{
public:
EmoteAction(PlayerbotAI* botAI);
bool Execute(Event event) override;
bool isUseful() override;
};
class TalkAction : public EmoteActionBase
{
public:
TalkAction(PlayerbotAI* botAI) : EmoteActionBase(botAI, "talk") { }
bool Execute(Event event) override;
static uint32 GetRandomEmote(Unit* unit, bool textEmote = false);
};
#endif

Some files were not shown because too many files have changed in this diff Show More