Shooting NPCs and more (#18042)
* Add pirate shooting * Shooting working * Basics working * Refactor time * More conversion * Update primitives * Update yml * weh * Building again * Draft * weh * b * Start shutdown * Starting to take form * Code side done * is it worky * Fix prototypes * stuff * Shitty working * Juke events working * Even more cleanup * RTX * Fix interaction combat mode and compquery * GetAmmoCount relays * Fix rotation speed * Juke fixes * fixes * weh * The collision avoidance never ends * Fixes * Pause support * framework * lazy * Fix idling * Fix drip * goobed * Fix takeover shutdown bug * Merge fixes * shitter * Fix carpos
This commit is contained in:
@@ -13,8 +13,7 @@ using JetBrains.Annotations;
|
||||
using Robust.Server.Player;
|
||||
using Robust.Shared.Players;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Random;
|
||||
using Robust.Shared.Timing;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Server.NPC.HTN;
|
||||
|
||||
@@ -25,20 +24,14 @@ public sealed class HTNSystem : EntitySystem
|
||||
[Dependency] private readonly NPCSystem _npc = default!;
|
||||
[Dependency] private readonly NPCUtilitySystem _utility = default!;
|
||||
|
||||
private ISawmill _sawmill = default!;
|
||||
private readonly JobQueue _planQueue = new(0.004);
|
||||
|
||||
private readonly HashSet<ICommonSession> _subscribers = new();
|
||||
|
||||
// hngngghghgh
|
||||
public IReadOnlyDictionary<HTNCompoundTask, List<HTNTask>[]> CompoundBranches => _compoundBranches;
|
||||
private Dictionary<HTNCompoundTask, List<HTNTask>[]> _compoundBranches = new();
|
||||
|
||||
// Hierarchical Task Network
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
_sawmill = Logger.GetSawmill("npc.htn");
|
||||
SubscribeLocalEvent<HTNComponent, ComponentShutdown>(OnHTNShutdown);
|
||||
SubscribeNetworkEvent<RequestHTNMessage>(OnHTNMessage);
|
||||
|
||||
@@ -69,7 +62,9 @@ public sealed class HTNSystem : EntitySystem
|
||||
private void OnLoad()
|
||||
{
|
||||
// Clear all NPCs in case they're hanging onto stale tasks
|
||||
foreach (var comp in EntityQuery<HTNComponent>(true))
|
||||
var query = AllEntityQuery<HTNComponent>();
|
||||
|
||||
while (query.MoveNext(out var comp))
|
||||
{
|
||||
comp.PlanningToken?.Cancel();
|
||||
comp.PlanningToken = null;
|
||||
@@ -77,73 +72,64 @@ public sealed class HTNSystem : EntitySystem
|
||||
if (comp.Plan != null)
|
||||
{
|
||||
var currentOperator = comp.Plan.CurrentOperator;
|
||||
currentOperator.Shutdown(comp.Blackboard, HTNOperatorStatus.Failed);
|
||||
ShutdownTask(currentOperator, comp.Blackboard, HTNOperatorStatus.Failed);
|
||||
ShutdownPlan(comp);
|
||||
comp.Plan = null;
|
||||
RequestPlan(comp);
|
||||
}
|
||||
}
|
||||
|
||||
_compoundBranches.Clear();
|
||||
|
||||
// Add dependencies for all operators.
|
||||
// We put code on operators as I couldn't think of a clean way to put it on systems.
|
||||
foreach (var compound in _prototypeManager.EnumeratePrototypes<HTNCompoundTask>())
|
||||
foreach (var compound in _prototypeManager.EnumeratePrototypes<HTNCompoundPrototype>())
|
||||
{
|
||||
UpdateCompound(compound);
|
||||
}
|
||||
|
||||
foreach (var primitive in _prototypeManager.EnumeratePrototypes<HTNPrimitiveTask>())
|
||||
{
|
||||
UpdatePrimitive(primitive);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnPrototypeLoad(PrototypesReloadedEventArgs obj)
|
||||
{
|
||||
if (!obj.ByType.ContainsKey(typeof(HTNCompoundPrototype)))
|
||||
return;
|
||||
|
||||
OnLoad();
|
||||
}
|
||||
|
||||
private void UpdatePrimitive(HTNPrimitiveTask primitive)
|
||||
private void UpdateCompound(HTNCompoundPrototype compound)
|
||||
{
|
||||
foreach (var precon in primitive.Preconditions)
|
||||
{
|
||||
precon.Initialize(EntityManager.EntitySysManager);
|
||||
}
|
||||
|
||||
primitive.Operator.Initialize(EntityManager.EntitySysManager);
|
||||
}
|
||||
|
||||
private void UpdateCompound(HTNCompoundTask compound)
|
||||
{
|
||||
var branchies = new List<HTNTask>[compound.Branches.Count];
|
||||
_compoundBranches.Add(compound, branchies);
|
||||
|
||||
for (var i = 0; i < compound.Branches.Count; i++)
|
||||
{
|
||||
var branch = compound.Branches[i];
|
||||
var brancho = new List<HTNTask>(branch.TaskPrototypes.Count);
|
||||
branchies[i] = brancho;
|
||||
|
||||
// Didn't do this in a typeserializer because we can't recursively grab our own prototype during it, woohoo!
|
||||
foreach (var proto in branch.TaskPrototypes)
|
||||
{
|
||||
if (_prototypeManager.TryIndex<HTNCompoundTask>(proto, out var compTask))
|
||||
{
|
||||
brancho.Add(compTask);
|
||||
}
|
||||
else if (_prototypeManager.TryIndex<HTNPrimitiveTask>(proto, out var primTask))
|
||||
{
|
||||
brancho.Add(primTask);
|
||||
}
|
||||
else
|
||||
{
|
||||
_sawmill.Error($"Unable to find HTNTask for {proto} on {compound.ID}");
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var precon in branch.Preconditions)
|
||||
{
|
||||
precon.Initialize(EntityManager.EntitySysManager);
|
||||
}
|
||||
|
||||
foreach (var task in branch.Tasks)
|
||||
{
|
||||
UpdateTask(task);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateTask(HTNTask task)
|
||||
{
|
||||
switch (task)
|
||||
{
|
||||
case HTNCompoundTask:
|
||||
// NOOP, handled elsewhere
|
||||
break;
|
||||
case HTNPrimitiveTask primitive:
|
||||
foreach (var precon in primitive.Preconditions)
|
||||
{
|
||||
precon.Initialize(EntityManager.EntitySysManager);
|
||||
}
|
||||
|
||||
primitive.Operator.Initialize(EntityManager.EntitySysManager);
|
||||
break;
|
||||
default:
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -177,7 +163,7 @@ public sealed class HTNSystem : EntitySystem
|
||||
{
|
||||
if (comp.PlanningJob.Exception != null)
|
||||
{
|
||||
_sawmill.Fatal($"Received exception on planning job for {uid}!");
|
||||
Log.Fatal($"Received exception on planning job for {uid}!");
|
||||
_npc.SleepNPC(uid);
|
||||
var exc = comp.PlanningJob.Exception;
|
||||
RemComp<HTNComponent>(uid);
|
||||
@@ -209,7 +195,13 @@ public sealed class HTNSystem : EntitySystem
|
||||
if (comp.Plan == null || newPlanBetter)
|
||||
{
|
||||
comp.CheckServices = false;
|
||||
comp.Plan?.CurrentTask.Operator.Shutdown(comp.Blackboard, HTNOperatorStatus.BetterPlan);
|
||||
|
||||
if (comp.Plan != null)
|
||||
{
|
||||
ShutdownTask(comp.Plan.CurrentOperator, comp.Blackboard, HTNOperatorStatus.BetterPlan);
|
||||
ShutdownPlan(comp);
|
||||
}
|
||||
|
||||
comp.Plan = comp.PlanningJob.Result;
|
||||
|
||||
// Startup the first task and anything else we need to do.
|
||||
@@ -227,7 +219,7 @@ public sealed class HTNSystem : EntitySystem
|
||||
{
|
||||
text.AppendLine($"BTR: {string.Join(", ", comp.Plan.BranchTraversalRecord)}");
|
||||
text.AppendLine($"tasks:");
|
||||
var root = _prototypeManager.Index<HTNCompoundTask>(comp.RootTask);
|
||||
var root = comp.RootTask;
|
||||
var btr = new List<int>();
|
||||
var level = -1;
|
||||
AppendDebugText(root, text, comp.Plan.BranchTraversalRecord, btr, ref level);
|
||||
@@ -267,23 +259,24 @@ public sealed class HTNSystem : EntitySystem
|
||||
|
||||
if (task is HTNPrimitiveTask primitive)
|
||||
{
|
||||
text.AppendLine(primitive.ID);
|
||||
text.AppendLine(primitive.ToString());
|
||||
return;
|
||||
}
|
||||
|
||||
if (task is HTNCompoundTask compound)
|
||||
if (task is HTNCompoundTask compTask)
|
||||
{
|
||||
var compound = _prototypeManager.Index<HTNCompoundPrototype>(compTask.Task);
|
||||
level++;
|
||||
text.AppendLine(compound.ID);
|
||||
var branches = _compoundBranches[compound];
|
||||
var branches = compound.Branches;
|
||||
|
||||
for (var i = 0; i < branches.Length; i++)
|
||||
for (var i = 0; i < branches.Count; i++)
|
||||
{
|
||||
var branch = branches[i];
|
||||
btr.Add(i);
|
||||
text.AppendLine($" branch {string.Join(", ", btr)}:");
|
||||
|
||||
foreach (var sub in branch)
|
||||
foreach (var sub in branch.Tasks)
|
||||
{
|
||||
AppendDebugText(sub, text, planBtr, btr, ref level);
|
||||
}
|
||||
@@ -344,21 +337,22 @@ public sealed class HTNSystem : EntitySystem
|
||||
case HTNOperatorStatus.Continuing:
|
||||
break;
|
||||
case HTNOperatorStatus.Failed:
|
||||
currentOperator.Shutdown(blackboard, status);
|
||||
component.Plan = null;
|
||||
ShutdownTask(currentOperator, blackboard, status);
|
||||
ShutdownPlan(component);
|
||||
break;
|
||||
// Operator completed so go to the next one.
|
||||
case HTNOperatorStatus.Finished:
|
||||
currentOperator.Shutdown(blackboard, status);
|
||||
ShutdownTask(currentOperator, blackboard, status);
|
||||
component.Plan.Index++;
|
||||
|
||||
// Plan finished!
|
||||
if (component.Plan.Tasks.Count <= component.Plan.Index)
|
||||
{
|
||||
component.Plan = null;
|
||||
ShutdownPlan(component);
|
||||
break;
|
||||
}
|
||||
|
||||
ConditionalShutdown(component.Plan, currentOperator, blackboard, HTNPlanState.TaskFinished);
|
||||
StartupTask(component.Plan.Tasks[component.Plan.Index], component.Blackboard, component.Plan.Effects[component.Plan.Index]);
|
||||
break;
|
||||
default:
|
||||
@@ -367,6 +361,50 @@ public sealed class HTNSystem : EntitySystem
|
||||
}
|
||||
}
|
||||
|
||||
public void ShutdownTask(HTNOperator currentOperator, NPCBlackboard blackboard, HTNOperatorStatus status)
|
||||
{
|
||||
if (currentOperator is IHtnConditionalShutdown conditional &&
|
||||
(conditional.ShutdownState & HTNPlanState.TaskFinished) != 0x0)
|
||||
{
|
||||
conditional.ConditionalShutdown(blackboard);
|
||||
}
|
||||
|
||||
currentOperator.TaskShutdown(blackboard, status);
|
||||
}
|
||||
|
||||
public void ShutdownPlan(HTNComponent component)
|
||||
{
|
||||
DebugTools.Assert(component.Plan != null);
|
||||
var blackboard = component.Blackboard;
|
||||
|
||||
foreach (var task in component.Plan.Tasks)
|
||||
{
|
||||
if (task.Operator is IHtnConditionalShutdown conditional &&
|
||||
(conditional.ShutdownState & HTNPlanState.PlanFinished) != 0x0)
|
||||
{
|
||||
conditional.ConditionalShutdown(blackboard);
|
||||
}
|
||||
|
||||
task.Operator.PlanShutdown(component.Blackboard);
|
||||
}
|
||||
|
||||
component.Plan = null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Shuts down the current operator conditionally.
|
||||
/// </summary>
|
||||
private void ConditionalShutdown(HTNPlan plan, HTNOperator currentOperator, NPCBlackboard blackboard, HTNPlanState state)
|
||||
{
|
||||
if (currentOperator is not IHtnConditionalShutdown conditional)
|
||||
return;
|
||||
|
||||
if ((conditional.ShutdownState & state) == 0x0)
|
||||
return;
|
||||
|
||||
conditional.ConditionalShutdown(blackboard);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Starts a new primitive task. Will apply effects from planning if applicable.
|
||||
/// </summary>
|
||||
@@ -400,8 +438,8 @@ public sealed class HTNSystem : EntitySystem
|
||||
|
||||
var job = new HTNPlanJob(
|
||||
0.02,
|
||||
this,
|
||||
_prototypeManager.Index<HTNCompoundTask>(component.RootTask),
|
||||
_prototypeManager,
|
||||
component.RootTask,
|
||||
component.Blackboard.ShallowClone(), branchTraversal, cancelToken.Token);
|
||||
|
||||
_planQueue.EnqueueJob(job);
|
||||
@@ -425,13 +463,13 @@ public sealed class HTNSystem : EntitySystem
|
||||
|
||||
if (task is HTNPrimitiveTask primitive)
|
||||
{
|
||||
builder.AppendLine(buffer + $"Primitive: {task.ID}");
|
||||
builder.AppendLine(buffer + $"Primitive: {task}");
|
||||
builder.AppendLine(buffer + $" operator: {primitive.Operator.GetType().Name}");
|
||||
}
|
||||
else if (task is HTNCompoundTask compound)
|
||||
else if (task is HTNCompoundTask compTask)
|
||||
{
|
||||
builder.AppendLine(buffer + $"Compound: {task.ID}");
|
||||
var compoundBranches = CompoundBranches[compound];
|
||||
var compound = _prototypeManager.Index<HTNCompoundPrototype>(compTask.Task);
|
||||
builder.AppendLine(buffer + $"Compound: {task}");
|
||||
|
||||
for (var i = 0; i < compound.Branches.Count; i++)
|
||||
{
|
||||
@@ -439,9 +477,8 @@ public sealed class HTNSystem : EntitySystem
|
||||
|
||||
builder.AppendLine(buffer + " branch:");
|
||||
indent++;
|
||||
var branchTasks = compoundBranches[i];
|
||||
|
||||
foreach (var branchTask in branchTasks)
|
||||
foreach (var branchTask in branch.Tasks)
|
||||
{
|
||||
AppendDomain(builder, branchTask, ref indent);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user