Browse Source

AgentGoals and Plans now working well

- Fixed inverted in PlanComponentManager::ExecutePlan() which caused all plans to be broken after the first task executed
- Modified Htn::Planner state enum to have None state so that I didn't have to spoof the state to get plans rolling in PlanComponentManager
- Renamed AgentGoal::GoalStatus::Initialized to StartGoal because it's more clear in context as to what will happen to the goal
- Removed the std::ostream Htn::TaskList print function. gv::Logging is used instead (although may be broken; I don't really care too much)
- Htn::Planner now has a Clear() function so that it could possibly be reused for another plan. I have not tested this function
- Added Notes.md with some notes about working on Galavant
- Slightly reformatted TODO.txt
combatComponentRefactor
Macoy Madson 6 years ago
parent
commit
274cd56a2c
  1. 14
      Notes.md
  2. 23
      TODO.txt
  3. 11
      src/ai/htn/HTNPlanner.cpp
  4. 20
      src/ai/htn/HTNPlanner.hpp
  5. 21
      src/ai/htn/HTNTasks.cpp
  6. 6
      src/ai/htn/HTNTasks.hpp
  7. 17
      src/game/agent/AgentComponentManager.cpp
  8. 2
      src/game/agent/AgentComponentManager.hpp
  9. 22
      src/game/agent/PlanComponentManager.cpp
  10. 6
      src/game/agent/PlanComponentManager.hpp

14
Notes.md

@ -0,0 +1,14 @@
# Notes
Miscellaneous notes regarding working on Galavant, the code, and other random shit.
## Working With Unreal
- 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
## 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
- @Callback: The marked function is used as a callback. Preferably @Callback [Callback type name]

23
TODO.txt

@ -1,6 +1,6 @@
----------------------------------------------------------------------------------------------------
TODO
----------------------------------------------------------------------------------------------------
------------------
Todo
------------------
Soak test editor - after ~1 hour it was looking pretty glitchy
@ -27,18 +27,19 @@ Put HTN Tasks etc. in resource dictionaries? Who owns them?
Sublime "syntax" tag for build systems
----------------------------------------------------------------------------------------------------
DOING
----------------------------------------------------------------------------------------------------
------------------
Doing
------------------
Test PlanComponent event stuff
It seems to have broken things
------------------
Done
------------------
----------------------------------------------------------------------------------------------------
DONE
----------------------------------------------------------------------------------------------------
Test PlanComponent event stuff
It seems to have broken things
Logging broke as shit
https://answers.unrealengine.com/questions/435366/linux-crash-in-stdstring-destructor.html ?

11
src/ai/htn/HTNPlanner.cpp

@ -411,4 +411,15 @@ Planner::Status Planner::PlanStep()
CurrentStatus = status;
return status;
}
void Planner::Clear()
{
InitialCallList.clear();
FinalCallList.clear();
DecompositionStack.clear();
WorkingCallList.clear();
State = {};
StacklessState = {};
CurrentStatus = Status::None;
}
}

20
src/ai/htn/HTNPlanner.hpp

