Browse Source

Pickups work; bug fixing

- Fixed frustrating bug in PooledComponentManager where member Subscribers was being used instead of ComponentManager Subscribers. The solution was to get rid of PooledComponentManager's Subscribers declaration
- ComponentManager now only goes all the way with unsubscribing if there are entities left to unsubscribe
- Fixed an apparent bug in EntityListSortAndRemoveDuplicates() and EntityListRemoveNonUniqueEntitiesInSuspect() where the iterator wouldn't be advanced on removal
- Filled in InteractComponentManager and added PickupDirect() which puts a pickup straight into an entity's need (for testing purposes)
- Added NeedTypes and AgentComponentManager interface to get an agent's specific need
- Added InteractPickupTask, which uses the PickupDirect command on a TargetEntity
- FindResource now marks the found resource as the TargetEntity
- WorldResourceLocator now stores an Entity and a ResourceType on the Resource, facilitating Pickups as well as making the zero position shenanigans go away
- Got all the Sublime project working dirs sorted out and added a bunch of helpful build systems (for me, at least)
- Added my TODO file to remember where I left off and such
combatComponentRefactor
Macoy Madson 6 years ago
parent
commit
b7c661b4df
  1. 8
      Jamfile
  2. 2
      Jamrules
  3. 17
      TODO.txt
  4. 21
      src/ai/WorldState.hpp
  5. 1
      src/ai/htn/HTNTaskDb.hpp
  6. 30
      src/entityComponentSystem/ComponentManager.cpp
  7. 2
      src/entityComponentSystem/ComponentManager.hpp
  8. 22
      src/entityComponentSystem/EntityTypes.cpp
  9. 3
      src/entityComponentSystem/EntityTypes.hpp
  10. 5
      src/entityComponentSystem/Jamfile
  11. 7
      src/entityComponentSystem/PooledComponentManager.hpp
  12. BIN
      src/experiments/flatbuffers/testFlatbuffers_write
  13. 71
      src/game/InteractComponentManager.cpp
  14. 14
      src/game/InteractComponentManager.hpp
  15. 6
      src/game/Jamfile
  16. 40
      src/game/agent/AgentComponentManager.cpp
  17. 2
      src/game/agent/AgentComponentManager.hpp
  18. 18
      src/game/agent/MovementManager.hpp
  19. 13
      src/game/agent/NeedTypes.hpp
  20. 15
      src/game/agent/Needs.hpp
  21. 14
      src/game/agent/PlanComponentManager.cpp
  22. 48
      src/game/agent/htnTasks/InteractTasks.cpp
  23. 30
      src/game/agent/htnTasks/InteractTasks.hpp
  24. 126
      src/game/agent/htnTasks/MovementTasks.cpp
  25. 64
      src/game/agent/htnTasks/MovementTasks.hpp
  26. 60
      src/project/galavantSublime/galavant.sublime-project
  27. 2
      src/world/Position.cpp
  28. 1
      src/world/Position.hpp
  29. 53
      src/world/WorldResourceLocator.cpp
  30. 28
      src/world/WorldResourceLocator.hpp

8
Jamfile

@ -5,7 +5,13 @@ SubDirC++Flags $(ALLLIBSC++FLAGS) ;
# Lazy pseudotarget for Galavant - make a dummy executable which will make Jam build the libs we're
# actually going to use. There's definitely a better way to do this but I'm too lazy to find it
Main GalavantPseudotarget : src/main.cpp ;
LinkLibraries GalavantPseudotarget : libGalaUtil libGalavant libGalaThirdPartyWrapper libGalaEntityComponent libGalaAi libGalaWorld libGalaGame ;
LinkLibraries GalavantPseudotarget : libGalaUtil
libGalavant
libGalaThirdPartyWrapper
libGalaEntityComponent
libGalaAi
libGalaWorld
libGalaGame ;
MakeLocate GalavantPseudotarget : bin ;

2
Jamrules

@ -62,7 +62,7 @@ else
OPTIM = -O0 ;
HDRS = thirdParty/flatbuffers/include thirdParty/plog/include ;
HDRS = thirdParty/flatbuffers/include thirdParty/plog/include src ;
# TODO: add project-specific filetype rules for things like flatbuffer .json compilation (see "UserObject rule":
# https://swarm.workshop.perforce.com/view/guest/perforce_software/jam/src/Jamfile.html ) ?

17
TODO.txt

@ -0,0 +1,17 @@
TODO
Soak test editor - after ~1 hour it was looking pretty glitchy
What does agent component manager do with triggers once plan is done? How to know plan is done?
statics don't work well with hot reloading
FragmentedPool crashed during in-editor compile hotreload
Still hitting TestMovementComponent segfault. Need a way to detect when an entity is destroyed outside GV control
Added code to EndPlay() which unsubscribes shit manually
Play with Polyvox chunk scale
**** LEFT OFF AT:
InteractTask added, it should be hooked up and working

21
src/ai/WorldState.hpp

