Browse Source

Merge branch 'combatComponentRefactor'

master
Macoy Madson 6 years ago
parent
commit
844df46591
  1. 13
      Jamfile
  2. 8
      Jamrules
  3. 4
      Notes.md
  4. 49
      TODO.txt
  5. 5
      src/Jamfile
  6. 2
      src/ai/Jamfile
  7. 8
      src/ai/htn/HTNPlanner.cpp
  8. 2
      src/ai/htn/HTNPlanner.hpp
  9. 39
      src/ai/htn/HTNTaskDb.cpp
  10. 30
      src/ai/htn/HTNTaskDb.hpp
  11. 4
      src/ai/htn/HTNTasks.cpp
  12. 10
      src/ai/htn/HTNTasks.hpp
  13. 4
      src/ai/htn/HTNTypes.hpp
  14. 22
      src/entityComponentSystem/ComponentManager.cpp
  15. 10
      src/entityComponentSystem/ComponentManager.hpp
  16. 17
      src/entityComponentSystem/ComponentTypes.hpp
  17. 78
      src/entityComponentSystem/EntityComponentManager.cpp
  18. 46
      src/entityComponentSystem/EntityComponentManager.hpp
  19. 4
      src/entityComponentSystem/EntitySharedData.cpp
  20. 2
      src/entityComponentSystem/EntityTypes.hpp
  21. 5
      src/frontends/Jamfile
  22. 147
      src/frontends/consoleOnly/ConsoleMovementComponentManager.cpp
  23. 56
      src/frontends/consoleOnly/ConsoleMovementComponentManager.hpp
  24. 396
      src/frontends/consoleOnly/ConsoleOnlyMain.cpp
  25. 15
      src/frontends/consoleOnly/Jamfile
  26. 66
      src/game/InteractComponentManager.cpp
  27. 11
      src/game/InteractComponentManager.hpp
  28. 1
      src/game/Jamfile
  29. 96
      src/game/agent/AgentComponentManager.cpp
  30. 46
      src/game/agent/AgentComponentManager.hpp
  31. 57
      src/game/agent/Needs.hpp
  32. 22
      src/game/agent/PlanComponentManager.cpp
  33. 2
      src/game/agent/PlanComponentManager.hpp
  34. 108
      src/game/agent/combat/CombatComponentManager.cpp
  35. 113
      src/game/agent/combat/CombatComponentManager.hpp
  36. 20
      src/main.cpp
  37. 10
      src/project/galavantSublime/galavant.sublime-project
  38. 13
      src/unitTesting/EntityComponentSystem_test.cpp
  39. 2
      src/unitTesting/HTN_test.cpp
  40. 3
      src/util/Jamfile
  41. 5
      src/util/Logging.hpp
  42. 6
      src/util/ResourceDictionary.cpp
  43. 31
      src/util/ResourceDictionary.hpp
  44. 6
      src/util/Time.hpp
  45. 20
      src/world/Position.cpp
  46. 4
      src/world/Position.hpp

13
Jamfile

@ -5,13 +5,14 @@ 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
LinkLibraries GalavantPseudotarget : libGalaGame
libGalaWorld
libGalaGame ;
libGalaAi
libGalaEntityComponent
libGalaThirdPartyWrapper
libGalavant
libGalaUtil ;
MakeLocate GalavantPseudotarget : bin ;

8
Jamrules

@ -62,7 +62,7 @@ else
OPTIM = -O0 ;
HDRS = thirdParty/flatbuffers/include thirdParty/plog/include src ;
HDRS = thirdParty/flatbuffers/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 ) ?
@ -94,4 +94,8 @@ AR = ar cr ;
# -sVAR=VAL : Set VAR to VAL. Note that setting UNREAL=false is the same as setting UNREAL=true,
# frustratingly
# -dx : print commands being used
# -n : don't actually run commands
# -n : don't actually run commands
# Note: When creating ConsoleOnly frontend, I had to reverse the lib link order. This is probably
# just because I didn't understand link order when making GalavantPseudotarget. I've since updated
# GalavantPseudotarget with the proper order

4
Notes.md

