Browse Source

AgentComponent, Callbacks, Small Fixes

- Added basic Needs system, which change conditionally and cause new goals to arise. Needs are the driving force of agent goals 
- Added AgentComponentManager which will be the brain of agents. It's strongly tied to Needs. I have not yet hooked it up to PlanComponentManager, so at the moment it doesn't do anything
- Excised Subject Observer from the codebase in favor of compositional, function-pointer-based CallbackContainer. This should reduce multiple inheritance and boilerplate throughout the code while also making behavior more explicit
- Made WorldResourceLocator, which is the code from TestWorldResourceLocator (Galavant-Unreal) made into a module, because I don't think I care for passing that around everywhere
- Fixed building tests where Logging was broken in tiny ways
- Updated Sublime Project with Unreal Debug Editor GDB option
- Added two kit pieces to the Building System draft
combatComponentRefactor
Macoy Madson 6 years ago
parent
commit
8344acb025
  1. BIN
      assets/models/BuildingSystem.blend
  2. 6
      src/ai/htn/HTNTasks.hpp
  3. 11
      src/ai/htn/HTNTypes.hpp
  4. 12
      src/entityComponentSystem/PooledComponentManager.hpp
  5. 2
      src/game/Jamfile
  6. 187
      src/game/agent/AgentComponentManager.cpp
  7. 69
      src/game/agent/AgentComponentManager.hpp
  8. 45
      src/game/agent/Needs.hpp
  9. 33
      src/game/agent/PlanComponentManager.cpp
  10. 6
      src/game/agent/PlanComponentManager.hpp
  11. 40
      src/project/galavantSublime/galavant.sublime-project
  12. 2
      src/unitTesting/Jamfile
  13. 4
      src/unitTesting/Log_test.cpp
  14. 52
      src/util/CallbackContainer.hpp
  15. 49
      src/util/SubjectObserver.hpp
  16. 2
      src/world/Jamfile
  17. 109
      src/world/WorldResourceLocator.cpp
  18. 36
      src/world/WorldResourceLocator.hpp

BIN
assets/models/BuildingSystem.blend

Binary file not shown.

6
src/ai/htn/HTNTasks.hpp

@ -2,7 +2,7 @@
#include "HTNTypes.hpp"
#include "../WorldState.hpp"
#include "../../util/SubjectObserver.hpp"
#include "../../util/CallbackContainer.hpp"
// For std::ostream
#include <iostream>
@ -116,8 +116,8 @@ struct TaskExecuteStatus
ExecutionStatus Status;
// If the status is to Subscribe, the thing running tasks should Observe the given subject
gv::Subject<TaskEvent>* Subject;
// If the status is to Subscribe, the thing running tasks should add its callback
gv::CallbackContainer<TaskEventCallback>* EventCallbackContainer;
};
class PrimitiveTask

11
src/ai/htn/HTNTypes.hpp

@ -37,8 +37,15 @@ struct Parameter
Parameter()
{
new(&PositionValue) gv::Position();
new (&PositionValue) gv::Position();
}
// TODO Make these helpers
/*void SetInt(Int& newInt);
void SetFloat(Float& newFloat);
void SetBool(Bool& newBool);
void SetEntity(Entity& newEntity);
void SetPosition(Position& newPosition);*/
};
typedef std::vector<Parameter> ParameterList;
@ -59,4 +66,6 @@ struct TaskEvent
TaskResult Result;
gv::Entity entity;
};
typedef std::vector<TaskEvent> TaskEventList;
using TaskEventCallback = void (*)(const TaskEventList&, void*);
}

12
src/entityComponentSystem/PooledComponentManager.hpp

