Browse Source

Added ConsoleOnly frontend, small Jamfile changes

- Added ConsoleOnly frontend. This is to make it quicker to test Galavant features which do not need Unreal Engine. I wrote this while the GalavantUnreal frontend was broken
- Fixed HTNPlanner includes to be absolute
- Fixed CombatComponentManager warning regarding my lazy resetting of current CombatAction
- Updated Jamfile for GalavantPseudotarget to have correct library ordering and added some comments in Jamrules about this
combatComponentRefactor
Macoy Madson 6 years ago
parent
commit
17e3b68fc3
  1. 13
      Jamfile
  2. 6
      Jamrules
  3. 17
      TODO.txt
  4. 5
      src/Jamfile
  5. 2
      src/ai/htn/HTNPlanner.hpp
  6. 4
      src/ai/htn/HTNTypes.hpp
  7. 5
      src/frontends/Jamfile
  8. 24
      src/frontends/consoleOnly/ConsoleMovementComponentManager.cpp
  9. 27
      src/frontends/consoleOnly/ConsoleMovementComponentManager.hpp
  10. 404
      src/frontends/consoleOnly/ConsoleOnlyMain.cpp
  11. 15
      src/frontends/consoleOnly/Jamfile
  12. 2
      src/game/agent/combat/CombatComponentManager.cpp

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 ;

6
Jamrules

@ -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

17
TODO.txt

@ -62,10 +62,16 @@ Cereal integration
Fix debug text over AgentCharacter for combat test
Automatic apply UEngine patch script
------------------
Doing
------------------
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
@ -78,14 +84,19 @@ Spawning very, very broken
Combat
Agent goals are broken after refactor
May be fixed by actually hooking in HTNPlan to FindFood goal def (update: not fixed)
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)
Remember to put Galavant-specific UE code into a patch or something
------------------
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

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/htn/HTNPlanner.hpp

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

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
{

5
src/frontends/Jamfile

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

24
src/frontends/consoleOnly/ConsoleMovementComponentManager.cpp

@ -0,0 +1,24 @@
#include "ConsoleMovementComponentManager.hpp"
ConsoleMovementComponentManager g_ConsoleMovementComponentManager;
ConsoleMovementComponentManager::ConsoleMovementComponentManager()
{
DebugName = "ConsoleMovementComponentManager";
}
void ConsoleMovementComponentManager::Initialize(
gv::CallbackContainer<Htn::TaskEventCallback>* taskEventCallbacks)
{
TaskEventCallbacks = taskEventCallbacks;
}
void ConsoleMovementComponentManager::Update(float deltaSeconds)
{
}
// 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)
{
}

27
src/frontends/consoleOnly/ConsoleMovementComponentManager.hpp

@ -0,0 +1,27 @@
#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"
class ConsoleMovementComponentManager : public gv::ComponentManager, public gv::MovementManager
{
private:
gv::CallbackContainer<Htn::TaskEventCallback>* TaskEventCallbacks;
public:
ConsoleMovementComponentManager();
virtual ~ConsoleMovementComponentManager() = default;
void Initialize(gv::CallbackContainer<Htn::TaskEventCallback>* taskEventCallbacks);
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;

404
src/frontends/consoleOnly/ConsoleOnlyMain.cpp

@ -0,0 +1,404 @@
#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
/*{
UnrealMovementComponent::UnrealMovementComponentList newEntityMovementComponents(
numTestEntities);
float spacing = 500.f;
int i = 0;
for (gv::EntityListIterator it = testEntities.begin(); it != testEntities.end(); ++it, i++)
{
newEntityMovementComponents[i].entity = (*it);
newEntityMovementComponents[i].data.ResourceType = gv::WorldResourceType::Agent;
newEntityMovementComponents[i].data.SpawnParams.CharacterToSpawn =
DefaultAgentCharacter;
newEntityMovementComponents[i].data.WorldPosition.Set(0.f, i * spacing,
TestEntityCreationZ);
newEntityMovementComponents[i].data.GoalManDistanceTolerance = 600.f;
newEntityMovementComponents[i].data.MaxSpeed = 500.f;
}
g_ConsoleMovementComponentManager.SubscribeEntities(newEntityMovementComponents);
}*/
// 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);
// UnrealMovementComponent::UnrealMovementComponentList newFood(numFood);
gv::PickupRefList newPickups;
newPickups.reserve(numFood);
gv::g_InteractComponentManager.CreatePickups(testFoodEntities, newPickups);
//float spacing = 2000.f;
int i = 0;
for (gv::EntityListIterator it = testFoodEntities.begin(); it != testFoodEntities.end();
++it, i++)
{
// Movement component
/*{
FVector location(-2000.f, i * spacing, TestEntityCreationZ);
newFood[i].entity = (*it);
newFood[i].data.WorldPosition.Set(location.X, location.Y, location.Z);
newFood[i].data.ResourceType = gv::WorldResourceType::Food;
newFood[i].data.SpawnParams.ActorToSpawn = TestFoodActor;
}*/
// Pickup component
if (i < (int)newPickups.size())
{
newPickups[i]->AffectsNeed = gv::NeedType::Hunger;
newPickups[i]->DestroySelfOnPickup = true;
}
}
// g_ConsoleMovementComponentManager.SubscribeEntities(newFood);
}
}
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 ;

2
src/game/agent/combat/CombatComponentManager.cpp

@ -99,7 +99,7 @@ void CombatComponentManager::Update(float deltaSeconds)
combatant.CurrentAction.ActivateTime + combatant.CurrentAction.Def->Duration;
if (gv::GetWorldTime() < actionEndTime)
{
combatant.CurrentAction = {0};
combatant.CurrentAction = {nullptr, nullptr, 0.f};
}
}
}

Loading…
Cancel
Save