[FIX] Folder restructure (#2018)

As requested

Poll
```
1. Yes
2. Yes
3. Maybe, but yes
```

---------

Co-authored-by: Celandriel <22352763+Celandriel@users.noreply.github.com>
This commit is contained in:
bashermens
2026-01-17 10:34:58 +01:00
committed by GitHub
parent 2eb98c3233
commit a1137dbddc
1160 changed files with 233 additions and 232 deletions

View File

@@ -0,0 +1,20 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
* and/or modify it under version 3 of the License, or (at your option), any later version.
*/
#include "Action.h"
#include "Playerbots.h"
#include "Timer.h"
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(getMSTime())
{
}
bool ActionBasket::isExpired(uint32_t msecs) { return getMSTime() - created >= msecs; }

View File

@@ -0,0 +1,147 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
* and/or modify it under version 3 of the License, or (at your option), any later version.
*/
#pragma once
#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)
: relevance(relevance), name(name) {} // name after relevance - whipowill
NextAction(NextAction const& o) : relevance(o.relevance), name(o.name) {} // name after relevance - whipowill
std::string const getName() { return name; }
float getRelevance() { return relevance; }
static std::vector<NextAction> merge(std::vector<NextAction> const& what, std::vector<NextAction> const& with)
{
std::vector<NextAction> result = {};
for (NextAction const& action : what)
{
result.push_back(action);
}
for (NextAction const& action : with)
{
result.push_back(action);
}
return result;
};
private:
float relevance;
std::string name;
};
class Action : public AiNamedObject
{
public:
enum class ActionThreatType
{
None = 0,
Single = 1,
Aoe = 2
};
Action(PlayerbotAI* botAI, std::string const name = "action")
: AiNamedObject(botAI, name), verbose(false) {} // verbose after ainamedobject - whipowill
virtual ~Action(void) {}
virtual bool Execute([[maybe_unused]] Event event) { return true; }
virtual bool isPossible() { return true; }
virtual bool isUseful() { return true; }
virtual std::vector<NextAction> getPrerequisites() { return {}; }
virtual std::vector<NextAction> getAlternatives() { return {}; }
virtual std::vector<NextAction> getContinuers() { return {}; }
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 name,
std::vector<NextAction> prerequisites = {},
std::vector<NextAction> alternatives = {},
std::vector<NextAction> continuers = {}
) :
name(std::move(name)),
action(nullptr),
continuers(continuers),
alternatives(alternatives),
prerequisites(prerequisites)
{}
virtual ~ActionNode() = default;
Action* getAction() { return action; }
void setAction(Action* action) { this->action = action; }
const std::string getName() { return name; }
std::vector<NextAction> getContinuers()
{
return NextAction::merge(this->continuers, action->getContinuers());
}
std::vector<NextAction> getAlternatives()
{
return NextAction::merge(this->alternatives, action->getAlternatives());
}
std::vector<NextAction> getPrerequisites()
{
return NextAction::merge(this->prerequisites, action->getPrerequisites());
}
private:
const std::string name;
Action* action;
std::vector<NextAction> continuers;
std::vector<NextAction> alternatives;
std::vector<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(uint32_t msecs);
private:
ActionNode* action;
float relevance;
bool skipPrerequisites;
Event event;
uint32_t created;
};

View File

@@ -0,0 +1,15 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
* and/or modify it under version 3 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(); }

View File

@@ -0,0 +1,444 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
* and/or modify it under version 3 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 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 DEBUFF_TRIGGER(clazz, spell) \
class clazz : public DebuffTrigger \
{ \
public: \
clazz(PlayerbotAI* botAI) : DebuffTrigger(botAI, spell) {} \
}
#define DEBUFF_CHECKISOWNER_TRIGGER(clazz, spell) \
class clazz : public DebuffTrigger \
{ \
public: \
clazz(PlayerbotAI* botAI) : DebuffTrigger(botAI, spell, 1, true) {} \
}
#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 CD_TRIGGER(clazz, spell) \
class clazz : public SpellNoCooldownTrigger \
{ \
public: \
clazz(PlayerbotAI* botAI) : SpellNoCooldownTrigger(botAI, spell) {} \
}
#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_PARTY_ACTION(clazz, spell, estAmount, manaEfficiency) \
class clazz : public HealPartyMemberAction \
{ \
public: \
clazz(PlayerbotAI* botAI) : HealPartyMemberAction(botAI, spell, estAmount, manaEfficiency) {} \
}
#define AOE_HEAL_ACTION(clazz, spell, estAmount, manaEfficiency) \
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_CHECKISOWNER_ACTION(clazz, spell) \
class clazz : public CastDebuffSpellAction \
{ \
public: \
clazz(PlayerbotAI* botAI) : CastDebuffSpellAction(botAI, spell, true) {} \
}
#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 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 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) {}
#endif

View File