@ -9,18 +9,28 @@ namespace gv
{
struct AgentState
{
//
// Immutable by tasks
//
Entity SourceEntity;
Position position;
//
// Mutable by tasks
//
Entity TargetEntity;
Position TargetPosition;
};
/* --WorldState--
WorldState represents a mutable, copyable reference to all AI-relevant data in the World.
Mutable: The data can be manipulated freely without repurcussion. Note that changing data in
WorldState is NOT expected to actually change the world - WorldState is like a mirror world
Copyable: The data can be copied without a significant performance impact. This means that in
order to support mutability, things like changelists might need to be implemented for large
datasets instead of actually copying the dataset
Mutable: The data can be manipulated freely without repurcussion. Note that changing data in
WorldState is NOT expected to actually change the world - WorldState is like a mirror world
Copyable: The data can be copied without a significant performance impact. This means that in
order to support mutability, things like changelists might need to be implemented for large
datasets instead of actually copying the dataset
*/
struct WorldState
{
@ -37,6 +47,7 @@ class WorldStateManager
private:
typedef std::map<Entity, WorldState> EntityWorldStateMap;
EntityWorldStateMap EntityWorldStates;
public:
WorldState& GetWorldStateForAgent(Entity agent);
};

1
src/ai/htn/HTNTaskDb.hpp

@ -16,6 +16,7 @@ enum class TaskName
// Primitives
FindResource,
MoveTo,
InteractPickup,
TaskName_count
};

30
src/entityComponentSystem/ComponentManager.cpp

@ -1,4 +1,5 @@
#include "ComponentManager.hpp"
#include "util/Logging.hpp"
namespace gv
{
@ -13,17 +14,30 @@ void ComponentManager::UnsubscribeEntitiesInternal(const EntityList& entities)
void ComponentManager::UnsubscribeEntities(const EntityList& entities)
{
// Copy for modification
EntityList entitiesToUnsubscribe;
EntityListAppendList(entitiesToUnsubscribe, entities);
if (!entities.empty())
{
// Copy for modification
EntityList entitiesToUnsubscribe;
EntityListAppendList(entitiesToUnsubscribe, entities);
// Make sure they're actually subscribed
EntityListRemoveUniqueEntitiesInSuspect(Subscribers, entitiesToUnsubscribe);
// Make sure they're actually subscribed
EntityListRemoveUniqueEntitiesInSuspect(Subscribers, entitiesToUnsubscribe);
UnsubscribeEntitiesInternal(entitiesToUnsubscribe);
if (!entitiesToUnsubscribe.empty())
{
UnsubscribeEntitiesInternal(entitiesToUnsubscribe);
// Remove from subscribers
EntityListRemoveNonUniqueEntitiesInSuspect(entitiesToUnsubscribe, Subscribers);
// Remove from subscribers
EntityListRemoveNonUniqueEntitiesInSuspect(entitiesToUnsubscribe, Subscribers);
LOGD << "Manager " << (int)Type << " unsubscribed " << entities.size() << " entities";
}
}
}
bool ComponentManager::IsSubscribed(Entity entity)
{
return EntityListFindEntity(Subscribers, entity);
}
ComponentType ComponentManager::GetType()

2
src/entityComponentSystem/ComponentManager.hpp

@ -26,6 +26,8 @@ public:
// non-subscribers), then once you're done it removes the entities from the Subscribers list
void UnsubscribeEntities(const EntityList& entities);
bool IsSubscribed(Entity entity);
ComponentType GetType();
};
};

22
src/entityComponentSystem/EntityTypes.cpp