@ -142,6 +142,7 @@ public:
Reset();
}
// TODO: Make the caller wrap their thing in a PooledComponent for no reason? Gross
// If the entity is already subscribed, the input component will be tossed out
void SubscribeEntities(const std::vector<PooledComponent<T>>& components)
{
@ -230,6 +231,17 @@ public:
EntityListRemoveNonUniqueEntitiesInSuspect(entitiesToUnsubscribe, Subscribers);
}
bool IsSubscribed(const Entity& entity)
{
for (const Entity& subscriber : Subscribers)
{
if (subscriber == entity)
return true;
}
return false;
}
void Reset()
{
PooledComponents.Clear();

2
src/game/Jamfile

@ -2,7 +2,7 @@ SubDir . src game ;
SubDirC++Flags $(ALLLIBSC++FLAGS) ;
Library libGalaGame : agent/PlanComponentManager.cpp ;
Library libGalaGame : agent/PlanComponentManager.cpp agent/AgentComponentManager.cpp ;
LinkLibraries libGalaGame : libGalaAi libGalaWorld ;

187
src/game/agent/AgentComponentManager.cpp

@ -0,0 +1,187 @@
#include "AgentComponentManager.hpp"
#include "../../util/Logging.hpp"
#include "../../entityComponentSystem/PooledComponentManager.hpp"
namespace gv
{
AgentComponentManager::AgentComponentManager() : gv::PooledComponentManager<AgentComponentData>(100)
{
}
AgentComponentManager::~AgentComponentManager()
{
}
void AgentComponentManager::Initialize(PlanComponentManager* newPlanComponentManager)
{
PlanManager = newPlanComponentManager;
}
void AddGoalIfUniqueType(AgentGoalList& goals, AgentGoal& goalToAdd)
{
for (const AgentGoal& goal : goals)
{
if (goalToAdd.Type == goal.Type)
return;
}
goals.push_back(goalToAdd);
}
void AgentComponentManager::Update(float deltaSeconds)
{
EntityList entitiesToUnsubscribe;
PlanComponentManager::PlanComponentList newPlans;
WorldTime += deltaSeconds;
// TODO: Adding true iterator support to pool will drastically help damning this to hell
gv::PooledComponentManager<AgentComponentData>::FragmentedPoolIterator it =
gv::PooledComponentManager<AgentComponentData>::NULL_POOL_ITERATOR;
for (gv::PooledComponent<AgentComponentData>* currentComponent = ActivePoolBegin(it);
currentComponent != nullptr &&
it != gv::PooledComponentManager<AgentComponentData>::NULL_POOL_ITERATOR;
currentComponent = GetNextActivePooledComponent(it))
{
if (!currentComponent)
continue;
Entity currentEntity = currentComponent->entity;
NeedList& needs = currentComponent->data.Needs;
AgentGoalList& goals = currentComponent->data.Goals;
if (!currentComponent->data.IsAlive)
continue;
// Update Needs
for (Need& need : needs)
{
if (need.Def)
{
bool needUpdated = false;
float updateDelta = WorldTime - need.LastUpdateTime;
while (updateDelta >= need.Def->UpdateRate)
{
need.Level += need.Def->AddPerUpdate;
updateDelta -= need.Def->UpdateRate;
needUpdated = true;
}
if (needUpdated)
{
need.LastUpdateTime = WorldTime;
for (const NeedLevelTrigger& needLevelTrigger : need.Def->LevelTriggers)
{
bool needTriggerHit = (needLevelTrigger.GreaterThanLevel &&
need.Level > needLevelTrigger.Level);
if (needTriggerHit)
{
if (needLevelTrigger.NeedsResource && needLevelTrigger.WorldResource)
{
AgentGoal newNeedResourceGoal{AgentGoal::GoalStatus::Initialized,
AgentGoal::GoalType::GetResource,
needLevelTrigger.WorldResource};
AddGoalIfUniqueType(goals, newNeedResourceGoal);
}
else if (needLevelTrigger.DieNow)
{
currentComponent->data.IsAlive = false;
LOGD_IF(DebugPrint) << "Agent Entity " << currentEntity
<< " has died!";
}
}
}
}
}
else
LOG_ERROR << "Need on entity " << currentEntity << " has no def!";
}
// Update head goal
if (!goals.empty())
{
AgentGoalList::iterator headGoalIt = goals.begin();
AgentGoal& goal = (*headGoalIt);
switch (goal.Status)
{
// Start up the goal
case AgentGoal::GoalStatus::Initialized:
// TODO: This IsSubscribed call will go away once PlanManager manages multiple
// plans for a single entity. For now, we'll just wait until the entity is no
// longer subscribed before adding our goal
if (PlanManager && !PlanManager->IsSubscribed(currentEntity))
{
if (goal.Type == AgentGoal::GoalType::GetResource)
{
LOGD_IF(DebugPrint)
<< "Agent starting GetResource goal plan (not actually hooked up)";
goal.Status = AgentGoal::GoalStatus::InProgress;
/*gv::PooledComponent<PlanComponentData> newPlanComponent;
Htn::Parameter resourceToFind;
resourceToFind.IntValue = goal.WorldResource;
resourceToFind.Type = Htn::Parameter::ParamType::Int;
Htn::ParameterList parameters = {resourceToFind};
Htn::TaskCall findAgentCall{testGetResourceTask.GetTask(), parameters};
Htn::TaskCallList findAgentTasks = {findAgentCall};
newPlanComponent.entity = currentEntity;
newPlanComponent.data.Tasks.insert(newPlanComponent.data.Tasks.end(),
GetResourceTasks.begin(),
GetResourceTasks.end());
newPlans.push_back(newPlanComponent);*/
}
}
else
{
LOG_ERROR << "Agent trying to start goal but no Plan Manager is set!";
goals.erase(headGoalIt);
}
break;
case AgentGoal::GoalStatus::Failed:
case AgentGoal::GoalStatus::Succeeded:
goals.erase(headGoalIt);
break;
default:
break;
}
}
}
if (PlanManager && !newPlans.empty())
PlanManager->SubscribeEntities(newPlans);
UnsubscribeEntities(entitiesToUnsubscribe);
}
void AgentComponentManager::SubscribeEntitiesInternal(const EntityList& subscribers,
AgentComponentRefList& components)
{
for (gv::PooledComponent<AgentComponentData>* currentComponent : components)
{
if (!currentComponent)
continue;
}
LOGD_IF(DebugPrint) << "AgentComponentManager: Subscribed " << subscribers.size()
<< " entities";
}
void AgentComponentManager::UnsubscribeEntitiesInternal(const EntityList& unsubscribers,
AgentComponentRefList& components)
{
for (gv::PooledComponent<AgentComponentData>* currentComponent : components)
{
if (!currentComponent)
continue;
// Perform unsubscription
}
LOGD_IF(unsubscribers.size()) << "AgentComponentManager: Unsubscribed " << unsubscribers.size()
<< " entities";
}
}

69
src/game/agent/AgentComponentManager.hpp

@ -0,0 +1,69 @@
#pragma once
#include "../../entityComponentSystem/PooledComponentManager.hpp"
#include "PlanComponentManager.hpp"
#include "Needs.hpp"
namespace gv
{
struct AgentGoal
{
enum class GoalStatus
{
None = 0,
Initialized,
InProgress,
Failed,
Succeeded
};
GoalStatus Status;
enum class GoalType
{
None = 0,
GetResource,
GoalType_Count
};
GoalType Type;
WorldResourceType WorldResource;
};
typedef std::vector<AgentGoal> AgentGoalList;
struct AgentComponentData
{
bool IsAlive = true;
NeedList Needs;
AgentGoalList Goals;
};
class AgentComponentManager : public PooledComponentManager<AgentComponentData>
{
private:
// TODO: Eventually this will be something more complicated
float WorldTime;
PlanComponentManager* PlanManager;
protected:
typedef std::vector<PooledComponent<AgentComponentData>*> AgentComponentRefList;
virtual void SubscribeEntitiesInternal(const EntityList& subscribers,
AgentComponentRefList& components);
virtual void UnsubscribeEntitiesInternal(const EntityList& unsubscribers,
AgentComponentRefList& components);
public:
typedef std::vector<PooledComponent<AgentComponentData>> AgentComponentList;
bool DebugPrint = false;
AgentComponentManager();
virtual ~AgentComponentManager();
void Initialize(PlanComponentManager* newPlanComponentManager);
virtual void Update(float deltaSeconds);
};
};

45
src/game/agent/Needs.hpp

@ -0,0 +1,45 @@
#pragma once
#include <vector>
#include "../../world/WorldResourceLocator.hpp"
namespace gv
{
struct NeedLevelTrigger
{
// Conditions
bool GreaterThanLevel;
float Level;
// Actions
bool NeedsResource;
WorldResourceType WorldResource;
bool DieNow;
};
typedef std::vector<NeedLevelTrigger> NeedLevelTriggerList;
struct NeedDef
{
const char* Name;
float UpdateRate;
float AddPerUpdate;
NeedLevelTriggerList LevelTriggers;
};
struct Need
{
NeedDef* Def;
float Level;
float LastUpdateTime;
};
typedef std::vector<Need> NeedList;
}

33
src/game/agent/PlanComponentManager.cpp

@ -19,6 +19,8 @@ void PlanComponentManager::Initialize(WorldStateManager* newWorldStateManager)
worldStateManager = newWorldStateManager;
}
void OnNotify(const Htn::TaskEventList& events, void* userData);
void PlanComponentManager::Update(float deltaSeconds)
{
EntityList entitiesToUnsubscribe;
@ -66,8 +68,8 @@ void PlanComponentManager::Update(float deltaSeconds)
// Don't get rid of the task - execute again
break;
case Htn::TaskExecuteStatus::Subscribe:
if (status.Subject)
status.Subject->AddObserver(this);
if (status.EventCallbackContainer)
status.EventCallbackContainer->AddCallback(&OnNotify, this);
componentPlanner.FinalCallList.erase(currentStep);
break;
@ -104,9 +106,12 @@ void PlanComponentManager::Update(float deltaSeconds)
{
if (status == Htn::Planner::Status::PlanComplete)
{
LOGD_IF(DebugPrint) << "PlanComponentManager: Sucessful plan for Entity "
<< currentComponent->entity << "! Final Call List:";
Htn::PrintTaskCallList(componentPlanner.FinalCallList);
if (DebugPrint)
{
LOGD << "PlanComponentManager: Sucessful plan for Entity "
<< currentComponent->entity << "! Final Call List:";
Htn::PrintTaskCallList(componentPlanner.FinalCallList);
}
// We'll execute the plan next Update()
}
@ -131,10 +136,18 @@ void PlanComponentManager::Update(float deltaSeconds)
UnsubscribeEntities(entitiesToUnsubscribe);
}
void PlanComponentManager::OnNotify(const Htn::TaskEvent& event)
void OnNotify(const Htn::TaskEventList& events, void* userData)
{
LOGD_IF(DebugPrint) << "Entity [" << event.entity << "] Task notify returned status "
<< (int)event.Result;
if (userData)
{
PlanComponentManager* planManager = (PlanComponentManager*)userData;
for (const Htn::TaskEvent& event : events)
{
LOGD_IF(planManager->DebugPrint) << "Entity [" << event.entity
<< "] Task notify returned status "
<< (int)event.Result;
}
}
}
void PlanComponentManager::SubscribeEntitiesInternal(const EntityList& subscribers,
@ -154,10 +167,10 @@ void PlanComponentManager::SubscribeEntitiesInternal(const EntityList& subscribe
// TODO: This is not kosher
planner.CurrentStatus = Htn::Planner::Status::Running_SuccessfulPrimitive;
planner.DebugPrint = true;
planner.DebugPrint = DebugPrint;
}
LOGD_IF(DebugPrint) << "PlanComponentManager: Subscribed " << components.size() << " entities";
LOGD_IF(DebugPrint) << "PlanComponentManager: Subscribed " << subscribers.size() << " entities";
}
void PlanComponentManager::UnsubscribeEntitiesInternal(const EntityList& unsubscribers,

6
src/game/agent/PlanComponentManager.hpp

@ -4,7 +4,6 @@
#include "../../ai/htn/HTNPlanner.hpp"
#include "../../ai/htn/HTNTypes.hpp"
#include "../../ai/WorldState.hpp"
#include "../../util/SubjectObserver.hpp"
namespace gv
{
@ -24,8 +23,7 @@ TODO: PooledComponentManager is going to need to be discarded in order to handle
plans.
*/
// TODO: Multiple inheritance :(
class PlanComponentManager : public gv::PooledComponentManager<PlanComponentData>,
public gv::Observer<Htn::TaskEvent>
class PlanComponentManager : public gv::PooledComponentManager<PlanComponentData>
{
private:
WorldStateManager* worldStateManager;
@ -47,7 +45,5 @@ public:
virtual ~PlanComponentManager();
void Initialize(WorldStateManager* newWorldStateManager);
virtual void Update(float deltaSeconds);
virtual void OnNotify(const Htn::TaskEvent& event);
};
};

40
src/project/galavantSublime/galavant.sublime-project

@ -27,22 +27,13 @@
],
"build_systems":
[
// Misc. Commands
{
"name": "List",
"shell_cmd": "ls -l"
},
{
"name": "Compile C++11 File",
"cmd": ["g++", "-c", "-std=c++11", "-g", "-Os", "-Wall", "$file"],
"file_regex": "^(..[^:]*):([0-9]+):?([0-9]+)?:? (.*)$",
"selector": "source.c++"
},
{
"name": "Build All Experiments",
"shell_cmd": "make",
"working_dir": "../../../src/experiments"
},
// Jam
{
"name": "Jam Current Directory",
"shell_cmd": "jam -j4 -q"
@ -61,7 +52,20 @@
"name": "Jam Unreal",
"shell_cmd": "jam -j4 -q -sUNREAL=true GalavantPseudotarget",
"working_dir": "../../../../galavant"
}
},
// Misc. C++ Commands
{
"name": "Compile C++11 File",
"cmd": ["g++", "-c", "-std=c++11", "-g", "-Os", "-Wall", "$file"],
"file_regex": "^(..[^:]*):([0-9]+):?([0-9]+)?:? (.*)$",
"selector": "source.c++"
},
{
"name": "Build All Experiments",
"shell_cmd": "make",
"working_dir": "../../../src/experiments"
},
],
"settings":
@ -75,17 +79,17 @@
"commandline": "gdb --interpreter=mi ./UE4Editor /home/macoy/Development/code/repositories/galavant-unreal/GalavantUnreal/GalavantUnreal.uproject"
},
"UnrealEditor Debug":
{
"workingdir": "/home/macoy/Downloads/UnrealEngine/Engine/Binaries/Linux",
"commandline": "gdb --interpreter=mi ./UE4Editor-Linux-Debug /home/macoy/Development/code/repositories/galavant-unreal/GalavantUnreal/GalavantUnreal.uproject"
},
"htnTest":
{
"workingdir": "../../../bin",
"commandline": "gdb --interpreter=mi ./htnTest"
},
// "second_executable_name":
// {
// "workingdir": "${folder:${project_path:second_executable_name}}",
// "commandline": "gdb --interpreter=mi ./second_executable"
// }
}
}
}