@ -35,15 +35,12 @@ Given the world state and a set of goal tasks, decompose them into a list of pri
when executed, will result in the desired goal tasks being completed. Note that the initial goal
task set can include compound and primitive tasks.
TODO: Either make planner allow making multiple plans with the same instance, or make it clear
that it is for a single plan only
You must call Clear() before using the Planner for another plan. Note that Planner allocates memory
dynamically due to its usage of vector
*/
class Planner
{
public:
Planner() = default;
~Planner() = default;
gv::WorldState State;
TaskCallList InitialCallList;
@ -69,8 +66,10 @@ public:
enum class Status
{
None = 0,
// Bad
Failed_BadData = 0,
Failed_BadData,
Failed_NoPossiblePlan,
Failed_NoTasks,
@ -89,13 +88,18 @@ public:
PlanComplete
};
Status CurrentStatus = Status::None;
bool IsPlannerRunning();
bool IsPlannerRunning(Status status);
Status CurrentStatus;
Status PlanStep();
void Clear();
Planner() = default;
~Planner() = default;
private:
GoalDecompositionStack DecompositionStack;

21
src/ai/htn/HTNTasks.cpp

@ -111,26 +111,6 @@ PrimitiveTask* Task::GetPrimitive()
return Primitive;
}
std::ostream& operator<<(std::ostream& os, const Task& task)
{
switch (task.GetType())
{
case TaskType::None:
os << "Task Type:None " << &task;
break;
case TaskType::Goal:
os << "Task Type:Goal task addr " << &task << " Goal addr " << task.Goal;
break;
case TaskType::Compound:
os << "Task Type:Compound addr " << &task << " Compound addr " << task.Compound;
break;
case TaskType::Primitive:
os << "Task Type:Primitive addr " << &task << " Primitive addr " << task.Primitive;
break;
}
return os;
}
void PrintTaskList(const TaskList& tasks)
{
LOGD << "TaskList size = " << tasks.size() << " addr " << &tasks << ":";
@ -146,6 +126,7 @@ void PrintTaskCallList(const TaskCallList& tasks)
}
}
// TODO: This might not actually work right now. Not very important
template <>
gv::Logging::Record& gv::Logging::Record::operator<<<Htn::Task>(const Htn::Task& task)
{

6
src/ai/htn/HTNTasks.hpp

@ -39,9 +39,7 @@ struct Task
CompoundTask* GetCompound();
PrimitiveTask* GetPrimitive();
friend std::ostream& operator<<(std::ostream& os, const Task& task);
protected:
private:
union
{
GoalTask* Goal;
@ -136,8 +134,6 @@ public:
Task* GetTask();
};
std::ostream& operator<<(std::ostream& os, const Task& task);
void PrintTaskList(const TaskList& tasks);
void PrintTaskCallList(const TaskCallList& tasks);
}

17
src/game/agent/AgentComponentManager.cpp