@ -34,7 +34,7 @@ void EntityListSortAndRemoveDuplicates(EntityList& list)
if (lastEntity != NullEntity && currentEntity == lastEntity)
{
list.erase(it);
it = list.erase(it);
continue;
}
@ -45,6 +45,17 @@ void EntityListSortAndRemoveDuplicates(EntityList& list)
}
}
void EntityListAddUniqueEntitiesToSuspect(const EntityList& list, EntityList& suspectList)
{
for (EntityListConstIterator it = list.begin(); it != list.end(); ++it)
{
Entity currentEntity = (*it);
if (!EntityListFindEntity(suspectList, currentEntity))
suspectList.push_back(currentEntity);
}
}
// Remove all entities from suspectList which are already in list
void EntityListRemoveNonUniqueEntitiesInSuspect(const EntityList& list, EntityList& suspectList)
{
@ -57,7 +68,7 @@ void EntityListRemoveNonUniqueEntitiesInSuspect(const EntityList& list, EntityLi
if (suspectEntity == currentEntity)
{
suspectList.erase(sIt);
sIt = suspectList.erase(sIt);
// don't assume there's only one non-unique; suspectList could have duplicates
// break;
@ -91,13 +102,13 @@ void EntityListRemoveUniqueEntitiesInSuspect(const EntityList& list, EntityList&
if (found)
it++;
else
suspectList.erase(it);
it = suspectList.erase(it);
}
}
bool EntityListFindEntity(EntityList& list, Entity entity)
bool EntityListFindEntity(const EntityList& list, Entity entity)
{
for (EntityListIterator it = list.begin(); it != list.end(); ++it)
for (EntityListConstIterator it = list.begin(); it != list.end(); ++it)
{
Entity currentEntity = (*it);
if (currentEntity == entity)
@ -105,5 +116,4 @@ bool EntityListFindEntity(EntityList& list, Entity entity)
}
return false;
}
}

3
src/entityComponentSystem/EntityTypes.hpp

@ -22,6 +22,7 @@ void EntityListAppendList(EntityList& list, const EntityList& listToAdd);
void EntityListSort(EntityList& list);
void EntityListSortAndRemoveDuplicates(EntityList& list);
void EntityListAddUniqueEntitiesToSuspect(const EntityList& list, EntityList& suspectList);
// Remove all entities from suspectList which are already in list
// This is bad. At least the name, possibly also the function itself.
void EntityListRemoveNonUniqueEntitiesInSuspect(const EntityList& list, EntityList& suspectList);
@ -29,5 +30,5 @@ void EntityListRemoveUniqueEntitiesInSuspect(const EntityList& list, EntityList&
// Linear search for entity. I'll eventually add a binary search function if it can be assumed that
// the list is sorted
bool EntityListFindEntity(EntityList& list, Entity entity);
bool EntityListFindEntity(const EntityList& list, Entity entity);
};

5
src/entityComponentSystem/Jamfile

@ -2,7 +2,10 @@ SubDir . src entityComponentSystem ;
SubDirC++Flags $(ALLLIBSC++FLAGS) ;
Library libGalaEntityComponent : EntityTypes.cpp EntitySharedData.cpp EntityComponentManager.cpp ComponentManager.cpp ;
Library libGalaEntityComponent : EntityTypes.cpp
EntitySharedData.cpp
EntityComponentManager.cpp
ComponentManager.cpp ;
LinkLibraries libGalaEntityComponent : libGalaWorld ;
MakeLocate libGalaEntityComponent.a : lib ;

7
src/entityComponentSystem/PooledComponentManager.hpp

@ -53,15 +53,10 @@ struct PooledComponent
template <class T>
class PooledComponentManager : public ComponentManager
{
private:
// This list is used only to quickly check whether an entity is subscribed; data should actually
// be stored in PooledComponents
EntityList Subscribers;
protected:
// TODO: Replace FragmentedPool with a better pool
FragmentedPool<PooledComponent<T>> PooledComponents;
protected:
typedef int FragmentedPoolIterator;
const int NULL_POOL_ITERATOR = -1;

BIN
src/experiments/flatbuffers/testFlatbuffers_write

Binary file not shown.

71
src/game/InteractComponentManager.cpp

@ -1,5 +1,8 @@
#include "InteractComponentManager.hpp"
#include "../entityComponentSystem/EntityComponentManager.hpp"
#include "agent/AgentComponentManager.hpp"
namespace gv
{
void InteractComponentManager::UnsubscribeEntitiesInternal(const EntityList& entities)
@ -23,11 +26,73 @@ void InteractComponentManager::UnsubscribeEntitiesInternal(const EntityList& ent
}
}
void InteractComponentManager::CreatePickups(int count, PickupRefList& newPickups)
void InteractComponentManager::CreatePickups(const EntityList& entities, PickupRefList& newPickups)
{
int numEntitiesCreated = 0;
unsigned int endBeforeResize = Pickups.size();
Pickups.resize(Pickups.size() + count);
Pickups.resize(Pickups.size() + entities.size());
for (unsigned int i = endBeforeResize; i < Pickups.size(); i++)
newPickups.push_back(&Pickups[i]);
{
Pickup* newPickup = &Pickups[i];
newPickup->entity = entities[numEntitiesCreated++];
newPickups.push_back(newPickup);
}
EntityListAddUniqueEntitiesToSuspect(entities, Subscribers);
}
Pickup* InteractComponentManager::GetPickup(Entity pickupEntity)
{
for (Pickup& pickup : Pickups)
{
if (pickup.entity == pickupEntity)
return &pickup;
}
return nullptr;
}
bool InteractComponentManager::PickupDirect(Entity pickupEntity, Entity claimer)
{
if (EntityListFindEntity(Subscribers, pickupEntity))
{
// TODO: This will eventually pipe pickups into inventory, but for testing, we're going to
// put the pickup directly into the need
EntityComponentManager* entityComponentManager = EntityComponentManager::GetSingleton();
if (entityComponentManager)
{
AgentComponentManager* agentComponentManager = static_cast<AgentComponentManager*>(
entityComponentManager->GetComponentManagerForType(ComponentType::Agent));
if (agentComponentManager && agentComponentManager->IsSubscribed(claimer))
{
Pickup* pickup = GetPickup(pickupEntity);
if (!pickup)
return false;
Need* needPickupAffects =
agentComponentManager->GetAgentNeed(claimer, pickup->AffectsNeed);
// TODO: For testing only
if (needPickupAffects)
{
needPickupAffects->Level += 100;
LOGV << "Entity " << claimer << " restored need "
<< needPickupAffects->Def->Name << " by 100 picking up " << pickupEntity;
return true;
}
// TODO: Are we allocating for this? :(
EntityList entitiesPickedUp;
entitiesPickedUp.push_back(pickupEntity);
UnsubscribeEntities(entitiesPickedUp);
if (pickup->DestroySelfOnPickup)
entityComponentManager->MarkDestroyEntities(entitiesPickedUp);
}
}
}
return false;
}
}

14
src/game/InteractComponentManager.hpp

@ -1,13 +1,19 @@
#pragma once
#include "../entityComponentSystem/ComponentManager.hpp"
#include "../entityComponentSystem/ComponentTypes.hpp"
#include "../world/WorldResourceLocator.hpp"
#include "agent/NeedTypes.hpp"
namespace gv
{
struct Pickup
{
Entity entity;
WorldResourceType ResourceType;
NeedType AffectsNeed;
// Destroy the entire entity when picked up
bool DestroySelfOnPickup;
};
typedef std::vector<Pickup> PickupList;
typedef std::vector<Pickup*> PickupRefList;
@ -24,6 +30,10 @@ public:
InteractComponentManager() = default;
virtual ~InteractComponentManager() = default;
void CreatePickups(int count, PickupRefList& newPickups);
void CreatePickups(const EntityList& entities, PickupRefList& newPickups);
bool PickupDirect(Entity pickupEntity, Entity claimer);
Pickup* GetPickup(Entity pickupEntity);
};
}

6
src/game/Jamfile

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

40
src/game/agent/AgentComponentManager.cpp

@ -121,14 +121,19 @@ void AgentComponentManager::Update(float deltaSeconds)
{
if (goal.Type == AgentGoal::GoalType::GetResource)
{
gv::PooledComponent<PlanComponentData> newPlanComponent;
Htn::Parameter resourceToFind;
resourceToFind.IntValue = goal.WorldResource;
resourceToFind.Type = Htn::Parameter::ParamType::Int;
Htn::ParameterList emptyParams; // TODO: For fuck's sake
Htn::ParameterList parameters = {resourceToFind};
Htn::TaskCall getResourceCall{
Htn::TaskDb::GetTask(Htn::TaskName::GetResource), parameters};
Htn::TaskCallList getResourceTasks = {getResourceCall};
Htn::TaskCall pickupResourceCall{
Htn::TaskDb::GetTask(Htn::TaskName::InteractPickup), parameters};
Htn::TaskCallList getResourceTasks = {getResourceCall,
pickupResourceCall};
gv::PooledComponent<PlanComponentData> newPlanComponent;
newPlanComponent.entity = currentEntity;
newPlanComponent.data.Tasks.insert(newPlanComponent.data.Tasks.end(),
getResourceTasks.begin(),
@ -159,7 +164,8 @@ void AgentComponentManager::Update(float deltaSeconds)
if (PlanManager && !newPlans.empty())
PlanManager->SubscribeEntities(newPlans);
UnsubscribeEntities(entitiesToUnsubscribe);
if (!entitiesToUnsubscribe.empty())
UnsubscribeEntities(entitiesToUnsubscribe);
}
void AgentComponentManager::SubscribeEntitiesInternal(const EntityList& subscribers,
@ -224,4 +230,32 @@ void AgentComponentManager::GetAgentConsciousStates(const EntityList& entities,
stateListOut.push_back(AgentConsciousState::None);
}
}
Need* AgentComponentManager::GetAgentNeed(Entity entity, NeedType needType)
{
// 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;
if (currentEntity == entity)
{
for (Need& need : currentComponent->data.Needs)
{
if (need.Type == needType)
return &need;
}
}
}
return nullptr;
}
}

2
src/game/agent/AgentComponentManager.hpp

@ -87,5 +87,7 @@ public:
virtual void Update(float deltaSeconds);
void GetAgentConsciousStates(const EntityList& entities, AgentConsciousStateList& stateListOut);
Need* GetAgentNeed(Entity entity, NeedType needType);
};
};

18
src/game/agent/MovementManager.hpp

@ -0,0 +1,18 @@
#pragma once
#include "../../entityComponentSystem/EntityTypes.hpp"
#include "../../world/Position.hpp"
namespace gv
{
/* --MovementManager--
This was needed so that I could have a heavily Unreal-specific class (TestMovementComponent) without
needing to compromise the Galavant lib. I'll probably think of a better way to do this eventually
*/
class MovementManager
{
public:
virtual ~MovementManager() = default;
virtual void PathEntitiesTo(const EntityList& entities, const PositionList& positions) = 0;
};
}

13
src/game/agent/NeedTypes.hpp

@ -0,0 +1,13 @@
#pragma once
namespace gv
{
enum class NeedType : unsigned int
{
None = 0,
Hunger,
NeedType_Count
};
}

15
src/game/agent/Needs.hpp