2
src/unitTesting/Jamfile

@ -8,7 +8,7 @@ LinkLibraries entityComponentTest : libGalaEntityComponent ;
Main objectPoolTest : ObjectPoolTest.cpp ;
Main htnTest : HTN_test.cpp ;
LinkLibraries htnTest : libGalaAi ;
LinkLibraries htnTest : libGalaAi libGalaUtil ;
Main positionTest : Position_test.cpp ;
LinkLibraries positionTest : libGalaWorld ;

4
src/unitTesting/Log_test.cpp

@ -4,9 +4,9 @@
#include "../../thirdParty/Catch/single_include/catch.hpp"
#include <plog/Log.h>
// for MyAppender
#include <plog/Formatters/FuncMessageFormatter.h>
#include <plog/Appenders/ColorConsoleAppender.h>
#include <list>
#include "../util/Logging.hpp"

52
src/util/CallbackContainer.hpp

@ -0,0 +1,52 @@
#pragma once
#include <vector>
namespace gv
{
template <class CallbackType>
struct CallbackCall
{
CallbackType Callback;
void* UserData;
};
template <class CallbackType>
class CallbackContainer
{
public:
CallbackContainer() = default;
~CallbackContainer() = default;
// It's your job to actually call the callbacks
std::vector<CallbackCall<CallbackType>> Callbacks;
typename std::vector<CallbackCall<CallbackType>>::iterator FindCallback(const CallbackType callbackToFind,
void* UserData)
{
for (typename std::vector<CallbackCall<CallbackType>>::iterator callbackIt = Callbacks.begin();
callbackIt != Callbacks.end(); ++callbackIt)
{
if ((*callbackIt).Callback == callbackToFind && (*callbackIt).UserData == UserData)
return callbackIt;
}
return Callbacks.end();
}
void AddCallback(const CallbackType callbackToAdd, void* UserData)
{
if (callbackToAdd && FindCallback(callbackToAdd, UserData) == Callbacks.end())
{
Callbacks.push_back({callbackToAdd, UserData});
}
}
void RemoveCallback(const CallbackType callback, void* UserData)
{
typename std::vector<CallbackCall<CallbackType>>::iterator foundCallbackIt =
FindCallback(callback, UserData);
if (foundCallbackIt != Callbacks.end())
Callbacks.erase(foundCallbackIt);
}
};
}