@ -54,7 +54,8 @@ void AgentComponentManager::Update(float deltaSeconds)
return;
}
const PlanExecutionEventList& planExecutionEvents = PlanManager->GetExecutionEvents();
const PlanExecutionEventList& planConclusiveExecutionEvents =
PlanManager->GetConclusiveExecutionEvents();
WorldTime += deltaSeconds;
@ -116,7 +117,7 @@ void AgentComponentManager::Update(float deltaSeconds)
if (needLevelTrigger.NeedsResource && needLevelTrigger.WorldResource)
{
AgentGoal newNeedResourceGoal{
AgentGoal::GoalStatus::Initialized,
AgentGoal::GoalStatus::StartGoal,
/*NumFailureRetries=*/0,
gv::g_AgentGoalDefDictionary.GetResource(RESKEY("GetResource")),
needLevelTrigger.WorldResource};
@ -142,8 +143,7 @@ void AgentComponentManager::Update(float deltaSeconds)
AgentGoal& goal = (*headGoalIt);
switch (goal.Status)
{
// Start up the goal
case AgentGoal::GoalStatus::Initialized:
case AgentGoal::GoalStatus::StartGoal:
// TODO: This IsSubscribed call will go away once PlanManager manages multiple
// plans for a single entity. For now, we'll just wait until the entity is no
// longer subscribed before adding our goal
@ -179,11 +179,12 @@ void AgentComponentManager::Update(float deltaSeconds)
{
// While goal is in progress, watch planner events for a conclusive status
bool entityConcludedPlan = false;
for (PlanExecutionEvent planEvent : planExecutionEvents)
for (PlanExecutionEvent planEvent : planConclusiveExecutionEvents)
{
if (planEvent.entity != currentEntity)
continue;
// If we got this far we received a relevant conclusive event
entityConcludedPlan = true;
if (planEvent.status == PlanExecuteStatus::Failed)
@ -197,10 +198,14 @@ void AgentComponentManager::Update(float deltaSeconds)
<< goal.Def->NumRetriesIfFailed << " max tries)";
goal.NumFailureRetries++;
goal.Status = AgentGoal::GoalStatus::Initialized;
goal.Status = AgentGoal::GoalStatus::StartGoal;
entityConcludedPlan = false;
}
else
goal.Status = AgentGoal::GoalStatus::Failed;
}
else if (planEvent.status == PlanExecuteStatus::Succeeded)
goal.Status = AgentGoal::GoalStatus::Succeeded;
break;
}

2
src/game/agent/AgentComponentManager.hpp

@ -31,7 +31,7 @@ struct AgentGoal
enum class GoalStatus
{
None = 0,
Initialized,
StartGoal,
InProgress,
Failed,
Succeeded

22
src/game/agent/PlanComponentManager.cpp

@ -99,7 +99,7 @@ PlanExecuteStatus PlanComponentManager::ExecutePlan(Entity currentEntity,
currentComponent.Planner.FinalCallList.erase(currentStep);
// We have finished all tasks
if (!currentComponent.Planner.FinalCallList.empty())
if (currentComponent.Planner.FinalCallList.empty())
planStatus = PlanExecuteStatus::Succeeded;
break;
@ -111,16 +111,13 @@ PlanExecuteStatus PlanComponentManager::ExecutePlan(Entity currentEntity,
}
else
{
// We have finished all tasks; remove this entity from the manager
LOGE_IF(DebugPrint) << "Call list empty; something weird happened";
planStatus = PlanExecuteStatus::Failed;
}
// We've finished in a conclusive way; unsubscribe the entity
if (planStatus == PlanExecuteStatus::Succeeded || planStatus == PlanExecuteStatus::Failed)
{
// TODO: Send event or callback that the plan execution has concluded
entitiesToUnsubscribe.push_back(currentEntity);
}
return planStatus;
}
@ -130,7 +127,7 @@ void PlanComponentManager::Update(float deltaSeconds)
EntityList entitiesToUnsubscribe;
// Things had their chance to read these
PlanExecutionEvents.clear();
PlanConclusiveExecutionEvents.clear();
if (!worldStateManager)
return;
@ -149,8 +146,9 @@ void PlanComponentManager::Update(float deltaSeconds)
Htn::Planner& componentPlanner = currentComponent->data.Planner;
Entity currentEntity = currentComponent->entity;
PlanExecuteStatus planStatus = PlanExecuteStatus::Running;
bool shouldStartPlanner = (componentPlanner.CurrentStatus == Htn::Planner::Status::None);
if (componentPlanner.IsPlannerRunning())
if (shouldStartPlanner || componentPlanner.IsPlannerRunning())
{
Htn::Planner::Status status = componentPlanner.PlanStep();
if (!componentPlanner.IsPlannerRunning())
@ -199,7 +197,7 @@ void PlanComponentManager::Update(float deltaSeconds)
if (planStatus > PlanExecuteStatus::Begin_Conclusive)
{
PlanExecutionEvent executionEvent = {currentEntity, planStatus};
PlanExecutionEvents.push_back(executionEvent);
PlanConclusiveExecutionEvents.push_back(executionEvent);
}
}
@ -242,12 +240,10 @@ void PlanComponentManager::SubscribeEntitiesInternal(const EntityList& subscribe
Htn::TaskCallList& goalCallList = currentComponent->data.Tasks;
planner.State = worldStateManager->GetWorldStateForAgent(currentComponent->entity);
// @Performance: Could possibly make planner look directly at lists given instead of copying
planner.InitialCallList.insert(planner.InitialCallList.end(), goalCallList.begin(),
goalCallList.end());
// TODO: This is not kosher
planner.CurrentStatus = Htn::Planner::Status::Running_SuccessfulPrimitive;
planner.DebugPrint = DebugPrint;
}
@ -268,8 +264,8 @@ void PlanComponentManager::UnsubscribePoolEntitiesInternal(const EntityList& uns
}
}
const PlanExecutionEventList& PlanComponentManager::GetExecutionEvents() const
const PlanExecutionEventList& PlanComponentManager::GetConclusiveExecutionEvents() const
{
return PlanExecutionEvents;
return PlanConclusiveExecutionEvents;
}
}

6
src/game/agent/PlanComponentManager.hpp

@ -24,7 +24,7 @@ enum class PlanExecuteStatus
None = 0,
Running,
Begin_Conclusive,
Succeeded,
Failed,
@ -48,7 +48,7 @@ class PlanComponentManager : public gv::PooledComponentManager<PlanComponentData
private:
WorldStateManager* worldStateManager;
PlanExecutionEventList PlanExecutionEvents;
PlanExecutionEventList PlanConclusiveExecutionEvents;
PlanExecuteStatus ExecutePlan(Entity currentEntity, PlanComponentData& currentComponent,
EntityList& entitiesToUnsubscribe);
@ -72,7 +72,7 @@ public:
CallbackContainer<Htn::TaskEventCallback>* taskEventCallbacks);
virtual void Update(float deltaSeconds);
const PlanExecutionEventList& GetExecutionEvents() const;
const PlanExecutionEventList& GetConclusiveExecutionEvents() const;
Htn::TaskEventList ReceivedEvents;
};

Loading…
Cancel
Save