AI: extracted into cpp files

master
Martin Gerhardy 2018-06-29 07:04:48 +02:00
parent b3e9737e20
commit e10a72f8e6
9 changed files with 471 additions and 411 deletions

39
src/modules/ai/AI.cpp Normal file
View File

@ -0,0 +1,39 @@
/**
* @file
*/
#include "AI.h"
namespace ai {
TreeNodePtr AI::setBehaviour(const TreeNodePtr& newBehaviour) {
TreeNodePtr current = _behaviour;
_behaviour = newBehaviour;
_reset = true;
return current;
}
void AI::update(int64_t dt, bool debuggingActive) {
if (isPause()) {
return;
}
if (_character) {
_character->update(dt, debuggingActive);
}
if (_reset) {
// safe to do it like this, because update is not called from multiple threads
_reset = false;
_lastStatus.clear();
_lastExecMillis.clear();
_filteredEntities.clear();
_selectorStates.clear();
}
_debuggingActive = debuggingActive;
_time += dt;
_aggroMgr.update(dt);
}
}

View File

@ -201,13 +201,6 @@ inline TreeNodePtr AI::getBehaviour() const {
return _behaviour;
}
inline TreeNodePtr AI::setBehaviour(const TreeNodePtr& newBehaviour) {
TreeNodePtr current = _behaviour;
_behaviour = newBehaviour;
_reset = true;
return current;
}
inline void AI::setPause(bool pause) {
_pause = pause;
}
@ -272,29 +265,6 @@ inline CharacterId AI::getId() const {
return _character->getId();
}
inline void AI::update(int64_t dt, bool debuggingActive) {
if (isPause()) {
return;
}
if (_character) {
_character->update(dt, debuggingActive);
}
if (_reset) {
// safe to do it like this, because update is not called from multiple threads
_reset = false;
_lastStatus.clear();
_lastExecMillis.clear();
_filteredEntities.clear();
_selectorStates.clear();
}
_debuggingActive = debuggingActive;
_time += dt;
_aggroMgr.update(dt);
}
typedef std::shared_ptr<AI> AIPtr;
}

View File