49
src/util/SubjectObserver.hpp

@ -1,49 +0,0 @@
#pragma once
#include <vector>
#include "../entityComponentSystem/EntityTypes.hpp"
namespace gv
{
template <class T>
class Observer
{
public:
Observer() = default;
virtual ~Observer() = default;
virtual void OnNotify(const T& event) = 0;
};
template <class T>
class Subject
{
private:
std::vector<Observer<T>*> Observers;
public:
Subject() = default;
~Subject() = default;
void AddObserver(Observer<T>* observer)
{
if (observer && std::find(Observers.begin(), Observers.end(), observer) == Observers.end())
Observers.push_back(observer);
}
void RemoveObserver(Observer<T>* observer)
{
typename std::vector<Observer<T>*>::iterator foundObserver =
std::find(Observers.begin(), Observers.end(), observer);
if (foundObserver != Observers.end())
Observers.remove(foundObserver);
}
// TODO: This could be improved by sending all events in one Notify...
void Notify(const T& event)
{
for (Observer<T>* currentObserver : Observers)
currentObserver->OnNotify(event);
}
};
};

2
src/world/Jamfile

@ -2,6 +2,6 @@ SubDir . src world ;
SubDirC++Flags $(ALLLIBSC++FLAGS) ;
Library libGalaWorld : Position.cpp ;
Library libGalaWorld : Position.cpp WorldResourceLocator.cpp ;
MakeLocate libGalaWorld.a : lib ;