@ -6,9 +6,13 @@ Miscellaneous notes regarding working on Galavant, the code, and other random sh
- When making changes to Galavant's libraries, simply touch a GalavantUnreal source file to ensure
you get the latest Galavant changes when using Unreal's hot reloading
- After rebuilding Unreal, you may need to delete GalavantUnreal/Binaries/* if you get 'Game Module Could Not Be Loaded' error on startup
- Even if you `make` the project, you'll still need to hit the Compile button in the editor to get Unreal to compile and hotreload your code
- I added a hack which makes the engine auto scale to my desired DPI settings. You'll probably want to change these to fit your preferences. Change the value of `FSlateApplication::Get().SetApplicationScale(1.53f)` in `AGalavantUnrealMain::AGalavantUnrealMain()` to your desired DPI (1.f is the engine default).
## Comment Tags
- TODO: Something needs to get done. I use TodoReview for Sublime to find all of these easily.
- @Performance: Not justifiable as a TODO, but could be looked at when thinking about performance
- @Purity: Look into changing the code for code purity/cleanliness' sake
- @Callback: The marked function is used as a callback. Preferably @Callback [Callback type name]

49
TODO.txt

@ -24,9 +24,6 @@ Some sort of resource system
Could be something like ResourceDictionary<key, ResourceType> resources
then things could stuff in things from load or even hard coded (via resources["new thing"] = {})
Put HTN Tasks etc. in resource dictionaries? Who owns them?
Position vs GlobalPosition
As soon as possible, I need to decide if I need Chunk XYZ with Position XYZ
This depends on whether I ever want to support really large worlds
@ -52,17 +49,59 @@ Combat System
Goal #1: Minecraft-quality
Goal #2: Minecraft but with strategy (this can be the stopping point for now)
Goal #3: Something closer to Chivalry instead of Minecraft
Massive convenience feature: Fuck, totally forgot as I was writing this. Fuck!
Was it Entity Actor integration?
Add player HUD UI - Only needs Minimap, Need bars
Save Jamfile documentation to repository
Cereal integration
Fix debug text over AgentCharacter for combat test
Automatic apply UEngine patch script
------------------
Doing
------------------
Add player HUD UI - Only needs Minimap, Need bars
UnrealMovementComponent segfault
https://wiki.unrealengine.com/How_To_Prevent_Crashes_Due_To_Dangling_Actor_Pointers
https://docs.unrealengine.com/latest/INT/Programming/UnrealArchitecture/SmartPointerLibrary/WeakPointer/
Spawning very, very broken
Seemed to see multiple actors spawned for a single berry
Actor cloning issue; see ActorEntityLifetimeManagement error. Walk back and forth to
spawn/despawn entities
Actually important: Put Entity integration into AActor!
Finish lifetime management
Only call ActorOnDestroy if it wasn't previously marked for destruction (in Actor.h)
Current lifetime management doesn't prevent segfault. Wait until agents all group up for repro
Combat
After extended period of time, PlanComponentManager is constantly trying to unsubscribe things
Goal retrying needs rethinking (goal retry status is forgotten once goal completely fails)
------------------
Done
------------------
Remember to put Galavant-specific UE code into a patch or something
Agent goals are broken after refactor
May be fixed by actually hooking in HTNPlan to FindFood goal def (update: not fixed)
Put HTN Tasks etc. in resource dictionaries? Who owns them?
Spawning very, very broken
Segfault every time you replay in editor on scene component thing
Oh god, now too many things spawned! Forgot to add new AActor pointers to tracking...
Sublime "syntax" tag for build systems

5
src/Jamfile

@ -13,6 +13,7 @@ SubInclude . src ai ;
SubInclude . src world ;
SubInclude . src game ;
# Experiments and Testing (feel free to remove these if you don't want them built)
# Experiments, testing, and frontends (feel free to remove these if you don't want them built)
SubInclude . src experiments ;
SubInclude . src unitTesting ;
SubInclude . src unitTesting ;
SubInclude . src frontends ;

2
src/ai/Jamfile

@ -2,7 +2,7 @@ SubDir . src ai ;
SubDirC++Flags $(ALLLIBSC++FLAGS) ;
Library libGalaAi : htn/HTNTasks.cpp htn/HTNPlanner.cpp htn/HTNTaskDb.cpp WorldState.cpp ;
Library libGalaAi : htn/HTNTasks.cpp htn/HTNPlanner.cpp WorldState.cpp ;
LinkLibraries LibGalaAi : LibGalaWorld ;

8
src/ai/htn/HTNPlanner.cpp

@ -216,7 +216,7 @@ Planner::Status Planner::PlanStep_StackFrame()
LOGD_IF(DebugPrint) << "Goal";
GoalTask* goalTask = currentTask->GetGoal();
// TODO erase ahead of time because fuck
// TODO: erase ahead of time because fuck
// Strange things are afoot when we push to stack
currentStackFrame.CallList.erase(currentTaskCallIter);
@ -379,9 +379,9 @@ Planner::Status Planner::PlanStep_StackFrame()
return Status::PlanComplete;
}
// TODO: Pool various task lists?
// TODO: Pull more things out into functions, if possible. It's bad that whenever I make a change to
// something I have to change it in two places
// TODO: @Performance Pool various task lists?
// TODO: @Purity Put shared code from BottomLevel/StackFrame into shared functions. It's bad that
// whenever I make a change to something I have to change it in two places
Planner::Status Planner::PlanStep()
{
Status status = Status::Failed_NoPossiblePlan;

2
src/ai/htn/HTNPlanner.hpp

@ -2,7 +2,7 @@
#include "HTNTypes.hpp"
#include "HTNTasks.hpp"
#include "../WorldState.hpp"
#include "ai/WorldState.hpp"
namespace Htn
{

39
src/ai/htn/HTNTaskDb.cpp

@ -1,39 +0,0 @@
#include "HTNTaskDb.hpp"
#include "../../util/Logging.hpp"
namespace Htn
{
namespace TaskDb
{
typedef std::map<TaskName, Task*> TaskDb;
static TaskDb s_TaskDb;
void ClearAllTasks()
{
s_TaskDb.clear();
}
void AddTask(Task* task, TaskName taskName)
{
if (task && taskName != TaskName::None)
{
TaskDb::iterator findIt = s_TaskDb.find(taskName);
if (findIt != s_TaskDb.end())
LOGW << "Replacing task with name " << (int)taskName << ". If in Unreal, this is fine";
s_TaskDb[taskName] = task;
}
}
Task* GetTask(TaskName taskName)
{
if (taskName != TaskName::None)
{
TaskDb::iterator findIt = s_TaskDb.find(taskName);
if (findIt != s_TaskDb.end())
return findIt->second;
}
return nullptr;
}
}
}

30
src/ai/htn/HTNTaskDb.hpp

@ -1,30 +0,0 @@
#pragma once
#include "HTNTasks.hpp"
namespace Htn
{
enum class TaskName
{
None = 0,
// Goals
// Compounds
GetResource,
// Primitives
FindResource,
MoveTo,
InteractPickup,
TaskName_count
};
namespace TaskDb
{
void ClearAllTasks();
void AddTask(Task* task, TaskName taskName);
Task* GetTask(TaskName taskName);
}
}

4
src/ai/htn/HTNTasks.cpp

@ -2,10 +2,12 @@
#include <cassert>
#include "../../util/Logging.hpp"
#include "util/Logging.hpp"
namespace Htn
{
gv::ResourceDictionary<Task> g_TaskDictionary;
int GoalTask::GetNumMethods()
{
return Methods ? Methods->size() : 0;

10
src/ai/htn/HTNTasks.hpp

@ -1,11 +1,9 @@
#pragma once
#include "HTNTypes.hpp"
#include "../WorldState.hpp"
#include "../../util/CallbackContainer.hpp"
// For std::ostream
#include <iostream>
#include "ai/WorldState.hpp"
#include "util/CallbackContainer.hpp"
#include "util/ResourceDictionary.hpp"
namespace Htn
{
@ -136,6 +134,8 @@ public:
void PrintTaskList(const TaskList& tasks);
void PrintTaskCallList(const TaskCallList& tasks);
extern gv::ResourceDictionary<Task> g_TaskDictionary;
}
template <>

4
src/ai/htn/HTNTypes.hpp

@ -1,7 +1,7 @@
#pragma once
#include "../entityComponentSystem/EntityTypes.hpp"
#include "../world/Position.hpp"
#include "entityComponentSystem/EntityTypes.hpp"
#include "world/Position.hpp"
namespace Htn
{

22
src/entityComponentSystem/ComponentManager.cpp

@ -1,8 +1,17 @@
#include "ComponentManager.hpp"
#include "entityComponentSystem/EntityComponentManager.hpp"
#include "util/Logging.hpp"
namespace gv
{
ComponentManager::ComponentManager()
{
}
ComponentManager::ComponentManager(const char* debugName) : DebugName(debugName)
{
}
ComponentManager::~ComponentManager()
{
}
@ -30,8 +39,12 @@ void ComponentManager::UnsubscribeEntities(const EntityList& entities)
// Remove from subscribers
EntityListRemoveNonUniqueEntitiesInSuspect(entitiesToUnsubscribe, Subscribers);
LOGD << "Manager " << (int)Type << " unsubscribed " << entitiesToUnsubscribe.size()
<< " entities";
if (DebugName)
LOGD << "Manager " << DebugName << " unsubscribed " << entitiesToUnsubscribe.size()
<< " entities";
else
LOGD << "Manager (UNNAMED) unsubscribed " << entitiesToUnsubscribe.size()
<< " entities";
}
}
}
@ -40,9 +53,4 @@ bool ComponentManager::IsSubscribed(Entity entity)
{
return EntityListFindEntity(Subscribers, entity);
}
ComponentType ComponentManager::GetType()
{
return Type;
}
}

10
src/entityComponentSystem/ComponentManager.hpp

@ -1,7 +1,6 @@
#pragma once
#include "EntityTypes.hpp"
#include "ComponentTypes.hpp"
namespace gv
{
@ -12,14 +11,15 @@ namespace gv
class ComponentManager
{
protected:
// You should set this type in your constructor
ComponentType Type;
EntityList Subscribers;
virtual void UnsubscribeEntitiesInternal(const EntityList& entities);
public:
const char* DebugName;
ComponentManager();
ComponentManager(const char* debugName);
virtual ~ComponentManager();
// Calls UnsubscribeEntitiesInternal on entities actually subscribed (filters out
@ -27,7 +27,5 @@ public:
void UnsubscribeEntities(const EntityList& entities);
bool IsSubscribed(Entity entity);
ComponentType GetType();
};
};

17
src/entityComponentSystem/ComponentTypes.hpp

@ -1,17 +0,0 @@
#pragma once
namespace gv
{
enum class ComponentType : unsigned int
{
None = 0,
Test,
Movement,
Agent,
Plan,
Interact,
ComponentType_count
};
};

78
src/entityComponentSystem/EntityComponentManager.cpp

@ -7,65 +7,37 @@
namespace gv
{
Entity EntityComponentManager::NextNewEntity = 1;
EntityComponentManager *EntityComponentManager::Singleton = nullptr;
EntityComponentManager g_EntityComponentManager;
EntityComponentManager::EntityComponentManager()
{
// Should not create more than one ECM! Commented due to Unreal's hotreloading :(
// assert(!Singleton);
Singleton = this;
}
EntityComponentManager::~EntityComponentManager()
{
DestroyAllEntities();
Singleton = nullptr;
}
// Sets the ComponentManager for a ComponentType. Returns false if there is already a manager
// for that type (it will not be set)
bool EntityComponentManager::AddComponentManagerOfType(ComponentType type,
ComponentManager *manager)
{
if (manager)
{
EntityComponentManager::ComponentManagerMapIterator findIt = ComponentManagers.find(type);
// Make sure there isn't already a ComponentManager for the type
assert(findIt == ComponentManagers.end());
ComponentManagers[type] = manager;
if (type != manager->GetType())
LOGW << "ComponentManager of type " << (int)manager->GetType()
<< " registered for type " << (int)type;
}
return false;
}
bool EntityComponentManager::AddComponentManager(ComponentManager *manager)
void EntityComponentManager::AddComponentManager(ComponentManager *manager)
{
if (!manager)
return false;
if (manager->GetType() == ComponentType::None)
LOGE << "Registered ComponentManager " << manager << " with no Type!";
return;
return AddComponentManagerOfType(manager->GetType(), manager);
ComponentManagers.push_back(manager);
}
// Returns the ComponentManager assigned to the provided type, or nullptr if there isn't one
// assigned. If your ComponentManager needs another, it is preferable to get its dependencies
// directly (i.e. passed in during a initialize() function)
ComponentManager *EntityComponentManager::GetComponentManagerForType(ComponentType type)
void EntityComponentManager::RemoveComponentManager(ComponentManager *manager)
{
EntityComponentManager::ComponentManagerMapIterator findIt = ComponentManagers.find(type);
if (findIt == ComponentManagers.end())
return nullptr;
return findIt->second;
for (ComponentManagerListIterator it = ComponentManagers.begin();
it != ComponentManagers.end();)
{
if ((*it) == manager)
{
it = ComponentManagers.erase(it);
}
else
++it;
}
}
void EntityComponentManager::GetNewEntities(EntityList &list, int count)
@ -80,7 +52,7 @@ void EntityComponentManager::GetNewEntities(EntityList &list, int count)
// Mark an Entity for destruction. It is not destroyed immediately; rather, it is destroyed when
// DestroyEntitiesPendingDestruction() is called.
void EntityComponentManager::MarkDestroyEntities(EntityList &entities)
void EntityComponentManager::MarkDestroyEntities(const EntityList &entities)
{
EntitiesPendingDestruction.insert(EntitiesPendingDestruction.end(), entities.begin(),
entities.end());
@ -91,13 +63,11 @@ void EntityComponentManager::UnsubscribeEntitiesFromAllManagers(EntityList &enti
// Unsubscribe all of the entities from all ComponentManagers
// Some component managers will not actually have the Entity being destroyed subscribed, but
// that's fine
for (EntityComponentManager::ComponentManagerMapIterator it = ComponentManagers.begin();
it != ComponentManagers.end(); ++it)
for (ComponentManager *currentComponentManager : ComponentManagers)
{
ComponentManager *currentComponentManager = it->second;
if (currentComponentManager)
currentComponentManager->UnsubscribeEntities(entitiesToUnsubscribe);
LOGD << "Destroying " << entitiesToUnsubscribe.size() << " entities from "
<< currentComponentManager->DebugName << " (they might not all be subscribed)";
currentComponentManager->UnsubscribeEntities(entitiesToUnsubscribe);
}
}
@ -135,12 +105,4 @@ void EntityComponentManager::DestroyAllEntities()
ActiveEntities.clear(); // this should be empty anyways
EntitiesPendingDestruction.clear();
}
EntityComponentManager *EntityComponentManager::GetSingleton()
{
// If failed, someone is requesting ECM before one has been initialized! Commented due to
// Unreal's hotreloading :(
// assert(Singleton);
return Singleton;
}
}

46
src/entityComponentSystem/EntityComponentManager.hpp

@ -3,7 +3,6 @@
#include <map>
#include "EntityTypes.hpp"
#include "ComponentTypes.hpp"
#include "ComponentManager.hpp"
namespace gv
@ -18,15 +17,15 @@ destroyed, the EntityComponentManager must send the entire list of all unsubscri
every ComponentManager which is registered with this ECM. This is less than optimal, but
permissable.
*/
typedef std::vector<ComponentManager *> ComponentManagerList;
class EntityComponentManager
{
private:
typedef std::map<ComponentType, ComponentManager *> ComponentManagerMap;
typedef ComponentManagerMap::iterator ComponentManagerMapIterator;
typedef ComponentManagerList::iterator ComponentManagerListIterator;
static EntityComponentManager *Singleton;
ComponentManagerMap ComponentManagers;
ComponentManagerList ComponentManagers;
EntityList ActiveEntities;
EntityList EntitiesPendingDestruction;
@ -43,16 +42,8 @@ public:
EntityComponentManager();
~EntityComponentManager();
// Sets the ComponentManager for a ComponentType. Returns false if there is already a manager
// for that type (it will not be set)
bool AddComponentManagerOfType(ComponentType type, ComponentManager *manager);
bool AddComponentManager(ComponentManager *manager);
// Returns the ComponentManager assigned to the provided type, or nullptr if there isn't one
// assigned. If your ComponentManager needs another, it is preferable to get its dependencies
// directly (i.e. passed in during a initialize() function) rather than using this function
ComponentManager *GetComponentManagerForType(ComponentType type);
void AddComponentManager(ComponentManager *manager);
void RemoveComponentManager(ComponentManager *manager);
// Creates the given number of entities, adds them to the ActiveEntities list, and appends them
// to the provided list
@ -60,7 +51,7 @@ public:
// Mark Entities for destruction. They are not destroyed immediately; rather, they is destroyed
// when DestroyEntitiesPendingDestruction() is called.
void MarkDestroyEntities(EntityList &entities);
void MarkDestroyEntities(const EntityList &entities);
// Destroy all entities which have been marked for destruction. Because an entity is just an ID
// and a collection of components, this function must notify all ComponentManagers that the
@ -70,24 +61,7 @@ public:
// Destroys all entities that were created by this EntityComponentManager (i.e. all entities in
// the ActiveEntities list)
void DestroyAllEntities();
static EntityComponentManager *GetSingleton();
};
// Convenience function
// TODO: Make it so ComponentManagers can just be functions instead of singletons
template <class T>
T *GetComponentManagerForType(ComponentType type)
{
EntityComponentManager *entityComponentManager = EntityComponentManager::GetSingleton();
if (entityComponentManager)
{
T *componentManager =
static_cast<T *>(entityComponentManager->GetComponentManagerForType(type));
return componentManager;
}
return nullptr;
}
};
extern EntityComponentManager g_EntityComponentManager;
}

