Commit 42a808bb authored by DennisSoemers's avatar DennisSoemers

Added source code of plugin and updated readme.

Readme not finished yet, and should probably clean up some commented code out of source code.
parent d8c6c88d
{
"FileVersion": 3,
"Version": 1,
"VersionName": "1.0",
"FriendlyName": "HTN Plugin",
"Description": "Plugin that adds a Hierarchical Task Network (HTN) Planner to Unreal Engine 4.",
"Category": "Artificial Intelligence",
"CreatedBy": "Dennis Soemers",
"CreatedByURL": "",
"DocsURL": "",
"MarketplaceURL": "",
"SupportURL": "",
"Modules": [
{
"Name": "HTN_Plugin",
"Type": "Developer",
"LoadingPhase": "PreDefault"
}
],
"EnabledByDefault": false,
"CanContainContent": false,
"IsBetaVersion": false,
"Installed": false
}
\ No newline at end of file
#pragma once
#include "HTNTask.h"
#include "CompoundTask.generated.h"
struct FHTNWorldState;
class UHTNPlannerComponent;
class UTaskNetwork;
UCLASS(HideDropdown)
class HTN_PLUGIN_API UCompoundTask : public UHTNTask
{
GENERATED_BODY()
public:
/**
* Should be implemented to return an Array of Task Network Instances, where every Task Network
* is one of the Networks that the Compound Task can be decomposed into given the World State.
*/
virtual TArray<TSharedPtr<FHTNTaskInstance>> FindDecompositions(UHTNPlannerComponent& HTNComp,
const TSharedPtr<FHTNWorldState> WorldState, uint8* TaskMemory);
/**
* Can be overridden to return a heuristic estimate of the cost of executing this action in a future World
* State. Must be an admissible heuristic (exact or underestimate of the real cost) to be able to guarantee
* finding an optimal plan.
*
* Default implementation returns a cost of 0.f
*/
virtual float GetHeuristicCost() const;
};
\ No newline at end of file
#pragma once
#include "HTNPlan.generated.h"
struct FHTNTaskInstance;
class UPrimitiveTask;
/**
* Describes a Plan of Tasks that need to be executed. Can be a partial plan constructed
* so far during the planning process that may turn out to be incorrect, or can be a completely
* finalized plan.
*/
USTRUCT(BlueprintType)
struct HTN_PLUGIN_API FHTNPlan
{
GENERATED_BODY()
public:
FHTNPlan();
void AppendSearchHistory(const TSharedPtr<FHTNTaskInstance>& NewTaskInstance);
void AppendSearchHistory(const TArray<TSharedPtr<FHTNTaskInstance>>& NewTaskInstances);
void AppendTaskInstance(const TSharedPtr<FHTNTaskInstance>& NewTaskInstance);
void AppendTaskInstances(const TArray<TSharedPtr<FHTNTaskInstance>>& NewTaskInstances);
TSharedPtr<FHTNPlan> Copy();
int32 GetLastStreakScore(const TSharedPtr<FHTNPlan>& OtherPlan, int32 MinStreakLength) const;
int32 GetLongestMatchingStreak(const TSharedPtr<FHTNPlan>& OtherPlan, int32 MinStreakLength) const;
int32 GetMatchingCompounds(const TSharedPtr<FHTNPlan>& OtherPlan, const TSharedPtr<FHTNTaskInstance>& TaskNetwork) const;
int32 GetMatchingStreak(const TSharedPtr<FHTNPlan>& OtherPlan, int32 MinStreakLength) const;
int32 GetPlanSize() const;
int32 GetPotentialSimilarity(const TSharedPtr<FHTNPlan>& OtherPlan, const TSharedPtr<FHTNTaskInstance>& TaskNetwork) const;
int32 GetRemainingPlanSize() const;
int32 GetSimilarity(const TSharedPtr<FHTNPlan>& OtherPlan, const TSharedPtr<FHTNTaskInstance>& TaskNetwork) const;
int32 GetStreakScore(const TSharedPtr<FHTNPlan>& OtherPlan, int32 MinStreakLength) const;
TSharedPtr<FHTNTaskInstance> GetTaskInstanceToExecute();
const TArray<TSharedPtr<FHTNTaskInstance>>& GetSearchHistory() const;
const TArray<TSharedPtr<FHTNTaskInstance>>& GetTasks() const;
void IncrementExecutionIndex();
bool IsComplete() const;
void RemoveExecutedTasks(int32 NumTasksExecuted);
void SetComplete(bool bNewStatus);
void Print();
protected:
/** List of primitive Task Instances that need to be executed */
TArray<TSharedPtr<FHTNTaskInstance>> TaskInstances;
/**
* List of all the Task Instances (Compound AND Primitive) that were processed in the search tree
* to reach this plan.
*/
TArray<TSharedPtr<FHTNTaskInstance>> SearchHistory;
/** Index of the next Task to execute. */
int32 ExecutionIndex;
/** If true, this Plan is complete (the planning process that generated it has finished) */
bool bComplete;
};
\ No newline at end of file
#pragma once
#include "HTNPlanner/HTNPlannerTypes.h"
#include "HTNPlanner/HTNWorldState.h"
#include "HTNPlanner.generated.h"
class UBlackboardData;
class UTaskNetwork;
UCLASS()
class HTN_PLUGIN_API UHTNPlanner : public UDataAsset
{
GENERATED_BODY()
public:
UHTNPlanner(const FObjectInitializer& ObjectInitializer);
/** blackboard asset for this planner */
UPROPERTY(EditAnywhere, Category = "Blackboard")
UBlackboardData* BlackboardAsset;
/** If true, the Planner will perform a breadth-first search. Otherwise, a more breadth-first / A* style search will be used */
UPROPERTY(EditAnywhere, Category = "Hierarchical Task Network Planner")
bool bDepthFirstSearch;
/** If true, the Planner will also execute the plan after finding it. */
UPROPERTY(EditAnywhere, Category = "Hierarchical Task Network Planner")
bool bExecutePlan;
/** If true, the Planner will ignore the costs of Tasks and simply produce the first plan it is able to find instead. */
UPROPERTY(EditAnywhere, Category = "Hierarchical Task Network Planner")
bool bIgnoreTaskCosts;
/** If true, the Planner will re-start from the initial Task Network to create a new plan once the previous plan has been executed. */
UPROPERTY(EditAnywhere, Category = "Hierarchical Task Network Planner")
bool bLoop;
/** If true, the Planner will use heuristic costs to prune the search early */
UPROPERTY(EditAnywhere, Category = "Hierarchical Task Network Planner")
bool bUseHeuristicCosts;
/** The maximum amount of time (in seconds) that the planner can dedicate to planning per Tick */
UPROPERTY(EditAnywhere, Category = "Hierarchical Task Network Planner", meta=(ClampMin=0, ClampMax=1))
float MaxSearchTime;
/** The Task Network for which a plan needs to be found when the planner is (re)started */
UPROPERTY(EditAnywhere, Category = "Hierarchical Task Network Planner")
TSubclassOf<UTaskNetwork> TaskNetwork;
/**
* Can be set to some initial World State struct so that the planner knows
* what struct type should be used for the World State (cannot use a TSubclassOf
* property because they're not compatible with non-UObjects).
*
* If not set, will use EmptyWorldState set in HTNPlannerComponent instead of in the Planner asset.
*
* Should not already be initialized, only needs to be of the correct type!
*/
TSharedPtr<FHTNWorldState> EmptyWorldState;
/**
* Will be used if TaskNetwork does not hold a valid blueprint.
* Can be used to dynamically create planning problems at runtime.
*/
TSharedPtr<FHTNTaskInstance> TaskNetworkInstance;
};
\ No newline at end of file
#pragma once
#include "BrainComponent.h"
#include "HTNPlannerTypes.generated.h"
class UBlackboardKeyType;
class UHTNTask;
// Since this enum is equivalent to EBTNodeResult in BehaviorTreeTypes.h, it would be cool if this was
// changed to be not specific to either type of BrainComponent
UENUM(BlueprintType)
enum class EHTNExecutionResult : uint8
{
Succeeded, // finished as success
Failed, // finished as failure
Aborted, // finished aborting = failure
InProgress, // not finished yet
};
// Since this enum is equivalent to EBTTaskStatus in BehaviorTreeTypes.h, it would be cool if this was
// changed to be not specific to either type of BrainComponent
enum class EHTNTaskStatus : uint8
{
Active,
Aborting,
Inactive,
};
// Since this enum is equivalent to the one in BehaviorTreeTypes.h, it would be cool if this was
// changed to be not specific to either type of BrainComponent
enum class EHTNStopMode : uint8
{
Safe,
Forced
};
/**
* Struct used to keep track of instances of Tasks during the planning process.
* Contains a pointer to a Task object (generally the CDO) for calling functions,
* and a pointer to the Task's memory specific to that instance.
*/
USTRUCT(BlueprintType)
struct FHTNTaskInstance
{
GENERATED_BODY()
private:
/** Memory of the specific Task instance */
TArray<uint8> TaskMemory;
public:
/** Task object (generally CDO) that we can call functions on */
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Task Instance")
class UHTNTask* Task;
~FHTNTaskInstance();
// TO DO keep track of memory stats (see BehaviorTreeTypes.h FBehaviorTreeInstance)?
FHTNTaskInstance() : Task(nullptr) {}
FHTNTaskInstance(class UHTNTask* Task, int32 MemorySize)
{
this->Task = Task;
TaskMemory.AddZeroed(MemorySize);
}
FORCEINLINE uint8* GetMemory() { return TaskMemory.GetData(); }
FORCEINLINE const TArray<uint8>& GetUint8Memory() const { return TaskMemory; }
};
UCLASS(Abstract)
class HTN_PLUGIN_API UHTNPlannerTypes : public UObject
{
GENERATED_BODY()
public:
static FString DescribeTaskHelper(const UHTNTask* Task, uint8* TaskMemory);
static FString DescribeTaskResult(EHTNExecutionResult TaskResult);
};
\ No newline at end of file
#pragma once
#include "HTNService.generated.h"
class UHTNPlannerComponent;
/**
* HTN Services are designed to perform "background" tasks.
*
* Services will continuously be ticked as soon as they are registered, and will only stop ticking
* they are unregistered again or when an HTNPlannerComponent is stopped.
*
* The HTNService fulfills a similar role in HTN Planners as the BTService does in a Behavior Tree.
*/
UCLASS(Abstract, HideDropdown)
class HTN_PLUGIN_API UHTNService : public UObject
{
GENERATED_BODY()
public:
UHTNService(const FObjectInitializer& ObjectInitializer);
void SetOwner(AActor* ActorOwner);
void Tick(UHTNPlannerComponent& HTNComp, float DeltaSeconds);
protected:
/** Cached AIController owner of HTNPlannerComponent. */
UPROPERTY(Transient)
AAIController* AIOwner;
/** Cached actor owner of HTNPlannerComponent. */
UPROPERTY(Transient)
AActor* ActorOwner;
/** defines time span between subsequent ticks of the service */
UPROPERTY(Category = Service, EditAnywhere, meta = (ClampMin = "0.001"))
float Interval;
/** adds random range to service's Interval */
UPROPERTY(Category = Service, EditAnywhere, meta = (ClampMin = "0.0"))
float RandomDeviation;
float NextTickRemainingTime;
float AccumulatedDeltaTime;
/** Called whenever the specified amount of time has expired. The service's functionality should be implemented here */
virtual void OnUpdate(UHTNPlannerComponent& HTNComp, float DeltaSeconds);
};
\ No newline at end of file
#pragma once
#include "HTNPlanner/HTNPlannerTypes.h"
#include "HTNTask.generated.h"
class UHTNPlannerComponent;
/**
* Base class for Tasks in a Hierarchical Task Network (HTN).
*
* Can be either a primitive Task (a Task that can be executed by an agent directly) or a compound Task
* (a list of other Tasks that need to be executed in order to consider the compound Task executed).
*/
UCLASS(Abstract)
class HTN_PLUGIN_API UHTNTask : public UObject
{
GENERATED_BODY()
public:
virtual void Cleanup(uint8* TaskMemory);
/**
* This will be called whenever a deep copy of a Task Instance is considered to be necessary to guarantee
* a correct planning process.
*
* The default implementation will simply return the given Task Instance instead of actually creating a copy.
*
* In cases where a Task has internal memory that is important and can change during the planning process,
* the function should be overridden to correctly create a real copy.
*/
virtual TSharedPtr<FHTNTaskInstance> Copy(const TSharedPtr<FHTNTaskInstance>& OriginalInstance);
/** size of instance memory */
virtual uint16 GetInstanceMemorySize() const;
/** Returns the name of this Task */
virtual FString GetTaskName(uint8* TaskMemory) const;
/**
* Will be called when creating instances through the static Instantiate() method.
* Can be used for instantiating sub-tasks.
*/
virtual void Instantiate(UHTNPlannerComponent& HTNComp, uint8* TaskMemory);
protected:
/** Name used in logs and debugging messages to describe this Task or Task Network */
UPROPERTY(EditAnywhere, Category = "Hierarchical Task Network", meta=(DisplayName="Name"))
FString TaskName;
};
\ No newline at end of file
#pragma once
#include "HTNPlannerTypes.h"
#include "HTNWorldState.generated.h"
class UBlackboardComponent;
class UPrimitiveTask;
/**
* This struct should be used to define the state of the world during the planning process of
* a Hierarchical Task Network (HTN) Planner. It fulfills a similar role to the HTN Planner as
* the Blackboard does to a Behavior Tree, but is different in that the Planner should be able
* to modify it during the planning process and should be able to efficiently make copies of
* the World State.
*/
USTRUCT(BlueprintType)
struct HTN_PLUGIN_API FHTNWorldState
{
GENERATED_BODY()
public:
virtual ~FHTNWorldState() {}
/**
* Called when a Task does not know how to apply itself to the World State, and therefore
* needs the World State to perform the application of the Task.
*/
virtual void ApplyTask(const UPrimitiveTask* Task, uint8* TaskMemory);
/**
* Called when a Task does not know how to check whether it is applicable to the World State
* itself, and therefore needs the World State to perform this check.
*
* Should be implemented to return true if and only if the given Task is applicable in the current World State
*/
virtual bool CanApply(const UPrimitiveTask* Task, uint8* TaskMemory) const;
/** Should be implemented to create and return a copy of this World State */
virtual TSharedPtr<FHTNWorldState> Copy() const;
/**
* Can be implemented to return an estimate of the cost of executing the entire given collection
* of tasks. Will be added to the heuristic costs of the individual tasks, so should only return extra
* cost that can be derived from the current world state.
*
* By default simply returns 0.f
*/
virtual float GetHeuristicCost(const TArray<TSharedPtr<struct FHTNTaskInstance>>& TaskInstances) const;
/** Can be implemented to return a specific name for the world state. By default returns "HTN World State" */
virtual FName GetWorldStateName() const;
/** Allows for initialization of the World State according to the given Owner and BlackboardComponent */
virtual void Initialize(AActor* Owner, UBlackboardComponent* BlackboardComponent);
};
\ No newline at end of file
#pragma once
#include "HTNPlannerTypes.h"
#include "HTNTask.h"
#include "PrimitiveTask.generated.h"
class UBrainComponent;
class UHTNPlannerComponent;
struct FAIMessage;
struct FHTNWorldState;
UCLASS(HideDropdown)
class HTN_PLUGIN_API UPrimitiveTask : public UHTNTask
{
GENERATED_BODY()
public:
/** Aborts execution of the Task, and should return the status of the execution. */
virtual EHTNExecutionResult AbortTask(UHTNPlannerComponent& HTNComp, uint8* TaskMemory);
/** Should be implemented to modify the given World State according to the effects of the Task */
virtual void ApplyTo(TSharedPtr<FHTNWorldState> WorldState, uint8* TaskMemory) const;
/**
* Starts execution of the Task, and should return the status of the execution.
*/
virtual EHTNExecutionResult ExecuteTask(UHTNPlannerComponent& HTNComp, uint8* TaskMemory);
/** helper function: finish latent executing */
void FinishLatentTask(UHTNPlannerComponent& HTNComp, EHTNExecutionResult TaskResult, uint8* TaskMemory);
/** helper function: finishes latent aborting */
void FinishLatentAbort(UHTNPlannerComponent& HTNComp, uint8* TaskMemory);
/**
* Can be overridden to return the cost of applying this Task in the given World State.
* Default implementation returns a cost of 1.0 regardless of the World State
*/
virtual float GetCost(const TSharedPtr<FHTNWorldState> WorldState, uint8* TaskMemory) const;
/**
* Can be overridden to return a heuristic estimate of the cost of executing this action in a future World
* State. Must be an admissible heuristic (exact or underestimate of the real cost) to be able to guarantee
* finding an optimal plan.
*
* Default implementation returns a cost of 0.f
*/
virtual float GetHeuristicCost() const;
/** Should be implemented to return true if the Task is applicable in the given World State. */
virtual bool IsApplicable(const TSharedPtr<FHTNWorldState> WorldState, uint8* TaskMemory) const;
/** message handler, default implementation will finish latent execution/abortion */
virtual void OnMessage(UHTNPlannerComponent& HTNComp, FName Message, int32 RequestID,
bool bSuccess, const TSharedPtr<FHTNTaskInstance>& TaskInstance);
/** Called when the Task's execution is finished */
virtual void OnTaskFinished(UHTNPlannerComponent& HTNComp, EHTNExecutionResult TaskResult, uint8* TaskMemory);
/** message observer's hook */
void ReceivedMessage(UBrainComponent* BrainComp, const FAIMessage& Message, TSharedPtr<FHTNTaskInstance> TaskInstance);
/** ticks this task */
virtual void TickTask(UHTNPlannerComponent& HTNComp, float DeltaSeconds, uint8* TaskMemory);
/** register message observer */
void WaitForMessage(UHTNPlannerComponent& HTNComp, FName MessageType, uint8* TaskMemory) const;
void WaitForMessage(UHTNPlannerComponent& HTNComp, FName MessageType, int32 RequestID, uint8* TaskMemory) const;
};
\ No newline at end of file
#pragma once
#include "HTNPlanner/HTNService.h"
#include "HTNService_BlueprintBase.generated.h"
/**
* Base class for blueprint based services in HTN. Do NOT use it for creating native c++ classes!
*
* This class fulfills the same role in HTN Planners as BTService_BlueprintBase does in Behavior Trees.
*/
UCLASS(Abstract, Blueprintable)
class HTN_PLUGIN_API UHTNService_BlueprintBase : public UHTNService
{
GENERATED_BODY()
public:
UHTNService_BlueprintBase(const FObjectInitializer& ObjectInitializer);
protected:
virtual void OnUpdate(UHTNPlannerComponent& HTNComp, float DeltaSeconds) override;
/** tick function
* @Note that if both generic and AI event versions are implemented only the more
* suitable one will be called, meaning the AI version if called for AI, generic one otherwise */
UFUNCTION(BlueprintImplementableEvent)
void ReceiveTick(AActor* OwnerActor, float DeltaSeconds);
/** Alternative AI version of ReceiveTick function.
* @see ReceiveTick for more details
* @Note that if both generic and AI event versions are implemented only the more
* suitable one will be called, meaning the AI version if called for AI, generic one otherwise */
UFUNCTION(BlueprintImplementableEvent, Category = AI)
void ReceiveTickAI(AAIController* OwnerController, APawn* ControlledPawn, float DeltaSeconds);
};
#pragma once
#include "HTNTask.h"
#include "TaskNetwork.generated.h"
class UHTNTask;
class UTaskNetwork;
USTRUCT(BlueprintType)
struct FTaskNetworkMemory
{
GENERATED_BODY()
public:
/** Instances of Task or Subnetwork Instances */
TArray<TSharedPtr<FHTNTaskInstance>> SubNetworkInstances;
/** If true, the Task will have to be executed or decomposed immediately when it no longer has any predecessors in the network. */
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Task Network Memory")
uint8 bImmediate : 1;
/** If true, the array of Sub-Network Instances is unordered and can be executed in any arbitrary order */
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Task Network Memory")
uint8 bUnordered : 1;
};
UCLASS(Blueprintable, HideDropdown)
class HTN_PLUGIN_API UTaskNetwork : public UHTNTask
{
GENERATED_BODY()
public:
UTaskNetwork(const FObjectInitializer& ObjectInitializer);
/** Adds a new task or sub-network to the network. Used for construction of networks at runtime */
void AddTaskOrSubNetwork(const TSharedPtr<FHTNTaskInstance>&, uint8* TaskMemory);
/** Overridden to control whether the bImmediate and bUnordered properties can be edited */
virtual bool CanEditChange(const UProperty* InProperty) const override;
/** Cleans up the Task Network's memory */
virtual void Cleanup(uint8* TaskMemory) override;
/** Returns an array of all the task instances in the network (the returned array does not have any meaningful order) */
TArray<TSharedPtr<FHTNTaskInstance>> CollectAllTasks(const uint8* TaskMemory) const;
/** Creates and returns a deep copy of the entire Network */
virtual TSharedPtr<FHTNTaskInstance> Copy(const TSharedPtr<FHTNTaskInstance>& OriginalInstance) override;
/**
* Finds and returns an array of all Tasks that have no predecessors in the Task Network
* (can contain multiple Tasks that have equal priority).
*/
TArray<TSharedPtr<FHTNTaskInstance>> FindTasksWithoutPredecessors(const uint8* TaskMemory);
float GetHeuristicCost(const TSharedPtr<struct FHTNWorldState>& WorldState, const uint8* TaskMemory) const;
virtual uint16 GetInstanceMemorySize() const override;
const TArray<TSubclassOf<UHTNTask>>& GetSubNetworks() const;
const TArray<TSharedPtr<FHTNTaskInstance>>& GetSubNetworkInstances(const uint8* TaskMemory) const;
/** Overridden to also instantiate sub-networks */
virtual void Instantiate(UHTNPlannerComponent& HTNComp, uint8* TaskMemory) override;
/**
* Returns true if this Task Network is atomic, and false otherwise.
*
* A Task Network is considered to be atomic if it only contains a single Task as ''sub-network''
* which is not of the type UTaskNetwork. Intuitively this means an atomic Task Network represents
* a single (primitive or compound) Task.
*/
bool IsAtomic(const uint8* TaskMemory) const;
/** Returns true if this Task Network is empty (has no compound or primitive tasks anymore anywhere) */
bool IsEmpty(const uint8* TaskMemory) const;
/** Returns true if this Task Network is atomic and immediate (being immediate has no meaning if it's not atomic). */
bool IsImmediate(const uint8* TaskMemory) const;
/** Returns true if this Task Network is not atomic and unordered (being unordered has no meaning if it's atomic). */
bool IsUnordered(const uint8* TaskMemory) const;
/** Removes the given Task Instance */
void Remove(const TSharedPtr<FHTNTaskInstance>& TaskInstance, uint8* TaskMemory);
/** Removes the Task Instance at the given index */
void Remove(int32 Index, uint8* TaskMemory); // TO DO make this private?
/** Replaces the given Task Instance with the given new sub-network */
void Replace(const TSharedPtr<FHTNTaskInstance>& TaskInstance, const TSharedPtr<FHTNTaskInstance>& NewNetwork, uint8* TaskMemory);
/** Replaces the Task Instance at the given index with the given new sub-network */
void Replace(int32 Index, const TSharedPtr<FHTNTaskInstance>& NewNetwork, uint8* TaskMemory); // TO DO make this private?
/** Set whether the Network is immediate. Will produce undefined results if called on a non-atomic Network! */
void SetImmediate(bool bStatus, uint8* TaskMemory);
/** Set whether the Network is unordered. Will produce undefined results if called on an atomic Network! */
void SetUnordered(bool bStatus, uint8* TaskMemory);
protected:
/**
* The sub-networks of this Hierarchical Task Network.
* A sub-network can either be another TaskNetwork (representing a lower level in the tree / network of Tasks),
* or a PrimitiveTask or CompoundTask (representing a Task that needs to be executed or decomposed into Tasks that need executing)
*/
UPROPERTY(EditAnywhere, Category = "Hierarchical Task Network")
TArray<TSubclassOf<UHTNTask>> SubNetworks;
/** Can be used to write some comments to explain the Network. Will not be used in-game. */
UPROPERTY(EditAnywhere, Category = "Hierarchical Task Network Comments", meta=(MultiLine=true))
FString NetworkComments;
/** If true, the Task will have to be executed or decomposed immediately when it no longer has any predecessors in the network. */
UPROPERTY(EditAnywhere, Category = "Hierarchical Task Network")
uint8 bImmediate : 1;
/** If true, the array of Sub-Networks is unordered and can be executed in any arbitrary order */
UPROPERTY(EditAnywhere, Category = "Hierarchical Task Network")
uint8 bUnordered : 1;
private:
/**
* Recursively called (initially by Remove() and Replace() to find out which sub-network
* contains the given Task Instance at which index.
*
* When the first call to this method finishes, ContainingNetwork should point to the sub-network
* that contains the given Task Instance, and Index should contain the index at which that sub-network
* contains the given Task Instance.
*
* This method uses the same rules for priority as FindTasksWithoutPredecessors.
*/
bool FindTaskInstance(const TSharedPtr<FHTNTaskInstance>& TaskInstance, UTaskNetwork*& ContainingNetwork,
uint8*& ContainingNetworkMemory, int32& Index, uint8* TaskMemory);
/**
* Recursively called (initially by FindTasksWithoutPredecessors() to collect all Task Instances
* without predecessors in the network.
*
* Returns true if a Task flagged as immediate was found, and false otherwise.
* Places all Tasks found in the given OutTasks array.
*/
bool FindTasksWithoutPredecessorsRecursion(TArray<TSharedPtr<FHTNTaskInstance>>& OutTasks, const uint8* TaskMemory);
};
\ No newline at end of file
#pragma once
#include "HTNPlanner/CompoundTask.h"
#include "HTNPlanner/HTNWorldState.h"
#include "CompoundTask_BlueprintBase.generated.h"
/**
* Wrapper struct passed into the BlueprintImplementable GetDecompositions() function so that
* that wrapper struct can be passed into a C++-based casting function from blueprint.
*/
USTRUCT(BlueprintType)
struct HTN_PLUGIN_API FHTNWorldStateWrapper
{
GENERATED_BODY()
public:
FHTNWorldStateWrapper() : WorldState(nullptr) {}
FHTNWorldStateWrapper(TSharedPtr<FHTNWorldState> WorldState) : WorldState(WorldState) {}
TSharedPtr<FHTNWorldState> WorldState;
};
/**
* Base class for Compound HTN Tasks that should be implemented in Blueprint.
*/
UCLASS(Blueprintable)
class HTN_PLUGIN_API UCompoundTask_BlueprintBase : public UCompoundTask
{
GENERATED_BODY()
public:
virtual TArray<TSharedPtr<FHTNTaskInstance>> FindDecompositions(UHTNPlannerComponent& HTNComp,
const TSharedPtr<FHTNWorldState> WorldState, uint8* TaskMemory) override;
/**
* Should be implemented to return an Array of Decompositions, where every Decomposition
* holds one of the Networks that the Compound Task can be decomposed into given the World State.
*/
UFUNCTION(BlueprintImplementableEvent, Category = "Compound Task")
TArray<TSubclassOf<UTaskNetwork>> GetDecompositions(UHTNPlannerComponent* HTNComp, const FHTNWorldStateWrapper& WorldStateWrapper);
};
\ No newline at end of file
#pragma once
#include "HTNPlanner/PrimitiveTask.h"
#include "HTNTask_MoveTo.generated.h"
class UHTNPlannerComponent;
class UNavigationQueryFilter;
struct FHTNTask_MoveToMemory
{
/** Move request ID */
FAIRequestID MoveRequestID;
/** If set, we're currently waiting for a path to be generated */
uint8 bWaitingForPath : 1;
FHTNTask_MoveToMemory() : bWaitingForPath(false) {}
};
/**
* Move To primitive Task.
* Moves the AI pawn toward the specified Actor or Location using the navigation system.
*
* This HTN Primitive Task is equivalent to the Behavior Tree's UBTTask_MoveTo node.
*/
UCLASS(config=Game, Blueprintable)
class HTN_PLUGIN_API UHTNTask_MoveTo : public UPrimitiveTask
{
GENERATED_BODY()
public:
UHTNTask_MoveTo(const FObjectInitializer& ObjectInitializer);
// overrides for in-game execution of task
virtual EHTNExecutionResult AbortTask(UHTNPlannerComponent& HTNComp, uint8* TaskMemory) override;
virtual EHTNExecutionResult ExecuteTask(UHTNPlannerComponent& HTNComp, uint8* TaskMemory) override;
virtual void OnMessage(UHTNPlannerComponent& HTNComp, FName Message, int32 RequestID,
bool bSuccess, const TSharedPtr<FHTNTaskInstance>& TaskInstance) override;
virtual void TickTask(UHTNPlannerComponent& HTNComp, float DeltaSeconds, uint8* TaskMemory) override;
// overrides for planning
virtual void ApplyTo(TSharedPtr<FHTNWorldState> WorldState, uint8* TaskMemory) const override;
uint16 GetInstanceMemorySize() const override;
virtual bool IsApplicable(const TSharedPtr<FHTNWorldState> WorldState, uint8* TaskMemory) const override;
protected:
/** "None" will result in default filter being used */
UPROPERTY(Category = Task, EditAnywhere, BlueprintReadWrite)
TSubclassOf<UNavigationQueryFilter> FilterClass;
/** The Name of the Blackboard Key that holds the target to move to if it is held in a Blackboard */
UPROPERTY(Category = Task, EditAnywhere, BlueprintReadWrite)
FName TargetBlackboardKey;
/** The Actor to move to. This value will only be used if TargetBlackboardKey is not set. */ // TO DO should maybe make this a weak ptr?
UPROPERTY(Category = Task, EditAnywhere, BlueprintReadWrite)
AActor* TargetActor;
/** The Location to move to. This value will only be used if TargetBlackboardKey and TargetLocation are not set. */
UPROPERTY(Category = Task, EditAnywhere, BlueprintReadWrite)
FVector TargetLocation;
/** The maximum distance between the AI pawn and the target for the Task to be considered as successfully finished */
UPROPERTY(config, Category = Task, EditAnywhere, meta = (ClampMin = "0.0"))
float AcceptableRadius;
/** If set, the pawn will be allowed to strafe */
UPROPERTY(Category = Task, EditAnywhere, BlueprintReadWrite)
uint8 bAllowStrafe : 1;
/** if set, use incomplete path when goal can't be reached */
UPROPERTY(Category = Task, EditAnywhere, AdvancedDisplay, BlueprintReadWrite)
uint8 bAllowPartialPath : 1;
/** if set to true agent's radius will be added to AcceptableRadius for purposes of checking
* if path's end point has been reached. Will result in AI stopping on contact with destination location*/
UPROPERTY(Category = Task, EditAnywhere, BlueprintReadWrite)
uint8 bStopOnOverlap : 1;
EHTNExecutionResult PerformMoveTask(UHTNPlannerComponent& HTNComp, uint8* TaskMemory);
};
\ No newline at end of file
#pragma once
#include "HTNPlanner/PrimitiveTask.h"
#include "HTNTask_RegisterService.generated.h"
struct FHTNWorldState;
class UHTNPlannerComponent;
class UHTNService;
/**
* A Primitive Task that instantly executes by registering a Service that should be ticked by the HTN Planner Component.
*/
UCLASS(Blueprintable, HideDropdown)
class HTN_PLUGIN_API UHTNTask_RegisterService : public UPrimitiveTask
{
GENERATED_BODY()
public:
UHTNTask_RegisterService(const FObjectInitializer& ObjectInitializer);
/** Won't have any effect on world state */
virtual void ApplyTo(TSharedPtr<FHTNWorldState> WorldState, uint8* TaskMemory) const override;
/** Execution will simply register the service */
virtual EHTNExecutionResult ExecuteTask(UHTNPlannerComponent& HTNComp, uint8* TaskMemory) override;
/** Will assume that the background behavior of Services will always have a cost of 0.0 */
virtual float GetCost(const TSharedPtr<FHTNWorldState> WorldState, uint8* TaskMemory) const override;
/** Will assume that services can always be registered in any World State */
virtual bool IsApplicable(const TSharedPtr<FHTNWorldState> WorldState, uint8* TaskMemory) const override;
protected:
/** The type of Service that should be registered */
UPROPERTY(Category = Task, EditAnywhere, BlueprintReadWrite)
TSubclassOf<UHTNService> Service;
};
\ No newline at end of file
#pragma once
#include "HTNPlanner/PrimitiveTask.h"
#include "HTNTask_UnregisterService.generated.h"
struct FHTNWorldState;
class UHTNPlannerComponent;
class UHTNService;
/**
* A Primitive Task that instantly executes by unregistering a Service that should no longer be ticked by the HTN Planner Component.
*/
UCLASS(Blueprintable, HideDropdown)
class HTN_PLUGIN_API UHTNTask_UnregisterService : public UPrimitiveTask
{
GENERATED_BODY()
public:
UHTNTask_UnregisterService(const FObjectInitializer& ObjectInitializer);
/** Won't have any effect on world state */
virtual void ApplyTo(TSharedPtr<FHTNWorldState> WorldState, uint8* TaskMemory) const override;
/** Execution will simply unregister the service */
virtual EHTNExecutionResult ExecuteTask(UHTNPlannerComponent& HTNComp, uint8* TaskMemory) override;
/** Will assume that stopping the background behavior of Services will always have a cost of 0.0 */
virtual float GetCost(const TSharedPtr<FHTNWorldState> WorldState, uint8* TaskMemory) const override;
/** Will assume that services can always be unregistered in any World State */
virtual bool IsApplicable(const TSharedPtr<FHTNWorldState> WorldState, uint8* TaskMemory) const override;
protected:
/** The type of Service that should be unregistered */
UPROPERTY(Category = Task, EditAnywhere, BlueprintReadWrite)
TSubclassOf<UHTNService> Service;
};
\ No newline at end of file
#pragma once
#include "HTNPlanner/PrimitiveTask.h"
#include "HTNTask_Wait.generated.h"
struct FHTNWorldState;
class UHTNPlannerComponent;
struct FHTNTask_WaitMemory
{
/** time left */
float RemainingWaitTime;
};
/**
* Wait task
* Wait for the specified time when executed.
*
* This HTN Task is equivalent to the Behavior Tree's BTTask_Wait
*/
UCLASS(Blueprintable)
class HTN_PLUGIN_API UHTNTask_Wait : public UPrimitiveTask
{
GENERATED_BODY()
public:
UHTNTask_Wait(const FObjectInitializer& ObjectInitializer);
/** No effects on world state */
virtual void ApplyTo(TSharedPtr<FHTNWorldState> WorldState, uint8* TaskMemory) const override;
virtual EHTNExecutionResult ExecuteTask(UHTNPlannerComponent& HTNComp, uint8* TaskMemory) override;
/** We'll assume waiting has a default cost of 0.0 */
virtual float GetCost(const TSharedPtr<FHTNWorldState> WorldState, uint8* TaskMemory) const override;
uint16 GetInstanceMemorySize() const override;
/** We'll assume waiting is always possible by default */
virtual bool IsApplicable(const TSharedPtr<FHTNWorldState> WorldState, uint8* TaskMemory) const override;
virtual void TickTask(UHTNPlannerComponent& HTNComp, float DeltaSeconds, uint8* TaskMemory) override;
protected:
/** wait time in seconds */
UPROPERTY(Category = Wait, EditAnywhere, meta = (ClampMin = "0.0", UIMin = "0.0"))
float WaitTime;
/** allows adding random time to wait time */
UPROPERTY(Category = Wait, EditAnywhere, meta = (UIMin = 0, ClampMin = 0))
float RandomDeviation;
};
#pragma once
#include "HTNPlanner/HTNWorldState.h"
#include "HTNWorldState_Blackboard.generated.h"
/**
* This type of World State will use a copy of the agent's Blackboard to represent the World State.
* This means that exactly those fields that are specified in the Blackboard asset can be read and
* changed during the planning process to interpret and reason about the World State.
*/
USTRUCT(BlueprintType)
struct HTN_PLUGIN_API FHTNWorldState_Blackboard : public FHTNWorldState
{
GENERATED_BODY()
public:
virtual ~FHTNWorldState_Blackboard() {}
// TO DO implement
};
\ No newline at end of file
#pragma once
#include "HTNPlanner/HTNWorldState.h"
#include "HTNWorldState_Basic.generated.h"
struct FHTNDummyObject;
/**
* Describes a World State in the 'basic' example domain included in the JSHOP2 download package.
* This is a very simple domain in which the planner can ''have'' certain objects, and can drop
* and pick up objects.
*/
USTRUCT(BlueprintType)
struct HTN_PLUGIN_API FHTNWorldState_Basic : public FHTNWorldState
{
GENERATED_BODY()
public:
virtual ~FHTNWorldState_Basic() {}
virtual TSharedPtr<FHTNWorldState> Copy() const override;
virtual void Initialize(AActor* Owner, UBlackboardComponent* BlackboardComponent) override;
void AddObject(const FHTNDummyObject* Object);
void AddObjects(const TArray<const FHTNDummyObject*>& Objects);
bool HasObject(const FHTNDummyObject* Object) const;
void RemoveObject(const FHTNDummyObject* Object);
protected:
/** Array of the objects that we ''have'' */
TArray<const FHTNDummyObject*> Objects;
};
\ No newline at end of file
#pragma once
#include "JSHOP2_Experiments/JSHOP2_Experimenter.h"
#include "JSHOP2_ExperimenterBasic.generated.h"
struct FHTNDummyObject;
struct FHTNWorldState_Basic;
UCLASS(HideDropdown)
class HTN_PLUGIN_API AJSHOP2_ExperimenterBasic : public AJSHOP2_Experimenter
{
GENERATED_BODY()
public:
virtual UHTNPlanner* GeneratePlanner() override;
void InitializeWorldState(FHTNWorldState_Basic* WorldState);
protected:
/** Will store all the objects that we have in our 'basic' problem here */
TArray<TSharedPtr<FHTNDummyObject>> ObjectPool;
};
\ No newline at end of file
#pragma once
#include "HTNPlanner/PrimitiveTask.h"
#include "HTNTask_DropBasic.generated.h"
struct FHTNDummyObject;
struct FHTNTask_DropBasicMemory
{
/** The Object that should be dropped */
FHTNDummyObject* Object;
};
/**
* 'Drop' task for the JSHOP2 'basic' example domain.
*/
UCLASS()
class HTN_PLUGIN_API UHTNTask_DropBasic : public UPrimitiveTask
{
GENERATED_BODY()
public:
virtual void ApplyTo(TSharedPtr<FHTNWorldState> WorldState, uint8* TaskMemory) const override;
virtual EHTNExecutionResult ExecuteTask(UHTNPlannerComponent& HTNComp, uint8* TaskMemory) override;
virtual float GetHeuristicCost() const override;
virtual uint16 GetInstanceMemorySize() const override;
virtual bool IsApplicable(const TSharedPtr<FHTNWorldState> WorldState, uint8* TaskMemory) const override;
};
\ No newline at end of file
#pragma once
#include "HTNPlanner/PrimitiveTask.h"
#include "HTNTask_PickupBasic.generated.h"
struct FHTNDummyObject;
struct FHTNTask_PickupBasicMemory
{
/** The Object that should be picked up */
FHTNDummyObject* Object;
};
/**
* 'Pickup' task for the JSHOP2 'basic' example domain.
*/
UCLASS()
class HTN_PLUGIN_API UHTNTask_PickupBasic : public UPrimitiveTask
{
GENERATED_BODY()
public:
virtual void ApplyTo(TSharedPtr<FHTNWorldState> WorldState, uint8* TaskMemory) const override;
virtual EHTNExecutionResult ExecuteTask(UHTNPlannerComponent& HTNComp, uint8* TaskMemory) override;
virtual float GetHeuristicCost() const override;
virtual uint16 GetInstanceMemorySize() const override;
virtual bool IsApplicable(const TSharedPtr<FHTNWorldState> WorldState, uint8* TaskMemory) const override;
};
\ No newline at end of file
#pragma once
#include "HTNPlanner/CompoundTask.h"
#include "HTNTask_SwapBasic.generated.h"
struct FHTNDummyObject;
struct FHTNTask_SwapBasicMemory
{
/** The first of the Objects that should be swapped */
FHTNDummyObject* Object1;
/** The second of the Objects that should be swapped */
FHTNDummyObject* Object2;
};
/**
* 'Swap' compound task for the JSHOP2 'basic' example domain.
*/
UCLASS()
class HTN_PLUGIN_API UHTNTask_SwapBasic : public UCompoundTask
{
GENERATED_BODY()
public:
virtual TArray<TSharedPtr<FHTNTaskInstance>> FindDecompositions(UHTNPlannerComponent& HTNComp,
const TSharedPtr<FHTNWorldState> WorldState, uint8* TaskMemory) override;
virtual float GetHeuristicCost() const override;
virtual uint16 GetInstanceMemorySize() const override;
};
\ No newline at end of file
#pragma once
#include "DataCollector.generated.h"
enum class EPlanningMode : uint8
{
Unknown,
InitialPlan,
ReplanningWithoutReuse,
ReplanningWithReuse_1_off,
ReplanningWithReuse_1_on,
ReplanningWithReuse_10_off,
ReplanningWithReuse_10_on,
ReplanningWithReuse_20_off,
ReplanningWithReuse_20_on,
ReplanningWithReuse_30_off,
ReplanningWithReuse_30_on,
};
USTRUCT()
struct FPlanningEpisode
{
GENERATED_BODY()
public:
UPROPERTY()
TArray<float> SolutionCosts;
UPROPERTY()
TArray<int32> SolutionPlanSizes;
UPROPERTY()
TArray<int32> SolutionSearchHistorySizes;
UPROPERTY()
TArray<double> SolutionSearchTimes;
UPROPERTY()
TArray<uint64> SolutionNumNodes;
double EarlyTerminationTime;
double OptimalityProofTime;
uint64 OptimalityNumNodes;
EPlanningMode PlanningMode;
FPlanningEpisode() : SolutionCosts(), SolutionPlanSizes(), SolutionSearchTimes(),
SolutionSearchHistorySizes(), SolutionNumNodes(), EarlyTerminationTime(-1.0),
OptimalityProofTime(-1.0), OptimalityNumNodes(-1), PlanningMode(EPlanningMode::Unknown)
{}
};
USTRUCT()
struct FPlanningProblemResult
{
GENERATED_BODY()
public:
UPROPERTY()
TArray<FPlanningEpisode> PlanningEpisodes;
UPROPERTY()
TArray<int32> TaskExecutionCounters;
UPROPERTY()
TArray<int32> PlanSizes;
int32 WorldGenerationSeed;
FPlanningProblemResult() : PlanningEpisodes(), TaskExecutionCounters(), PlanSizes(), WorldGenerationSeed(-1) {}
};
/**
* Class to collect data/results of experiments and write it to files
*/
UCLASS()
class HTN_PLUGIN_API UDataCollector : public UObject
{
GENERATED_BODY()
public:
UDataCollector(const FObjectInitializer& ObjectInitializer);
void EarlyTermination(double Milliseconds);
void ExportResults();
void FoundSolution(float Cost, int32 PlanSize, int32 SearchHistorySize, double SearchTime, uint64 NumNodes);
void OptimalityProven(double SearchTime, uint64 NumNodes);
void SetPlanningMode(EPlanningMode Mode);
void SetTasksExecuted(int32 NumTasks, int32 PlanSize);
void SetWorldStateSeed(int32 Seed);
void StartNewPlanningEpisode();
void StartNewProblem();
protected:
UPROPERTY()
TArray<FPlanningProblemResult> PlanningProblemResults;
};
\ No newline at end of file
#pragma once
#include "HTNDummyObject.generated.h"
/**
* A dummy object used in a number of the JSHOP2 Experiments to represent objects that have a name.
* In a ''real'' planning problem, other classes (typically AActors or UObjects) should be used instead.
*/
USTRUCT()
struct HTN_PLUGIN_API FHTNDummyObject
{
GENERATED_BODY()
public:
virtual ~FHTNDummyObject() {}
FString ObjectName;
};
\ No newline at end of file
#pragma once
#include "JSHOP2_BatchExperimenter.generated.h"
UCLASS(Abstract, HideDropdown, HideCategories = ("Actor Tick", "Rendering", "Replication", "Input"))
class HTN_PLUGIN_API AJSHOP2_BatchExperimenter : public AActor
{
GENERATED_BODY()
public:
AJSHOP2_BatchExperimenter(const FObjectInitializer& ObjectInitializer);
virtual void BeginPlay() override;
virtual void EndPlay(const EEndPlayReason::Type EndPlayReason) override;
virtual void Tick(float DeltaSeconds) override;
protected:
/** Asset that should be used for each experiment */
UPROPERTY(EditAnywhere, Category = "Batch Experimenter")
TSubclassOf<class AJSHOP2_Experimenter> ExperimenterAsset;
/** The number of experiments to do */
UPROPERTY(EditAnywhere, Category = "Batch Experimenter")
int32 NumberOfExperiments;
int32 ExperimentProgress;
UPROPERTY(transient)
class AJSHOP2_Experimenter* Experimenter;
UPROPERTY(transient)
class UDataCollector* DataCollector;
};
\ No newline at end of file
#pragma once
#include "JSHOP2_Experimenter.generated.h"
class UDataCollector;
class UHTNPlanner;
class UHTNPlannerComponent;
/**
* Base class for Actors that can be placed in levels to carry out experiments based on JSHOP2
* example domains/problems when gameplay in a level begins.
*
* Subclasses should implement specific initial Task Network and can also be used to set up initial
* World State.
*/
UCLASS(Abstract, HideDropdown, HideCategories=("Actor Tick", "Rendering", "Replication", "Input"))
class HTN_PLUGIN_API AJSHOP2_Experimenter : public AActor
{
GENERATED_BODY()
public:
AJSHOP2_Experimenter(const FObjectInitializer& ObjectInitializer);
/** Overridden to start the experiment */
virtual void BeginPlay() override;
/** Overridden to clean up experiment */
virtual void EndPlay(const EEndPlayReason::Type EndPlayReason) override;
bool FinishedExperiment() const;
/** Must be implemented by subclasses to dynamically generate and return a Planner asset to be used in experiment */
virtual UHTNPlanner* GeneratePlanner();
void SetDataCollector(UDataCollector* DataCollector);
virtual void Tick(float DeltaSeconds) override;
protected:
/** If true, the Planner will perform a breadth-first search. Otherwise, a more breadth-first / A* style search will be used */
UPROPERTY(EditAnywhere, Category = "Hierarchical Task Network Planner")
bool bDepthFirstSearch;
/** If true, the Planner will ignore costs assigned to Tasks and only search for the first plan */
UPROPERTY(EditAnywhere, Category = "Hierarchical Task Network Planner")
bool bIgnoreCosts;
/** If true, the Planner will re-start from the initial Task Network to create a new plan once the previous plan has been executed. */
UPROPERTY(EditAnywhere, Category = "Hierarchical Task Network Planner")
bool bLoop;
/** The maximum amount of time (in seconds) that the planner can dedicate to planning per Tick */
UPROPERTY(EditAnywhere, Category = "Hierarchical Task Network Planner", meta = (ClampMin = 0, ClampMax = 1))
float MaxSearchTime;
/** The maximum amount of time (in seconds) that the planner is allowed to spend on finding a first solution. */
UPROPERTY(EditAnywhere, Category = "Experiments")
float MaxSearchTimeFirstSolution;
/** The maximum amount of time (in seconds) that the planner is allowed to spend on finding an optimal solution */
UPROPERTY(EditAnywhere, Category = "Experiments")
float MaxSearchTimeOptimalSolution;
UPROPERTY(EditAnywhere, Category = "Experiments")
uint8 bExperimentPlanReuse : 1;
uint8 bConstructedInitialPlan : 1;
uint8 bExecutedPlan : 1;
uint8 bReplannedWithoutReuse : 1;
// flags for replanning with reuse (number = min streak length, on/off = probabilistic replanning)
uint8 bReplannedWithReuse_1_off : 1;
uint8 bReplannedWithReuse_1_on : 1;
uint8 bReplannedWithReuse_10_off : 1;
uint8 bReplannedWithReuse_10_on : 1;
uint8 bReplannedWithReuse_20_off : 1;
uint8 bReplannedWithReuse_20_on : 1;
uint8 bReplannedWithReuse_30_off : 1;
uint8 bReplannedWithReuse_30_on : 1;
uint8 bFinishedExperiment : 1;
/** The Component that will be used for the planning process */
UPROPERTY()
UHTNPlannerComponent* HTNComp;
UPROPERTY(transient)
UHTNPlanner* Planner;
/** Pointer to the current world state, used so that execute plans on it and re-plan when after partial execution it fails */
TSharedPtr<struct FHTNWorldState> CurrentWorldState;
TSharedPtr<struct FHTNPlan> PartiallyExecutedPlan;
};
\ No newline at end of file
#pragma once
#include "JSHOP2_Experiments/JSHOP2_Experimenter.h"
#include "JSHOP2_ExperimenterSimpleFPS.generated.h"
struct FHTNWorldState_SimpleFPS;
UCLASS(HideDropdown)
class HTN_PLUGIN_API AJSHOP2_ExperimenterSimpleFPS : public AJSHOP2_Experimenter
{
GENERATED_BODY()
public:
AJSHOP2_ExperimenterSimpleFPS(const FObjectInitializer& ObjectInitializer);
virtual UHTNPlanner* GeneratePlanner() override;
int32 GetNumAreas() const;
void InitializeWorldState(FHTNWorldState_SimpleFPS* WorldState);
protected:
/** Will cache the Initial World State's Const Data here so that objects required for printing the plans remain alive */
TSharedPtr<const struct FHTNWorldState_SimpleFPS_ConstData> WorldStateConstData;
/** The number of Areas in the SimpleFPS domain */
UPROPERTY(EditAnywhere, Category = "Simple FPS", meta=(ClampMin=0))
int32 NumberOfAreas;
/** The number of extra Points of Interest (guns, knives, medikits and cover points) in the SimpleFPS domain */
UPROPERTY(EditAnywhere, Category = "Simple FPS", meta = (ClampMin = 0))
int32 NumberOfPointsOfInterest;
/** The probability for every possible pair of unique areas that the two areas are connected through a Waypoint */
UPROPERTY(EditAnywhere, Category = "Simple FPS", meta = (ClampMin = "0.0", ClampMax = "1.0"))
float ProbabilityConnected;
/** The probability that the NPC starts a problem as injured */
UPROPERTY(EditAnywhere, Category = "Simple FPS", meta = (ClampMin = "0.0", ClampMax = "1.0"))
float ProbabilityInjured;
/** The probability that an area starts a problem in the ''lighted'' state */
UPROPERTY(EditAnywhere, Category = "Simple FPS", meta = (ClampMin = "0.0", ClampMax = "1.0"))
float ProbabilityLighted;
/** The probability that a gun starts a problem as ''loaded'' */
UPROPERTY(EditAnywhere, Category = "Simple FPS", meta = (ClampMin = "0.0", ClampMax = "1.0"))
float ProbabilityLoaded;
/** The probability that a gun has night vision */
UPROPERTY(EditAnywhere, Category = "Simple FPS", meta = (ClampMin = "0.0", ClampMax = "1.0"))
float ProbabilityNightVision;
/** The probability that a waypoint starts a problem as ''open'' */
UPROPERTY(EditAnywhere, Category = "Simple FPS", meta = (ClampMin = "0.0", ClampMax = "1.0"))
float ProbabilityOpen;
/** The probability that a Point of Interest is a gun */
UPROPERTY(EditAnywhere, Category = "Simple FPS (Points of Interest)", meta = (ClampMin = "0.0", ClampMax = "1.0"))
float ProbabilityGun;
/** The probability that a Point of Interest is a knife */
UPROPERTY(EditAnywhere, Category = "Simple FPS (Points of Interest)", meta = (ClampMin = "0.0", ClampMax = "1.0"))
float ProbabilityKnife;
/** The probability that a Point of Interest is a medikit */
UPROPERTY(EditAnywhere, Category = "Simple FPS (Points of Interest)", meta = (ClampMin = "0.0", ClampMax = "1.0"))
float ProbabilityMedikit;
/** If true, generated maps will not be completely random but more realistic */
UPROPERTY(EditAnywhere, Category = "Simple FPS (Map Generation)")
bool bGenerateRealisticMaps;
/** If true, will print generated problems in JSHOP2 format to output log */
UPROPERTY(EditAnywhere, Category = "JSHOP2")
bool bPrintJSHOP2Format;
/** Seed used for RNG in the initialization of world state */
int32 InitializationSeed;
/** True if the InitializationSeed has been set yet, false otherwise */
bool bInitializationSeedSet;
/** Will cache an initial world state here that we can use to initialize multiple equal world states for experiments */
TSharedPtr<FHTNWorldState_SimpleFPS> InitialWorldState;
};
\ No newline at end of file
#pragma once
#include "JSHOP2_Experiments/HTNDummyObject.h"
#include "SimpleFPSObject.generated.h"
/**
* The different types of Objects we have in the SimpleFPS domain
*/
enum class ESimpleFPSObjectTypes : uint8
{
Ammo,
Area,
ControlBox,
CoverPoint,
Gun,
Keycard,
Knife,
Medikit,
Player,
Waypoint,
};
/**
* An Object in the Simple FPS domain. Inherits the ObjectName property from
* FHTNDummyObject, but also has a type and an index.
*/
USTRUCT()
struct HTN_PLUGIN_API FSimpleFPSObject : public FHTNDummyObject
{
GENERATED_BODY()
public:
virtual ~FSimpleFPSObject() {}
int32 Index;
ESimpleFPSObjectTypes ObjectType;
};
\ No newline at end of file
#pragma once
#include "HTNPlanner/CompoundTask.h"
#include "HTNTask_AttackMeleeCompoundSimpleFPS.generated.h"
struct FSimpleFPSObject;
struct FHTNTask_AttackMeleeCompoundSimpleFPSMemory
{
FSimpleFPSObject* Player;
int32 Area;
};
/**
* 'Attack-Melee' compound task for the SimpleFPS domain.
*/
UCLASS()
class HTN_PLUGIN_API UHTNTask_AttackMeleeCompoundSimpleFPS : public UCompoundTask
{
GENERATED_BODY()
public:
UHTNTask_AttackMeleeCompoundSimpleFPS(const FObjectInitializer& ObjectInitializer);
virtual TArray<TSharedPtr<FHTNTaskInstance>> FindDecompositions(UHTNPlannerComponent& HTNComp,
const TSharedPtr<FHTNWorldState> WorldState, uint8* TaskMemory) override;
virtual float GetHeuristicCost() const override;
virtual uint16 GetInstanceMemorySize() const override;
protected:
/**
* Caching the heuristic cost here. Only the class CDO needs to memorize this,
* so it doesn't go into the TaskMemory of individual instances of this class.
*/
float HeuristicCost;
};
\ No newline at end of file
#pragma once
#include "HTNPlanner/PrimitiveTask.h"
#include "HTNTask_AttackMeleeSimpleFPS.generated.h"
struct FSimpleFPSObject;
struct FHTNTask_AttackMeleeSimpleFPSMemory
{
/** The Knife that should be used */
FSimpleFPSObject* Knife;
/** The Player that should be attacked */
FSimpleFPSObject* Player;
int32 Area;
};
/**
* 'Attack-Melee' primitive task for the SimpleFPS domain.
*/
UCLASS()
class HTN_PLUGIN_API UHTNTask_AttackMeleeSimpleFPS : public UPrimitiveTask
{
GENERATED_BODY()
public:
UHTNTask_AttackMeleeSimpleFPS(const FObjectInitializer& ObjectInitializer);
virtual void ApplyTo(TSharedPtr<FHTNWorldState> WorldState, uint8* TaskMemory) const override;
virtual EHTNExecutionResult ExecuteTask(UHTNPlannerComponent& HTNComp, uint8* TaskMemory) override;
virtual float GetHeuristicCost() const override;
virtual uint16 GetInstanceMemorySize() const override;
virtual bool IsApplicable(const TSharedPtr<FHTNWorldState> WorldState, uint8* TaskMemory) const override;
protected:
/**
* Caching the heuristic cost here. Only the class CDO needs to memorize this,
* so it doesn't go into the TaskMemory of individual instances of this class.
*/
float HeuristicCost;
};
\ No newline at end of file
#pragma once
#include "HTNPlanner/CompoundTask.h"
#include "HTNTask_AttackRangedCompoundSimpleFPS.generated.h"
struct FSimpleFPSObject;
struct FHTNTask_AttackRangedCompoundSimpleFPSMemory
{
FSimpleFPSObject* Player;
int32 Area;
};
/**
* 'Attack-Ranged' compound task for the SimpleFPS domain.
*/
UCLASS()
class HTN_PLUGIN_API UHTNTask_AttackRangedCompoundSimpleFPS : public UCompoundTask
{
GENERATED_BODY()
public:
UHTNTask_AttackRangedCompoundSimpleFPS(const FObjectInitializer& ObjectInitializer);
virtual TArray<TSharedPtr<FHTNTaskInstance>> FindDecompositions(UHTNPlannerComponent& HTNComp,
const TSharedPtr<FHTNWorldState> WorldState, uint8* TaskMemory) override;
virtual float GetHeuristicCost() const override;
virtual uint16 GetInstanceMemorySize() const override;
protected:
/**
* Caching the heuristic cost here. Only the class CDO needs to memorize this,
* so it doesn't go into the TaskMemory of individual instances of this class.
*/
float HeuristicCost;
};
\ No newline at end of file
#pragma once
#include "HTNPlanner/PrimitiveTask.h"
#include "HTNTask_AttackRangedSimpleFPS.generated.h"
struct FSimpleFPSObject;
struct FHTNTask_AttackRangedSimpleFPSMemory
{
/** The Gun that should be used */
FSimpleFPSObject* Gun;
/** The Player that should be attacked */
FSimpleFPSObject* Player;
int32 Area;
};
/**
* 'Attack-Ranged' primitive task for the SimpleFPS domain.
*/
UCLASS()
class HTN_PLUGIN_API UHTNTask_AttackRangedSimpleFPS : public UPrimitiveTask
{
GENERATED_BODY()
public:
UHTNTask_AttackRangedSimpleFPS(const FObjectInitializer& ObjectInitializer);
virtual void ApplyTo(TSharedPtr<FHTNWorldState> WorldState, uint8* TaskMemory) const override;
virtual EHTNExecutionResult ExecuteTask(UHTNPlannerComponent& HTNComp, uint8* TaskMemory) override;
virtual float GetHeuristicCost() const override;
virtual uint16 GetInstanceMemorySize() const override;
virtual bool IsApplicable(const TSharedPtr<FHTNWorldState> WorldState, uint8* TaskMemory) const override;
protected:
/**
* Caching the heuristic cost here. Only the class CDO needs to memorize this,
* so it doesn't go into the TaskMemory of individual instances of this class.
*/
float HeuristicCost;
};
\ No newline at end of file
#pragma once
#include "HTNPlanner/CompoundTask.h"
#include "HTNTask_BehaveSimpleFPS.generated.h"
/**
* 'Behave' compound task for the SimpleFPS domain.
*/
UCLASS()
class HTN_PLUGIN_API UHTNTask_BehaveSimpleFPS : public UCompoundTask
{
GENERATED_BODY()
public:
UHTNTask_BehaveSimpleFPS(const FObjectInitializer& ObjectInitializer);
virtual TArray<TSharedPtr<FHTNTaskInstance>> FindDecompositions(UHTNPlannerComponent& HTNComp,
const TSharedPtr<FHTNWorldState> WorldState, uint8* TaskMemory) override;
virtual float GetHeuristicCost() const override;
protected:
/**
* Caching the heuristic cost here. Only the class CDO needs to memorize this,
* so it doesn't go into the TaskMemory of individual instances of this class.
*/
float HeuristicCost;
};
\ No newline at end of file
#pragma once
#include "HTNPlanner/PrimitiveTask.h"
#include "HTNTask_EvaluateWeaponChoiceSimpleFPS.generated.h"
enum class EHTNWeaponChoiceSimpleFPS : uint8
{
NoChoice,
Ranged,
Stealth,
Melee
};
struct FHTNTask_EvaluateWeaponChoiceSimpleFPSMemory
{
EHTNWeaponChoiceSimpleFPS Choice;
};
/**
* An extra bookkeeping task we use in SimpleFPS to punish changing to a different choice of
* attack, resulting in a greater likelihood that old plans will be reusable
*/
UCLASS()
class HTN_PLUGIN_API UHTNTask_EvaluateWeaponChoiceSimpleFPS : public UPrimitiveTask
{
GENERATED_BODY()
public:
UHTNTask_EvaluateWeaponChoiceSimpleFPS(const FObjectInitializer& ObjectInitializer);
virtual void ApplyTo(TSharedPtr<FHTNWorldState> WorldState, uint8* TaskMemory) const override;
virtual EHTNExecutionResult ExecuteTask(UHTNPlannerComponent& HTNComp, uint8* TaskMemory) override;
virtual float GetCost(const TSharedPtr<FHTNWorldState> WorldState, uint8* TaskMemory) const override;
virtual uint16 GetInstanceMemorySize() const override;
virtual bool IsApplicable(const TSharedPtr<FHTNWorldState> WorldState, uint8* TaskMemory) const override;
virtual FString GetTaskName(uint8* TaskMemory) const override;
protected:
/**
* Caching the heuristic cost here. Only the class CDO needs to memorize this,
* so it doesn't go into the TaskMemory of individual instances of this class.
*/
float HeuristicCost;
};
\ No newline at end of file
#pragma once
#include "HTNPlanner/PrimitiveTask.h"
#include "HTNTask_ExploreSimpleFPS.generated.h"
struct FHTNTask_ExploreSimpleFPSMemory
{
int32 Area;
};
/**
* 'Explore' primitive task for the SimpleFPS domain.
*/
UCLASS()
class HTN_PLUGIN_API UHTNTask_ExploreSimpleFPS : public UPrimitiveTask
{
GENERATED_BODY()
public:
UHTNTask_ExploreSimpleFPS(const FObjectInitializer& ObjectInitializer);
virtual void ApplyTo(TSharedPtr<FHTNWorldState> WorldState, uint8* TaskMemory) const override;
virtual EHTNExecutionResult ExecuteTask(UHTNPlannerComponent& HTNComp, uint8* TaskMemory) override;
virtual float GetCost(const TSharedPtr<FHTNWorldState> WorldState, uint8* TaskMemory) const override;
virtual uint16 GetInstanceMemorySize() const override;
virtual bool IsApplicable(const TSharedPtr<FHTNWorldState> WorldState, uint8* TaskMemory) const override;
virtual FString GetTaskName(uint8* TaskMemory) const override;
protected:
/**
* Caching the heuristic cost here. Only the class CDO needs to memorize this,
* so it doesn't go into the TaskMemory of individual instances of this class.
*/
float HeuristicCost;
};
\ No newline at end of file
#pragma once
#include "HTNPlanner/CompoundTask.h"
#include "HTNTask_GetItemSimpleFPS.generated.h"
struct FSimpleFPSObject;
struct FHTNTask_GetItemSimpleFPSMemory
{
FSimpleFPSObject* Item;
int32 Area;
FHTNTask_GetItemSimpleFPSMemory() : Item(nullptr), Area(-1) {}
};
/**
* 'Get-Item' compound task for the SimpleFPS domain.
*/
UCLASS()
class HTN_PLUGIN_API UHTNTask_GetItemSimpleFPS : public UCompoundTask
{
GENERATED_BODY()
public:
UHTNTask_GetItemSimpleFPS(const FObjectInitializer& ObjectInitializer);
virtual TArray<TSharedPtr<FHTNTaskInstance>> FindDecompositions(UHTNPlannerComponent& HTNComp,
const TSharedPtr<FHTNWorldState> WorldState, uint8* TaskMemory) override;
virtual float GetHeuristicCost() const override;
virtual uint16 GetInstanceMemorySize() const override;
protected:
/**
* Caching the heuristic cost here. Only the class CDO needs to memorize this,
* so it doesn't go into the TaskMemory of individual instances of this class.
*/
float HeuristicCost;
};
\ No newline at end of file
#pragma once
#include "HTNPlanner/CompoundTask.h"
#include "HTNTask_GetKnifeSimpleFPS.generated.h"
/**
* 'Get-Knife' compound task for the SimpleFPS domain.
*/
UCLASS()
class HTN_PLUGIN_API UHTNTask_GetKnifeSimpleFPS : public UCompoundTask
{
GENERATED_BODY()
public:
UHTNTask_GetKnifeSimpleFPS(const FObjectInitializer& ObjectInitializer);
virtual TArray<TSharedPtr<FHTNTaskInstance>> FindDecompositions(UHTNPlannerComponent& HTNComp,
const TSharedPtr<FHTNWorldState> WorldState, uint8* TaskMemory) override;
virtual float GetHeuristicCost() const override;
protected:
/**
* Caching the heuristic cost here. Only the class CDO needs to memorize this,
* so it doesn't go into the TaskMemory of individual instances of this class.
*/
float HeuristicCost;
};
\ No newline at end of file
#pragma once
#include "HTNPlanner/CompoundTask.h"
#include "HTNTask_GetLoadedGunSimpleFPS.generated.h"
/**
* 'Get-Loaded-Gun' compound task for the SimpleFPS domain.
*/
UCLASS()
class HTN_PLUGIN_API UHTNTask_GetLoadedGunSimpleFPS : public UCompoundTask
{
GENERATED_BODY()
public:
UHTNTask_GetLoadedGunSimpleFPS(const FObjectInitializer& ObjectInitializer);
virtual TArray<TSharedPtr<FHTNTaskInstance>> FindDecompositions(UHTNPlannerComponent& HTNComp,
const TSharedPtr<FHTNWorldState> WorldState, uint8* TaskMemory) override;
virtual float GetHeuristicCost() const override;
protected:
/**
* Caching the heuristic cost here. Only the class CDO needs to memorize this,
* so it doesn't go into the TaskMemory of individual instances of this class.
*/
float HeuristicCost;
};
\ No newline at end of file
#pragma once
#include "HTNPlanner/CompoundTask.h"
#include "HTNTask_GetLoadedGunWithNightVisionSimpleFPS.generated.h"
/**
* 'Get-Loaded-Gun-With-Night-Vision' compound task for the SimpleFPS domain.
*/
UCLASS()
class HTN_PLUGIN_API UHTNTask_GetLoadedGunWithNightVisionSimpleFPS : public UCompoundTask
{
GENERATED_BODY()
public:
UHTNTask_GetLoadedGunWithNightVisionSimpleFPS(const FObjectInitializer& ObjectInitializer);
virtual TArray<TSharedPtr<FHTNTaskInstance>> FindDecompositions(UHTNPlannerComponent& HTNComp,
const TSharedPtr<FHTNWorldState> WorldState, uint8* TaskMemory) override;
virtual float GetHeuristicCost() const override;
protected:
/**
* Caching the heuristic cost here. Only the class CDO needs to memorize this,
* so it doesn't go into the TaskMemory of individual instances of this class.
*/
float HeuristicCost;
};
\ No newline at end of file
#pragma once
#include "HTNPlanner/PrimitiveTask.h"
#include "HTNTask_MakeAccessibleSimpleFPS.generated.h"
struct FSimpleFPSObject;
struct FHTNTask_MakeAccessibleSimpleFPSMemory
{
/** The Keycard that should be used to open the waypoint */
FSimpleFPSObject* Keycard;
/** The Waypoint that should be opened */
FSimpleFPSObject* Waypoint;
int32 FromArea;
int32 ToArea;
};
/**
* 'Make-Accessible' primitive task for the SimpleFPS domain.
*/
UCLASS()
class HTN_PLUGIN_API UHTNTask_MakeAccessibleSimpleFPS : public UPrimitiveTask
{
GENERATED_BODY()
public:
UHTNTask_MakeAccessibleSimpleFPS(const FObjectInitializer& ObjectInitializer);
virtual void ApplyTo(TSharedPtr<FHTNWorldState> WorldState, uint8* TaskMemory) const override;
virtual EHTNExecutionResult ExecuteTask(UHTNPlannerComponent& HTNComp, uint8* TaskMemory) override;
virtual float GetHeuristicCost() const override;
virtual uint16 GetInstanceMemorySize() const override;
virtual bool IsApplicable(const TSharedPtr<FHTNWorldState> WorldState, uint8* TaskMemory) const override;
protected:
/**
* Caching the heuristic cost here. Only the class CDO needs to memorize this,
* so it doesn't go into the TaskMemory of individual instances of this class.
*/
float HeuristicCost;
};
\ No newline at end of file
#pragma once
#include "HTNPlanner/CompoundTask.h"
#include "HTNTask_MakeContactCompoundSimpleFPS.generated.h"
struct FSimpleFPSObject;
struct FHTNTask_MakeContactCompoundSimpleFPSMemory
{
FSimpleFPSObject* Player;
};
/**
* 'Make-Contact' compound task for the SimpleFPS domain.
*/
UCLASS()
class HTN_PLUGIN_API UHTNTask_MakeContactCompoundSimpleFPS : public UCompoundTask
{
GENERATED_BODY()
public:
UHTNTask_MakeContactCompoundSimpleFPS(const FObjectInitializer& ObjectInitializer);
virtual TArray<TSharedPtr<FHTNTaskInstance>> FindDecompositions(UHTNPlannerComponent& HTNComp,
const TSharedPtr<FHTNWorldState> WorldState, uint8* TaskMemory) override;
virtual float GetHeuristicCost() const override;
virtual uint16 GetInstanceMemorySize() const override;
protected:
/**
* Caching the heuristic cost here. Only the class CDO needs to memorize this,
* so it doesn't go into the TaskMemory of individual instances of this class.
*/
float HeuristicCost;
};
\ No newline at end of file
#pragma once
#include "HTNPlanner/PrimitiveTask.h"
#include "HTNTask_MakeContactSimpleFPS.generated.h"
struct FSimpleFPSObject;
struct FHTNTask_MakeContactSimpleFPSMemory
{
/** The Player that the NPC should make contact with */
FSimpleFPSObject* Player;
int32 Area;
};
/**
* 'Make-Contact' primitive task for the SimpleFPS domain.
*/
UCLASS()
class HTN_PLUGIN_API UHTNTask_MakeContactSimpleFPS : public UPrimitiveTask
{
GENERATED_BODY()
public:
UHTNTask_MakeContactSimpleFPS(const FObjectInitializer& ObjectInitializer);
virtual void ApplyTo(TSharedPtr<FHTNWorldState> WorldState, uint8* TaskMemory) const override;
virtual EHTNExecutionResult ExecuteTask(UHTNPlannerComponent& HTNComp, uint8* TaskMemory) override;
virtual float GetHeuristicCost() const override;
virtual uint16 GetInstanceMemorySize() const override;
virtual bool IsApplicable(const TSharedPtr<FHTNWorldState> WorldState, uint8* TaskMemory) const override;
protected:
/**
* Caching the heuristic cost here. Only the class CDO needs to memorize this,
* so it doesn't go into the TaskMemory of individual instances of this class.
*/
float HeuristicCost;
};
\ No newline at end of file
#pragma once
#include "HTNPlanner/CompoundTask.h"
#include "HTNTask_MoveToAreaSimpleFPS.generated.h"
struct FSimpleFPSObject;
struct FHTNTask_MoveToAreaSimpleFPSMemory
{
int32 Area;
};
/**
* 'Move-To-Area' compound task for the SimpleFPS domain.
*/
UCLASS()
class HTN_PLUGIN_API UHTNTask_MoveToAreaSimpleFPS : public UCompoundTask
{
GENERATED_BODY()
public:
UHTNTask_MoveToAreaSimpleFPS(const FObjectInitializer& ObjectInitializer);
virtual TArray<TSharedPtr<FHTNTaskInstance>> FindDecompositions(UHTNPlannerComponent& HTNComp,
const TSharedPtr<FHTNWorldState> WorldState, uint8* TaskMemory) override;
virtual float GetHeuristicCost() const override;
virtual uint16 GetInstanceMemorySize() const override;
protected:
/**
* Caching the heuristic cost here. Only the class CDO needs to memorize this,
* so it doesn't go into the TaskMemory of individual instances of this class.
*/
float HeuristicCost;
};
\ No newline at end of file
#pragma once
#include "HTNPlanner/CompoundTask.h"
#include "HTNTask_MoveToPointCompoundSimpleFPS.generated.h"
struct FSimpleFPSObject;
struct FHTNTask_MoveToPointCompoundSimpleFPSMemory
{
FSimpleFPSObject* PointOfInterest;
int32 Area;
};
/**
* 'Move-To-Point' compound task for the SimpleFPS domain.
*/
UCLASS()
class HTN_PLUGIN_API UHTNTask_MoveToPointCompoundSimpleFPS : public UCompoundTask
{
GENERATED_BODY()
public:
UHTNTask_MoveToPointCompoundSimpleFPS(const FObjectInitializer& ObjectInitializer);
virtual TArray<TSharedPtr<FHTNTaskInstance>> FindDecompositions(UHTNPlannerComponent& HTNComp,
const TSharedPtr<FHTNWorldState> WorldState, uint8* TaskMemory) override;
virtual float GetHeuristicCost() const override;
virtual uint16 GetInstanceMemorySize() const override;
protected:
/**
* Caching the heuristic cost here. Only the class CDO needs to memorize this,
* so it doesn't go into the TaskMemory of individual instances of this class.
*/
float HeuristicCost;
};
\ No newline at end of file
#pragma once
#include "HTNPlanner/PrimitiveTask.h"
#include "HTNTask_MoveToPointFromPointSimpleFPS.generated.h"
struct FSimpleFPSObject;
struct FHTNTask_MoveToPointFromPointSimpleFPSMemory
{
/** The Point of Interest to move to */
FSimpleFPSObject* PointOfInterest;
/** The Point of Interest we were previously at */
FSimpleFPSObject* Previous;
int32 Area;
};
/**
* 'Move-to-Point-from-Point' primitive task for the SimpleFPS domain.
*/
UCLASS()
class HTN_PLUGIN_API UHTNTask_MoveToPointFromPointSimpleFPS : public UPrimitiveTask
{
GENERATED_BODY()
public:
UHTNTask_MoveToPointFromPointSimpleFPS(const FObjectInitializer& ObjectInitializer);
virtual void ApplyTo(TSharedPtr<FHTNWorldState> WorldState, uint8* TaskMemory) const override;
virtual EHTNExecutionResult ExecuteTask(UHTNPlannerComponent& HTNComp, uint8* TaskMemory) override;
virtual float GetHeuristicCost() const override;
virtual uint16 GetInstanceMemorySize() const override;
virtual bool IsApplicable(const TSharedPtr<FHTNWorldState> WorldState, uint8* TaskMemory) const override;
protected:
/**
* Caching the heuristic cost here. Only the class CDO needs to memorize this,
* so it doesn't go into the TaskMemory of individual instances of this class.
*/
float HeuristicCost;
};
\ No newline at end of file
#pragma once
#include "HTNPlanner/PrimitiveTask.h"
#include "HTNTask_MoveToPointSimpleFPS.generated.h"
struct FSimpleFPSObject;
struct FHTNTask_MoveToPointSimpleFPSMemory
{
/** The Point of Interest to move to */
FSimpleFPSObject* PointOfInterest;
int32 Area;
};
/**
* 'Move-to-Point' primitive task for the SimpleFPS domain.
*/
UCLASS()
class HTN_PLUGIN_API UHTNTask_MoveToPointSimpleFPS : public UPrimitiveTask
{
GENERATED_BODY()
public:
UHTNTask_MoveToPointSimpleFPS(const FObjectInitializer& ObjectInitializer);
virtual void ApplyTo(TSharedPtr<FHTNWorldState> WorldState, uint8* TaskMemory) const override;
virtual EHTNExecutionResult ExecuteTask(UHTNPlannerComponent& HTNComp, uint8* TaskMemory) override;
virtual float GetHeuristicCost() const override;
virtual uint16 GetInstanceMemorySize() const override;
virtual bool IsApplicable(const TSharedPtr<FHTNWorldState> WorldState, uint8* TaskMemory) const override;
protected:
/**
* Caching the heuristic cost here. Only the class CDO needs to memorize this,
* so it doesn't go into the TaskMemory of individual instances of this class.
*/
float HeuristicCost;
};
\ No newline at end of file
#pragma once
#include "HTNPlanner/CompoundTask.h"
#include "HTNTask_MovingSimpleFPS.generated.h"
struct FSimpleFPSObject;
struct FHTNTask_MovingSimpleFPSMemory
{
FSimpleFPSObject* Waypoint;
int32 FromArea;
int32 ToArea;
};
/**
* 'Moving' compound task for the SimpleFPS domain.
*/
UCLASS()
class HTN_PLUGIN_API UHTNTask_MovingSimpleFPS : public UCompoundTask
{
GENERATED_BODY()
public:
UHTNTask_MovingSimpleFPS(const FObjectInitializer& ObjectInitializer);
virtual TArray<TSharedPtr<FHTNTaskInstance>> FindDecompositions(UHTNPlannerComponent& HTNComp,
const TSharedPtr<FHTNWorldState> WorldState, uint8* TaskMemory) override;
virtual float GetHeuristicCost() const override;
virtual uint16 GetInstanceMemorySize() const override;
protected:
/**
* Caching the heuristic cost here. Only the class CDO needs to memorize this,
* so it doesn't go into the TaskMemory of individual instances of this class.
*/
float HeuristicCost;
};
\ No newline at end of file
#pragma once
#include "HTNPlanner/PrimitiveTask.h"
#include "HTNTask_MovingToPatrolSimpleFPS.generated.h"
struct FSimpleFPSObject;
struct FHTNTask_MovingToPatrolSimpleFPSMemory
{
/** The Waypoint to use for the movement */
FSimpleFPSObject* Waypoint;
int32 FromArea;
int32 ToArea;
};
/**
* 'Moving-to-Patrol' primitive task for the SimpleFPS domain.
*/
UCLASS()
class HTN_PLUGIN_API UHTNTask_MovingToPatrolSimpleFPS : public UPrimitiveTask
{
GENERATED_BODY()
public:
UHTNTask_MovingToPatrolSimpleFPS(const FObjectInitializer& ObjectInitializer);
virtual void ApplyTo(TSharedPtr<FHTNWorldState> WorldState, uint8* TaskMemory) const override;
virtual EHTNExecutionResult ExecuteTask(UHTNPlannerComponent& HTNComp, uint8* TaskMemory) override;
virtual float GetHeuristicCost() const override;
virtual uint16 GetInstanceMemorySize() const override;
virtual bool IsApplicable(const TSharedPtr<FHTNWorldState> WorldState, uint8* TaskMemory) const override;
protected:
/**
* Caching the heuristic cost here. Only the class CDO needs to memorize this,
* so it doesn't go into the TaskMemory of individual instances of this class.
*/
float HeuristicCost;
};
\ No newline at end of file
#pragma once
#include "HTNPlanner/PrimitiveTask.h"
#include "HTNTask_MovingToTakePositionSimpleFPS.generated.h"
struct FSimpleFPSObject;
struct FHTNTask_MovingToTakePositionSimpleFPSMemory
{
/** The Waypoint to use for the movement */
FSimpleFPSObject* Waypoint;
int32 FromArea;
int32 ToArea;
};
/**
* 'Moving-to-Take-Position' primitive task for the SimpleFPS domain.
*/
UCLASS()
class HTN_PLUGIN_API UHTNTask_MovingToTakePositionSimpleFPS : public UPrimitiveTask
{
GENERATED_BODY()
public:
UHTNTask_MovingToTakePositionSimpleFPS(const FObjectInitializer& ObjectInitializer);
virtual void ApplyTo(TSharedPtr<FHTNWorldState> WorldState, uint8* TaskMemory) const override;
virtual EHTNExecutionResult ExecuteTask(UHTNPlannerComponent& HTNComp, uint8* TaskMemory) override;
virtual float GetHeuristicCost() const override;
virtual uint16 GetInstanceMemorySize() const override;
virtual bool IsApplicable(const TSharedPtr<FHTNWorldState> WorldState, uint8* TaskMemory) const override;
protected:
/**
* Caching the heuristic cost here. Only the class CDO needs to memorize this,
* so it doesn't go into the TaskMemory of individual instances of this class.
*/
float HeuristicCost;
};
\ No newline at end of file
#pragma once
#include "HTNPlanner/PrimitiveTask.h"
#include "HTNTask_PlaceInInventorySimpleFPS.generated.h"
struct FSimpleFPSObject;
struct FHTNTask_PlaceInInventorySimpleFPSMemory
{
/** The Item that should be placed in inventory */
FSimpleFPSObject* Item;
int32 Area;
};
/**
* 'Place-in-Inventory' primitive task for the SimpleFPS domain.
*/
UCLASS()
class HTN_PLUGIN_API UHTNTask_PlaceInInventorySimpleFPS : public UPrimitiveTask
{
GENERATED_BODY()
public:
UHTNTask_PlaceInInventorySimpleFPS(const FObjectInitializer& ObjectInitializer);
virtual void ApplyTo(TSharedPtr<FHTNWorldState> WorldState, uint8* TaskMemory) const override;
virtual EHTNExecutionResult ExecuteTask(UHTNPlannerComponent& HTNComp, uint8* TaskMemory) override;
virtual float GetHeuristicCost() const override;
virtual uint16 GetInstanceMemorySize() const override;
virtual bool IsApplicable(const TSharedPtr<FHTNWorldState> WorldState, uint8* TaskMemory) const override;
protected:
/**
* Caching the heuristic cost here. Only the class CDO needs to memorize this,
* so it doesn't go into the TaskMemory of individual instances of this class.
*/
float HeuristicCost;
};
\ No newline at end of file
#pragma once
#include "HTNPlanner/CompoundTask.h"
#include "HTNTask_ReloadCompoundSimpleFPS.generated.h"
struct FSimpleFPSObject;
struct FHTNTask_ReloadCompoundSimpleFPSMemory
{
FSimpleFPSObject* Gun;
};
/**
* 'Reload' compound task for the SimpleFPS domain.
*/
UCLASS()
class HTN_PLUGIN_API UHTNTask_ReloadCompoundSimpleFPS : public UCompoundTask
{
GENERATED_BODY()
public:
UHTNTask_ReloadCompoundSimpleFPS(const FObjectInitializer& ObjectInitializer);
virtual TArray<TSharedPtr<FHTNTaskInstance>> FindDecompositions(UHTNPlannerComponent& HTNComp,
const TSharedPtr<FHTNWorldState> WorldState, uint8* TaskMemory) override;
virtual float GetHeuristicCost() const override;
virtual uint16 GetInstanceMemorySize() const override;
protected:
/**
* Caching the heuristic cost here. Only the class CDO needs to memorize this,
* so it doesn't go into the TaskMemory of individual instances of this class.
*/
float HeuristicCost;
};
\ No newline at end of file
#pragma once
#include "HTNPlanner/PrimitiveTask.h"
#include "HTNTask_ReloadSimpleFPS.generated.h"
struct FSimpleFPSObject;
struct FHTNTask_ReloadSimpleFPSMemory
{
/** The Ammo that should be used to reload the Gun */
FSimpleFPSObject* Ammo;
/** The Gun that should be reloaded */
FSimpleFPSObject* Gun;
int32 Area;
};
/**
* 'Reload' primitive task for the SimpleFPS domain.
*/
UCLASS()
class HTN_PLUGIN_API UHTNTask_ReloadSimpleFPS : public UPrimitiveTask
{
GENERATED_BODY()
public:
UHTNTask_ReloadSimpleFPS(const FObjectInitializer& ObjectInitializer);
virtual void ApplyTo(TSharedPtr<FHTNWorldState> WorldState, uint8* TaskMemory) const override;
virtual EHTNExecutionResult ExecuteTask(UHTNPlannerComponent& HTNComp, uint8* TaskMemory) override;
virtual float GetHeuristicCost() const override;
virtual uint16 GetInstanceMemorySize() const override;
virtual bool IsApplicable(const TSharedPtr<FHTNWorldState> WorldState, uint8* TaskMemory) const override;
protected:
/**
* Caching the heuristic cost here. Only the class CDO needs to memorize this,
* so it doesn't go into the TaskMemory of individual instances of this class.
*/
float HeuristicCost;
};
\ No newline at end of file
#pragma once
#include "HTNPlanner/CompoundTask.h"
#include "HTNTask_RestoreFullHealthSimpleFPS.generated.h"
/**
* 'Restore-Full-Health' compound task for the SimpleFPS domain.
*/
UCLASS()
class HTN_PLUGIN_API UHTNTask_RestoreFullHealthSimpleFPS : public UCompoundTask
{
GENERATED_BODY()
public:
UHTNTask_RestoreFullHealthSimpleFPS(const FObjectInitializer& ObjectInitializer);
virtual TArray<TSharedPtr<FHTNTaskInstance>> FindDecompositions(UHTNPlannerComponent& HTNComp,
const TSharedPtr<FHTNWorldState> WorldState, uint8* TaskMemory) override;
virtual float GetHeuristicCost() const override;
protected:
/**
* Caching the heuristic cost here. Only the class CDO needs to memorize this,
* so it doesn't go into the TaskMemory of individual instances of this class.
*/
float HeuristicCost;
};
\ No newline at end of file
#pragma once
#include "HTNPlanner/CompoundTask.h"
#include "HTNTask_SneakKillCompoundSimpleFPS.generated.h"
struct FSimpleFPSObject;
struct FHTNTask_SneakKillCompoundSimpleFPSMemory
{
FSimpleFPSObject* Player;
int32 Area;
};
/**
* 'Sneak-Kill' compound task for the SimpleFPS domain.
*/
UCLASS()
class HTN_PLUGIN_API UHTNTask_SneakKillCompoundSimpleFPS : public UCompoundTask
{
GENERATED_BODY()
public:
UHTNTask_SneakKillCompoundSimpleFPS(const FObjectInitializer& ObjectInitializer);
virtual TArray<TSharedPtr<FHTNTaskInstance>> FindDecompositions(UHTNPlannerComponent& HTNComp,
const TSharedPtr<FHTNWorldState> WorldState, uint8* TaskMemory) override;
virtual float GetHeuristicCost() const override;
virtual uint16 GetInstanceMemorySize() const override;
protected:
/**
* Caching the heuristic cost here. Only the class CDO needs to memorize this,
* so it doesn't go into the TaskMemory of individual instances of this class.
*/
float HeuristicCost;
};
\ No newline at end of file
#pragma once
#include "HTNPlanner/PrimitiveTask.h"
#include "HTNTask_SneakKillSimpleFPS.generated.h"
struct FSimpleFPSObject;
struct FHTNTask_SneakKillSimpleFPSMemory
{
/** The Gun that should be used */
FSimpleFPSObject* Gun;
/** The Player that should be attacked */
FSimpleFPSObject* Player;
int32 Area;
};
/**
* 'Sneak-Kill' primitive task for the SimpleFPS domain.
*/
UCLASS()
class HTN_PLUGIN_API UHTNTask_SneakKillSimpleFPS : public UPrimitiveTask
{
GENERATED_BODY()
public:
UHTNTask_SneakKillSimpleFPS(const FObjectInitializer& ObjectInitializer);
virtual void ApplyTo(TSharedPtr<FHTNWorldState> WorldState, uint8* TaskMemory) const override;
virtual EHTNExecutionResult ExecuteTask(UHTNPlannerComponent& HTNComp, uint8* TaskMemory) override;
virtual float GetHeuristicCost() const override;
virtual uint16 GetInstanceMemorySize() const override;
virtual bool IsApplicable(const TSharedPtr<FHTNWorldState> WorldState, uint8* TaskMemory) const override;
protected:
/**
* Caching the heuristic cost here. Only the class CDO needs to memorize this,
* so it doesn't go into the TaskMemory of individual instances of this class.
*/
float HeuristicCost;
};
\ No newline at end of file
#pragma once
#include "HTNPlanner/CompoundTask.h"
#include "HTNTask_TakeCoverAndWoundPlayerSimpleFPS.generated.h"
struct FSimpleFPSObject;
struct FHTNTask_TakeCoverAndWoundPlayerSimpleFPSMemory
{
FSimpleFPSObject* CoverPoint;
FSimpleFPSObject* Player;
int32 Area;
};
/**
* 'Take-Cover-And-Wound-Player' compound task for the SimpleFPS domain.
*/
UCLASS()
class HTN_PLUGIN_API UHTNTask_TakeCoverAndWoundPlayerSimpleFPS : public UCompoundTask
{
GENERATED_BODY()
public:
UHTNTask_TakeCoverAndWoundPlayerSimpleFPS(const FObjectInitializer& ObjectInitializer);
virtual TArray<TSharedPtr<FHTNTaskInstance>> FindDecompositions(UHTNPlannerComponent& HTNComp,
const TSharedPtr<FHTNWorldState> WorldState, uint8* TaskMemory) override;
virtual float GetHeuristicCost() const override;
virtual uint16 GetInstanceMemorySize() const override;
protected:
/**
* Caching the heuristic cost here. Only the class CDO needs to memorize this,
* so it doesn't go into the TaskMemory of individual instances of this class.
*/
float HeuristicCost;
};
\ No newline at end of file
#pragma once
#include "HTNPlanner/PrimitiveTask.h"
#include "HTNTask_TakeCoverSimpleFPS.generated.h"
struct FSimpleFPSObject;
struct FHTNTask_TakeCoverSimpleFPSMemory
{
/** The Cover Point that should be used */
FSimpleFPSObject* CoverPoint;
int32 Area;
};
/**
* 'Take-Cover' primitive task for the SimpleFPS domain.
*/
UCLASS()
class HTN_PLUGIN_API UHTNTask_TakeCoverSimpleFPS : public UPrimitiveTask
{
GENERATED_BODY()
public:
UHTNTask_TakeCoverSimpleFPS(const FObjectInitializer& ObjectInitializer);
virtual void ApplyTo(TSharedPtr<FHTNWorldState> WorldState, uint8* TaskMemory) const override;
virtual EHTNExecutionResult ExecuteTask(UHTNPlannerComponent& HTNComp, uint8* TaskMemory) override;
virtual float GetHeuristicCost() const override;
virtual uint16 GetInstanceMemorySize() const override;
virtual bool IsApplicable(const TSharedPtr<FHTNWorldState> WorldState, uint8* TaskMemory) const override;
protected:
/**
* Caching the heuristic cost here. Only the class CDO needs to memorize this,
* so it doesn't go into the TaskMemory of individual instances of this class.
*/
float HeuristicCost;
};
\ No newline at end of file
#pragma once
#include "HTNPlanner/CompoundTask.h"
#include "HTNTask_TurnOffLightsCompoundSimpleFPS.generated.h"
struct FHTNTask_TurnOffLightsCompoundSimpleFPSMemory
{
int32 Area;
};
/**
* 'Turn-off-Lights' compound task for the SimpleFPS domain.
*/
UCLASS()
class HTN_PLUGIN_API UHTNTask_TurnOffLightsCompoundSimpleFPS : public UCompoundTask
{
GENERATED_BODY()
public:
UHTNTask_TurnOffLightsCompoundSimpleFPS(const FObjectInitializer& ObjectInitializer);
virtual TArray<TSharedPtr<FHTNTaskInstance>> FindDecompositions(UHTNPlannerComponent& HTNComp,
const TSharedPtr<FHTNWorldState> WorldState, uint8* TaskMemory) override;
virtual float GetHeuristicCost() const override;
virtual uint16 GetInstanceMemorySize() const override;
protected:
/**
* Caching the heuristic cost here. Only the class CDO needs to memorize this,
* so it doesn't go into the TaskMemory of individual instances of this class.
*/
float HeuristicCost;
};
\ No newline at end of file
#pragma once
#include "HTNPlanner/PrimitiveTask.h"
#include "HTNTask_TurnOffLightsSimpleFPS.generated.h"
struct FSimpleFPSObject;
struct FHTNTask_TurnOffLightsSimpleFPSMemory
{
/** The Control Box that should be used to turn off lights */
FSimpleFPSObject* ControlBox;
int32 Area;
};
/**
* 'Turn-off-Lights' primitive task for the SimpleFPS domain.
*/
UCLASS()
class HTN_PLUGIN_API UHTNTask_TurnOffLightsSimpleFPS : public UPrimitiveTask
{
GENERATED_BODY()
public:
UHTNTask_TurnOffLightsSimpleFPS(const FObjectInitializer& ObjectInitializer);
virtual void ApplyTo(TSharedPtr<FHTNWorldState> WorldState, uint8* TaskMemory) const override;
virtual EHTNExecutionResult ExecuteTask(UHTNPlannerComponent& HTNComp, uint8* TaskMemory) override;
virtual float GetHeuristicCost() const override;
virtual uint16 GetInstanceMemorySize() const override;
virtual bool IsApplicable(const TSharedPtr<FHTNWorldState> WorldState, uint8* TaskMemory) const override;
protected:
/**
* Caching the heuristic cost here. Only the class CDO needs to memorize this,
* so it doesn't go into the TaskMemory of individual instances of this class.
*/
float HeuristicCost;
};
\ No newline at end of file
#pragma once
#include "HTNPlanner/CompoundTask.h"
#include "HTNTask_TurnOnLightsCompoundSimpleFPS.generated.h"
struct FHTNTask_TurnOnLightsCompoundSimpleFPSMemory
{
int32 Area;
};
/**
* 'Turn-on-Lights' compound task for the SimpleFPS domain.
*/
UCLASS()
class HTN_PLUGIN_API UHTNTask_TurnOnLightsCompoundSimpleFPS : public UCompoundTask
{
GENERATED_BODY()
public:
UHTNTask_TurnOnLightsCompoundSimpleFPS(const FObjectInitializer& ObjectInitializer);
virtual TArray<TSharedPtr<FHTNTaskInstance>> FindDecompositions(UHTNPlannerComponent& HTNComp,
const TSharedPtr<FHTNWorldState> WorldState, uint8* TaskMemory) override;
virtual float GetHeuristicCost() const override;
virtual uint16 GetInstanceMemorySize() const override;
protected:
/**
* Caching the heuristic cost here. Only the class CDO needs to memorize this,
* so it doesn't go into the TaskMemory of individual instances of this class.
*/
float HeuristicCost;
};
\ No newline at end of file
#pragma once
#include "HTNPlanner/PrimitiveTask.h"
#include "HTNTask_TurnOnLightsSimpleFPS.generated.h"
struct FSimpleFPSObject;
struct FHTNTask_TurnOnLightsSimpleFPSMemory
{
/** The Control Box that should be used to turn on lights */
FSimpleFPSObject* ControlBox;
int32 Area;
};
/**
* 'Turn-on-Lights' primitive task for the SimpleFPS domain.
*/
UCLASS()
class HTN_PLUGIN_API UHTNTask_TurnOnLightsSimpleFPS : public UPrimitiveTask
{
GENERATED_BODY()
public:
UHTNTask_TurnOnLightsSimpleFPS(const FObjectInitializer& ObjectInitializer);
virtual void ApplyTo(TSharedPtr<FHTNWorldState> WorldState, uint8* TaskMemory) const override;
virtual EHTNExecutionResult ExecuteTask(UHTNPlannerComponent& HTNComp, uint8* TaskMemory) override;
virtual float GetHeuristicCost() const override;
virtual uint16 GetInstanceMemorySize() const override;
virtual bool IsApplicable(const TSharedPtr<FHTNWorldState> WorldState, uint8* TaskMemory) const override;
protected:
/**
* Caching the heuristic cost here. Only the class CDO needs to memorize this,
* so it doesn't go into the TaskMemory of individual instances of this class.
*/
float HeuristicCost;
};
\ No newline at end of file
#pragma once
#include "HTNPlanner/CompoundTask.h"
#include "HTNTask_UncoverCompoundSimpleFPS.generated.h"
struct FSimpleFPSObject;
/**
* 'Uncover' compound task for the SimpleFPS domain.
*/
UCLASS()
class HTN_PLUGIN_API UHTNTask_UncoverCompoundSimpleFPS : public UCompoundTask
{
GENERATED_BODY()
public:
UHTNTask_UncoverCompoundSimpleFPS(const FObjectInitializer& ObjectInitializer);
virtual TArray<TSharedPtr<FHTNTaskInstance>> FindDecompositions(UHTNPlannerComponent& HTNComp,
const TSharedPtr<FHTNWorldState> WorldState, uint8* TaskMemory) override;
virtual float GetHeuristicCost() const override;
protected:
/**
* Caching the heuristic cost here. Only the class CDO needs to memorize this,
* so it doesn't go into the TaskMemory of individual instances of this class.
*/
float HeuristicCost;
};
\ No newline at end of file
#pragma once
#include "HTNPlanner/PrimitiveTask.h"
#include "HTNTask_UncoverSimpleFPS.generated.h"
struct FSimpleFPSObject;
struct FHTNTask_UncoverSimpleFPSMemory
{
/** The Cover Point that we should get out of */
FSimpleFPSObject* CoverPoint;
};
/**
* 'Uncover' primitive task for the SimpleFPS domain.
*/
UCLASS()
class HTN_PLUGIN_API UHTNTask_UncoverSimpleFPS : public UPrimitiveTask
{
GENERATED_BODY()
public:
UHTNTask_UncoverSimpleFPS(const FObjectInitializer& ObjectInitializer);
virtual void ApplyTo(TSharedPtr<FHTNWorldState> WorldState, uint8* TaskMemory) const override;
virtual EHTNExecutionResult ExecuteTask(UHTNPlannerComponent& HTNComp, uint8* TaskMemory) override;
virtual float GetHeuristicCost() const override;
virtual uint16 GetInstanceMemorySize() const override;
virtual bool IsApplicable(const TSharedPtr<FHTNWorldState> WorldState, uint8* TaskMemory) const override;
protected:
/**
* Caching the heuristic cost here. Only the class CDO needs to memorize this,
* so it doesn't go into the TaskMemory of individual instances of this class.
*/
float HeuristicCost;
};
\ No newline at end of file
#pragma once
#include "HTNPlanner/PrimitiveTask.h"
#include "HTNTask_UnexploreSimpleFPS.generated.h"
struct FHTNTask_UnexploreSimpleFPSMemory
{
int32 Area;
};
/**
* 'Unexplore' primitive task for the SimpleFPS domain.
*/
UCLASS()
class HTN_PLUGIN_API UHTNTask_UnexploreSimpleFPS : public UPrimitiveTask
{
GENERATED_BODY()
public:
UHTNTask_UnexploreSimpleFPS(const FObjectInitializer& ObjectInitializer);
virtual void ApplyTo(TSharedPtr<FHTNWorldState> WorldState, uint8* TaskMemory) const override;
virtual EHTNExecutionResult ExecuteTask(UHTNPlannerComponent& HTNComp, uint8* TaskMemory) override;
virtual float GetCost(const TSharedPtr<FHTNWorldState> WorldState, uint8* TaskMemory) const override;
virtual uint16 GetInstanceMemorySize() const override;
virtual bool IsApplicable(const TSharedPtr<FHTNWorldState> WorldState, uint8* TaskMemory) const override;
protected:
/**
* Caching the heuristic cost here. Only the class CDO needs to memorize this,
* so it doesn't go into the TaskMemory of individual instances of this class.
*/
float HeuristicCost;
};
\ No newline at end of file
#pragma once
#include "HTNPlanner/PrimitiveTask.h"
#include "HTNTask_UseMedikitSimpleFPS.generated.h"
struct FSimpleFPSObject;
struct FHTNTask_UseMedikitSimpleFPSMemory
{
/** The Medikit that should be used */
FSimpleFPSObject* Medikit;
};
/**
* 'Use-Medikit' primitive task for the SimpleFPS domain.
*/
UCLASS()
class HTN_PLUGIN_API UHTNTask_UseMedikitSimpleFPS : public UPrimitiveTask
{
GENERATED_BODY()
public:
UHTNTask_UseMedikitSimpleFPS(const FObjectInitializer& ObjectInitializer);
virtual void ApplyTo(TSharedPtr<FHTNWorldState> WorldState, uint8* TaskMemory) const override;
virtual EHTNExecutionResult ExecuteTask(UHTNPlannerComponent& HTNComp, uint8* TaskMemory) override;
virtual float GetHeuristicCost() const override;
virtual uint16 GetInstanceMemorySize() const override;
virtual bool IsApplicable(const TSharedPtr<FHTNWorldState> WorldState, uint8* TaskMemory) const override;
protected:
/**
* Caching the heuristic cost here. Only the class CDO needs to memorize this,
* so it doesn't go into the TaskMemory of individual instances of this class.
*/
float HeuristicCost;
};
\ No newline at end of file
#pragma once
#include "HTNPlanner/CompoundTask.h"
#include "HTNTask_WoundPlayerSimpleFPS.generated.h"
struct FSimpleFPSObject;
struct FHTNTask_WoundPlayerSimpleFPSMemory
{
FSimpleFPSObject* Player;
int32 Area;
};
/**
* 'Wound-Player' compound task for the SimpleFPS domain.
*/
UCLASS()
class HTN_PLUGIN_API UHTNTask_WoundPlayerSimpleFPS : public UCompoundTask
{
GENERATED_BODY()
public:
UHTNTask_WoundPlayerSimpleFPS(const FObjectInitializer& ObjectInitializer);
virtual TArray<TSharedPtr<FHTNTaskInstance>> FindDecompositions(UHTNPlannerComponent& HTNComp,
const TSharedPtr<FHTNWorldState> WorldState, uint8* TaskMemory) override;
virtual float GetHeuristicCost() const override;
virtual uint16 GetInstanceMemorySize() const override;
protected:
/**
* Caching the heuristic cost here. Only the class CDO needs to memorize this,
* so it doesn't go into the TaskMemory of individual instances of this class.
*/
float HeuristicCost;
};
\ No newline at end of file
using UnrealBuildTool;
public class HTN_Plugin : ModuleRules
{
public HTN_Plugin(TargetInfo Target)
{
PublicIncludePaths.AddRange(
new string[] {
"HTN_Plugin/Public"
}
);
PrivateIncludePaths.AddRange(
new string[] {
"HTN_Plugin/Private",
}
);
PublicDependencyModuleNames.AddRange(
new string[]
{
"Core",
}
);
PrivateDependencyModuleNames.AddRange(
new string[]
{
"CoreUObject", "Engine", "Slate", "SlateCore", "AIModule"
}
);
DynamicallyLoadedModuleNames.AddRange(
new string[]
{
}
);
// if 1, we collect stats on our runtime performance (time it takes to find plan etc.) and print it to log
Definitions.Add("HTN_LOG_RUNTIME_STATS=1");
// if 1, we want to compile code that is compatible with the UE4 version that Unreal Tournament uses
Definitions.Add("HTN_COMPILE_UT_COMPATIBLE=0");
}
}
#include "HTN_PluginPrivatePCH.h"
#include "HTNPlanner/CompoundTask.h"
#include "HTNPlanner/HTNPlannerComponent.h"
#include "HTNPlanner/HTNWorldState.h"
#include "HTNPlanner/TaskNetwork.h"
TArray<TSharedPtr<FHTNTaskInstance>> UCompoundTask::FindDecompositions(UHTNPlannerComponent& HTNComp,
const TSharedPtr<FHTNWorldState> WorldState, uint8* TaskMemory)
{
// default implementation: no decompositions possible
return TArray<TSharedPtr<FHTNTaskInstance>>();
}
float UCompoundTask::GetHeuristicCost() const
{
return 0.f;
}
\ No newline at end of file
This diff is collapsed.
#include "HTN_PluginPrivatePCH.h"
#include "HTNPlanner/HTNPlanner.h"
#include "HTNPlanner/HTNWorldState.h"
DEFINE_LOG_CATEGORY(LogHTNPlanner);
UHTNPlanner::UHTNPlanner(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
{
// default values of properties
bExecutePlan = true;
bDepthFirstSearch = true;
bIgnoreTaskCosts = false;
bLoop = true;
bUseHeuristicCosts = true;
MaxSearchTime = 0.05f;
EmptyWorldState = nullptr;
TaskNetworkInstance = nullptr;
}
\ No newline at end of file
#include "HTN_PluginPrivatePCH.h"
#include "HTNPlanner/HTNPlannerTypes.h"
#include "HTNPlanner/HTNTask.h"
FHTNTaskInstance::~FHTNTaskInstance()
{
if(Task && !Task->IsPendingKill())
{
Task->Cleanup(GetMemory());
}
}
FString UHTNPlannerTypes::DescribeTaskHelper(const UHTNTask* Task, uint8* TaskMemory)
{
return Task ? FString::Printf(TEXT("%s"), *Task->GetTaskName(TaskMemory)) : FString();
}
FString UHTNPlannerTypes::DescribeTaskResult(EHTNExecutionResult TaskResult)
{
static FString ResultDesc[] = { TEXT("Succeeded"), TEXT("Failed"), TEXT("Aborted"), TEXT("InProgress") };
uint32 TaskResultUint = static_cast<uint32>(TaskResult);
return (TaskResultUint < ARRAY_COUNT(ResultDesc)) ? ResultDesc[TaskResultUint] : FString();
}
\ No newline at end of file
#include "HTN_PluginPrivatePCH.h"
#include "HTNPlanner/HTNPlannerComponent.h"
#include "HTNPlanner/HTNService.h"
UHTNService::UHTNService(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
{
NextTickRemainingTime = 0.f;
AccumulatedDeltaTime = 0.f;
Interval = 0.5f;
RandomDeviation = 0.1f;
}
void UHTNService::OnUpdate(UHTNPlannerComponent& HTNComp, float DeltaSeconds)
{
// no default implementation
}
void UHTNService::SetOwner(AActor* InActorOwner)
{
ActorOwner = InActorOwner;
AIOwner = Cast<AAIController>(InActorOwner);
}
void UHTNService::Tick(UHTNPlannerComponent& HTNComp, float DeltaSeconds)
{
float UseDeltaTime = DeltaSeconds;
NextTickRemainingTime -= DeltaSeconds;
AccumulatedDeltaTime += DeltaSeconds;
if(NextTickRemainingTime > 0.0f)
{
return;
}
UseDeltaTime = AccumulatedDeltaTime;
AccumulatedDeltaTime = 0.0f;
OnUpdate(HTNComp, UseDeltaTime);
NextTickRemainingTime = FMath::FRandRange(FMath::Max(0.0f, Interval - RandomDeviation), (Interval + RandomDeviation));
}
\ No newline at end of file
#include "HTN_PluginPrivatePCH.h"
#include "HTNPlanner/HTNTask.h"
void UHTNTask::Cleanup(uint8* TaskMemory)
{
// no default implementation
}
TSharedPtr<FHTNTaskInstance> UHTNTask::Copy(const TSharedPtr<FHTNTaskInstance>& OriginalInstance)
{
TSharedPtr<FHTNTaskInstance> Copy = TSharedPtr<FHTNTaskInstance>(OriginalInstance);
return Copy;
}
uint16 UHTNTask::GetInstanceMemorySize() const
{
return 0;
}
FString UHTNTask::GetTaskName(uint8* TaskMemory) const
{
return TaskName;
}
void UHTNTask::Instantiate(UHTNPlannerComponent& HTNComp, uint8* TaskMemory)
{
// don't need to do anything in base class
}
\ No newline at end of file
#include "HTN_PluginPrivatePCH.h"
#include "BehaviorTree/BlackboardComponent.h"
#include "HTNPlanner/HTNWorldState.h"
#include "HTNPlanner/PrimitiveTask.h"
void FHTNWorldState::ApplyTask(const UPrimitiveTask* Task, uint8* TaskMemory)
{
UE_LOG(LogHTNPlanner, Warning, TEXT("FHTNWorldState::ApplyTask() cannot handle the given Task!"))
}
bool FHTNWorldState::CanApply(const UPrimitiveTask* Task, uint8* TaskMemory) const
{
UE_LOG(LogHTNPlanner, Warning, TEXT("FHTNWorldState::CanApply() cannot handle the given Task!"))
return false;
}
TSharedPtr<FHTNWorldState> FHTNWorldState::Copy() const
{
UE_LOG(LogHTNPlanner, Warning, TEXT("FHTNWorldState::Copy() should not be called on the base class!"))
return nullptr;
}
float FHTNWorldState::GetHeuristicCost(const TArray<TSharedPtr<FHTNTaskInstance>>& TaskInstances) const
{
return 0.f;
}
FName FHTNWorldState::GetWorldStateName() const
{
return "HTN World State";
}
void FHTNWorldState::Initialize(AActor* Owner, UBlackboardComponent* BlackboardComponent)
{
// no default initialization
}
\ No newline at end of file
#include "HTN_PluginPrivatePCH.h"
#include "HTNPlanner/HTNPlannerComponent.h"
#include "HTNPlanner/HTNWorldState.h"
#include "HTNPlanner/PrimitiveTask.h"
EHTNExecutionResult UPrimitiveTask::AbortTask(UHTNPlannerComponent& HTNComp, uint8* TaskMemory)
{
return EHTNExecutionResult::Aborted;
}
void UPrimitiveTask::ApplyTo(TSharedPtr<FHTNWorldState> WorldState, uint8* TaskMemory) const
{
// no default implementation
}
EHTNExecutionResult UPrimitiveTask::ExecuteTask(UHTNPlannerComponent& HTNComp, uint8* TaskMemory)
{
return EHTNExecutionResult::Succeeded;
}
void UPrimitiveTask::FinishLatentTask(UHTNPlannerComponent& HTNComp, EHTNExecutionResult TaskResult, uint8* TaskMemory)
{
HTNComp.OnTaskFinished(this, TaskMemory, TaskResult);
}
void UPrimitiveTask::FinishLatentAbort(UHTNPlannerComponent& HTNComp, uint8* TaskMemory)
{
HTNComp.OnTaskFinished(this, TaskMemory, EHTNExecutionResult::Aborted);
}
float UPrimitiveTask::GetCost(const TSharedPtr<FHTNWorldState> WorldState, uint8* TaskMemory) const
{
return 1.f; // TO DO default cost of 0.0 probably makes more sense in context of UE, but 1.0 as default corresponds to SHOP2 / JSHOP2 / academia
}
float UPrimitiveTask::GetHeuristicCost() const
{
return 0.f;
}
bool UPrimitiveTask::IsApplicable(const TSharedPtr<FHTNWorldState> WorldState, uint8* TaskMemory) const
{
return false;
}
void UPrimitiveTask::OnMessage(UHTNPlannerComponent& HTNComp, FName Message, int32 RequestID,
bool bSuccess, const TSharedPtr<FHTNTaskInstance>& TaskInstance)
{
const EHTNTaskStatus Status = HTNComp.GetTaskStatus(TaskInstance);
if(Status == EHTNTaskStatus::Active)
{
FinishLatentTask(HTNComp, bSuccess ? EHTNExecutionResult::Succeeded : EHTNExecutionResult::Failed, TaskInstance->GetMemory());
}
else if(Status == EHTNTaskStatus::Aborting)
{
FinishLatentAbort(HTNComp, TaskInstance->GetMemory());
}
}
void UPrimitiveTask::OnTaskFinished(UHTNPlannerComponent& HTNComp, EHTNExecutionResult TaskResult, uint8* TaskMemory)
{
//no default implementation
}
void UPrimitiveTask::ReceivedMessage(UBrainComponent* BrainComp, const FAIMessage& Message, TSharedPtr<FHTNTaskInstance> TaskInstance)
{
UHTNPlannerComponent* OwnerComp = static_cast<UHTNPlannerComponent*>(BrainComp);
check(OwnerComp);
OnMessage(*OwnerComp, Message.MessageName, Message.RequestID, Message.Status == FAIMessage::Success, TaskInstance);
}
void UPrimitiveTask::TickTask(UHTNPlannerComponent& HTNComp, float DeltaSeconds, uint8* TaskMemory)
{
// empty in base class
}
void UPrimitiveTask::WaitForMessage(UHTNPlannerComponent& HTNComp, FName MessageType, uint8* TaskMemory) const
{
HTNComp.RegisterMessageObserver(this, TaskMemory, MessageType);
}
void UPrimitiveTask::WaitForMessage(UHTNPlannerComponent& HTNComp, FName MessageType, int32 RequestID, uint8* TaskMemory) const
{
HTNComp.RegisterMessageObserver(this, TaskMemory, MessageType, RequestID);
}
\ No newline at end of file
#include "HTN_PluginPrivatePCH.h"
#include "HTNPlanner/Services/HTNService_BlueprintBase.h"
UHTNService_BlueprintBase::UHTNService_BlueprintBase(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer)
{}
void UHTNService_BlueprintBase::OnUpdate(UHTNPlannerComponent& HTNComp, float DeltaSeconds)
{
Super::OnUpdate(HTNComp, DeltaSeconds);
if(AIOwner != nullptr)
{
ReceiveTickAI(AIOwner, AIOwner->GetPawn(), DeltaSeconds);
}
else
{
ReceiveTick(ActorOwner, DeltaSeconds);
}
}
\ No newline at end of file
#include "HTN_PluginPrivatePCH.h"
#include "HTNPlanner/HTNPlannerComponent.h"
#include "HTNPlanner/Tasks/CompoundTask_BlueprintBase.h"
TArray<TSharedPtr<FHTNTaskInstance>> UCompoundTask_BlueprintBase::FindDecompositions(UHTNPlannerComponent& HTNComp,
const TSharedPtr<FHTNWorldState> WorldState,
uint8* TaskMemory)
{
TArray<TSubclassOf<UTaskNetwork>> Decompositions = GetDecompositions(&HTNComp, FHTNWorldStateWrapper(WorldState));
TArray<TSharedPtr<FHTNTaskInstance>> DecompositionInstances;
DecompositionInstances.Reserve(Decompositions.Num());
for(const TSubclassOf<UTaskNetwork>& Decomposition : Decompositions)
{
DecompositionInstances.Add(HTNComp.InstantiateNetwork(Decomposition));
}
return DecompositionInstances;
}
\ No newline at end of file
#include "HTN_PluginPrivatePCH.h"
#include "BehaviorTree/BlackboardComponent.h"
#include "BehaviorTree/Blackboard/BlackboardKeyType_Object.h"
#include "BehaviorTree/Blackboard/BlackboardKeyType_Vector.h"
#include "HTNPlanner/HTNPlannerComponent.h"
#include "HTNPlanner/HTNWorldState.h"
#include "HTNPlanner/Tasks/HTNTask_MoveTo.h"
UHTNTask_MoveTo::UHTNTask_MoveTo(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
{
TaskName = "Move To";
#if HTN_COMPILE_UT_COMPATIBLE
AcceptableRadius = 50.f;
bAllowStrafe = false;
bAllowPartialPath = true;
bStopOnOverlap = true;
#else
AcceptableRadius = GET_AI_CONFIG_VAR(AcceptanceRadius);
bStopOnOverlap = GET_AI_CONFIG_VAR(bFinishMoveOnGoalOverlap);
bAllowStrafe = GET_AI_CONFIG_VAR(bAllowStrafing);
bAllowPartialPath = GET_AI_CONFIG_VAR(bAcceptPartialPaths);
#endif // HTN_COMPILE_UT_COMPATIBLE
// initialize all ways to specify the target location to none/null/invalid
TargetBlackboardKey = NAME_None;
TargetActor = nullptr;
TargetLocation = FAISystem::InvalidLocation;
}
EHTNExecutionResult UHTNTask_MoveTo::AbortTask(UHTNPlannerComponent& HTNComp, uint8* TaskMemory)
{
FHTNTask_MoveToMemory* Memory = (FHTNTask_MoveToMemory*)TaskMemory;
if(!Memory->bWaitingForPath)
{
AAIController* MyController = HTNComp.GetAIOwner();
if(MyController && MyController->GetPathFollowingComponent())
{
MyController->GetPathFollowingComponent()->AbortMove(TEXT("HTN Planner abort"), Memory->MoveRequestID);
}
}
return Super::AbortTask(HTNComp, TaskMemory);
}
void UHTNTask_MoveTo::ApplyTo(TSharedPtr<FHTNWorldState> WorldState, uint8* TaskMemory) const
{
// TO DO default implementation for HTNWorldState_Blackboard
WorldState->ApplyTask(this, TaskMemory);
}
EHTNExecutionResult UHTNTask_MoveTo::ExecuteTask(UHTNPlannerComponent& HTNComp, uint8* TaskMemory)
{
FHTNTask_MoveToMemory* Memory = (FHTNTask_MoveToMemory*)TaskMemory;
EHTNExecutionResult Result = EHTNExecutionResult::InProgress;
AAIController* MyController = HTNComp.GetAIOwner();
Memory->bWaitingForPath = MyController->ShouldPostponePathUpdates();
if(!Memory->bWaitingForPath)
{
Result = PerformMoveTask(HTNComp, TaskMemory);
}
else
{
UE_VLOG(MyController, LogHTNPlanner, Log, TEXT("Pathfinding requests are freezed, waiting..."));
}
return Result;
}
uint16 UHTNTask_MoveTo::GetInstanceMemorySize() const
{
return sizeof(FHTNTask_MoveToMemory);
}
bool UHTNTask_MoveTo::IsApplicable(const TSharedPtr<FHTNWorldState> WorldState, uint8* TaskMemory) const
{
// TO DO default implementation for HTNWorldState_Blackboard
return WorldState->CanApply(this, TaskMemory);
}
void UHTNTask_MoveTo::OnMessage(UHTNPlannerComponent& HTNComp, FName Message, int32 SenderID,
bool bSuccess, const TSharedPtr<FHTNTaskInstance>& TaskInstance)
{
// AIMessage_RepathFailed means task has failed
bSuccess &= (Message != UBrainComponent::AIMessage_RepathFailed);
Super::OnMessage(HTNComp, Message, SenderID, bSuccess, TaskInstance);
}
EHTNExecutionResult UHTNTask_MoveTo::PerformMoveTask(UHTNPlannerComponent& HTNComp, uint8* TaskMemory)
{
const UBlackboardComponent* MyBlackboard = HTNComp.GetBlackboardComponent();
AAIController* MyController = HTNComp.GetAIOwner();
EHTNExecutionResult Result = EHTNExecutionResult::Failed;
if(MyController && MyBlackboard)
{
EPathFollowingRequestResult::Type RequestResult = EPathFollowingRequestResult::Failed;
FAIMoveRequest MoveReq;
MoveReq.SetNavigationFilter(FilterClass);
MoveReq.SetAllowPartialPath(bAllowPartialPath);
MoveReq.SetAcceptanceRadius(AcceptableRadius);
MoveReq.SetCanStrafe(bAllowStrafe);
MoveReq.SetStopOnOverlap(bStopOnOverlap);
if(TargetBlackboardKey != NAME_None)
{
FBlackboard::FKey KeyID = MyBlackboard->GetKeyID(TargetBlackboardKey);
TSubclassOf<UBlackboardKeyType> KeyType = MyBlackboard->GetKeyType(KeyID);
if(KeyType->IsChildOf(UBlackboardKeyType_Vector::StaticClass()))
{
const FVector Loc = MyBlackboard->GetValue<UBlackboardKeyType_Vector>(KeyID);
MoveReq.SetGoalLocation(Loc);
RequestResult = MyController->MoveTo(MoveReq);
}
else if(KeyType->IsChildOf(UBlackboardKeyType_Object::StaticClass()))
{
UObject* Object = MyBlackboard->GetValue<UBlackboardKeyType_Object>(KeyID);
if(AActor* Actor = Cast<AActor>(Object))
{
MoveReq.SetGoalActor(Actor);
RequestResult = MyController->MoveTo(MoveReq);
}
else
{
UE_VLOG(MyController, LogHTNPlanner, Warning, TEXT("UHTNTask_MoveTo::PerformMoveTask() tried to go to actor while BB %s entry was empty"), *TargetBlackboardKey.ToString());
}
}
else
{
UE_VLOG(MyController, LogHTNPlanner, Warning, TEXT("UHTNTask_MoveTo::PerformMoveTask() TargetBlackboardKey Type is neither Vector nor UObject!"));
}
}
else if(TargetActor)
{
MoveReq.SetGoalActor(TargetActor);
RequestResult = MyController->MoveTo(MoveReq);
}
else if(TargetLocation != FAISystem::InvalidLocation)
{
MoveReq.SetGoalLocation(TargetLocation);
RequestResult = MyController->MoveTo(MoveReq);
}
if(RequestResult == EPathFollowingRequestResult::RequestSuccessful)
{
const FAIRequestID RequestID = MyController->GetCurrentMoveRequestID();
((FHTNTask_MoveToMemory*)TaskMemory)->MoveRequestID = RequestID;
WaitForMessage(HTNComp, UBrainComponent::AIMessage_MoveFinished, RequestID, TaskMemory);
WaitForMessage(HTNComp, UBrainComponent::AIMessage_RepathFailed, TaskMemory);
Result = EHTNExecutionResult::InProgress;
}
else if(RequestResult == EPathFollowingRequestResult::AlreadyAtGoal)
{
Result = EHTNExecutionResult::Succeeded;
}
}
return Result;
}
void UHTNTask_MoveTo::TickTask(UHTNPlannerComponent& HTNComp, float DeltaSeconds, uint8* TaskMemory)
{
FHTNTask_MoveToMemory* Memory = (FHTNTask_MoveToMemory*)TaskMemory;
if(Memory->bWaitingForPath && !HTNComp.IsPaused())
{
AAIController* MyController = HTNComp.GetAIOwner();
if(MyController && !MyController->ShouldPostponePathUpdates())
{
UE_VLOG(MyController, LogHTNPlanner, Log, TEXT("Pathfinding requests are unlocked!"));
Memory->bWaitingForPath = false;
const EHTNExecutionResult Result = PerformMoveTask(HTNComp, TaskMemory);
if(Result != EHTNExecutionResult::InProgress)
{
FinishLatentTask(HTNComp, Result, TaskMemory);
}
}
}
}
\ No newline at end of file
#include "HTN_PluginPrivatePCH.h"
#include "HTNPlanner/HTNPlannerComponent.h"
#include "HTNPlanner/Tasks/HTNTask_RegisterService.h"
UHTNTask_RegisterService::UHTNTask_RegisterService(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
{
TaskName = "Register Service";
}
void UHTNTask_RegisterService::ApplyTo(TSharedPtr<FHTNWorldState> WorldState, uint8* TaskMemory) const
{
// no effect
}
EHTNExecutionResult UHTNTask_RegisterService::ExecuteTask(UHTNPlannerComponent& HTNComp, uint8* TaskMemory)
{
HTNComp.RegisterService(Service);
return EHTNExecutionResult::Succeeded;
}
float UHTNTask_RegisterService::GetCost(const TSharedPtr<FHTNWorldState> WorldState, uint8* TaskMemory) const
{
return 0.f;
}
bool UHTNTask_RegisterService::IsApplicable(const TSharedPtr<FHTNWorldState> WorldState, uint8* TaskMemory) const
{
return true;
}
\ No newline at end of file
#include "HTN_PluginPrivatePCH.h"
#include "HTNPlanner/WorldStates/HTNWorldState_Blackboard.h"
\ No newline at end of file
This diff is collapsed.
This diff is collapsed.
#include "HTN_PluginPrivatePCH.h"
#include "JSHOP2_Experiments/HTNDummyObject.h"
\ No newline at end of file
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment