Browse Source

PlanComponentManager now fully functional

- Primitive Tasks can now return statuses which give the executor instructions on how to proceed. It's a little dirty, but powerful
- Position can now be logged directly, which is super handy
- WorldState manager now returns refs to worldstate, letting Primitives modify the World state as needed
- Fixed bug in includes of SubjectObserver
combatComponentRefactor
Macoy Madson 6 years ago
parent
commit
2b2a07f1f8
  1. 2
      src/ai/WorldState.cpp
  2. 2
      src/ai/WorldState.hpp
  3. 20
      src/ai/htn/HTNTasks.hpp
  4. 63
      src/game/agent/PlanComponentManager.cpp
  5. 10
      src/game/agent/PlanComponentManager.hpp
  6. 18
      src/unitTesting/HTN_test.cpp
  7. 2
      src/util/SubjectObserver.hpp
  8. 8
      src/world/Position.cpp
  9. 9
      src/world/Position.hpp

2
src/ai/WorldState.cpp

@ -4,7 +4,7 @@
namespace gv
{
WorldState WorldStateManager::GetWorldStateForAgent(Entity agent)
WorldState& WorldStateManager::GetWorldStateForAgent(Entity agent)
{
EntityWorldStateMap::iterator findEnt = EntityWorldStates.find(agent);

2
src/ai/WorldState.hpp

@ -38,6 +38,6 @@ private:
typedef std::map<Entity, WorldState> EntityWorldStateMap;
EntityWorldStateMap EntityWorldStates;
public:
WorldState GetWorldStateForAgent(Entity agent);
WorldState& GetWorldStateForAgent(Entity agent);
};
};

20
src/ai/htn/HTNTasks.hpp

@ -2,6 +2,7 @@
#include "HTNTypes.hpp"
#include "../WorldState.hpp"
#include "../../util/SubjectObserver.hpp"
// For std::ostream
#include <iostream>
@ -102,6 +103,23 @@ public:
Task* GetTask();
};
struct TaskExecuteStatus
{
enum ExecutionStatus
{
Failed = 0,
Succeeded = 1,
Running = 2,
Subscribe = 3,
Reexecute = 4
};
ExecutionStatus Status;
// If the status is to Subscribe, the thing running tasks should Observe the given subject
gv::Subject<TaskEvent>* Subject;
};
class PrimitiveTask
{
private:
@ -116,7 +134,7 @@ public:
// Returns whether or not starting the task was successful (NOT whether the task completed)
// Execution should (when completed etc.) modify the world state the same as ApplyStateChange
// would. Call that function for extra safety
virtual bool Execute(gv::WorldState& state, const ParameterList& parameters) = 0;
virtual TaskExecuteStatus Execute(gv::WorldState& state, const ParameterList& parameters) = 0;
Task* GetTask();
};

63
src/game/agent/PlanComponentManager.cpp