4
src/entityComponentSystem/EntitySharedData.cpp

@ -51,8 +51,8 @@ void EntityCreatePositions(const EntityList& entities, PositionRefList& position
void EntityDestroyPositions(const EntityList& entities)
{
// TODO: This is a bit bad: loop through all positions for each entity to remove approaches N
// squared
// TODO: @Performance This is a bit bad: loop through all positions for each entity to remove
// approaches N squared
for (EntityPositionRefMap::iterator it = s_Data.EntityPositionRefs.begin();
it != s_Data.EntityPositionRefs.end();)
{

2
src/entityComponentSystem/EntityTypes.hpp

@ -29,6 +29,6 @@ void EntityListRemoveNonUniqueEntitiesInSuspect(const EntityList& list, EntityLi
void EntityListRemoveUniqueEntitiesInSuspect(const EntityList& list, EntityList& suspectList);
// Linear search for entity.
// TODO: Add binary search (need to figure out when to sort these lists)
// TODO: @Performance Add binary search (need to figure out when to sort these lists)
bool EntityListFindEntity(const EntityList& list, Entity entity);
};

5
src/frontends/Jamfile

@ -0,0 +1,5 @@
SubDir . src frontends ;
SubDirC++Flags $(TESTSC++FLAGS) ;
SubInclude . src frontends consoleOnly ;

147
src/frontends/consoleOnly/ConsoleMovementComponentManager.cpp

@ -0,0 +1,147 @@
#include "ConsoleMovementComponentManager.hpp"
#include <algorithm>
ConsoleMovementComponentManager g_ConsoleMovementComponentManager;
ConsoleMovementComponentManager::ConsoleMovementComponentManager()
{
DebugName = "ConsoleMovementComponentManager";
}
void ConsoleMovementComponentManager::Initialize(
gv::CallbackContainer<Htn::TaskEventCallback>* taskEventCallbacks)
{
TaskEventCallbacks = taskEventCallbacks;
}
void ConsoleMovementComponentManager::UnsubscribeEntitiesInternal(const gv::EntityList& entities)
{
for (MovementComponentList::iterator it = MovementComponents.begin();
it != MovementComponents.end();)
{
gv::Entity currentEntity = (*it).entity;
bool foundEntity = false;
for (const gv::Entity& unsubscribeEntity : entities)
{
if (currentEntity == unsubscribeEntity)
{
it = MovementComponents.erase(it);
foundEntity = true;
break;
}
}
if (!foundEntity)
++it;
}
}
void ConsoleMovementComponentManager::CreateMovementComponents(
const gv::EntityList& entities, MovementComponentRefList& newMovementComponents)
{
// Only create MovementComponents which aren't already subscribed
gv::EntityList entitiesToSubscribe = entities;
gv::EntityListRemoveNonUniqueEntitiesInSuspect(Subscribers, entitiesToSubscribe);
unsigned int endBeforeResize = MovementComponents.size();
MovementComponents.resize(MovementComponents.size() + entitiesToSubscribe.size());
int numEntitiesCreated = 0;
for (unsigned int i = endBeforeResize; i < MovementComponents.size(); i++)
{
MovementComponent* newMovementComponent = &MovementComponents[i];
newMovementComponent->entity = entitiesToSubscribe[numEntitiesCreated++];
newMovementComponents.push_back(newMovementComponent);
}
// We've already made sure all entities in the list are unique
gv::EntityListAppendList(Subscribers, entitiesToSubscribe);
}
void ConsoleMovementComponentManager::Update(float deltaSeconds)
{
Htn::TaskEventList eventList;
gv::EntityList entitiesToUnsubscribe;
for (MovementComponent& component : MovementComponents)
{
gv::Position targetPosition;
// Decide where we're going to go
{
if (component.GoalWorldPosition)
{
if (component.Position.ManhattanTo(component.GoalWorldPosition) >
component.GoalManDistanceTolerance)
{
targetPosition = component.GoalWorldPosition;
}
else
{
component.GoalWorldPosition.Reset();
Htn::TaskEvent goalPositionReachedEvent{
Htn::TaskEvent::TaskResult::TaskSucceeded, component.entity};
eventList.push_back(goalPositionReachedEvent);
}
}
}
if (component.ResourceType != gv::WorldResourceType::None &&
!component.ResourcePosition.Equals(component.Position, 100.f))
{
// Give ResourceLocator our new position
gv::WorldResourceLocator::MoveResource(component.ResourceType, component.entity,
component.ResourcePosition, component.Position);
component.ResourcePosition = component.Position;
}
// Perform movement
if (targetPosition)
{
gv::Position deltaPosition = targetPosition - component.Position;
gv::Position deltaVelocity = deltaPosition.GetSafeNormal(0.1f);
deltaVelocity.Scale(component.MaxSpeed * deltaSeconds);
// Disallow flying
deltaVelocity[2] = 0;
component.Position += deltaVelocity;
}
}
if (TaskEventCallbacks && !eventList.empty())
{
for (gv::CallbackCall<Htn::TaskEventCallback>& callback : TaskEventCallbacks->Callbacks)
callback.Callback(eventList, callback.UserData);
}
if (!entitiesToUnsubscribe.empty())
UnsubscribeEntities(entitiesToUnsubscribe);
}
// TODO: This should return whether it was actually successful (i.e. the entity exists)
void ConsoleMovementComponentManager::PathEntitiesTo(const gv::EntityList& entities,
const gv::PositionList& positions)
{
if (entities.empty() || entities.size() != positions.size())
return;
for (unsigned int i = 0; i < entities.size(); i++)
{
const gv::Entity& entityToMove = entities[i];
const gv::Position& targetPosition = positions[i];
// For now, entities which are not subscribed will be tossed out
if (std::find(Subscribers.begin(), Subscribers.end(), entityToMove) == Subscribers.end())
continue;
for (MovementComponent& component : MovementComponents)
{
if (component.entity == entityToMove)
{
component.GoalWorldPosition = targetPosition;
}
}
}
}

56
src/frontends/consoleOnly/ConsoleMovementComponentManager.hpp

@ -0,0 +1,56 @@
#pragma once
#include "entityComponentSystem/EntityTypes.hpp"
#include "entityComponentSystem/ComponentManager.hpp"
#include "world/Position.hpp"
#include "world/WorldResourceLocator.hpp"
#include "ai/htn/HTNTypes.hpp"
#include "game/agent/MovementManager.hpp"
#include "util/CallbackContainer.hpp"
struct MovementComponent
{
gv::Entity entity;
gv::Position Position;
gv::Position GoalWorldPosition;
float GoalManDistanceTolerance = 400.f;
float MaxSpeed = 500.f;
// If provided, this entity will be registered in the WorldResourceLocator under this type
gv::WorldResourceType ResourceType = gv::WorldResourceType::None;
// The last position we told the ResourceLocator we were at (used so that when we move we can
// find the agent to move in ResourceLocator)
gv::Position ResourcePosition;
};
typedef std::vector<MovementComponent> MovementComponentList;
typedef std::vector<MovementComponent*> MovementComponentRefList;
class ConsoleMovementComponentManager : public gv::ComponentManager, public gv::MovementManager
{
private:
gv::CallbackContainer<Htn::TaskEventCallback>* TaskEventCallbacks;
MovementComponentList MovementComponents;
protected:
virtual void UnsubscribeEntitiesInternal(const gv::EntityList& entities);
public:
ConsoleMovementComponentManager();
virtual ~ConsoleMovementComponentManager() = default;
void Initialize(gv::CallbackContainer<Htn::TaskEventCallback>* taskEventCallbacks);
void CreateMovementComponents(const gv::EntityList& entities,
MovementComponentRefList& newMovementComponents);
virtual void Update(float deltaSeconds);
// TODO: This should return whether it was actually successful (i.e. the entity exists)
virtual void PathEntitiesTo(const gv::EntityList& entities, const gv::PositionList& positions);
};
extern ConsoleMovementComponentManager g_ConsoleMovementComponentManager;

396
src/frontends/consoleOnly/ConsoleOnlyMain.cpp

@ -0,0 +1,396 @@
#include "util/Logging.hpp"
#include "world/WorldResourceLocator.hpp"
#include "world/ProceduralWorld.hpp"
#include "entityComponentSystem/EntityTypes.hpp"
#include "entityComponentSystem/EntityComponentManager.hpp"
#include "game/agent/PlanComponentManager.hpp"
#include "game/agent/AgentComponentManager.hpp"
#include "game/agent/combat/CombatComponentManager.hpp"
#include "game/InteractComponentManager.hpp"
#include "game/agent/Needs.hpp"
#include "ai/htn/HTNTasks.hpp"
#include "game/agent/htnTasks/MovementTasks.hpp"
#include "game/agent/htnTasks/InteractTasks.hpp"
#include "util/CallbackContainer.hpp"
#include "game/EntityLevelOfDetail.hpp"
#include "util/StringHashing.hpp"
#include "ConsoleMovementComponentManager.hpp"
#include <iostream>
#include <cstring>
#include <cstdlib>
#include <cstdio>
// @Callback: CustomLogOutputFunc
void ConsoleOnlyLogOutput(const gv::Logging::Record& record)
{
bool IsWarning = (record.severity == gv::Logging::Severity::warning);
bool IsError = (record.severity <= gv::Logging::Severity::error);
static char funcNameBuffer[256];
gv::Logging::FormatFuncName(funcNameBuffer, record.Function, sizeof(funcNameBuffer));
static char buffer[2048];
snprintf(buffer, sizeof(buffer), "%s():%lu: %s", funcNameBuffer, (unsigned long)record.Line,
record.OutBuffer);
if (IsError)
{
std::cout << "Error: " << buffer << "\n";
}
else if (IsWarning)
{
std::cout << "Warning: " << buffer << "\n";
}
else
{
std::cout << "Log: " << buffer << "\n";
}
}
static gv::Logging::Logger s_UnrealLogger(gv::Logging::Severity::debug, &ConsoleOnlyLogOutput);
void InitializeResources()
{
// Hunger Need
{
static gv::NeedDef TestHungerNeed;
TestHungerNeed.Type = gv::NeedType::Hunger;
TestHungerNeed.Name = "Hunger";
TestHungerNeed.InitialLevel = 70.f;
TestHungerNeed.MaxLevel = 300.f;
TestHungerNeed.MinLevel = 0.f;
TestHungerNeed.UpdateRate = 10.f;
TestHungerNeed.AddPerUpdate = 10.f;
// Find food goal def
{
Htn::Parameter resourceToFind;
resourceToFind.IntValue = gv::WorldResourceType::Food;
resourceToFind.Type = Htn::Parameter::ParamType::Int;
Htn::ParameterList parameters = {resourceToFind};
Htn::TaskCall getResourceCall{Htn::g_TaskDictionary.GetResource(RESKEY("GetResource")),
parameters};
Htn::TaskCall pickupResourceCall{
Htn::g_TaskDictionary.GetResource(RESKEY("InteractPickup")), parameters};
Htn::TaskCallList getResourceTasks = {getResourceCall, pickupResourceCall};
static gv::AgentGoalDef s_findFoodGoalDef;
s_findFoodGoalDef.Type = gv::AgentGoalDef::GoalType::HtnPlan;
s_findFoodGoalDef.NumRetriesIfFailed = 2;
s_findFoodGoalDef.Tasks = getResourceTasks;
gv::g_AgentGoalDefDictionary.AddResource(RESKEY("FindFood"), &s_findFoodGoalDef);
}
// Hunger Need Triggers
{
gv::NeedLevelTrigger lookForFood;
lookForFood.Condition = gv::NeedLevelTrigger::ConditionType::GreaterThanLevel;
lookForFood.Level = 100.f;
lookForFood.NeedsResource = true;
lookForFood.WorldResource = gv::WorldResourceType::Food;
TestHungerNeed.LevelTriggers.push_back(lookForFood);
gv::NeedLevelTrigger desperateLookForFood;
desperateLookForFood.Condition = gv::NeedLevelTrigger::ConditionType::GreaterThanLevel;
desperateLookForFood.Level = 200.f;
desperateLookForFood.GoalDef =
gv::g_AgentGoalDefDictionary.GetResource(RESKEY("FindFood"));
TestHungerNeed.LevelTriggers.push_back(desperateLookForFood);
gv::NeedLevelTrigger deathByStarvation;
deathByStarvation.Condition = gv::NeedLevelTrigger::ConditionType::GreaterThanLevel;
deathByStarvation.Level = 290.f;
deathByStarvation.SetConsciousState = gv::AgentConsciousState::Dead;
TestHungerNeed.LevelTriggers.push_back(deathByStarvation);
}
gv::g_NeedDefDictionary.AddResource(RESKEY("Hunger"), &TestHungerNeed);
}
// Blood Need
{
static gv::NeedDef BloodNeed;
BloodNeed.Type = gv::NeedType::Blood;
BloodNeed.Name = "Blood";
BloodNeed.InitialLevel = 100.f;
BloodNeed.MaxLevel = 100.f;
BloodNeed.MinLevel = 0.f;
// Agents will only gain blood over time, but there's a maximum and they start at max
BloodNeed.UpdateRate = 10.f;
BloodNeed.AddPerUpdate = 10.f;
// Blood Need Triggers
{
gv::NeedLevelTrigger lowBloodUnconscious;
lowBloodUnconscious.Condition = gv::NeedLevelTrigger::ConditionType::LessThanLevel;
lowBloodUnconscious.Level = 20.f;
lowBloodUnconscious.SetConsciousState = gv::AgentConsciousState::Unconscious;
BloodNeed.LevelTriggers.push_back(lowBloodUnconscious);
gv::NeedLevelTrigger deathByBleedingOut;
deathByBleedingOut.Condition = gv::NeedLevelTrigger::ConditionType::Zero;
deathByBleedingOut.SetConsciousState = gv::AgentConsciousState::Dead;
BloodNeed.LevelTriggers.push_back(deathByBleedingOut);
}
gv::g_NeedDefDictionary.AddResource(RESKEY("Blood"), &BloodNeed);
}
// Goals
{
static gv::AgentGoalDef s_getResourceGoalDef;
s_getResourceGoalDef.Type = gv::AgentGoalDef::GoalType::GetResource;
s_getResourceGoalDef.NumRetriesIfFailed = 2;
gv::g_AgentGoalDefDictionary.AddResource(RESKEY("GetResource"), &s_getResourceGoalDef);
}
// CombatActionDefs
{
// Punch
{
static gv::CombatActionDef s_punchAction;
s_punchAction.Type = gv::CombatActionDef::CombatActionType::Attack;
s_punchAction.Duration = .75f;
s_punchAction.Damage = {RESKEY("Blood"), 10.f, 50.f};
s_punchAction.Knockback = {1.f, 500.f};
gv::g_CombatActionDefDictionary.AddResource(RESKEY("Punch"), &s_punchAction);
}
}
}
void InitializeEntityTests()
{
// Create a couple test entities
int numTestEntities = 20;
gv::EntityList testEntities;
testEntities.reserve(numTestEntities);
gv::g_EntityComponentManager.GetNewEntities(testEntities, numTestEntities);
// Add Movement components to all of them
{
MovementComponentRefList newMovementComponents;
newMovementComponents.reserve(numTestEntities);
g_ConsoleMovementComponentManager.CreateMovementComponents(testEntities,
newMovementComponents);
float spacing = 500.f;
int i = 0;
for (MovementComponent* newAgentMovementComponent : newMovementComponents)
{
newAgentMovementComponent->ResourceType = gv::WorldResourceType::Agent;
newAgentMovementComponent->Position.Set(0.f, i++ * spacing, 0.f);
newAgentMovementComponent->GoalManDistanceTolerance = 600.f;
newAgentMovementComponent->MaxSpeed = 500.f;
}
}
// Setup agent components for all of them and give them a need
{
gv::Need hungerNeed(RESKEY("Hunger"));
gv::Need bloodNeed(RESKEY("Blood"));
// TODO: Will eventually need a thing which creates agents based on a creation def and sets
// up the needs accordingly
gv::AgentComponentManager::AgentComponentList newAgentComponents(numTestEntities);
int i = 0;
for (gv::PooledComponent<gv::AgentComponentData>& currentAgentComponent :
newAgentComponents)
{
currentAgentComponent.entity = testEntities[i++];
currentAgentComponent.data.Needs.push_back(hungerNeed);
currentAgentComponent.data.Needs.push_back(bloodNeed);
}
gv::g_AgentComponentManager.SubscribeEntities(newAgentComponents);
}
// Add food
{
int numFood = 4;
gv::EntityList testFoodEntities;
testFoodEntities.reserve(numFood);
gv::g_EntityComponentManager.GetNewEntities(testFoodEntities, numFood);
MovementComponentRefList newMovementComponents;
newMovementComponents.reserve(numFood);
g_ConsoleMovementComponentManager.CreateMovementComponents(testFoodEntities,
newMovementComponents);
gv::PickupRefList newPickups;
newPickups.reserve(numFood);
gv::g_InteractComponentManager.CreatePickups(testFoodEntities, newPickups);
// Movement components
float spacing = 2000.f;
int i = 0;
for (MovementComponent* newFoodMovementComponent : newMovementComponents)
{
newFoodMovementComponent->ResourceType = gv::WorldResourceType::Food;
newFoodMovementComponent->Position.Set(-2000.f, i++ * spacing, 0.f);
// Food doesn't move
newFoodMovementComponent->MaxSpeed = 0.f;
}
// Pickup components
for (gv::Pickup* newPickup : newPickups)
{
newPickup->AffectsNeed = gv::NeedType::Hunger;
newPickup->DestroySelfOnPickup = true;
}
}
}
void InitializeProceduralWorld()
{
gv::ProceduralWorld::ProceduralWorldParams& Params =
gv::ProceduralWorld::GetActiveWorldParams();
Params.WorldCellMaxHeight = 200.f;
Params.WorldCellMinHeight = -10000.f;
Params.Seed = 5138008;
for (int i = 0; i < 3; i++)
Params.WorldCellTileSize[i] = 120.f;
// Hardcoded noise values because I won't be changing these often
Params.ScaledNoiseParams.lowBound = 0.f;
Params.ScaledNoiseParams.highBound = 255.f;
Params.ScaledNoiseParams.octaves = 10;
Params.ScaledNoiseParams.scale = 0.00001f;
Params.ScaledNoiseParams.persistence = 0.55f;
Params.ScaledNoiseParams.lacunarity = 2.f;
}
void InitializeGalavant()
{
LOGI << "Initializing Galavant...";
InitializeProceduralWorld();
gv::WorldResourceLocator::ClearResources();
// Initialize Entity Components
{
// I originally made this happen via static initialization, but using that in combination
// with A) split Galavant static library and GalavantUnreal library and B) Unreal
// Hotreloading caused issues (mainly that Unreal-specific ComponentManagers weren't being
// registered in the same list)
gv::g_EntityComponentManager.AddComponentManager(&gv::g_InteractComponentManager);
gv::g_EntityComponentManager.AddComponentManager(&gv::g_CombatComponentManager);
gv::g_EntityComponentManager.AddComponentManager(&gv::g_AgentComponentManager);
gv::g_EntityComponentManager.AddComponentManager(&gv::g_PlanComponentManager);
gv::g_EntityComponentManager.AddComponentManager(&g_ConsoleMovementComponentManager);
static gv::CallbackContainer<Htn::TaskEventCallback> TaskEventCallbacks;
g_ConsoleMovementComponentManager.Initialize(&TaskEventCallbacks);
{
static gv::WorldStateManager WorldStateManager;
gv::g_PlanComponentManager.Initialize(&WorldStateManager, &TaskEventCallbacks);
//gv::g_PlanComponentManager.DebugPrint = true;
}
{
gv::g_AgentComponentManager.Initialize(&gv::g_PlanComponentManager);
gv::g_AgentComponentManager.DebugPrint = true;
}
// gv::g_CombatComponentManager.Initialize(&CombatFxHandler);
gv::g_CombatComponentManager.Initialize(nullptr);
}
// Initialize Tasks
{
static gv::FindResourceTask FindResourceTask;
static gv::MoveToTask MoveToTask;
static gv::GetResourceTask GetResourceTask;
static gv::InteractPickupTask InteractPickupTask;
MoveToTask.Initialize(&g_ConsoleMovementComponentManager);
GetResourceTask.Initialize(&FindResourceTask, &MoveToTask);
InteractPickupTask.Initialize(&gv::g_InteractComponentManager);
Htn::g_TaskDictionary.AddResource(RESKEY("FindResource"), FindResourceTask.GetTask());
Htn::g_TaskDictionary.AddResource(RESKEY("MoveTo"), MoveToTask.GetTask());
Htn::g_TaskDictionary.AddResource(RESKEY("GetResource"), GetResourceTask.GetTask());
Htn::g_TaskDictionary.AddResource(RESKEY("InteractPickup"), InteractPickupTask.GetTask());
}
InitializeResources();
// Initialize test levels
{
InitializeEntityTests();
}
// Initialize LOD settings
{
float PlayerManhattanViewDistance = 10000.f;
gv::EntityLOD::g_EntityLODSettings.PlayerManhattanViewDistance =
PlayerManhattanViewDistance;
}
LOGI << "Galavant Initialized";
}
static float s_currentWorldTime = 0.f;
int main()
{
InitializeGalavant();
LOGI << "******* Starting main loop ********";
// Main loop
float totalRuntime = 100.f;
float tickRate = 1.f;
int numTicks = 0;
for (float currentTime = 0.f; currentTime < totalRuntime; currentTime += tickRate)
{
s_currentWorldTime = currentTime;
float deltaTime = tickRate;
gv::g_EntityComponentManager.DestroyEntitiesPendingDestruction();
// GalavantMain.Update(deltaTime);
gv::g_CombatComponentManager.Update(deltaTime);
gv::g_AgentComponentManager.Update(deltaTime);
gv::g_PlanComponentManager.Update(deltaTime);
g_ConsoleMovementComponentManager.Update(deltaTime);
numTicks++;
}
LOGI << "******** Finished; cleaning up... (ticked " << numTicks << " times) ********";
// Cleanup
{
gv::g_EntityComponentManager.DestroyAllEntities();
LOGI << "Destroyed all entities";
gv::WorldResourceLocator::ClearResources();
gv::ResourceDictionaryBase::ClearAllDictionaries();
}
LOGI << "Exiting";
return 1;
}
// Latelinked functions
namespace gv
{
float GetWorldTime()
{
return s_currentWorldTime;
}
}

15
src/frontends/consoleOnly/Jamfile

@ -0,0 +1,15 @@
SubDir . src frontends consoleOnly ;
SubDirC++Flags $(TESTSC++FLAGS) ;
Main consoleOnlyFrontend : ConsoleOnlyMain.cpp ConsoleMovementComponentManager.cpp ;
LinkLibraries consoleOnlyFrontend : libGalaGame
libGalaWorld
libGalaAi
libGalaEntityComponent
libGalaThirdPartyWrapper
libGalavant
libGalaUtil ;
MakeLocate consoleOnlyFrontend : bin ;

66
src/game/InteractComponentManager.cpp

@ -1,10 +1,17 @@
#include "InteractComponentManager.hpp"
#include "../entityComponentSystem/EntityComponentManager.hpp"
#include "entityComponentSystem/EntityComponentManager.hpp"
#include "agent/AgentComponentManager.hpp"
namespace gv
{
InteractComponentManager g_InteractComponentManager;
InteractComponentManager::InteractComponentManager()
: ComponentManager("InteractComponentManager")
{
}
void InteractComponentManager::UnsubscribeEntitiesInternal(const EntityList& entities)
{
for (PickupList::iterator it = Pickups.begin(); it != Pickups.end();)
@ -28,17 +35,21 @@ void InteractComponentManager::UnsubscribeEntitiesInternal(const EntityList& ent
void InteractComponentManager::CreatePickups(const EntityList& entities, PickupRefList& newPickups)
{
int numEntitiesCreated = 0;
EntityList entitiesToSubscribe = entities;
EntityListRemoveNonUniqueEntitiesInSuspect(Subscribers, entitiesToSubscribe);
unsigned int endBeforeResize = Pickups.size();
Pickups.resize(Pickups.size() + entities.size());
Pickups.resize(Pickups.size() + entitiesToSubscribe.size());
int numEntitiesCreated = 0;
for (unsigned int i = endBeforeResize; i < Pickups.size(); i++)
{
Pickup* newPickup = &Pickups[i];
newPickup->entity = entities[numEntitiesCreated++];
newPickup->entity = entitiesToSubscribe[numEntitiesCreated++];
newPickups.push_back(newPickup);
}
EntityListAddUniqueEntitiesToSuspect(entities, Subscribers);
EntityListAppendList(Subscribers, entitiesToSubscribe);
}
Pickup* InteractComponentManager::GetPickup(Entity pickupEntity)
@ -58,39 +69,32 @@ bool InteractComponentManager::PickupDirect(Entity pickupEntity, Entity claimer)
{
// 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)
if (g_AgentComponentManager.IsSubscribed(claimer))
{
AgentComponentManager* agentComponentManager = static_cast<AgentComponentManager*>(
entityComponentManager->GetComponentManagerForType(ComponentType::Agent));
if (agentComponentManager && agentComponentManager->IsSubscribed(claimer))
{
Pickup* pickup = GetPickup(pickupEntity);
Pickup* pickup = GetPickup(pickupEntity);
if (!pickup)
return false;
if (!pickup)
return false;
Need* needPickupAffects =
agentComponentManager->GetAgentNeed(claimer, pickup->AffectsNeed);
Need* needPickupAffects =
g_AgentComponentManager.GetAgentNeed(claimer, pickup->AffectsNeed);
// TODO: For testing only
if (needPickupAffects)
{
needPickupAffects->Level -= 100;
LOGD << "Entity " << claimer << " restored need "
<< needPickupAffects->Def->Name << " by 100 picking up " << pickupEntity;
}
// TODO: For testing only; Directly contribute to need on pickup
if (needPickupAffects)
{
needPickupAffects->Level -= 100;
LOGD << "Entity " << claimer << " restored need " << needPickupAffects->Def->Name
<< " by 100 picking up " << pickupEntity;
}
EntityList entitiesPickedUp;
entitiesPickedUp.push_back(pickupEntity);
UnsubscribeEntities(entitiesPickedUp);
EntityList entitiesPickedUp;
entitiesPickedUp.push_back(pickupEntity);
UnsubscribeEntities(entitiesPickedUp);
if (pickup->DestroySelfOnPickup)
entityComponentManager->MarkDestroyEntities(entitiesPickedUp);
if (pickup->DestroySelfOnPickup)
g_EntityComponentManager.MarkDestroyEntities(entitiesPickedUp);
return true;
}
return true;
}
}
return false;

11
src/game/InteractComponentManager.hpp

@ -1,8 +1,7 @@
#pragma once
#include "../entityComponentSystem/ComponentManager.hpp"
#include "../entityComponentSystem/ComponentTypes.hpp"
#include "../world/WorldResourceLocator.hpp"
#include "entityComponentSystem/ComponentManager.hpp"
#include "world/WorldResourceLocator.hpp"
#include "agent/NeedTypes.hpp"
namespace gv
@ -24,12 +23,10 @@ private:
PickupList Pickups;
protected:
ComponentType Type = ComponentType::Interact;
virtual void UnsubscribeEntitiesInternal(const EntityList& entities);
public:
InteractComponentManager() = default;
InteractComponentManager();
virtual ~InteractComponentManager() = default;
void CreatePickups(const EntityList& entities, PickupRefList& newPickups);
@ -38,4 +35,6 @@ public:
Pickup* GetPickup(Entity pickupEntity);
};
extern InteractComponentManager g_InteractComponentManager;
}

1
src/game/Jamfile

@ -4,6 +4,7 @@ SubDirC++Flags $(ALLLIBSC++FLAGS) ;
Library libGalaGame : agent/PlanComponentManager.cpp
agent/AgentComponentManager.cpp
agent/combat/CombatComponentManager.cpp
agent/Needs.cpp
agent/htnTasks/MovementTasks.cpp
agent/htnTasks/InteractTasks.cpp

96
src/game/agent/AgentComponentManager.cpp

@ -3,19 +3,18 @@
#include "util/Logging.hpp"
#include "entityComponentSystem/PooledComponentManager.hpp"
#include "entityComponentSystem/ComponentTypes.hpp"
#include "entityComponentSystem/EntityComponentManager.hpp"
#include "ai/htn/HTNTaskDb.hpp"
#include "util/Math.hpp"
namespace gv
{
ResourceDictionary<AgentGoalDef> g_AgentGoalDefDictionary;
AgentComponentManager g_AgentComponentManager;
AgentComponentManager::AgentComponentManager() : gv::PooledComponentManager<AgentComponentData>(100)
{
Type = gv::ComponentType::Agent;
DebugName = "AgentComponentManager";
}
AgentComponentManager::~AgentComponentManager()
@ -116,23 +115,36 @@ void AgentComponentManager::Update(float deltaSeconds)
if (!needLevelTrigger.ConditionsMet(need))
continue;
if (needLevelTrigger.NeedsResource && needLevelTrigger.WorldResource)
LOGD_IF(DebugPrint) << "Agent Entity " << currentEntity
<< " has hit need trigger for need " << need.Def->Name;
if (needLevelTrigger.SetConsciousState != AgentConsciousState::None)
{
AgentGoal newNeedResourceGoal{
AgentGoal::GoalStatus::StartGoal,
/*NumFailureRetries=*/0,
gv::g_AgentGoalDefDictionary.GetResource(RESKEY("GetResource")),
needLevelTrigger.WorldResource};
AddGoalIfUniqueType(goals, newNeedResourceGoal);
LOGD_IF(DebugPrint) << "Agent Entity " << currentEntity
<< " has hit need trigger for need " << need.Def->Name;
currentComponent->data.ConsciousState = needLevelTrigger.SetConsciousState;
if (currentComponent->data.ConsciousState == AgentConsciousState::Dead)
{
currentComponent->data.IsAlive = false;
LOGD_IF(DebugPrint) << "Agent Entity " << currentEntity
<< " has died from need " << need.Def->Name;
}
}
if (needLevelTrigger.GoalDef)
{
AgentGoal newGoal;
newGoal.Def = needLevelTrigger.GoalDef;
newGoal.Status = AgentGoal::GoalStatus::StartGoal;
AddGoalIfUniqueType(goals, newGoal);
}
else if (needLevelTrigger.DieNow)
else if (needLevelTrigger.NeedsResource && needLevelTrigger.WorldResource)
{
currentComponent->data.IsAlive = false;
currentComponent->data.ConsciousState = AgentConsciousState::Dead;
LOGD_IF(DebugPrint) << "Agent Entity " << currentEntity
<< " has died from need " << need.Def->Name;
AgentGoal newNeedResourceGoal;
newNeedResourceGoal.Def =
gv::g_AgentGoalDefDictionary.GetResource(RESKEY("GetResource"));
newNeedResourceGoal.WorldResource = needLevelTrigger.WorldResource;
newNeedResourceGoal.Status = AgentGoal::GoalStatus::StartGoal;
AddGoalIfUniqueType(goals, newNeedResourceGoal);
}
}
}
@ -151,16 +163,32 @@ void AgentComponentManager::Update(float deltaSeconds)
// longer subscribed before adding our goal
if (!PlanManager->IsSubscribed(currentEntity))
{
if (goal.Def->Type == AgentGoalDef::GoalType::GetResource)
if (goal.Def->Type == AgentGoalDef::GoalType::HtnPlan)
{
// TODO: @Purity Move validation to somewhere cleaner
if (goal.Def->Tasks.empty())
LOGE << "AgentGoalDef marked as a plan but has no Tasks!";
gv::PooledComponent<PlanComponentData> newPlanComponent;
newPlanComponent.entity = currentEntity;
newPlanComponent.data.Tasks.insert(newPlanComponent.data.Tasks.end(),
goal.Def->Tasks.begin(),
goal.Def->Tasks.end());
newPlans.push_back(newPlanComponent);
}
else if (goal.Def->Type == AgentGoalDef::GoalType::GetResource)
{