Tie NPC services to plans (#16451)
This commit is contained in:
parent
cbd5db3ca5
commit
66ae137b70
|
|
@ -14,19 +14,18 @@ public sealed class HTNComponent : NPCComponent
|
|||
DataField("rootTask", required: true, customTypeSerializer: typeof(PrototypeIdSerializer<HTNCompoundTask>))]
|
||||
public string RootTask = default!;
|
||||
|
||||
/// <summary>
|
||||
/// Check any active services for our current plan. This is used to find new targets for example without changing our plan.
|
||||
/// </summary>
|
||||
[DataField("checkServices")]
|
||||
public bool CheckServices = true;
|
||||
|
||||
/// <summary>
|
||||
/// The NPC's current plan.
|
||||
/// </summary>
|
||||
[ViewVariables]
|
||||
public HTNPlan? Plan;
|
||||
|
||||
// TODO: Need dictionary timeoffsetserializer.
|
||||
/// <summary>
|
||||
/// Last time we tried a particular <see cref="UtilityService"/>.
|
||||
/// </summary>
|
||||
[DataField("serviceCooldowns")]
|
||||
public Dictionary<string, TimeSpan> ServiceCooldowns = new();
|
||||
|
||||
/// <summary>
|
||||
/// How long to wait after having planned to try planning again.
|
||||
/// </summary>
|
||||
|
|
|
|||
|
|
@ -21,9 +21,7 @@ namespace Content.Server.NPC.HTN;
|
|||
public sealed class HTNSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly IAdminManager _admin = default!;
|
||||
[Dependency] private readonly IGameTiming _timing = default!;
|
||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||
[Dependency] private readonly IRobustRandom _random = default!;
|
||||
[Dependency] private readonly NPCSystem _npc = default!;
|
||||
[Dependency] private readonly NPCUtilitySystem _utility = default!;
|
||||
|
||||
|
|
@ -42,22 +40,12 @@ public sealed class HTNSystem : EntitySystem
|
|||
base.Initialize();
|
||||
_sawmill = Logger.GetSawmill("npc.htn");
|
||||
SubscribeLocalEvent<HTNComponent, ComponentShutdown>(OnHTNShutdown);
|
||||
SubscribeLocalEvent<HTNComponent, EntityUnpausedEvent>(OnHTNUnpaused);
|
||||
SubscribeNetworkEvent<RequestHTNMessage>(OnHTNMessage);
|
||||
|
||||
_prototypeManager.PrototypesReloaded += OnPrototypeLoad;
|
||||
OnLoad();
|
||||
}
|
||||
|
||||
private void OnHTNUnpaused(EntityUid uid, HTNComponent component, ref EntityUnpausedEvent args)
|
||||
{
|
||||
foreach (var (service, cooldown) in component.ServiceCooldowns)
|
||||
{
|
||||
var newCooldown = cooldown + args.PausedTime;
|
||||
component.ServiceCooldowns[service] = newCooldown;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnHTNMessage(RequestHTNMessage msg, EntitySessionEventArgs args)
|
||||
{
|
||||
if (!_admin.HasAdminFlag((IPlayerSession) args.SenderSession, AdminFlags.Debug))
|
||||
|
|
@ -220,6 +208,7 @@ public sealed class HTNSystem : EntitySystem
|
|||
|
||||
if (comp.Plan == null || newPlanBetter)
|
||||
{
|
||||
comp.CheckServices = false;
|
||||
comp.Plan?.CurrentTask.Operator.Shutdown(comp.Blackboard, HTNOperatorStatus.BetterPlan);
|
||||
comp.Plan = comp.PlanningJob.Result;
|
||||
|
||||
|
|
@ -251,6 +240,11 @@ public sealed class HTNSystem : EntitySystem
|
|||
}, session.ConnectedClient);
|
||||
}
|
||||
}
|
||||
// Keeping old plan
|
||||
else
|
||||
{
|
||||
comp.CheckServices = true;
|
||||
}
|
||||
|
||||
comp.PlanningJob = null;
|
||||
comp.PlanningToken = null;
|
||||
|
|
@ -331,20 +325,16 @@ public sealed class HTNSystem : EntitySystem
|
|||
var currentTask = component.Plan.CurrentTask;
|
||||
var blackboard = component.Blackboard;
|
||||
|
||||
foreach (var service in currentTask.Services)
|
||||
// Service still on cooldown.
|
||||
if (component.CheckServices)
|
||||
{
|
||||
// Service still on cooldown.
|
||||
if (component.ServiceCooldowns.TryGetValue(service.ID, out var lastService) &&
|
||||
_timing.CurTime < lastService)
|
||||
foreach (var service in currentTask.Services)
|
||||
{
|
||||
continue;
|
||||
var serviceResult = _utility.GetEntities(blackboard, service.Prototype);
|
||||
blackboard.SetValue(service.Key, serviceResult.GetHighest());
|
||||
}
|
||||
|
||||
var serviceResult = _utility.GetEntities(blackboard, service.Prototype);
|
||||
blackboard.SetValue(service.Key, serviceResult.GetHighest());
|
||||
|
||||
var cooldown = TimeSpan.FromSeconds(_random.NextFloat(service.MinCooldown, service.MaxCooldown));
|
||||
component.ServiceCooldowns[service.ID] = _timing.CurTime + cooldown;
|
||||
component.CheckServices = false;
|
||||
}
|
||||
|
||||
status = currentOperator.Update(blackboard, frameTime);
|
||||
|
|
@ -355,7 +345,6 @@ public sealed class HTNSystem : EntitySystem
|
|||
break;
|
||||
case HTNOperatorStatus.Failed:
|
||||
currentOperator.Shutdown(blackboard, status);
|
||||
component.ServiceCooldowns.Clear();
|
||||
component.Plan = null;
|
||||
break;
|
||||
// Operator completed so go to the next one.
|
||||
|
|
@ -366,7 +355,6 @@ public sealed class HTNSystem : EntitySystem
|
|||
// Plan finished!
|
||||
if (component.Plan.Tasks.Count <= component.Plan.Index)
|
||||
{
|
||||
component.ServiceCooldowns.Clear();
|
||||
component.Plan = null;
|
||||
break;
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue