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:
metalgearsloth
2023-08-02 10:48:56 +10:00
committed by GitHub
parent 018e465fad
commit c31c848afd
103 changed files with 2089 additions and 810 deletions

View File

@@ -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);
}