@ -1,12 +1,13 @@
#pragma once
#include "NeedTypes.hpp"
#include <vector>
#include "../../world/WorldResourceLocator.hpp"
namespace gv
{
struct NeedLevelTrigger
{
// Conditions
@ -17,7 +18,7 @@ struct NeedLevelTrigger
// Actions
bool NeedsResource;
WorldResourceType WorldResource;
bool DieNow;
};
@ -25,6 +26,8 @@ typedef std::vector<NeedLevelTrigger> NeedLevelTriggerList;
struct NeedDef
{
NeedType Type;
const char* Name;
float UpdateRate;
@ -36,9 +39,11 @@ struct NeedDef
struct Need
{
NeedDef* Def;
float Level;
float LastUpdateTime;
NeedType Type = gv::NeedType::None;
NeedDef* Def = nullptr;
float Level = 0;
float LastUpdateTime = 0;
};
typedef std::vector<Need> NeedList;

14
src/game/agent/PlanComponentManager.cpp

@ -169,9 +169,13 @@ void PlanComponentManager::Update(float deltaSeconds)
}
}
LOGD_IF(entitiesToUnsubscribe.size()) << "Unsubscribed " << entitiesToUnsubscribe.size()
<< " entities";
UnsubscribeEntities(entitiesToUnsubscribe);
if (!entitiesToUnsubscribe.empty())
{
LOGD << "Unsubscribing " << entitiesToUnsubscribe.size() << " entities (we have "
<< Subscribers.size() << " subscribers and " << PooledComponents.GetTotalActiveData()
<< " components)";
UnsubscribeEntities(entitiesToUnsubscribe);
}
}
// #Callback: TaskEventCallback
@ -206,12 +210,14 @@ void PlanComponentManager::SubscribeEntitiesInternal(const EntityList& subscribe
planner.DebugPrint = DebugPrint;
}
LOGD_IF(DebugPrint) << "Subscribed " << subscribers.size() << " entities";
LOGD << "Subscribed " << subscribers.size() << " entities";
}
void PlanComponentManager::UnsubscribePoolEntitiesInternal(const EntityList& unsubscribers,
PlanComponentRefList& components)
{
LOGD << "Unsubscribing " << unsubscribers.size() << " unsubscribers (" << components.size()
<< " components)";
for (gv::PooledComponent<PlanComponentData>* currentComponent : components)
{
if (!currentComponent)

48
src/game/agent/htnTasks/InteractTasks.cpp

@ -0,0 +1,48 @@
#include "InteractTasks.hpp"
#include "util/Logging.hpp"
#include "world/Position.hpp"
#include "world/WorldResourceLocator.hpp"
namespace gv
{
void InteractPickupTask::Initialize(InteractComponentManager* newInteractManager)
{
InteractManager = newInteractManager;
}
bool InteractPickupTask::StateMeetsPreconditions(const gv::WorldState& state,
const Htn::ParameterList& parameters) const
{
return state.SourceAgent.TargetEntity && InteractManager &&
InteractManager->IsSubscribed(state.SourceAgent.TargetEntity);
}
void InteractPickupTask::ApplyStateChange(gv::WorldState& state,
const Htn::ParameterList& parameters)
{
// No WorldState records needs yet
}
Htn::TaskExecuteStatus InteractPickupTask::Execute(gv::WorldState& state,
const Htn::ParameterList& parameters)
{
// TODO: should the PlanComponentManager call this before executing any task?
if (StateMeetsPreconditions(state, parameters))
{
if (InteractManager->PickupDirect(state.SourceAgent.TargetEntity,
state.SourceAgent.SourceEntity))
{
LOGD << "Found resource at " << state.SourceAgent.TargetPosition;
Htn::TaskExecuteStatus status{Htn::TaskExecuteStatus::ExecutionStatus::Succeeded};
return status;
}
}
else
LOGD << "State no longer matches preconditions";
LOGD << "Failed to pickup";
Htn::TaskExecuteStatus status{Htn::TaskExecuteStatus::ExecutionStatus::Failed};
return status;
}
}

30
src/game/agent/htnTasks/InteractTasks.hpp

@ -0,0 +1,30 @@
#pragma once
#include "../../../ai/htn/HTNTypes.hpp"
#include "../../../ai/htn/HTNTasks.hpp"
#include "../../../ai/WorldState.hpp"
#include "../../InteractComponentManager.hpp"
namespace gv
{
// Parameters:
// None
// Dependent WorldState:
// TargetEntity
class InteractPickupTask : public Htn::PrimitiveTask
{
private:
InteractComponentManager* InteractManager;
public:
InteractPickupTask() = default;
virtual ~InteractPickupTask() = default;
void Initialize(InteractComponentManager* newInteractManager);
virtual bool StateMeetsPreconditions(const gv::WorldState& state,
const Htn::ParameterList& parameters) const;
virtual void ApplyStateChange(gv::WorldState& state, const Htn::ParameterList& parameters);
virtual Htn::TaskExecuteStatus Execute(gv::WorldState& state,
const Htn::ParameterList& parameters);
};
}

126
src/game/agent/htnTasks/MovementTasks.cpp

@ -0,0 +1,126 @@
#include "MovementTasks.hpp"
#include "util/Logging.hpp"
#include "world/Position.hpp"
#include "world/WorldResourceLocator.hpp"
namespace gv
{
bool FindResourceTask::StateMeetsPreconditions(const gv::WorldState& state,
const Htn::ParameterList& parameters) const
{
return parameters.size() == 1 && parameters[0].Type == Htn::Parameter::ParamType::Int &&
gv::WorldResourceLocator::ResourceExistsInWorld(
(gv::WorldResourceType)parameters[0].IntValue);
}
void FindResourceTask::ApplyStateChange(gv::WorldState& state, const Htn::ParameterList& parameters)
{
// TODO: Should this be the case? Should StateMeetsPreconditions find the position? This isn't
// that much of a problem if ResourceLocator caches searches
float manhattanTo = 0.f;
gv::WorldResourceLocator::Resource* resource = gv::WorldResourceLocator::FindNearestResource(
(gv::WorldResourceType)parameters[0].IntValue, state.SourceAgent.position, false,
manhattanTo);
if (resource)
{
state.SourceAgent.TargetPosition = resource->position;
state.SourceAgent.TargetEntity = resource->entity;
}
}
Htn::TaskExecuteStatus FindResourceTask::Execute(gv::WorldState& state,
const Htn::ParameterList& parameters)
{
float manhattanTo = 0.f;
gv::WorldResourceLocator::Resource* resource = gv::WorldResourceLocator::FindNearestResource(
(gv::WorldResourceType)parameters[0].IntValue, state.SourceAgent.position, false,
manhattanTo);
if (resource)
{
LOGD << "Found resource at " << state.SourceAgent.TargetPosition;
// TODO: Execute and ApplyStateChange duplicate code
state.SourceAgent.TargetPosition = resource->position;
state.SourceAgent.TargetEntity = resource->entity;
Htn::TaskExecuteStatus status{Htn::TaskExecuteStatus::ExecutionStatus::Succeeded};
return status;
}
else
LOGD << "Couldn't find resource!";
LOGD << "Failed to find resource";
Htn::TaskExecuteStatus status{Htn::TaskExecuteStatus::ExecutionStatus::Failed};
return status;
}
void MoveToTask::Initialize(MovementManager* newMovementManager)
{
movementManager = newMovementManager;
}
bool MoveToTask::StateMeetsPreconditions(const gv::WorldState& state,
const Htn::ParameterList& parameters) const
{
// We're good to move to a position as long as we have a target
if (state.SourceAgent.TargetPosition)
return true;
return false;
}
void MoveToTask::ApplyStateChange(gv::WorldState& state, const Htn::ParameterList& parameters)
{
state.SourceAgent.position = state.SourceAgent.TargetPosition;
}
Htn::TaskExecuteStatus MoveToTask::Execute(gv::WorldState& state,
const Htn::ParameterList& parameters)
{
if (movementManager)
{
gv::EntityList entitiesToMove{state.SourceAgent.SourceEntity};
std::vector<gv::Position> positions{state.SourceAgent.TargetPosition};
LOGD << "Moving Ent[" << state.SourceAgent.SourceEntity << "] to "
<< state.SourceAgent.TargetPosition;
movementManager->PathEntitiesTo(entitiesToMove, positions);
Htn::TaskExecuteStatus status{Htn::TaskExecuteStatus::ExecutionStatus::Subscribe};
return status;
}
Htn::TaskExecuteStatus status{Htn::TaskExecuteStatus::ExecutionStatus::Failed};
return status;
}
void GetResourceTask::Initialize(FindResourceTask* newFindResourceTask, MoveToTask* newMoveToTask)
{
FindResourceTaskRef.Initialize(newFindResourceTask);
MoveToTaskRef.Initialize(newMoveToTask);
}
bool GetResourceTask::StateMeetsPreconditions(const gv::WorldState& state,
const Htn::ParameterList& parameters) const
{
// TODO: What is the purpose of the compound task checking preconditions if they'll be near
// identical to the combined primitive task preconditions?
bool parametersValid =
parameters.size() == 1 && parameters[0].Type == Htn::Parameter::ParamType::Int;
LOGD_IF(!parametersValid) << "GetResourceTask parameters invalid! Expected Int";
return parametersValid;
}
bool GetResourceTask::Decompose(Htn::TaskCallList& taskCallList, const gv::WorldState& state,
const Htn::ParameterList& parameters)
{
Htn::ParameterList findResourceParams = parameters;
Htn::ParameterList moveToParams;
Htn::TaskCall FindResourceTaskCall{&FindResourceTaskRef, findResourceParams};
Htn::TaskCall MoveToTaskCall{&MoveToTaskRef, moveToParams};
// TODO: Make it clear that users should only ever push to this list
taskCallList.push_back(FindResourceTaskCall);
taskCallList.push_back(MoveToTaskCall);
return true;
}
}

64
src/game/agent/htnTasks/MovementTasks.hpp

@ -0,0 +1,64 @@
#pragma once
#include "../../../ai/htn/HTNTypes.hpp"
#include "../../../ai/htn/HTNTasks.hpp"
#include "../../../ai/WorldState.hpp"
#include "../MovementManager.hpp"
namespace gv
{
// Parameters:
// [0]: Resource type (int)
class FindResourceTask : public Htn::PrimitiveTask
{
public:
FindResourceTask() = default;
virtual ~FindResourceTask() = default;
void Initialize();
virtual bool StateMeetsPreconditions(const gv::WorldState& state,
const Htn::ParameterList& parameters) const;
virtual void ApplyStateChange(gv::WorldState& state, const Htn::ParameterList& parameters);
virtual Htn::TaskExecuteStatus Execute(gv::WorldState& state,
const Htn::ParameterList& parameters);
};
// Parameters:
// None - Entity to move and target come from world state
class MoveToTask : public Htn::PrimitiveTask
{
private:
MovementManager* movementManager;
public:
MoveToTask() = default;
virtual ~MoveToTask() = default;
void Initialize(MovementManager* newMovementManager);
virtual bool StateMeetsPreconditions(const gv::WorldState& state,
const Htn::ParameterList& parameters) const;
virtual void ApplyStateChange(gv::WorldState& state, const Htn::ParameterList& parameters);
virtual Htn::TaskExecuteStatus Execute(gv::WorldState& state,
const Htn::ParameterList& parameters);
};
// Parameters:
// [0]: Resource type (int)
class GetResourceTask : public Htn::CompoundTask
{
private:
// Should this be how it is? Should tasks be singletons?
Htn::Task FindResourceTaskRef;
Htn::Task MoveToTaskRef;
public:
GetResourceTask() = default;
virtual ~GetResourceTask() = default;
void Initialize(FindResourceTask* newFindResourceTask, MoveToTask* newMoveToTask);
virtual bool StateMeetsPreconditions(const gv::WorldState& state,
const Htn::ParameterList& parameters) const;
virtual bool Decompose(Htn::TaskCallList& taskCallList, const gv::WorldState& state,
const Htn::ParameterList& parameters);
};
}

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

@ -27,10 +27,46 @@
],
"build_systems":
[
// For galavant/, use
// "working_dir": "$project_path/../../.."
// Not sure why, but Sublime will actually keep going up across builds if you just do ../ first
// Misc. Commands
{
"name": "List",
"shell_cmd": "ls -l"
"shell_cmd": "ls -l",
"working_dir": "$project_path/../../.."
},
// Convenience for Macoy
//guake -n MyTabName -e "command args"
{
"name": "Guake Galavant",
"shell_cmd": "guake -n Galavant -e \"cd Development/code/repositories/galavant\"",
},
{
"name": "Guake Galavant Unreal",
"shell_cmd": "guake -n GalavantUnreal -e \"cd Development/code/repositories/galavant-unreal/GalavantUnreal\"",
},
// "Full build"
{
"name": "Full Unreal Build",
"shell_cmd": "cd galavant && jam -j4 -q -sUNREAL=true GalavantPseudotarget && cd ../galavant-unreal/GalavantUnreal && make GalavantUnreal",
"working_dir": "$project_path/../../../.."
},
// Unreal
{
"name": "Unreal Build",
"shell_cmd": "make GalavantUnreal",
"working_dir": "$project_path/../../../../galavant-unreal/GalavantUnreal"
},
{
"name": "Unreal Quick Clean",
"shell_cmd": "rm -r Intermediate/Build",
"working_dir": "$project_path/../../../../galavant-unreal/GalavantUnreal"
},
// Jam
@ -41,17 +77,17 @@
{
"name": "Jam Clean All",
"shell_cmd": "jam clean",
"working_dir": "../../../../galavant"
"working_dir": "$project_path/../../.."
},
{
"name": "Jam Build (not Unreal)",
"shell_cmd": "jam -j4 -q",
"working_dir": "../../../../galavant"
"working_dir": "$project_path/../../.."
},
{
"name": "Jam Unreal",
"name": "Jam Build Unreal",
"shell_cmd": "jam -j4 -q -sUNREAL=true GalavantPseudotarget",
"working_dir": "../../../../galavant"
"working_dir": "$project_path/../../.."
},
// Misc. C++ Commands
@ -64,7 +100,7 @@
{
"name": "Build All Experiments",
"shell_cmd": "make",
"working_dir": "../../../src/experiments"
"working_dir": "$project_path/../../../src/experiments"
},
],
@ -74,7 +110,9 @@
{
"UnrealEditor":
{
// Find and attach to running UE4 instance:
// sudo gdb /home/macoy/Downloads/UnrealEngine/Engine/Binaries/Linux/UE4Editor $(ps -A | grep UE4Editor | awk '{print $1;}')
"workingdir": "/home/macoy/Downloads/UnrealEngine/Engine/Binaries/Linux",
"commandline": "gdb --interpreter=mi ./UE4Editor /home/macoy/Development/code/repositories/galavant-unreal/GalavantUnreal/GalavantUnreal.uproject"
},
@ -85,11 +123,11 @@
"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"
},
//"htnTest":
//{
// "workingdir": "../../../bin",
// "commandline": "gdb --interpreter=mi ./htnTest"
//},
}
}
}