@ -1,7 +1,7 @@
set(SRCS
aggro/AggroMgr.h
aggro/AggroMgr.h aggro/AggroMgr.cpp
aggro/Entry.h
AI.h
AI.h AI.cpp
AIFactories.h
AIRegistry.h AIRegistry.cpp
common/IFactoryRegistry.h
@ -44,7 +44,7 @@ set(SRCS
IAIFactory.h
ICharacter.h
group/GroupId.h
group/GroupMgr.h
group/GroupMgr.h group/GroupMgr.cpp
movement/SelectionSeek.h
movement/GroupFlee.h
movement/GroupSeek.h
@ -79,7 +79,7 @@ set(SRCS
server/Server.h server/Server.cpp
server/StepHandler.h
server/UpdateNodeHandler.h
zone/Zone.h
zone/Zone.h zone/Zone.cpp
SimpleAI.h
tree/Fail.h
tree/Limit.h

View File

@ -0,0 +1,138 @@
/**
* @file
*/
#include "AggroMgr.h"
#include <algorithm>
namespace ai {
class CharacterIdPredicate {
private:
const CharacterId& _id;
public:
explicit CharacterIdPredicate(const CharacterId& id) :
_id(id) {
}
inline bool operator()(const Entry &n1) {
return n1.getCharacterId() == _id;
}
};
static bool EntrySorter(const Entry& a, const Entry& b) {
if (a.getAggro() > b.getAggro()) {
return false;
}
if (::fabs(a.getAggro() - b.getAggro()) < 0.0000001f) {
return a.getCharacterId() < b.getCharacterId();
}
return true;
}
/**
* @brief Remove the entries from the list that have no aggro left.
* This list is ordered, so we will only remove the first X elements.
*/
void AggroMgr::cleanupList() {
EntriesIter::difference_type remove = 0;
for (EntriesIter i = _entries.begin(); i != _entries.end(); ++i) {
const float aggroValue = i->getAggro();
if (aggroValue > 0.0f) {
break;
}
++remove;
}
if (remove == 0) {
return;
}
const int size = static_cast<int>(_entries.size());
if (size == remove) {
_entries.clear();
return;
}
EntriesIter i = _entries.begin();
std::advance(i, remove);
_entries.erase(_entries.begin(), i);
}
void AggroMgr::sort() const {
if (!_dirty) {
return;
}
std::sort(_entries.begin(), _entries.end(), EntrySorter);
_dirty = false;
}
void AggroMgr::setReduceByRatio(float reduceRatioSecond, float minAggro) {
_reduceType = RATIO;
_reduceValueSecond = 0.0f;
_reduceRatioSecond = reduceRatioSecond;
_minAggro = minAggro;
}
void AggroMgr::setReduceByValue(float reduceValueSecond) {
_reduceType = VALUE;
_reduceValueSecond = reduceValueSecond;
_reduceRatioSecond = 0.0f;
_minAggro = 0.0f;
}
void AggroMgr::resetReduceValue() {
_reduceType = DISABLED;
_reduceValueSecond = 0.0f;
_reduceRatioSecond = 0.0f;
_minAggro = 0.0f;
}
void AggroMgr::update(int64_t deltaMillis) {
for (EntriesIter i = _entries.begin(); i != _entries.end(); ++i) {
_dirty |= i->reduceByTime(deltaMillis);
}
if (_dirty) {
sort();
cleanupList();
}
}
EntryPtr AggroMgr::addAggro(CharacterId id, float amount) {
const CharacterIdPredicate p(id);
EntriesIter i = std::find_if(_entries.begin(), _entries.end(), p);
if (i == _entries.end()) {
Entry newEntry(id, amount);
switch (_reduceType) {
case RATIO:
newEntry.setReduceByRatio(_reduceRatioSecond, _minAggro);
break;
case VALUE:
newEntry.setReduceByValue(_reduceValueSecond);
break;
default:
break;
}
_entries.push_back(newEntry);
_dirty = true;
return &_entries.back();
}
i->addAggro(amount);
_dirty = true;
return &*i;
}
EntryPtr AggroMgr::getHighestEntry() const {
if (_entries.empty()) {
return nullptr;
}
sort();
return &_entries.back();
}
}

View File

@ -10,7 +10,6 @@
#include <vector>
#include <memory>
#include "ICharacter.h"
#include <algorithm>
#include "aggro/Entry.h"
namespace ai {
@ -32,66 +31,13 @@ protected:
float _reduceValueSecond = 0.0f;
ReductionType _reduceType = DISABLED;
class CharacterIdPredicate {
private:
const CharacterId& _id;
public:
explicit CharacterIdPredicate(const CharacterId& id) :
_id(id) {
}
inline bool operator()(const Entry &n1) {
return n1.getCharacterId() == _id;
}
};
static bool EntrySorter(const Entry& a, const Entry& b) {
if (a.getAggro() > b.getAggro()) {
return false;
}
if (::fabs(a.getAggro() - b.getAggro()) < 0.0000001f) {
return a.getCharacterId() < b.getCharacterId();
}
return true;
}
/**
* @brief Remove the entries from the list that have no aggro left.
* This list is ordered, so we will only remove the first X elements.
*/
void cleanupList() {
EntriesIter::difference_type remove = 0;
for (EntriesIter i = _entries.begin(); i != _entries.end(); ++i) {
const float aggroValue = i->getAggro();
if (aggroValue > 0.0f) {
break;
}
void cleanupList();
++remove;
}
if (remove == 0) {
return;
}
const int size = static_cast<int>(_entries.size());
if (size == remove) {
_entries.clear();
return;
}
EntriesIter i = _entries.begin();
std::advance(i, remove);
_entries.erase(_entries.begin(), i);
}
inline void sort() const {
if (!_dirty) {
return;
}
std::sort(_entries.begin(), _entries.end(), EntrySorter);
_dirty = false;
}
inline void sort() const;
public:
explicit AggroMgr(std::size_t expectedEntrySize = 0u) :
_dirty(false) {
@ -103,41 +49,17 @@ public:
virtual ~AggroMgr() {
}
inline void setReduceByRatio(float reduceRatioSecond, float minAggro) {
_reduceType = RATIO;
_reduceValueSecond = 0.0f;
_reduceRatioSecond = reduceRatioSecond;
_minAggro = minAggro;
}
void setReduceByRatio(float reduceRatioSecond, float minAggro);
inline void setReduceByValue(float reduceValueSecond) {
_reduceType = VALUE;
_reduceValueSecond = reduceValueSecond;
_reduceRatioSecond = 0.0f;
_minAggro = 0.0f;
}
void setReduceByValue(float reduceValueSecond);
inline void resetReduceValue() {
_reduceType = DISABLED;
_reduceValueSecond = 0.0f;
_reduceRatioSecond = 0.0f;
_minAggro = 0.0f;
}
void resetReduceValue();
/**
* @brief this will update the aggro list according to the reduction type of an entry.
* @param[in] deltaMillis The current milliseconds to use to update the aggro value of the entries.
*/
void update(int64_t deltaMillis) {
for (EntriesIter i = _entries.begin(); i != _entries.end(); ++i) {
_dirty |= i->reduceByTime(deltaMillis);
}
if (_dirty) {
sort();
cleanupList();
}
}
void update(int64_t deltaMillis);
/**
* @brief will increase the aggro
@ -145,30 +67,7 @@ public:
* @param[in] amount The amount to increase the aggro for
* @return The aggro @c Entry that was added or updated. Useful for changing the reduce type or amount.
*/
EntryPtr addAggro(CharacterId id, float amount) {
const CharacterIdPredicate p(id);
EntriesIter i = std::find_if(_entries.begin(), _entries.end(), p);
if (i == _entries.end()) {
Entry newEntry(id, amount);
switch (_reduceType) {
case RATIO:
newEntry.setReduceByRatio(_reduceRatioSecond, _minAggro);
break;
case VALUE:
newEntry.setReduceByValue(_reduceValueSecond);
break;
default:
break;
}
_entries.push_back(newEntry);
_dirty = true;
return &_entries.back();
}
i->addAggro(amount);
_dirty = true;
return &*i;
}
EntryPtr addAggro(CharacterId id, float amount);
/**
* @return All the aggro entries
@ -182,15 +81,7 @@ public:
*
* @note Might execute a sort on the list if its dirty
*/
EntryPtr getHighestEntry() const {
if (_entries.empty()) {
return nullptr;
}
sort();
return &_entries.back();
}
EntryPtr getHighestEntry() const;
};
}

View File

@ -0,0 +1,157 @@
/**
* @file
*/
#include "GroupMgr.h"
namespace ai {
struct AveragePositionFunctor {
glm::vec3 operator()(const glm::vec3& result, const AIPtr& ai) {
return ai->getCharacter()->getPosition() + result;
}
};
void GroupMgr::update(int64_t) {
ScopedReadLock scopedLock(_lock);
for (auto i = _groups.begin(); i != _groups.end(); ++i) {
Group& group = i->second;
glm::vec3 averagePosition(0.0f);
{
ScopedReadLock lock(_groupLock);
averagePosition = std::accumulate(group.members.begin(), group.members.end(), glm::vec3(0.0f), AveragePositionFunctor());
averagePosition *= 1.0f / (float) group.members.size();
}
ScopedWriteLock lock(_groupLock);
group.position = averagePosition;
}
}
bool GroupMgr::add(GroupId id, const AIPtr& ai) {
ScopedWriteLock scopedLock(_lock);
GroupsIter i = _groups.find(id);
if (i == _groups.end()) {
Group group;
group.leader = ai;
i = _groups.insert(std::pair<GroupId, Group>(id, group)).first;
}
Group& group = i->second;
ScopedWriteLock lock(_groupLock);
std::pair<GroupMembersSetIter, bool> ret = group.members.insert(ai);
if (ret.second) {
_groupMembers.insert(GroupMembers::value_type(ai, id));
return true;
}
return false;
}
bool GroupMgr::remove(GroupId id, const AIPtr& ai) {
ScopedWriteLock scopedLock(_lock);
const GroupsIter& i = _groups.find(id);
if (i == _groups.end()) {
return false;
}
Group& group = i->second;
GroupMembersSetIter si;
{
ScopedReadLock lock(_groupLock);
si = group.members.find(ai);
if (si == group.members.end()) {
return false;
}
}
{
ScopedWriteLock lock(_groupLock);
group.members.erase(si);
if (group.members.empty()) {
_groups.erase(i);
} else if (group.leader == ai) {
group.leader = *group.members.begin();
}
}
auto range = _groupMembers.equal_range(ai);
for (auto it = range.first; it != range.second; ++it) {
if (it->second == id) {
_groupMembers.erase(it);
break;
}
}
return true;
}
bool GroupMgr::removeFromAllGroups(const AIPtr& ai) {
std::list<GroupId> groups;
{
ScopedReadLock scopedLock(_lock);
auto range = _groupMembers.equal_range(ai);
for (auto it = range.first; it != range.second; ++it) {
groups.push_back(it->second);
}
}
for (GroupId groupId : groups) {
remove(groupId, ai);
}
return true;
}
AIPtr GroupMgr::getLeader(GroupId id) const {
ScopedReadLock scopedLock(_lock);
const GroupsConstIter& i = _groups.find(id);
if (i == _groups.end()) {
return AIPtr();
}
ScopedReadLock lock(_groupLock);
return i->second.leader;
}
glm::vec3 GroupMgr::getPosition(GroupId id) const {
ScopedReadLock scopedLock(_lock);
const GroupsConstIter& i = _groups.find(id);
if (i == _groups.end()) {
return VEC3_INFINITE;
}
ScopedReadLock lock(_groupLock);
return i->second.position;
}
bool GroupMgr::isGroupLeader(GroupId id, const AIPtr& ai) const {
ScopedReadLock scopedLock(_lock);
const GroupsConstIter& i = _groups.find(id);
if (i == _groups.end()) {
return 0;
}
ScopedReadLock lock(_groupLock);
return i->second.leader == ai;
}
int GroupMgr::getGroupSize(GroupId id) const {
ScopedReadLock scopedLock(_lock);
const GroupsConstIter& i = _groups.find(id);
if (i == _groups.end()) {
return 0;
}
ScopedReadLock lock(_groupLock);
return static_cast<int>(std::distance(i->second.members.begin(), i->second.members.end()));
}
bool GroupMgr::isInAnyGroup(const AIPtr& ai) const {
ScopedReadLock scopedLock(_lock);
return _groupMembers.find(ai) != _groupMembers.end();
}
bool GroupMgr::isInGroup(GroupId id, const AIPtr& ai) const {
ScopedReadLock scopedLock(_lock);
auto range = _groupMembers.equal_range(ai);
for (auto it = range.first; it != range.second; ++it) {
if (it->second == id) {
return true;
}
}
return false;
}
}

View File

@ -1,6 +1,7 @@
/**
* @file
*/
#pragma once
#include "common/Thread.h"
@ -26,12 +27,6 @@ namespace ai {
*/
class GroupMgr {
private:
struct AveragePositionFunctor {
glm::vec3 operator()(const glm::vec3& result, const AIPtr& ai) {
return ai->getCharacter()->getPosition() + result;
}
};
typedef std::unordered_set<AIPtr> GroupMembersSet;
typedef GroupMembersSet::iterator GroupMembersSetIter;
typedef GroupMembersSet::const_iterator GroupMembersSetConstIter;
@ -160,147 +155,4 @@ public:
bool isGroupLeader(GroupId id, const AIPtr& ai) const;
};
inline void GroupMgr::update(int64_t) {
ScopedReadLock scopedLock(_lock);
for (auto i = _groups.begin(); i != _groups.end(); ++i) {
Group& group = i->second;
glm::vec3 averagePosition(0.0f);
{
ScopedReadLock lock(_groupLock);
averagePosition = std::accumulate(group.members.begin(), group.members.end(), glm::vec3(0.0f), AveragePositionFunctor());
averagePosition *= 1.0f / (float) group.members.size();
}
ScopedWriteLock lock(_groupLock);
group.position = averagePosition;
}
}
inline bool GroupMgr::add(GroupId id, const AIPtr& ai) {
ScopedWriteLock scopedLock(_lock);
GroupsIter i = _groups.find(id);
if (i == _groups.end()) {
Group group;
group.leader = ai;
i = _groups.insert(std::pair<GroupId, Group>(id, group)).first;
}
Group& group = i->second;
ScopedWriteLock lock(_groupLock);
std::pair<GroupMembersSetIter, bool> ret = group.members.insert(ai);
if (ret.second) {
_groupMembers.insert(GroupMembers::value_type(ai, id));
return true;
}
return false;
}
inline bool GroupMgr::remove(GroupId id, const AIPtr& ai) {
ScopedWriteLock scopedLock(_lock);
const GroupsIter& i = _groups.find(id);
if (i == _groups.end()) {
return false;
}
Group& group = i->second;
GroupMembersSetIter si;
{
ScopedReadLock lock(_groupLock);
si = group.members.find(ai);
if (si == group.members.end()) {
return false;
}
}
{
ScopedWriteLock lock(_groupLock);
group.members.erase(si);
if (group.members.empty()) {
_groups.erase(i);
} else if (group.leader == ai) {
group.leader = *group.members.begin();
}
}
auto range = _groupMembers.equal_range(ai);
for (auto it = range.first; it != range.second; ++it) {
if (it->second == id) {
_groupMembers.erase(it);
break;
}
}
return true;
}
inline bool GroupMgr::removeFromAllGroups(const AIPtr& ai) {
std::list<GroupId> groups;
{
ScopedReadLock scopedLock(_lock);
auto range = _groupMembers.equal_range(ai);
for (auto it = range.first; it != range.second; ++it) {
groups.push_back(it->second);
}
}
for (GroupId groupId : groups) {
remove(groupId, ai);
}
return true;
}
inline AIPtr GroupMgr::getLeader(GroupId id) const {
ScopedReadLock scopedLock(_lock);
const GroupsConstIter& i = _groups.find(id);
if (i == _groups.end()) {
return AIPtr();
}
ScopedReadLock lock(_groupLock);
return i->second.leader;
}
inline glm::vec3 GroupMgr::getPosition(GroupId id) const {
ScopedReadLock scopedLock(_lock);
const GroupsConstIter& i = _groups.find(id);
if (i == _groups.end()) {
return VEC3_INFINITE;
}
ScopedReadLock lock(_groupLock);
return i->second.position;
}
inline bool GroupMgr::isGroupLeader(GroupId id, const AIPtr& ai) const {
ScopedReadLock scopedLock(_lock);
const GroupsConstIter& i = _groups.find(id);
if (i == _groups.end()) {
return 0;
}
ScopedReadLock lock(_groupLock);
return i->second.leader == ai;
}
inline int GroupMgr::getGroupSize(GroupId id) const {
ScopedReadLock scopedLock(_lock);
const GroupsConstIter& i = _groups.find(id);
if (i == _groups.end()) {
return 0;
}
ScopedReadLock lock(_groupLock);
return static_cast<int>(std::distance(i->second.members.begin(), i->second.members.end()));
}
inline bool GroupMgr::isInAnyGroup(const AIPtr& ai) const {
ScopedReadLock scopedLock(_lock);
return _groupMembers.find(ai) != _groupMembers.end();
}
inline bool GroupMgr::isInGroup(GroupId id, const AIPtr& ai) const {
ScopedReadLock scopedLock(_lock);
auto range = _groupMembers.equal_range(ai);
for (auto it = range.first; it != range.second; ++it) {
if (it->second == id) {
return true;
}
}
return false;
}
}

View File

@ -0,0 +1,122 @@
/**
* @file
*/
#include "Zone.h"
namespace ai {
AIPtr Zone::getAI(CharacterId id) const {
ScopedReadLock scopedLock(_lock);
auto i = _ais.find(id);
if (i == _ais.end()) {
return AIPtr();
}
const AIPtr& ai = i->second;
return ai;
}
std::size_t Zone::size() const {
ScopedReadLock scopedLock(_lock);
return _ais.size();
}
bool Zone::doAddAI(const AIPtr& ai) {
if (ai == nullptr) {
return false;
}
const CharacterId& id = ai->getCharacter()->getId();
if (_ais.find(id) != _ais.end()) {
return false;
}
_ais.insert(std::make_pair(id, ai));
ai->setZone(this);
return true;
}
bool Zone::doRemoveAI(const AIPtr& ai) {
if (!ai) {
return false;
}
const CharacterId& id = ai->getCharacter()->getId();
AIMapIter i = _ais.find(id);
if (i == _ais.end()) {
return false;
}
i->second->setZone(nullptr);
_groupManager.removeFromAllGroups(i->second);
_ais.erase(i);
return true;
}
bool Zone::doDestroyAI(const CharacterId& id) {
AIMapIter i = _ais.find(id);
if (i == _ais.end()) {
return false;
}
_ais.erase(i);
return true;
}
bool Zone::addAI(const AIPtr& ai) {
if (!ai) {
return false;
}
ScopedWriteLock scopedLock(_scheduleLock);
_scheduledAdd.push_back(ai);
return true;
}
bool Zone::destroyAI(const CharacterId& id) {
ScopedWriteLock scopedLock(_scheduleLock);
_scheduledDestroy.push_back(id);
return true;
}
bool Zone::removeAI(const AIPtr& ai) {
if (!ai) {
return false;
}
ScopedWriteLock scopedLock(_scheduleLock);
_scheduledRemove.push_back(ai);
return true;
}
void Zone::update(int64_t dt) {
{
AIScheduleList scheduledRemove;
AIScheduleList scheduledAdd;
CharacterIdList scheduledDestroy;
{
ScopedWriteLock scopedLock(_scheduleLock);
scheduledAdd.swap(_scheduledAdd);
scheduledRemove.swap(_scheduledRemove);
scheduledDestroy.swap(_scheduledDestroy);
}
ScopedWriteLock scopedLock(_lock);
for (const AIPtr& ai : scheduledAdd) {
doAddAI(ai);
}
scheduledAdd.clear();
for (const AIPtr& ai : scheduledRemove) {
doRemoveAI(ai);
}
scheduledRemove.clear();
for (auto id : scheduledDestroy) {
doDestroyAI(id);
}
scheduledDestroy.clear();
}
auto func = [&] (const AIPtr& ai) {
if (ai->isPause()) {
return;
}
ai->update(dt, _debug);
ai->getBehaviour()->execute(ai, dt);
};
executeParallel(func);
_groupManager.update(dt);
}
}

View File

@ -171,15 +171,7 @@ public:
*
* @note This locks the zone for reading to perform the CharacterId lookup
*/
inline AIPtr getAI(CharacterId id) const {
ScopedReadLock scopedLock(_lock);
auto i = _ais.find(id);
if (i == _ais.end()) {
return AIPtr();
}
const AIPtr& ai = i->second;
return ai;
}
AIPtr getAI(CharacterId id) const;
/**
* @brief Executes a lambda or functor for the given character
@ -328,10 +320,7 @@ public:
}
}
inline std::size_t size() const {
ScopedReadLock scopedLock(_lock);
return _ais.size();
}
std::size_t size() const;
};
inline void Zone::setDebug (bool debug) {
@ -354,102 +343,4 @@ inline const GroupMgr& Zone::getGroupMgr() const {
return _groupManager;
}
inline bool Zone::doAddAI(const AIPtr& ai) {
if (ai == nullptr) {
return false;
}
const CharacterId& id = ai->getCharacter()->getId();
if (_ais.find(id) != _ais.end()) {
return false;
}
_ais.insert(std::make_pair(id, ai));
ai->setZone(this);
return true;
}
inline bool Zone::doRemoveAI(const AIPtr& ai) {
if (!ai) {
return false;
}
const CharacterId& id = ai->getCharacter()->getId();
AIMapIter i = _ais.find(id);
if (i == _ais.end()) {
return false;
}
i->second->setZone(nullptr);
_groupManager.removeFromAllGroups(i->second);
_ais.erase(i);
return true;
}
inline bool Zone::doDestroyAI(const CharacterId& id) {
AIMapIter i = _ais.find(id);
if (i == _ais.end()) {
return false;
}
_ais.erase(i);
return true;
}
inline bool Zone::addAI(const AIPtr& ai) {
if (!ai) {
return false;
}
ScopedWriteLock scopedLock(_scheduleLock);
_scheduledAdd.push_back(ai);
return true;
}
inline bool Zone::destroyAI(const CharacterId& id) {
ScopedWriteLock scopedLock(_scheduleLock);
_scheduledDestroy.push_back(id);
return true;
}
inline bool Zone::removeAI(const AIPtr& ai) {
if (!ai) {
return false;
}
ScopedWriteLock scopedLock(_scheduleLock);
_scheduledRemove.push_back(ai);
return true;
}
inline void Zone::update(int64_t dt) {
{
AIScheduleList scheduledRemove;
AIScheduleList scheduledAdd;
CharacterIdList scheduledDestroy;
{
ScopedWriteLock scopedLock(_scheduleLock);
scheduledAdd.swap(_scheduledAdd);
scheduledRemove.swap(_scheduledRemove);
scheduledDestroy.swap(_scheduledDestroy);
}
ScopedWriteLock scopedLock(_lock);
for (const AIPtr& ai : scheduledAdd) {
doAddAI(ai);
}
scheduledAdd.clear();
for (const AIPtr& ai : scheduledRemove) {
doRemoveAI(ai);
}
scheduledRemove.clear();
for (auto id : scheduledDestroy) {
doDestroyAI(id);
}
scheduledDestroy.clear();
}
auto func = [&] (const AIPtr& ai) {
if (ai->isPause()) {
return;
}
ai->update(dt, _debug);
ai->getBehaviour()->execute(ai, dt);
};
executeParallel(func);
_groupManager.update(dt);
}
}