@@ -0,0 +1,269 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
* and/or modify it under version 3 of the License, or (at your option), any later version.
*/
#include "AiObjectContext.h"
#include "ActionContext.h"
#include "ChatActionContext.h"
#include "ChatTriggerContext.h"
#include "DKAiObjectContext.h"
#include "DruidAiObjectContext.h"
#include "HunterAiObjectContext.h"
#include "MageAiObjectContext.h"
#include "PaladinAiObjectContext.h"
#include "Playerbots.h"
#include "PriestAiObjectContext.h"
#include "RaidUlduarActionContext.h"
#include "RaidUlduarTriggerContext.h"
#include "RogueAiObjectContext.h"
#include "ShamanAiObjectContext.h"
#include "SharedValueContext.h"
#include "StrategyContext.h"
#include "TriggerContext.h"
#include "ValueContext.h"
#include "WarlockAiObjectContext.h"
#include "WarriorAiObjectContext.h"
#include "WorldPacketActionContext.h"
#include "WorldPacketTriggerContext.h"
#include "Extend/DungeonAi/DungeonStrategyContext.h"
#include "Extend/DungeonAi/Wotlk/WotlkDungeonActionContext.h"
#include "Extend/DungeonAi/Wotlk/WotlkDungeonTriggerContext.h"
#include "Extend/RaidAi/RaidStrategyContext.h"
#include "Extend/RaidAi/Aq20/RaidAq20ActionContext.h"
#include "Extend/RaidAi/Aq20/RaidAq20TriggerContext.h"
#include "Extend/RaidAi/MoltenCore/RaidMcActionContext.h"
#include "Extend/RaidAi/MoltenCore/RaidMcTriggerContext.h"
#include "Extend/RaidAi/BlackwingLair/RaidBwlActionContext.h"
#include "Extend/RaidAi/BlackwingLair/RaidBwlTriggerContext.h"
#include "Extend/RaidAi/Karazhan/RaidKarazhanActionContext.h"
#include "Extend/RaidAi/Karazhan/RaidKarazhanTriggerContext.h"
#include "Extend/RaidAi/Magtheridon/RaidMagtheridonActionContext.h"
#include "Extend/RaidAi/Magtheridon/RaidMagtheridonTriggerContext.h"
#include "Extend/RaidAi/GruulsLair/RaidGruulsLairActionContext.h"
#include "Extend/RaidAi/GruulsLair/RaidGruulsLairTriggerContext.h"
#include "Extend/RaidAi/EyeOfEternity/RaidEoEActionContext.h"
#include "Extend/RaidAi/EyeOfEternity/RaidEoETriggerContext.h"
#include "Extend/RaidAi/VaultOfArchavon/RaidVoAActionContext.h"
#include "Extend/RaidAi/VaultOfArchavon/RaidVoATriggerContext.h"
#include "Extend/RaidAi/ObsidianSanctum/RaidOsActionContext.h"
#include "Extend/RaidAi/ObsidianSanctum/RaidOsTriggerContext.h"
#include "Extend/RaidAi/Onyxia/RaidOnyxiaActionContext.h"
#include "Extend/RaidAi/Onyxia/RaidOnyxiaTriggerContext.h"
#include "Extend/RaidAi/Icecrown/RaidIccActionContext.h"
#include "Extend/RaidAi/Icecrown/RaidIccTriggerContext.h"
SharedNamedObjectContextList<Strategy> AiObjectContext::sharedStrategyContexts;
SharedNamedObjectContextList<Action> AiObjectContext::sharedActionContexts;
SharedNamedObjectContextList<Trigger> AiObjectContext::sharedTriggerContexts;
SharedNamedObjectContextList<UntypedValue> AiObjectContext::sharedValueContexts;
AiObjectContext::AiObjectContext(PlayerbotAI* botAI, SharedNamedObjectContextList<Strategy>& sharedStrategyContext,
SharedNamedObjectContextList<Action>& sharedActionContext,
SharedNamedObjectContextList<Trigger>& sharedTriggerContext,
SharedNamedObjectContextList<UntypedValue>& sharedValueContext)
: PlayerbotAIAware(botAI),
strategyContexts(sharedStrategyContext),
actionContexts(sharedActionContext),
triggerContexts(sharedTriggerContext),
valueContexts(sharedValueContext)
{
}
void AiObjectContext::BuildAllSharedContexts()
{
AiObjectContext::BuildSharedContexts();
PriestAiObjectContext::BuildSharedContexts();
MageAiObjectContext::BuildSharedContexts();
WarlockAiObjectContext::BuildSharedContexts();
WarriorAiObjectContext::BuildSharedContexts();
ShamanAiObjectContext::BuildSharedContexts();
PaladinAiObjectContext::BuildSharedContexts();
DruidAiObjectContext::BuildSharedContexts();
HunterAiObjectContext::BuildSharedContexts();
RogueAiObjectContext::BuildSharedContexts();
DKAiObjectContext::BuildSharedContexts();
}
void AiObjectContext::BuildSharedContexts()
{
BuildSharedStrategyContexts(sharedStrategyContexts);
BuildSharedActionContexts(sharedActionContexts);
BuildSharedTriggerContexts(sharedTriggerContexts);
BuildSharedValueContexts(sharedValueContexts);
}
void AiObjectContext::BuildSharedStrategyContexts(SharedNamedObjectContextList<Strategy>& strategyContexts)
{
strategyContexts.Add(new StrategyContext());
strategyContexts.Add(new MovementStrategyContext());
strategyContexts.Add(new AssistStrategyContext());
strategyContexts.Add(new QuestStrategyContext());
strategyContexts.Add(new DungeonStrategyContext());
strategyContexts.Add(new RaidStrategyContext());
}
void AiObjectContext::BuildSharedActionContexts(SharedNamedObjectContextList<Action>& actionContexts)
{
actionContexts.Add(new ActionContext());
actionContexts.Add(new ChatActionContext());
actionContexts.Add(new WorldPacketActionContext());
actionContexts.Add(new RaidAq20ActionContext());
actionContexts.Add(new RaidMcActionContext());
actionContexts.Add(new RaidBwlActionContext());
actionContexts.Add(new RaidKarazhanActionContext());
actionContexts.Add(new RaidMagtheridonActionContext());
actionContexts.Add(new RaidGruulsLairActionContext());
actionContexts.Add(new RaidOsActionContext());
actionContexts.Add(new RaidEoEActionContext());
actionContexts.Add(new RaidVoAActionContext());
actionContexts.Add(new RaidUlduarActionContext());
actionContexts.Add(new RaidOnyxiaActionContext());
actionContexts.Add(new RaidIccActionContext());
actionContexts.Add(new WotlkDungeonUKActionContext());
actionContexts.Add(new WotlkDungeonNexActionContext());
actionContexts.Add(new WotlkDungeonANActionContext());
actionContexts.Add(new WotlkDungeonOKActionContext());
actionContexts.Add(new WotlkDungeonDTKActionContext());
actionContexts.Add(new WotlkDungeonVHActionContext());
actionContexts.Add(new WotlkDungeonGDActionContext());
actionContexts.Add(new WotlkDungeonHoSActionContext());
actionContexts.Add(new WotlkDungeonHoLActionContext());
actionContexts.Add(new WotlkDungeonOccActionContext());
actionContexts.Add(new WotlkDungeonUPActionContext());
actionContexts.Add(new WotlkDungeonCoSActionContext());
actionContexts.Add(new WotlkDungeonFoSActionContext());
actionContexts.Add(new WotlkDungeonPoSActionContext());
actionContexts.Add(new WotlkDungeonToCActionContext());
}
void AiObjectContext::BuildSharedTriggerContexts(SharedNamedObjectContextList<Trigger>& triggerContexts)
{
triggerContexts.Add(new TriggerContext());
triggerContexts.Add(new ChatTriggerContext());
triggerContexts.Add(new WorldPacketTriggerContext());
triggerContexts.Add(new RaidAq20TriggerContext());
triggerContexts.Add(new RaidMcTriggerContext());
triggerContexts.Add(new RaidBwlTriggerContext());
triggerContexts.Add(new RaidKarazhanTriggerContext());
triggerContexts.Add(new RaidMagtheridonTriggerContext());
triggerContexts.Add(new RaidGruulsLairTriggerContext());
triggerContexts.Add(new RaidOsTriggerContext());
triggerContexts.Add(new RaidEoETriggerContext());
triggerContexts.Add(new RaidVoATriggerContext());
triggerContexts.Add(new RaidUlduarTriggerContext());
triggerContexts.Add(new RaidOnyxiaTriggerContext());
triggerContexts.Add(new RaidIccTriggerContext());
triggerContexts.Add(new WotlkDungeonUKTriggerContext());
triggerContexts.Add(new WotlkDungeonNexTriggerContext());
triggerContexts.Add(new WotlkDungeonANTriggerContext());
triggerContexts.Add(new WotlkDungeonOKTriggerContext());
triggerContexts.Add(new WotlkDungeonDTKTriggerContext());
triggerContexts.Add(new WotlkDungeonVHTriggerContext());
triggerContexts.Add(new WotlkDungeonGDTriggerContext());
triggerContexts.Add(new WotlkDungeonHoSTriggerContext());
triggerContexts.Add(new WotlkDungeonHoLTriggerContext());
triggerContexts.Add(new WotlkDungeonOccTriggerContext());
triggerContexts.Add(new WotlkDungeonUPTriggerContext());
triggerContexts.Add(new WotlkDungeonCoSTriggerContext());
triggerContexts.Add(new WotlkDungeonFoSTriggerContext());
triggerContexts.Add(new WotlkDungeonPoSTriggerContext());
triggerContexts.Add(new WotlkDungeonToCTriggerContext());
}
void AiObjectContext::BuildSharedValueContexts(SharedNamedObjectContextList<UntypedValue>& valueContexts)
{
valueContexts.Add(new ValueContext());
}
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 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();
}

