mirror of
https://github.com/mod-playerbots/mod-playerbots
synced 2025-11-29 15:58:20 +08:00
Directory reorganization
This commit is contained in:
@@ -1,104 +0,0 @@
|
||||
/*
|
||||
* 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"
|
||||
#include "Timer.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(getMSTime())
|
||||
{
|
||||
}
|
||||
|
||||
bool ActionBasket::isExpired(uint32 msecs) { return getMSTime() - created >= msecs; }
|
||||
@@ -1,134 +0,0 @@
|
||||
/*
|
||||
* 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)
|
||||
: 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 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")
|
||||
: 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 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)
|
||||
: name(name), action(nullptr), continuers(continuers), alternatives(alternatives), prerequisites(prerequisites)
|
||||
{
|
||||
} // reorder arguments - whipowill
|
||||
|
||||
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(uint32 msecs);
|
||||
|
||||
private:
|
||||
ActionNode* action;
|
||||
float relevance;
|
||||
bool skipPrerequisites;
|
||||
Event event;
|
||||
uint32 created;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -17,28 +17,28 @@
|
||||
#include "ValueContext.h"
|
||||
#include "WorldPacketActionContext.h"
|
||||
#include "WorldPacketTriggerContext.h"
|
||||
#include "raids/RaidStrategyContext.h"
|
||||
#include "raids/blackwinglair/RaidBwlActionContext.h"
|
||||
#include "raids/blackwinglair/RaidBwlTriggerContext.h"
|
||||
#include "raids/naxxramas/RaidNaxxActionContext.h"
|
||||
#include "raids/naxxramas/RaidNaxxTriggerContext.h"
|
||||
#include "raids/icecrown/RaidIccActionContext.h"
|
||||
#include "raids/icecrown/RaidIccTriggerContext.h"
|
||||
#include "raids/obsidiansanctum/RaidOsActionContext.h"
|
||||
#include "raids/obsidiansanctum/RaidOsTriggerContext.h"
|
||||
#include "raids/eyeofeternity/RaidEoEActionContext.h"
|
||||
#include "raids/vaultofarchavon/RaidVoATriggerContext.h"
|
||||
#include "raids/onyxia/RaidOnyxiaActionContext.h"
|
||||
#include "raids/onyxia/RaidOnyxiaTriggerContext.h"
|
||||
#include "raids/vaultofarchavon/RaidVoAActionContext.h"
|
||||
#include "raids/eyeofeternity/RaidEoETriggerContext.h"
|
||||
#include "raids/moltencore/RaidMcActionContext.h"
|
||||
#include "raids/moltencore/RaidMcTriggerContext.h"
|
||||
#include "raids/aq20/RaidAq20ActionContext.h"
|
||||
#include "raids/aq20/RaidAq20TriggerContext.h"
|
||||
#include "dungeons/DungeonStrategyContext.h"
|
||||
#include "dungeons/wotlk/WotlkDungeonActionContext.h"
|
||||
#include "dungeons/wotlk/WotlkDungeonTriggerContext.h"
|
||||
#include "RaidStrategyContext.h"
|
||||
#include "RaidBwlActionContext.h"
|
||||
#include "RaidBwlTriggerContext.h"
|
||||
#include "RaidNaxxActionContext.h"
|
||||
#include "RaidNaxxTriggerContext.h"
|
||||
#include "RaidIccActionContext.h"
|
||||
#include "RaidIccTriggerContext.h"
|
||||
#include "RaidOsActionContext.h"
|
||||
#include "RaidOsTriggerContext.h"
|
||||
#include "RaidEoEActionContext.h"
|
||||
#include "RaidVoATriggerContext.h"
|
||||
#include "RaidOnyxiaActionContext.h"
|
||||
#include "RaidOnyxiaTriggerContext.h"
|
||||
#include "RaidVoAActionContext.h"
|
||||
#include "RaidEoETriggerContext.h"
|
||||
#include "RaidMcActionContext.h"
|
||||
#include "RaidMcTriggerContext.h"
|
||||
#include "RaidAq20ActionContext.h"
|
||||
#include "RaidAq20TriggerContext.h"
|
||||
#include "DungeonStrategyContext.h"
|
||||
#include "WotlkDungeonActionContext.h"
|
||||
#include "WotlkDungeonTriggerContext.h"
|
||||
|
||||
AiObjectContext::AiObjectContext(PlayerbotAI* botAI) : PlayerbotAIAware(botAI)
|
||||
{
|
||||
|
||||
@@ -1,662 +0,0 @@
|
||||
/*
|
||||
* 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 "PerformanceMonitor.h"
|
||||
#include "Playerbots.h"
|
||||
#include "Queue.h"
|
||||
#include "Strategy.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();
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
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);
|
||||
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
|
||||
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(), 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)
|
||||
{
|
||||
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, 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;
|
||||
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())
|
||||
{
|
||||
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();
|
||||
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)
|
||||
{
|
||||
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, ...)
|
||||
{
|
||||
Player* bot = botAI->GetBot();
|
||||
if (sPlayerbotAIConfig->logInGroupOnly && (!bot->GetGroup() || !botAI->HasRealPlayerMaster()) && !testMode)
|
||||
return;
|
||||
|
||||
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, "'%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());
|
||||
}
|
||||
@@ -1,119 +0,0 @@
|
||||
/*
|
||||
* 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 <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(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;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,27 +0,0 @@
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
/*
|
||||
* 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
|
||||
@@ -1,67 +0,0 @@
|
||||
/*
|
||||
* 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 "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;
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
/*
|
||||
* 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 <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
|
||||
@@ -1,95 +0,0 @@
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
|
||||
bool FindUsableNamedItemVisitor::Accept(ItemTemplate const* proto)
|
||||
{
|
||||
return proto && !proto->Name1.empty() && strstri(proto->Name1.c_str(), name.c_str());
|
||||
}
|
||||
@@ -1,418 +0,0 @@
|
||||
/*
|
||||
* 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(), itemClass(itemClass), itemSubClass(itemSubClass), count(count)
|
||||
{
|
||||
} // reorder args - whipowill
|
||||
|
||||
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([[maybe_unused]] 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;
|
||||
};
|
||||
|
||||
class FindUsableNamedItemVisitor : public FindUsableItemVisitor
|
||||
{
|
||||
public:
|
||||
FindUsableNamedItemVisitor(Player* bot, std::string name) : FindUsableItemVisitor(bot), name(name) {}
|
||||
|
||||
bool Accept(ItemTemplate const* proto) override;
|
||||
|
||||
private:
|
||||
std::string name;
|
||||
};
|
||||
#endif
|
||||
@@ -1,23 +0,0 @@
|
||||
/*
|
||||
* 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([[maybe_unused]] Action* action) { return 1.0f; }
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,52 +0,0 @@
|
||||
/*
|
||||
* 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, 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(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]);
|
||||
}
|
||||
@@ -1,272 +0,0 @@
|
||||
/*
|
||||
* 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 <list>
|
||||
#include <set>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#include <vector>
|
||||
|
||||
#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(std::vector<std::string> qualifiers, const std::string& separator, const std::string_view brackets = "{}");
|
||||
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::unordered_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::unordered_map<std::string, ActionCreator>::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(); }
|
||||
|
||||
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::unordered_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::unordered_map<std::string, T*>::iterator i = created.begin(); i != created.end(); i++)
|
||||
{
|
||||
if (i->second)
|
||||
i->second->Update();
|
||||
}
|
||||
}
|
||||
|
||||
void Reset()
|
||||
{
|
||||
for (typename std::unordered_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::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 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::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 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 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
|
||||
@@ -1,134 +0,0 @@
|
||||
/*
|
||||
* 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)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
for (ActionBasket* basket : actions)
|
||||
{
|
||||
if (action->getAction()->getName() == basket->getAction()->getName())
|
||||
{
|
||||
updateExistingBasket(basket, action);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
actions.push_back(action);
|
||||
}
|
||||
|
||||
ActionNode* Queue::Pop()
|
||||
{
|
||||
ActionBasket* highestRelevanceBasket = findHighestRelevanceBasket();
|
||||
if (!highestRelevanceBasket)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return extractAndDeleteBasket(highestRelevanceBasket);
|
||||
}
|
||||
|
||||
ActionBasket* Queue::Peek()
|
||||
{
|
||||
return findHighestRelevanceBasket();
|
||||
}
|
||||
|
||||
uint32 Queue::Size()
|
||||
{
|
||||
return actions.size();
|
||||
}
|
||||
|
||||
void Queue::RemoveExpired()
|
||||
{
|
||||
if (!sPlayerbotAIConfig->expireActionTime)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
std::list<ActionBasket*> expiredBaskets;
|
||||
collectExpiredBaskets(expiredBaskets);
|
||||
removeAndDeleteBaskets(expiredBaskets);
|
||||
}
|
||||
|
||||
// Private helper methods
|
||||
void Queue::updateExistingBasket(ActionBasket* existing, ActionBasket* newBasket)
|
||||
{
|
||||
if (existing->getRelevance() < newBasket->getRelevance())
|
||||
{
|
||||
existing->setRelevance(newBasket->getRelevance());
|
||||
}
|
||||
|
||||
if (ActionNode* actionNode = newBasket->getAction())
|
||||
{
|
||||
delete actionNode;
|
||||
}
|
||||
|
||||
delete newBasket;
|
||||
}
|
||||
|
||||
ActionBasket* Queue::findHighestRelevanceBasket() const
|
||||
{
|
||||
if (actions.empty())
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
float maxRelevance = -1.0f;
|
||||
ActionBasket* selection = nullptr;
|
||||
|
||||
for (ActionBasket* basket : actions)
|
||||
{
|
||||
if (basket->getRelevance() > maxRelevance)
|
||||
{
|
||||
maxRelevance = basket->getRelevance();
|
||||
selection = basket;
|
||||
}
|
||||
}
|
||||
|
||||
return selection;
|
||||
}
|
||||
|
||||
ActionNode* Queue::extractAndDeleteBasket(ActionBasket* basket)
|
||||
{
|
||||
ActionNode* action = basket->getAction();
|
||||
actions.remove(basket);
|
||||
delete basket;
|
||||
return action;
|
||||
}
|
||||
|
||||
void Queue::collectExpiredBaskets(std::list<ActionBasket*>& expiredBaskets)
|
||||
{
|
||||
uint32 expiryTime = sPlayerbotAIConfig->expireActionTime;
|
||||
for (ActionBasket* basket : actions)
|
||||
{
|
||||
if (basket->isExpired(expiryTime))
|
||||
{
|
||||
expiredBaskets.push_back(basket);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Queue::removeAndDeleteBaskets(std::list<ActionBasket*>& basketsToRemove)
|
||||
{
|
||||
for (ActionBasket* basket : basketsToRemove)
|
||||
{
|
||||
actions.remove(basket);
|
||||
|
||||
if (ActionNode* action = basket->getAction())
|
||||
{
|
||||
delete action;
|
||||
}
|
||||
|
||||
delete basket;
|
||||
}
|
||||
}
|
||||
@@ -1,94 +0,0 @@
|
||||
/*
|
||||
* 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 "Action.h"
|
||||
#include "Common.h"
|
||||
|
||||
/**
|
||||
* @class Queue
|
||||
* @brief Manages a priority queue of actions for the playerbot system
|
||||
*
|
||||
* This queue maintains a list of ActionBasket objects, each containing an action
|
||||
* and its relevance score. Actions with higher relevance scores are prioritized.
|
||||
*/
|
||||
class Queue
|
||||
{
|
||||
public:
|
||||
Queue() = default;
|
||||
~Queue() = default;
|
||||
|
||||
/**
|
||||
* @brief Adds an action to the queue or updates existing action's relevance
|
||||
* @param action Pointer to the ActionBasket to be added
|
||||
*
|
||||
* If an action with the same name exists, updates its relevance if the new
|
||||
* relevance is higher, then deletes the new action. Otherwise, adds the new
|
||||
* action to the queue.
|
||||
*/
|
||||
void Push(ActionBasket* action);
|
||||
|
||||
/**
|
||||
* @brief Removes and returns the action with highest relevance
|
||||
* @return Pointer to the highest relevance ActionNode, or nullptr if queue is empty
|
||||
*
|
||||
* Ownership of the returned ActionNode is transferred to the caller.
|
||||
* The associated ActionBasket is deleted.
|
||||
*/
|
||||
ActionNode* Pop();
|
||||
|
||||
/**
|
||||
* @brief Returns the action with highest relevance without removing it
|
||||
* @return Pointer to the ActionBasket with highest relevance, or nullptr if queue is empty
|
||||
*/
|
||||
ActionBasket* Peek();
|
||||
|
||||
/**
|
||||
* @brief Returns the current size of the queue
|
||||
* @return Number of actions in the queue
|
||||
*/
|
||||
uint32 Size();
|
||||
|
||||
/**
|
||||
* @brief Removes and deletes expired actions from the queue
|
||||
*
|
||||
* Uses sPlayerbotAIConfig->expireActionTime to determine if actions have expired.
|
||||
* Both the ActionNode and ActionBasket are deleted for expired actions.
|
||||
*/
|
||||
void RemoveExpired();
|
||||
|
||||
private:
|
||||
/**
|
||||
* @brief Updates existing basket with new relevance and cleans up new basket
|
||||
*/
|
||||
void updateExistingBasket(ActionBasket* existing, ActionBasket* newBasket);
|
||||
|
||||
/**
|
||||
* @brief Finds the basket with the highest relevance score
|
||||
* @return Pointer to the highest relevance basket, or nullptr if queue is empty
|
||||
*/
|
||||
ActionBasket* findHighestRelevanceBasket() const;
|
||||
|
||||
/**
|
||||
* @brief Extracts action from basket and handles basket cleanup
|
||||
*/
|
||||
ActionNode* extractAndDeleteBasket(ActionBasket* basket);
|
||||
|
||||
/**
|
||||
* @brief Collects all expired baskets into the provided list
|
||||
*/
|
||||
void collectExpiredBaskets(std::list<ActionBasket*>& expiredBaskets);
|
||||
|
||||
/**
|
||||
* @brief Removes and deletes all baskets in the provided list
|
||||
*/
|
||||
void removeAndDeleteBaskets(std::list<ActionBasket*>& basketsToRemove);
|
||||
|
||||
std::list<ActionBasket*> actions; /**< Container for action baskets */
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,123 +0,0 @@
|
||||
/*
|
||||
* 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([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode("melee",
|
||||
/*P*/ nullptr,
|
||||
/*A*/ nullptr,
|
||||
/*C*/ nullptr);
|
||||
}
|
||||
|
||||
static ActionNode* healthstone([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode("healthstone",
|
||||
/*P*/ nullptr,
|
||||
/*A*/ NextAction::array(0, new NextAction("healing potion"), nullptr),
|
||||
/*C*/ nullptr);
|
||||
}
|
||||
|
||||
static ActionNode* follow_master_random([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode("be near",
|
||||
/*P*/ nullptr,
|
||||
/*A*/ NextAction::array(0, new NextAction("follow"), nullptr),
|
||||
/*C*/ nullptr);
|
||||
}
|
||||
|
||||
static ActionNode* attack_anything([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode("attack anything",
|
||||
/*P*/ nullptr,
|
||||
/*A*/ nullptr,
|
||||
/*C*/ nullptr);
|
||||
}
|
||||
|
||||
static ActionNode* move_random([[maybe_unused]] 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([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode("move to loot",
|
||||
/*P*/ nullptr,
|
||||
/*A*/ nullptr,
|
||||
/*C*/ nullptr);
|
||||
}
|
||||
|
||||
static ActionNode* food([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode("food",
|
||||
/*P*/ nullptr,
|
||||
/*A*/ nullptr,
|
||||
/*C*/ nullptr);
|
||||
}
|
||||
|
||||
static ActionNode* drink([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode("drink",
|
||||
/*P*/ nullptr,
|
||||
/*A*/ nullptr,
|
||||
/*C*/ nullptr);
|
||||
}
|
||||
|
||||
static ActionNode* mana_potion([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode("mana potion",
|
||||
/*P*/ nullptr,
|
||||
/*A*/ nullptr,
|
||||
/*C*/ nullptr);
|
||||
}
|
||||
|
||||
static ActionNode* healing_potion([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode("healing potion",
|
||||
/*P*/ nullptr,
|
||||
/*A*/ NextAction::array(0, new NextAction("food"), nullptr),
|
||||
/*C*/ nullptr);
|
||||
}
|
||||
|
||||
static ActionNode* flee([[maybe_unused]] 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); }
|
||||
@@ -1,75 +0,0 @@
|
||||
/*
|
||||
* 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 "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_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 NextAction** getDefaultActions() { return nullptr; }
|
||||
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() {}
|
||||
|
||||
protected:
|
||||
NamedObjectFactoryList<ActionNode> actionNodeFactories;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,48 +0,0 @@
|
||||
/*
|
||||
* 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"
|
||||
#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()
|
||||
{
|
||||
if (checkInterval < 2)
|
||||
return true;
|
||||
|
||||
uint32 now = getMSTime();
|
||||
if (!lastCheckTime || now - lastCheckTime >= checkInterval)
|
||||
{
|
||||
lastCheckTime = now;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
@@ -1,64 +0,0 @@
|
||||
/*
|
||||
* 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 "Action.h"
|
||||
#include "Common.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([[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 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;
|
||||
uint32 lastCheckTime;
|
||||
};
|
||||
|
||||
class TriggerNode
|
||||
{
|
||||
public:
|
||||
TriggerNode(std::string const name, NextAction** handlers = nullptr)
|
||||
: trigger(nullptr), handlers(handlers), name(name)
|
||||
{
|
||||
} // reorder args - whipowill
|
||||
|
||||
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
|
||||
@@ -1,157 +0,0 @@
|
||||
/*
|
||||
* 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"
|
||||
#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)
|
||||
{
|
||||
PerformanceMonitorOperation* pmo = sPerformanceMonitor->start(
|
||||
PERF_MON_VALUE, this->getName(), this->context ? &this->context->performanceStack : nullptr);
|
||||
value = Calculate();
|
||||
if (pmo)
|
||||
pmo->finish();
|
||||
}
|
||||
else
|
||||
{
|
||||
time_t now = getMSTime();
|
||||
if (!lastCheckTime || now - lastCheckTime >= checkInterval)
|
||||
{
|
||||
lastCheckTime = now;
|
||||
PerformanceMonitorOperation* pmo = sPerformanceMonitor->start(
|
||||
PERF_MON_VALUE, this->getName(), this->context ? &this->context->performanceStack : nullptr);
|
||||
value = Calculate();
|
||||
if (pmo)
|
||||
pmo->finish();
|
||||
}
|
||||
}
|
||||
// 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;
|
||||
}
|
||||
@@ -1,419 +0,0 @@
|
||||
/*
|
||||
* 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 <time.h>
|
||||
|
||||
#include "AiObject.h"
|
||||
#include "ObjectGuid.h"
|
||||
#include "PerformanceMonitor.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)
|
||||
{
|
||||
// PerformanceMonitorOperation* pmo = sPerformanceMonitor->start(PERF_MON_VALUE, this->getName(),
|
||||
// this->context ? &this->context->performanceStack : nullptr);
|
||||
value = Calculate();
|
||||
// if (pmo)
|
||||
// pmo->finish();
|
||||
}
|
||||
else
|
||||
{
|
||||
time_t now = getMSTime();
|
||||
if (!lastCheckTime || now - lastCheckTime >= checkInterval)
|
||||
{
|
||||
lastCheckTime = now;
|
||||
// PerformanceMonitorOperation* pmo = sPerformanceMonitor->start(PERF_MON_VALUE, this->getName(),
|
||||
// this->context ? &this->context->performanceStack : nullptr);
|
||||
value = Calculate();
|
||||
// if (pmo)
|
||||
// pmo->finish();
|
||||
}
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
T LazyGet() override
|
||||
{
|
||||
if (!lastCheckTime)
|
||||
return Get();
|
||||
|
||||
return value;
|
||||
}
|
||||
T& RefGet() override
|
||||
{
|
||||
if (checkInterval < 2)
|
||||
{
|
||||
// PerformanceMonitorOperation* pmo = sPerformanceMonitor->start(PERF_MON_VALUE, this->getName(),
|
||||
// this->context ? &this->context->performanceStack : nullptr);
|
||||
value = Calculate();
|
||||
// if (pmo)
|
||||
// pmo->finish();
|
||||
}
|
||||
else
|
||||
{
|
||||
time_t now = getMSTime();
|
||||
if (!lastCheckTime || now - lastCheckTime >= checkInterval)
|
||||
{
|
||||
lastCheckTime = now;
|
||||
// PerformanceMonitorOperation* pmo = sPerformanceMonitor->start(PERF_MON_VALUE, this->getName(),
|
||||
// this->context ? &this->context->performanceStack : nullptr);
|
||||
value = Calculate();
|
||||
// if (pmo)
|
||||
// pmo->finish();
|
||||
}
|
||||
}
|
||||
return value;
|
||||
}
|
||||
void Set(T val) override { value = val; }
|
||||
void Update() override {}
|
||||
void Reset() override { lastCheckTime = 0; }
|
||||
|
||||
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;
|
||||
|
||||
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([[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
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user