@ -50,31 +50,50 @@ void PlanComponentManager::Update(float deltaSeconds)
{
Htn::ParameterList& parameters = (*currentStep).Parameters;
Htn::Task* task = (*currentStep).TaskToCall;
gv::WorldState worldState =
gv::WorldState& worldState =
worldStateManager->GetWorldStateForAgent(currentEntity);
// We are ignorant of type here because the plan consists only of primitives at
// this point
if (task)
task->GetPrimitive()->Execute(worldState, parameters);
// Remove the current step because we've executed it.
// TODO: Only remove if the step has finished; handle failed steps
// For now, just edit FinalCallList directly
componentPlanner.FinalCallList.erase(currentStep);
{
Htn::TaskExecuteStatus status;
// We are ignorant of type here because the plan consists only of primitives
// at this point
status = task->GetPrimitive()->Execute(worldState, parameters);
switch (status.Status)
{
case Htn::TaskExecuteStatus::Reexecute:
// Don't get rid of the task - execute again
break;
case Htn::TaskExecuteStatus::Subscribe:
if (status.Subject)
status.Subject->AddObserver(this);
componentPlanner.FinalCallList.erase(currentStep);
break;
// All other statuses result in us getting rid of the task
default:
LOGD_IF(DebugPrint) << "Task returned conclusive status "
<< status.Status;
componentPlanner.FinalCallList.erase(currentStep);
break;
}
}
else
componentPlanner.FinalCallList.erase(currentStep);
}
else
{
// We have finished all tasks; remove this entity from the manager
// TODO: We'll eventually hook up some event shit
LOGD << "PlanComponentManager: Call list empty";
LOGD_IF(DebugPrint) << "PlanComponentManager: Call list empty";
entitiesToUnsubscribe.push_back(currentEntity);
}
}
else
{
LOGD << "PlanComponentManager: Plan not complete, status "
<< (int)componentPlanner.CurrentStatus;
LOGD_IF(DebugPrint) << "PlanComponentManager: Plan not complete, status "
<< (int)componentPlanner.CurrentStatus;
entitiesToUnsubscribe.push_back(currentEntity);
}
}
@ -85,22 +104,22 @@ void PlanComponentManager::Update(float deltaSeconds)
{
if (status == Htn::Planner::Status::PlanComplete)
{
LOGD << "PlanComponentManager: Sucessful plan for Entity "
<< currentComponent->entity << "! Final Call List:";
LOGD_IF(DebugPrint) << "PlanComponentManager: Sucessful plan for Entity "
<< currentComponent->entity << "! Final Call List:";
Htn::PrintTaskCallList(componentPlanner.FinalCallList);
// We'll execute the plan next Update()
}
if (status < Htn::Planner::Status::Running_EnumBegin)
{
LOGD << "PlanComponentManager: Failed plan for Entity "
<< currentComponent->entity << " with code " << int(status)
<< "! Initial Call List:";
LOGD_IF(DebugPrint) << "PlanComponentManager: Failed plan for Entity "
<< currentComponent->entity << " with code " << int(status)
<< "! Initial Call List:";
Htn::PrintTaskCallList(componentPlanner.InitialCallList);
// Plan failed, remove entity
// TODO: Hook up events
LOGD << "PlanComponentManager: Plan not running/failed";
LOGD_IF(DebugPrint) << "PlanComponentManager: Plan not running/failed";
entitiesToUnsubscribe.push_back(currentEntity);
}
}
@ -112,6 +131,12 @@ void PlanComponentManager::Update(float deltaSeconds)
UnsubscribeEntities(entitiesToUnsubscribe);
}
void PlanComponentManager::OnNotify(const Htn::TaskEvent& event)
{
LOGD_IF(DebugPrint) << "Entity [" << event.entity << "] Task notify returned status "
<< (int)event.Result;
}
void PlanComponentManager::SubscribeEntitiesInternal(const EntityList& subscribers,
PlanComponentRefList& components)
{
@ -132,7 +157,7 @@ void PlanComponentManager::SubscribeEntitiesInternal(const EntityList& subscribe
planner.DebugPrint = true;
}
LOGD << "PlanComponentManager: Subscribed " << components.size() << " entities";
LOGD_IF(DebugPrint) << "PlanComponentManager: Subscribed " << components.size() << " entities";
}
void PlanComponentManager::UnsubscribeEntitiesInternal(const EntityList& unsubscribers,

10
src/game/agent/PlanComponentManager.hpp

@ -2,7 +2,9 @@
#include "../../entityComponentSystem/PooledComponentManager.hpp"
#include "../../ai/htn/HTNPlanner.hpp"
#include "../../ai/htn/HTNTypes.hpp"
#include "../../ai/WorldState.hpp"
#include "../../util/SubjectObserver.hpp"
namespace gv
{
@ -21,7 +23,9 @@ Prepare, manage, and execute plan(s) for Entities.
TODO: PooledComponentManager is going to need to be discarded in order to handle Entities with many
plans.
*/
class PlanComponentManager : public gv::PooledComponentManager<PlanComponentData>
// TODO: Multiple inheritance :(
class PlanComponentManager : public gv::PooledComponentManager<PlanComponentData>,
public gv::Observer<Htn::TaskEvent>
{
private:
WorldStateManager* worldStateManager;
@ -37,9 +41,13 @@ protected:
public:
typedef std::vector<gv::PooledComponent<PlanComponentData>> PlanComponentList;
bool DebugPrint = false;
PlanComponentManager();
virtual ~PlanComponentManager();
void Initialize(WorldStateManager* newWorldStateManager);
virtual void Update(float deltaSeconds);
virtual void OnNotify(const Htn::TaskEvent& event);
};
};

18
src/unitTesting/HTN_test.cpp

@ -27,10 +27,12 @@ public:
std::cout << "\tApplyStateChange AlwaysFailPrimitiveTask\n";
}
virtual bool Execute(gv::WorldState& state, const Htn::ParameterList& parameters)
virtual Htn::TaskExecuteStatus Execute(gv::WorldState& state,
const Htn::ParameterList& parameters)
{
std::cout << "\texecute AlwaysFailPrimitiveTask: " << parameters[0].IntValue << "\n";
return false;
Htn::TaskExecuteStatus status {Htn::TaskExecuteStatus::ExecutionStatus::Failed, NULL};
return status;
}
};
@ -53,10 +55,12 @@ public:
std::cout << "\tApplyStateChange RequiresStatePrimitiveTask\n";
}
virtual bool Execute(gv::WorldState& state, const Htn::ParameterList& parameters)
virtual Htn::TaskExecuteStatus Execute(gv::WorldState& state,
const Htn::ParameterList& parameters)
{
std::cout << "\texecute RequiresStatePrimitiveTask: " << parameters[0].IntValue << "\n";
return true;
Htn::TaskExecuteStatus status {Htn::TaskExecuteStatus::ExecutionStatus::Succeeded, NULL};
return status;
}
};
@ -79,10 +83,12 @@ public:
state.TestStateChange = 1;
}
virtual bool Execute(gv::WorldState& state, const Htn::ParameterList& parameters)
virtual Htn::TaskExecuteStatus Execute(gv::WorldState& state,
const Htn::ParameterList& parameters)
{
std::cout << "\texecute TestPrimitiveTask: " << parameters[0].IntValue << "\n";
return true;
Htn::TaskExecuteStatus status {Htn::TaskExecuteStatus::ExecutionStatus::Succeeded, NULL};
return status;
}
};

2
src/util/SubjectObserver.hpp

@ -2,7 +2,7 @@
#include <vector>
#include "entityComponentSystem/EntityTypes.hpp"
#include "../entityComponentSystem/EntityTypes.hpp"
namespace gv
{

8
src/world/Position.cpp

@ -126,4 +126,12 @@ bool Position::operator==(const Position& otherPosition) const
GlobalPosition::GlobalPosition(Position& localPosition) : LocalPosition(localPosition)
{
}
}
namespace plog
{
Record& operator<<(Record& record, const gv::Position& position)
{
return record << "(" << position.X << ", " << position.Y << ", " << position.Z << ")";
}
}

9
src/world/Position.hpp

@ -2,6 +2,8 @@
#include <vector>
#include "../util/Logging.hpp"
namespace gv
{
struct Position
@ -51,4 +53,9 @@ struct GlobalPosition
typedef std::vector<Position> PositionList;
typedef std::vector<Position*> PositionRefList;
};
};
namespace plog
{
Record& operator<<(Record& record, const gv::Position& position);
}
Loading…
Cancel
Save