View File

@@ -0,0 +1,95 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
* and/or modify it under version 3 of the License, or (at your option), any later version.
*/
#ifndef _PLAYERBOT_AIOBJECTCONTEXT_H
#define _PLAYERBOT_AIOBJECTCONTEXT_H
#include <sstream>
#include <string>
#include "Common.h"
#include "DynamicObject.h"
#include "NamedObjectContext.h"
#include "PlayerbotAIAware.h"
#include "Strategy.h"
#include "Trigger.h"
#include "Value.h"
class PlayerbotAI;
typedef Strategy* (*StrategyCreator)(PlayerbotAI* botAI);
typedef Action* (*ActionCreator)(PlayerbotAI* botAI);
typedef Trigger* (*TriggerCreator)(PlayerbotAI* botAI);
typedef UntypedValue* (*ValueCreator)(PlayerbotAI* botAI);
class AiObjectContext : public PlayerbotAIAware
{
public:
static BoolCalculatedValue* custom_glyphs(PlayerbotAI* ai); // Added for cutom glyphs
AiObjectContext(PlayerbotAI* botAI,
SharedNamedObjectContextList<Strategy>& sharedStrategyContext = sharedStrategyContexts,
SharedNamedObjectContextList<Action>& sharedActionContext = sharedActionContexts,
SharedNamedObjectContextList<Trigger>& sharedTriggerContext = sharedTriggerContexts,
SharedNamedObjectContextList<UntypedValue>& sharedValueContext = sharedValueContexts);
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();
std::vector<std::string> Save();
void Load(std::vector<std::string> data);
std::vector<std::string> performanceStack;
static void BuildAllSharedContexts();
static void BuildSharedContexts();
static void BuildSharedStrategyContexts(SharedNamedObjectContextList<Strategy>& strategyContexts);
static void BuildSharedActionContexts(SharedNamedObjectContextList<Action>& actionContexts);
static void BuildSharedTriggerContexts(SharedNamedObjectContextList<Trigger>& triggerContexts);
static void BuildSharedValueContexts(SharedNamedObjectContextList<UntypedValue>& valueContexts);
protected:
NamedObjectContextList<Strategy> strategyContexts;
NamedObjectContextList<Action> actionContexts;
NamedObjectContextList<Trigger> triggerContexts;
NamedObjectContextList<UntypedValue> valueContexts;
private:
static SharedNamedObjectContextList<Strategy> sharedStrategyContexts;
static SharedNamedObjectContextList<Action> sharedActionContexts;
static SharedNamedObjectContextList<Trigger> sharedTriggerContexts;
static SharedNamedObjectContextList<UntypedValue> sharedValueContexts;
};
#endif

View File