2
src/world/Position.cpp

@ -3,6 +3,8 @@
namespace gv
{
const float POSITION_TOLERANCE = 0.1f;
Position::Position(float x, float y, float z) : X(x), Y(y), Z(z)
{
}

1
src/world/Position.hpp

@ -6,6 +6,7 @@
namespace gv
{
extern const float POSITION_TOLERANCE;
struct Position
{
float X = 0.f;

53
src/world/WorldResourceLocator.cpp

@ -5,6 +5,11 @@ namespace gv
{
namespace WorldResourceLocator
{
bool operator==(const Resource& a, const Resource& b)
{
return (a.entity == b.entity && a.position.Equals(b.position, POSITION_TOLERANCE));
}
static const unsigned int DEFAULT_RESOURCELIST_SIZE = 10;
typedef std::map<WorldResourceType, ResourceList*> ResourceMap;
@ -36,11 +41,9 @@ int NumResourcesInWorld(const WorldResourceType type)
return 0;
}
void AddResource(const WorldResourceType type, const gv::Position& location)
void AddResource(const WorldResourceType type, Entity entity, 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;
Resource newResource = {entity, location};
if (ResourceListExists(type))
{
@ -55,31 +58,36 @@ void AddResource(const WorldResourceType type, const gv::Position& location)
}
}
void RemoveResource(const WorldResourceType type, const gv::Position& location)
void RemoveResource(const WorldResourceType type, Entity entity, const gv::Position& location)
{
if (ResourceListExists(type))
{
ResourceList* resourceList = s_Resources[type];
ResourceList::iterator resourceIt =
std::find(resourceList->begin(), resourceList->end(), location);
if (resourceIt != resourceList->end())
resourceList->erase(resourceIt);
Resource resource = {entity, location};
for (ResourceList::iterator resourceIt = resourceList->begin();
resourceIt != resourceList->end(); ++resourceIt)
{
if (resource == (*resourceIt))
{
resourceList->erase(resourceIt);
break;
}
}
}
}
void MoveResource(const WorldResourceType type, const gv::Position& oldLocation,
void MoveResource(const WorldResourceType type, Entity entity, const gv::Position& oldLocation,
const gv::Position& newLocation)
{
if (ResourceListExists(type))
{
for (gv::Position& currentResource : *s_Resources[type])
Resource resource = {entity, oldLocation};
for (Resource& currentResource : *s_Resources[type])
{
// They should be exactly equal. It's the caller's responsibility to keep track of this
if (currentResource.Equals(oldLocation, 1.f))
if (currentResource == resource)
{
currentResource = newLocation;
// Ensure we're not exactly 0,0,0 because I designed this poorly
currentResource.Z = !currentResource ? 0.1f : currentResource.Z;
currentResource.position = newLocation;
break;
}
}
@ -88,23 +96,22 @@ void MoveResource(const WorldResourceType type, const gv::Position& oldLocation,
// Find the nearest resource. Uses Manhattan distance
// Manhattan distance of -1 indicates no resource was found
gv::Position FindNearestResource(const WorldResourceType type, const gv::Position& location,
bool allowSameLocation, float& manhattanToOut)
Resource* FindNearestResource(const WorldResourceType type, const gv::Position& location,
bool allowSameLocation, float& manhattanToOut)
{
gv::Position zeroPosition;
if (ResourceListExists(type))
{
gv::Position closestResource;
Resource* closestResource = nullptr;
float closestResourceDistance = std::numeric_limits<float>::max();
for (gv::Position& currentResource : *s_Resources[type])
for (Resource& currentResource : *s_Resources[type])
{
float currentResourceDistance = location.ManhattanTo(currentResource);
float currentResourceDistance = location.ManhattanTo(currentResource.position);
if (currentResourceDistance < closestResourceDistance &&
(allowSameLocation || currentResourceDistance > 0.f))
{
closestResourceDistance = currentResourceDistance;
closestResource = currentResource;
closestResource = &currentResource;
}
}
@ -113,7 +120,7 @@ gv::Position FindNearestResource(const WorldResourceType type, const gv::Positio
}
manhattanToOut = -1.f;
return zeroPosition;
return nullptr;
}
}
}

28
src/world/WorldResourceLocator.hpp

@ -3,6 +3,7 @@
#include <map>
#include <vector>
#include "../entityComponentSystem/EntityTypes.hpp"
#include "Position.hpp"
namespace gv
@ -10,30 +11,37 @@ namespace gv
enum WorldResourceType
{
None = 0,
Agent = 1,
Food = 2,
WorldResourceType_count
};
typedef std::vector<gv::Position> ResourceList;
namespace WorldResourceLocator
{
struct Resource
{
Entity entity;
Position position;
};
bool operator==(const Resource& a, const Resource& b);
typedef std::vector<Resource> ResourceList;
void ClearResources();
bool ResourceExistsInWorld(const WorldResourceType type);
int NumResourcesInWorld(const WorldResourceType type);
void AddResource(const WorldResourceType type, const gv::Position& location);
void RemoveResource(const WorldResourceType type, const gv::Position& location);
void MoveResource(const WorldResourceType type, const gv::Position& oldLocation,
const gv::Position& newLocation);
void AddResource(const WorldResourceType type, Entity entity, const Position& location);
void RemoveResource(const WorldResourceType type, Entity entity, const Position& location);
void MoveResource(const WorldResourceType type, Entity entity, const Position& oldLocation,
const Position& newLocation);
// Find the nearest resource. Uses Manhattan distance
// Manhattan distance of -1 indicates no resource was found
gv::Position FindNearestResource(const WorldResourceType type, const gv::Position& location,
bool allowSameLocation, float& manhattanToOut);
// Returns nullptr if no reasource nearby
Resource* FindNearestResource(const WorldResourceType type, const Position& location,
bool allowSameLocation, float& manhattanToOut);
}
}
Loading…
Cancel
Save