109
src/world/WorldResourceLocator.cpp

@ -0,0 +1,109 @@
#include "GalavantUnreal.h"
#include "TestWorldResourceLocator.hpp"
#include <limits>
static const unsigned int DEFAULT_RESOURCELIST_SIZE = 10;
typedef std::map<WorldResourceType, ResourceList*> ResourceMap;
static ResourceMap s_Resources;
void WorldResourceLocator_ClearResources()
{
for (std::pair<const WorldResourceType, ResourceList*>& resourceTypeList : s_Resources)
{
delete resourceTypeList.second;
}
s_Resources.clear();
}
bool WorldResourceLocator_ResourceListExists(const WorldResourceType type) const
{
return s_Resources.find(type) != s_Resources.end();
}
bool WorldResourceLocator_ResourceExistsInWorld(const WorldResourceType type)
{
return WorldResourceLocator_ResourceListExists(type) && !s_Resources[type]->empty();
}
void WorldResourceLocator_AddResource(const WorldResourceType type, const gv::Position& location)
{
gv::Position newResource(location);
// Ensure we're not exactly 0,0,0 because I designed this poorly
newResource.Z = !newResource ? 0.1f : newResource.Z;
if (WorldResourceLocator_ResourceListExists(type))
{
s_Resources[type]->push_back(newResource);
}
else
{
ResourceList* newResourceList = new ResourceList(DEFAULT_RESOURCELIST_SIZE);
newResourceList->push_back(newResource);
s_Resources[type] = newResourceList;
}
}
void WorldResourceLocator_RemoveResource(const WorldResourceType type, const gv::Position& location)
{
if (WorldResourceLocator_ResourceListExists(type))
{
ResourceList* resourceList = s_Resources[type];
ResourceList::iterator resourceIt =
std::find(resourceList->begin(), resourceList->end(), location);
if (resourceIt != resourceList->end())
resourceList->erase(resourceIt);
}
}
void WorldResourceLocator_MoveResource(const WorldResourceType type,
const gv::Position& oldLocation,
const gv::Position& newLocation)
{
if (WorldResourceLocator_ResourceListExists(type))
{
for (gv::Position& currentResource : *s_Resources[type])
{
// They should be exactly equal. It's the caller's responsibility to keep track of this
if (currentResource.Equals(oldLocation, 0.f))
{
currentResource = newLocation;
// Ensure we're not exactly 0,0,0 because I designed this poorly
currentResource.Z = !currentResource ? 0.1f : currentResource.Z;
break;
}
}
}
}
// Find the nearest resource. Uses Manhattan distance
// Manhattan distance of -1 indicates no resource was found
gv::Position WorldResourceLocator_FindNearestResource(const WorldResourceType type,
const gv::Position& location,
bool allowSameLocation, float& manhattanToOut)
{
gv::Position zeroPosition;
if (WorldResourceLocator_ResourceListExists(type))
{
gv::Position closestResource;
float closestResourceDistance = std::numeric_limits<float>::max();
for (gv::Position& currentResource : *s_Resources[type])
{
float currentResourceDistance = location.ManhattanTo(currentResource);
if (currentResourceDistance < closestResourceDistance &&
(allowSameLocation || currentResourceDistance > 0.f))
{
closestResourceDistance = currentResourceDistance;
closestResource = currentResource;
}
}
manhattanToOut = closestResourceDistance;
return closestResource;
}
manhattanToOut = -1.f;
return zeroPosition;
}

36
src/world/WorldResourceLocator.hpp

@ -0,0 +1,36 @@
#pragma once
#include <map>
#include <vector>
#include "Position.hpp"
namespace gv
{
enum WorldResourceType
{
None = 0,
Agent = 1,
BusStop = 2
};
typedef std::vector<gv::Position> ResourceList;
void WorldResourceLocator_ClearResources();
bool WorldResourceLocator_ResourceExistsInWorld(const WorldResourceType type);
void WorldResourceLocator_AddResource(const WorldResourceType type, const gv::Position& location);
void WorldResourceLocator_RemoveResource(const WorldResourceType type,
const gv::Position& location);
void WorldResourceLocator_MoveResource(const WorldResourceType type,
const gv::Position& oldLocation,
const gv::Position& newLocation);
// Find the nearest resource. Uses Manhattan distance
// Manhattan distance of -1 indicates no resource was found
gv::Position WorldResourceLocator_FindNearestResource(const WorldResourceType type,
const gv::Position& location,
bool allowSameLocation,
float& manhattanToOut);
}
Loading…
Cancel
Save