@@ -0,0 +1,669 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
* and/or modify it under version 3 of the License, or (at your option), any later version.
*/
#include "Engine.h"
#include "Action.h"
#include "Event.h"
#include "PerfMonitor.h"
#include "Playerbots.h"
#include "Queue.h"
#include "Strategy.h"
#include "Timer.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();
// for (std::map<std::string, Strategy*>::iterator i = strategies.begin(); i != strategies.end(); i++)
// {
// Strategy* strategy = i->second;
// if (strategy)
// {
// delete strategy;
// }
// }
strategies.clear();
}
void Engine::Reset()
{
strategyTypeMask = 0;
ActionNode* action = nullptr;
while ((action = queue.Pop()) != nullptr)
{
delete action;
}
for (TriggerNode* trigger : triggers)
{
delete trigger;
}
triggers.clear();
for (Multiplier* multiplier : multipliers)
{
delete multiplier;
}
multipliers.clear();
actionNodeFactories.creators.clear();
}
void Engine::Init()
{
Reset();
for (std::map<std::string, Strategy*>::iterator i = strategies.begin(); i != strategies.end(); i++)
{
Strategy* strategy = i->second;
strategyTypeMask |= strategy->GetType();
strategy->InitMultipliers(multipliers);
strategy->InitTriggers(triggers);
for (auto &iter : strategy->actionNodeFactories.creators)
{
actionNodeFactories.creators[iter.first] = iter.second;
}
}
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);
// Update triggers and push default actions
ProcessTriggers(minimal);
PushDefaultActions();
uint32 iterations = 0;
uint32 iterationsPerTick = queue.Size() * (minimal ? 2 : sPlayerbotAIConfig->iterationsPerTick);
while (++iterations <= iterationsPerTick)
{
basket = queue.Peek();
if (!basket)
break;
float relevance = basket->getRelevance(); // for reference
bool skipPrerequisites = basket->isSkipPrerequisites();
if (minimal && (relevance < 100))
continue;
Event event = basket->getEvent();
ActionNode* actionNode = queue.Pop(); // NOTE: Pop() deletes basket
Action* action = InitializeAction(actionNode);
if (!action)
{
LogAction("A:%s - UNKNOWN", actionNode->getName().c_str());
}
else if (action->isUseful())
{
// Apply multipliers early to avoid unnecessary iterations
for (Multiplier* multiplier : multipliers)
{
relevance *= multiplier->GetValue(action);
action->setRelevance(relevance);
if (relevance <= 0)
{
LogAction("Multiplier %s made action %s useless", multiplier->getName().c_str(), action->getName().c_str());
break;
}
}
if (action->isPossible() && relevance > 0)
{
if (!skipPrerequisites)
{
LogAction("A:%s - PREREQ", action->getName().c_str());
if (MultiplyAndPush(actionNode->getPrerequisites(), relevance + 0.002f, false, event, "prereq"))
{
PushAgain(actionNode, relevance + 0.001f, event);
continue;
}
}
PerfMonitorOperation* pmo = sPerfMonitor->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(), relevance, false, event, "cont");
lastRelevance = relevance;
delete actionNode; // Safe memory management
break;
}
else
{
LogAction("A:%s - FAILED", action->getName().c_str());
MultiplyAndPush(actionNode->getAlternatives(), relevance + 0.003f, false, event, "alt");
}
}
else
{
LogAction("A:%s - IMPOSSIBLE", action->getName().c_str());
MultiplyAndPush(actionNode->getAlternatives(), relevance + 0.003f, false, event, "alt");
}
}
else
{
LogAction("A:%s - USELESS", action->getName().c_str());
lastRelevance = relevance;
}
delete actionNode; // Always delete after processing the action node
}
if (time(nullptr) - currentTime > 1)
{
LogAction("Execution time exceeded 1 second");
}
if (!actionExecuted)
LogAction("no actions executed");
queue.RemoveExpired();
return actionExecuted;
}
ActionNode* Engine::CreateActionNode(std::string const name)
{
ActionNode* node = actionNodeFactories.GetContextObject(name, botAI);
if (node)
return node;
return new ActionNode(name,
/*P*/ {},
/*A*/ {},
/*C*/ {});
}
bool Engine::MultiplyAndPush(
std::vector<NextAction> actions,
float forceRelevance,
bool skipPrerequisites,
Event event,
char const* pushType
)
{
bool pushed = false;
for (NextAction nextAction : actions)
{
ActionNode* action = this->CreateActionNode(nextAction.getName());
this->InitializeAction(action);
float k = nextAction.getRelevance();
if (forceRelevance > 0.0f)
{
k = forceRelevance;
}
if (k > 0)
{
this->LogAction("PUSH:%s - %f (%s)", action->getName().c_str(), k, pushType);
queue.Push(new ActionBasket(action, k, skipPrerequisites, event));
pushed = true;
continue;
}
delete action;
}
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, bool init)
{
removeStrategy(name, init);
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, init);
LogAction("S:+%s", strategy->getName().c_str());
strategies[strategy->getName()] = strategy;
}
if (init)
Init();
}
void Engine::addStrategies(std::string first, ...)
{
addStrategy(first, false);
va_list vl;
va_start(vl, first);
const char* cur;
do
{
cur = va_arg(vl, const char*);
if (cur)
addStrategy(cur, false);
} while (cur);
Init();
va_end(vl);
}
void Engine::addStrategiesNoInit(std::string first, ...)
{
addStrategy(first, false);
va_list vl;
va_start(vl, first);
const char* cur;
do
{
cur = va_arg(vl, const char*);
if (cur)
addStrategy(cur, false);
} while (cur);
va_end(vl);
}
bool Engine::removeStrategy(std::string const name, bool init)
{
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);
if (init)
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::unordered_map<Trigger*, Event> fires;
uint32 now = getMSTime();
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 (fires.find(trigger) != fires.end())
continue;
if (testMode || trigger->needCheck(now))
{
if (minimal && node->getFirstRelevance() < 100)
continue;
PerfMonitorOperation* pmo =
sPerfMonitor->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();
if (fires.find(trigger) == fires.end())
continue;
Event event = fires[trigger];
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 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 result;
}
void Engine::PushAgain(ActionNode* actionNode, float relevance, Event event)
{
std::vector<NextAction> nextAction = { NextAction(actionNode->getName(), relevance) };
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 (action == nullptr)
{
LOG_ERROR("playerbots", "Action is nullptr");
return actionExecuted;
}
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, ...)
{
Player* bot = botAI->GetBot();
if (sPlayerbotAIConfig->logInGroupOnly && (!bot->GetGroup() || !botAI->HasRealPlayerMaster()) && !testMode)
return;
char buf[1024];
va_list ap;
va_start(ap, format);
vsnprintf(buf, sizeof(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, "'%s'", buf);
fprintf(file, "\n");
fclose(file);
}
else
{
LOG_DEBUG("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() || !botAI->HasRealPlayerMaster()))
return;
std::string const text = botAI->GetAiObjectContext()->FormatValues();
LOG_DEBUG("playerbots", "Values for {}: {}", bot->GetName().c_str(), text.c_str());
}

120
src/Core/Ai/Engine/Engine.h Normal file
View File

@@ -0,0 +1,120 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
* and/or modify it under version 3 of the License, or (at your option), any later version.
*/
#ifndef _PLAYERBOT_ENGINE_H
#define _PLAYERBOT_ENGINE_H
#include <map>
#include "Multiplier.h"
#include "PlayerbotAIAware.h"
#include "Queue.h"
#include "Strategy.h"
#include "Trigger.h"
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, bool init = true);
void addStrategies(std::string first, ...);
void addStrategiesNoInit(std::string first, ...);
bool removeStrategy(std::string const name, bool init = true);
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); }
bool HasStrategyType(StrategyType type) { return strategyTypeMask & type; }
virtual ~Engine(void);
bool testMode;
private:
bool MultiplyAndPush(std::vector<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;
uint32 strategyTypeMask;
NamedObjectFactoryList<ActionNode> actionNodeFactories;
};
#endif

View File

@@ -0,0 +1,67 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
* and/or modify it under version 3 of the License, or (at your option), any later version.
*/
#include "ExternalEventHelper.h"
#include "ChatHelper.h"
#include "Playerbots.h"
#include "Trigger.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,30 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
* and/or modify it under version 3 of the License, or (at your option), any later version.
*/
#ifndef _PLAYERBOT_EXTERNALEVENTHELPER_H
#define _PLAYERBOT_EXTERNALEVENTHELPER_H
#include <map>
#include "Common.h"
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,23 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
* and/or modify it under version 3 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([[maybe_unused]] Action* action) { return 1.0f; }
};
#endif

View File

@@ -0,0 +1,52 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
* and/or modify it under version 3 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(const std::vector<std::string>& qualifiers, const std::string& separator, const std::string_view brackets)
{
std::stringstream out;
for (uint8 i = 0; i < qualifiers.size(); ++i)
{
const std::string& qualifier = qualifiers[i];
if (i == qualifiers.size() - 1)
{
out << qualifier;
}
else
{
out << qualifier << separator;
}
}
if (brackets.empty())
{
return out.str();
}
else
{
return brackets[0] + out.str() + brackets[1];
}
}
std::vector<std::string> Qualified::getMultiQualifiers(const std::string& qualifier1)
{
std::istringstream iss(qualifier1);
return {std::istream_iterator<std::string>{iss}, std::istream_iterator<std::string>{}};
}
int32 Qualified::getMultiQualifier(const std::string& qualifier1, uint32 pos)
{
return std::stoi(getMultiQualifiers(qualifier1)[pos]);
}

View File

@@ -0,0 +1,310 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
* and/or modify it under version 3 of the License, or (at your option), any later version.
*/
#ifndef _PLAYERBOT_NAMEDOBJECTCONEXT_H
#define _PLAYERBOT_NAMEDOBJECTCONEXT_H
#include <list>
#include <set>
#include <unordered_map>
#include <unordered_set>
#include <vector>
#include <functional>
#include "Common.h"
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(const std::vector<std::string>& qualifiers, const std::string& separator,
const std::string_view brackets = "{}");
static std::vector<std::string> getMultiQualifiers(const std::string& qualifier1);
static int32 getMultiQualifier(const std::string& qualifier1, uint32 pos);
protected:
std::string qualifier;
};
template <class T>
class NamedObjectFactory
{
public:
using ObjectCreator = std::function<T*(PlayerbotAI* ai)>;
std::unordered_map<std::string, ObjectCreator> creators;
public:
virtual ~NamedObjectFactory() = default;
virtual 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;
ObjectCreator& creator = creators[name];
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::unordered_map<std::string, ObjectCreator>::const_iterator it = creators.begin();
it != creators.end(); it++)
keys.insert(it->first);
return 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(); }
virtual T* create(std::string name, PlayerbotAI* botAI) override
{
if (created.find(name) == created.end())
return created[name] = NamedObjectFactory<T>::create(name, botAI);
return created[name];
}
void Clear()
{
for (typename std::unordered_map<std::string, T*>::const_iterator i = created.begin(); i != created.end(); i++)
{
if (i->second)
delete i->second;
}
created.clear();
}
bool IsShared() { return shared; }
bool IsSupportsSiblings() { return supportsSiblings; }
std::set<std::string> GetCreated()
{
std::set<std::string> keys;
for (typename std::unordered_map<std::string, T*>::iterator it = created.begin(); it != created.end(); it++)
keys.insert(it->first);
return keys;
}
protected:
std::unordered_map<std::string, T*> created;
bool shared;
bool supportsSiblings;
};
template <class T>
class SharedNamedObjectContextList
{
public:
using ObjectCreator = std::function<T*(PlayerbotAI* ai)>;
std::unordered_map<std::string, ObjectCreator> creators;
std::vector<NamedObjectContext<T>*> contexts;
~SharedNamedObjectContextList()
{
for (typename std::vector<NamedObjectContext<T>*>::const_iterator i = contexts.begin(); i != contexts.end(); i++)
delete *i;
}
void Add(NamedObjectContext<T>* context)
{
contexts.push_back(context);
for (auto const& iter : context->creators)
creators[iter.first] = iter.second;
}
};
template <class T>
class NamedObjectContextList
{
public:
using ObjectCreator = std::function<T*(PlayerbotAI* ai)>;
const std::unordered_map<std::string, ObjectCreator>& creators;
const std::vector<NamedObjectContext<T>*>& contexts;
std::unordered_map<std::string, T*> created;
NamedObjectContextList(const SharedNamedObjectContextList<T>& shared)
: creators(shared.creators), contexts(shared.contexts)
{
}
~NamedObjectContextList()
{
for (typename std::unordered_map<std::string, T*>::const_iterator i = created.begin(); i != created.end(); i++)
{
if (i->second)
delete i->second;
}
created.clear();
}
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;
const ObjectCreator& creator = creators.at(name);
T* object = creator(botAI);
Qualified* q = dynamic_cast<Qualified*>(object);
if (q && found != std::string::npos)
q->Qualify(qualifier);
return object;
}
T* GetContextObject(const std::string& name, PlayerbotAI* botAI)
{
if (created.find(name) == created.end())
{
if (T* object = create(name, botAI))
return created[name] = object;
}
return created[name];
}
std::set<std::string> GetSiblings(const std::string& name)
{
for (auto 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::set<std::string>();
}
std::set<std::string> supports()
{
std::set<std::string> result;
for (auto i = contexts.begin(); i != contexts.end(); i++)
{
std::set<std::string> supported = (*i)->supports();
for (std::set<std::string>::const_iterator j = supported.begin(); j != supported.end(); ++j)
result.insert(*j);
}
return result;
}
std::set<std::string> GetCreated()
{
std::set<std::string> result;
for (typename std::unordered_map<std::string, T*>::const_iterator i = created.begin(); i != created.end(); i++)
result.insert(i->first);
return result;
}
};
template <class T>
class NamedObjectFactoryList
{
public:
using ObjectCreator = std::function<T*(PlayerbotAI* ai)>;
std::vector<NamedObjectFactory<T>*> factories;
std::unordered_map<std::string, ObjectCreator> creators;
virtual ~NamedObjectFactoryList()
{
for (typename std::vector<NamedObjectFactory<T>*>::const_iterator i = factories.begin(); i != factories.end(); i++)
delete *i;
}
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;
const ObjectCreator& creator = creators[name];
T* object = creator(botAI);
Qualified* q = dynamic_cast<Qualified*>(object);
if (q && found != std::string::npos)
q->Qualify(qualifier);
return object;
}
void Add(NamedObjectFactory<T>* context)
{
factories.push_back(context);
for (auto const& iter : context->creators)
creators[iter.first] = iter.second;
}
T* GetContextObject(const std::string& name, PlayerbotAI* botAI)
{
if (T* object = create(name, botAI))
return object;
return nullptr;
}
};
#endif

View File

@@ -0,0 +1,54 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
* and/or modify it under version 3 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");
allowedActions.push_back("lfg");
}
if (allowedParts.empty())
{
allowedParts.push_back("follow");
allowedParts.push_back("move from group");
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,28 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
* and/or modify it under version 3 of the License, or (at your option), any later version.
*/
#ifndef _PLAYERBOT_PASSIVEMULTIPLIER_H
#define _PLAYERBOT_PASSIVEMULTIPLIER_H
#include <vector>
#include "Multiplier.h"
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

View File

@@ -0,0 +1,20 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
* and/or modify it under version 3 of the License, or (at your option), any later version.
*/
#ifndef _PLAYERBOT_PLAYERbotAIAWARE_H
#define _PLAYERBOT_PLAYERbotAIAWARE_H
class PlayerbotAI;
class PlayerbotAIAware
{
public:
PlayerbotAIAware(PlayerbotAI* botAI) : botAI(botAI) {}
protected:
PlayerbotAI* botAI;
};
#endif

View File

@@ -0,0 +1,60 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
* and/or modify it under version 3 of the License, or (at your option), any later version.
*/
#include "PlayerbotAIBase.h"
#include "Playerbots.h"
PlayerbotAIBase::PlayerbotAIBase(bool isBotAI) : nextAICheckDelay(0), _isBotAI(isBotAI) {}
void PlayerbotAIBase::UpdateAI(uint32 elapsed, bool minimal)
{
if (totalPmo)
totalPmo->finish();
totalPmo = sPerfMonitor->start(PERF_MON_TOTAL, "PlayerbotAIBase::FullTick");
if (nextAICheckDelay > elapsed)
nextAICheckDelay -= elapsed;
else
nextAICheckDelay = 0;
if (!CanUpdateAI())
return;
UpdateAIInternal(elapsed, minimal);
YieldThread();
}
void PlayerbotAIBase::SetNextCheckDelay(uint32 const delay)
{
// if (nextAICheckDelay < delay)
// LOG_DEBUG("playerbots", "Setting lesser delay {} -> {}", nextAICheckDelay, delay);
nextAICheckDelay = delay;
// if (nextAICheckDelay > sPlayerbotAIConfig->globalCoolDown)
// LOG_DEBUG("playerbots", "std::set next check delay: {}", nextAICheckDelay);
}
void PlayerbotAIBase::IncreaseNextCheckDelay(uint32 delay)
{
nextAICheckDelay += delay;
// if (nextAICheckDelay > sPlayerbotAIConfig->globalCoolDown)
// LOG_DEBUG("playerbots", "increase next check delay: {}", nextAICheckDelay);
}
bool PlayerbotAIBase::CanUpdateAI() { return nextAICheckDelay == 0; }
void PlayerbotAIBase::YieldThread(uint32 delay)
{
if (nextAICheckDelay < delay)
nextAICheckDelay = delay;
}
bool PlayerbotAIBase::IsActive() { return nextAICheckDelay < sPlayerbotAIConfig->maxWaitForMove; }
bool PlayerbotAIBase::IsBotAI() const { return _isBotAI; }

View File

@@ -0,0 +1,34 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
* and/or modify it under version 3 of the License, or (at your option), any later version.
*/
#ifndef _PLAYERBOT_PLAYERBOTAIBASE_H
#define _PLAYERBOT_PLAYERBOTAIBASE_H
#include "Define.h"
#include "PlayerbotAIConfig.h"
class PlayerbotAIBase
{
public:
PlayerbotAIBase(bool isBotAI);
bool CanUpdateAI();
void SetNextCheckDelay(uint32 const delay);
void IncreaseNextCheckDelay(uint32 delay);
void YieldThread(uint32 delay = sPlayerbotAIConfig->reactDelay);
virtual void UpdateAI(uint32 elapsed, bool minimal = false);
virtual void UpdateAIInternal(uint32 elapsed, bool minimal = false) = 0;
bool IsActive();
bool IsBotAI() const;
protected:
uint32 nextAICheckDelay;
class PerfMonitorOperation* totalPmo = nullptr;
private:
bool _isBotAI;
};
#endif

View File

@@ -0,0 +1,116 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
* and/or modify it under version 3 of the License, or (at your option), any later version.
*/
#include "CustomStrategy.h"
#include <regex>
#include <stdexcept>
#include "Playerbots.h"
std::map<std::string, std::string> CustomStrategy::actionLinesCache;
NextAction toNextAction(std::string const action)
{
std::vector<std::string> tokens = split(action, '!');
if (tokens[0].empty())
throw std::invalid_argument("Invalid action");
if (tokens.size() == 2)
return NextAction(tokens[0], atof(tokens[1].c_str()));
if (tokens.size() == 1)
return NextAction(tokens[0], ACTION_NORMAL);
LOG_ERROR("playerbots", "Invalid action {}", action.c_str());
throw std::invalid_argument("Invalid action");
}
std::vector<NextAction> toNextActionArray(const std::string actions)
{
const std::vector<std::string> tokens = split(actions, ',');
std::vector<NextAction> res = {};
for (const std::string token : tokens)
{
res.push_back(toNextAction(token));
}
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[1].Get<std::string>();
actionLines.push_back(action);
} while (result->NextRow());
}
}
void CustomStrategy::Reset()
{
actionLines.clear();
actionLinesCache[qualifier].clear();
}

View File

@@ -0,0 +1,31 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
* and/or modify it under version 3 of the License, or (at your option), any later version.
*/
#ifndef _PLAYERBOT_CUSTOMSTRATEGY_H
#define _PLAYERBOT_CUSTOMSTRATEGY_H
#include <map>
#include "Strategy.h"
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

View File

@@ -0,0 +1,145 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
* and/or modify it under version 3 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([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode(
"melee",
/*P*/ {},
/*A*/ {},
/*C*/ {}
);
}
static ActionNode* healthstone([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode(
"healthstone",
/*P*/ {},
/*A*/ { NextAction("healing potion") },
/*C*/ {}
);
}
static ActionNode* follow_master_random([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode(
"be near",
/*P*/ {},
/*A*/ { NextAction("follow") },
/*C*/ {}
);
}
static ActionNode* attack_anything([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode(
"attack anything",
/*P*/ {},
/*A*/ {},
/*C*/ {}
);
}
static ActionNode* move_random([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode(
"move random",
/*P*/ {},
/*A*/ { NextAction("stay line") },
/*C*/ {}
);
}
static ActionNode* move_to_loot([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode(
"move to loot",
/*P*/ {},
/*A*/ {},
/*C*/ {}
);
}
static ActionNode* food([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode(
"food",
/*P*/ {},
/*A*/ {},
/*C*/ {}
);
}
static ActionNode* drink([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode(
"drink",
/*P*/ {},
/*A*/ {},
/*C*/ {}
);
}
static ActionNode* mana_potion([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode(
"mana potion",
/*P*/ {},
/*A*/ {},
/*C*/ {}
);
}
static ActionNode* healing_potion([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode(
"healing potion",
/*P*/ {},
/*A*/ { NextAction("food") },
/*C*/ {}
);
}
static ActionNode* flee([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode(
"flee",
/*P*/ {},
/*A*/ {},
/*C*/ {}
);
}
};
Strategy::Strategy(PlayerbotAI* botAI) : PlayerbotAIAware(botAI)
{
actionNodeFactories.Add(new ActionNodeFactoryInternal());
}
ActionNode* Strategy::GetAction(std::string const name) { return actionNodeFactories.GetContextObject(name, botAI); }

View File

@@ -0,0 +1,76 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
* and/or modify it under version 3 of the License, or (at your option), any later version.
*/
#ifndef _PLAYERBOT_STRATEGY_H
#define _PLAYERBOT_STRATEGY_H
#include "Action.h"
#include "Multiplier.h"
#include "NamedObjectContext.h"
#include "PlayerbotAIAware.h"
#include "Trigger.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_DEFAULT = 5,
// ACTION_NORMAL = 10,
// ACTION_HIGH = 20,
// ACTION_MOVE = 30,
// ACTION_INTERRUPT = 40,
// ACTION_DISPEL = 50,
// ACTION_RAID = 60,
// ACTION_LIGHT_HEAL = 10,
// ACTION_MEDIUM_HEAL = 20,
// ACTION_CRITICAL_HEAL = 30,
// ACTION_EMERGENCY = 90
// };
static float ACTION_IDLE = 0.0f;
static float ACTION_BG = 1.0f;
static float ACTION_DEFAULT = 5.0f;
static float ACTION_NORMAL = 10.0f;
static float ACTION_HIGH = 20.0f;
static float ACTION_MOVE = 30.0f;
static float ACTION_INTERRUPT = 40.0f;
static float ACTION_DISPEL = 50.0f;
static float ACTION_RAID = 60.0f;
static float ACTION_LIGHT_HEAL = 10.0f;
static float ACTION_MEDIUM_HEAL = 20.0f;
static float ACTION_CRITICAL_HEAL = 30.0f;
static float ACTION_EMERGENCY = 90.0f;
class Strategy : public PlayerbotAIAware
{
public:
Strategy(PlayerbotAI* botAI);
virtual ~Strategy() {}
virtual std::vector<NextAction> getDefaultActions() { return {}; }
virtual void InitTriggers([[maybe_unused]] std::vector<TriggerNode*>& triggers) {}
virtual void InitMultipliers([[maybe_unused]] 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() {}
public:
NamedObjectFactoryList<ActionNode> actionNodeFactories;
};
#endif

View File

@@ -0,0 +1,47 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
* and/or modify it under version 3 of the License, or (at your option), any later version.
*/
#include "Trigger.h"
#include "Event.h"
#include "Playerbots.h"
#include "Timer.h"
Trigger::Trigger(PlayerbotAI* botAI, std::string const name, int32 checkInterval)
: AiNamedObject(botAI, name),
checkInterval(checkInterval == 1 ? 1 : (checkInterval < 100 ? checkInterval * 1000 : checkInterval)),
lastCheckTime(0)
{
}
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(uint32 now)
{
if (checkInterval < 2)
return true;
if (!lastCheckTime || now - lastCheckTime >= checkInterval)
{
lastCheckTime = now;
return true;
}
return false;
}

View File

@@ -0,0 +1,84 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
* and/or modify it under version 3 of the License, or (at your option), any later version.
*/
#pragma once
#include "Action.h"
#include "Common.h"
class PlayerbotAI;
class Unit;
class Trigger : public AiNamedObject
{
public:
Trigger(
PlayerbotAI* botAI,
const std::string name = "trigger",
int32_t checkInterval = 1
);
virtual ~Trigger() {}
virtual Event Check();
virtual void ExternalEvent([[maybe_unused]] std::string const param, [[maybe_unused]] Player* owner = nullptr) {}
virtual void ExternalEvent([[maybe_unused]] WorldPacket& packet, [[maybe_unused]] Player* owner = nullptr) {}
virtual bool IsActive() { return false; }
virtual std::vector<NextAction> getHandlers() { return {}; }
void Update() {}
virtual void Reset() {}
virtual Unit* GetTarget();
virtual Value<Unit*>* GetTargetValue();
virtual std::string const GetTargetName() { return "self target"; }
bool needCheck(uint32 now);
protected:
int32_t checkInterval;
uint32_t lastCheckTime;
};
class TriggerNode
{
public:
TriggerNode(
const std::string& name,
std::vector<NextAction> handlers = {}
) :
trigger(nullptr),
handlers(std::move(handlers)),
name(name)
{}
Trigger* getTrigger() { return trigger; }
void setTrigger(Trigger* trigger) { this->trigger = trigger; }
const std::string getName() { return name; }
std::vector<NextAction> getHandlers()
{
std::vector<NextAction> result = this->handlers;
if (trigger != nullptr)
{
std::vector<NextAction> extra = trigger->getHandlers();
result.insert(result.end(), extra.begin(), extra.end());
}
return result;
}
float getFirstRelevance()
{
if (this->handlers.size() > 0)
return this->handlers[0].getRelevance();
return -1;
}
private:
Trigger* trigger;
std::vector<NextAction> handlers;
const std::string name;
};

View File

@@ -0,0 +1,157 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
* and/or modify it under version 3 of the License, or (at your option), any later version.
*/
#include "Value.h"
#include "PerfMonitor.h"
#include "Playerbots.h"
#include "Timer.h"
UnitCalculatedValue::UnitCalculatedValue(PlayerbotAI* botAI, std::string const name, int32 checkInterval)
: CalculatedValue<Unit*>(botAI, name, checkInterval)
{
}
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 = getMSTime() - checkInterval / 2;
}
std::string const CDPairCalculatedValue::Format()
{
CreatureData const* creatureData = Calculate();
if (creatureData)
{
CreatureTemplate const* bmTemplate = sObjectMgr->GetCreatureTemplate(creatureData->id1);
return bmTemplate ? bmTemplate->Name : "<none>";
}
return "<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)
{
}
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();
}
Unit* UnitCalculatedValue::Get()
{
if (checkInterval < 2)
{
PerfMonitorOperation* pmo = sPerfMonitor->start(
PERF_MON_VALUE, this->getName(), this->context ? &this->context->performanceStack : nullptr);
value = Calculate();
if (pmo)
pmo->finish();
}
else
{
time_t now = getMSTime();
if (!lastCheckTime || now - lastCheckTime >= checkInterval)
{
lastCheckTime = now;
PerfMonitorOperation* pmo = sPerfMonitor->start(
PERF_MON_VALUE, this->getName(), this->context ? &this->context->performanceStack : nullptr);
value = Calculate();
if (pmo)
pmo->finish();
}
}
// Prevent crashing by InWorld check
if (value && value->IsInWorld())
return value;
return nullptr;
}
Unit* UnitManualSetValue::Get()
{
// Prevent crashing by InWorld check
if (value && value->IsInWorld())
return value;
return nullptr;
}

View File

@@ -0,0 +1,419 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
* and/or modify it under version 3 of the License, or (at your option), any later version.
*/
#ifndef _PLAYERBOT_VALUE_H
#define _PLAYERBOT_VALUE_H
#include <time.h>
#include "AiObject.h"
#include "ObjectGuid.h"
#include "PerfMonitor.h"
#include "Timer.h"
#include "Unit.h"
class PlayerbotAI;
class Unit;
class FleeInfo
{
public:
Position fromPos;
float radius;
float angle;
uint32 timestamp;
int GetAngleRangeIndex() { return (angle + 2 * M_PI) / (M_PI / 2); } // [0, 7)
};
struct CreatureData;
class UntypedValue : public AiNamedObject
{
public:
UntypedValue(PlayerbotAI* botAI, std::string const name) : AiNamedObject(botAI, name) {}
virtual ~UntypedValue() {}
virtual void Update() {}
virtual void Reset() {}
virtual std::string const Format() { return "?"; }
virtual std::string const Save() { return "?"; }
virtual bool Load([[maybe_unused]] std::string const value) { return false; }
};
template <class T>
class Value
{
public:
virtual ~Value() {}
virtual T Get() = 0;
virtual T LazyGet() = 0;
virtual T& RefGet() = 0;
virtual void Reset() {}
virtual void Set(T value) = 0;
operator T() { return Get(); }
};
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 == 1 ? 1 : (checkInterval < 100 ? checkInterval * 1000 : checkInterval)) /*turn s -> ms?*/,
lastCheckTime(0)
{
}
virtual ~CalculatedValue() {}
T Get() override
{
if (checkInterval < 2)
{
// PerfMonitorOperation* pmo = sPerfMonitor->start(PERF_MON_VALUE, this->getName(),
// this->context ? &this->context->performanceStack : nullptr);
value = Calculate();
// if (pmo)
// pmo->finish();
}
else
{
time_t now = getMSTime();
if (!lastCheckTime || now - lastCheckTime >= checkInterval)
{
lastCheckTime = now;
// PerfMonitorOperation* pmo = sPerfMonitor->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;
}
T& RefGet() override
{
if (checkInterval < 2)
{
// PerfMonitorOperation* pmo = sPerfMonitor->start(PERF_MON_VALUE, this->getName(),
// this->context ? &this->context->performanceStack : nullptr);
value = Calculate();
// if (pmo)
// pmo->finish();
}
else
{
time_t now = getMSTime();
if (!lastCheckTime || now - lastCheckTime >= checkInterval)
{
lastCheckTime = now;
// PerfMonitorOperation* pmo = sPerfMonitor->start(PERF_MON_VALUE, this->getName(),
// this->context ? &this->context->performanceStack : nullptr);
value = Calculate();
// if (pmo)
// pmo->finish();
}
}
return value;
}
void Set(T val) override { value = val; }
void Update() override {}
void Reset() override { lastCheckTime = 0; }
protected:
virtual T Calculate() = 0;
uint32 checkInterval;
uint32 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;
PerfMonitorOperation* pmo = sPerfMonitor->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([[maybe_unused]] 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;
Unit* Get() 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; }
T& RefGet() 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;
Unit* Get() override;
};
class DisperseDistanceValue : public ManualSetValue<float>
{
public:
DisperseDistanceValue(PlayerbotAI* botAI, float defaultValue = -1.0f, std::string const name = "disperse distance")
: ManualSetValue<float>(botAI, defaultValue, name)
{
}
};
class LastFleeAngleValue : public ManualSetValue<float>
{
public:
LastFleeAngleValue(PlayerbotAI* botAI, float defaultValue = 0.0f, std::string const name = "last flee angle")
: ManualSetValue<float>(botAI, defaultValue, name)
{
}
};
class LastFleeTimestampValue : public ManualSetValue<uint32>
{
public:
LastFleeTimestampValue(PlayerbotAI* botAI, uint32 defaultValue = 0, std::string const name = "last flee timestamp")
: ManualSetValue<uint32>(botAI, defaultValue, name)
{
}
};
class RecentlyFleeInfo : public ManualSetValue<std::list<FleeInfo>&>
{
public:
RecentlyFleeInfo(PlayerbotAI* botAI, std::string const name = "recently flee info")
: ManualSetValue<std::list<FleeInfo>&>(botAI, data, name)
{
}
private:
std::list<FleeInfo> data = {};
};
#endif

View File

@@ -0,0 +1,27 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
* and/or modify it under version 3 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;
}

View File

@@ -0,0 +1,45 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
* and/or modify it under version 3 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