* Use new Subs.CVar helper Removes manual config OnValueChanged calls, removes need to remember to manually unsubscribe. This both reduces boilerplate and fixes many issues where subscriptions weren't removed on entity system shutdown. * Fix a bunch of warnings * More warning fixes * Use new DateTime serializer to get rid of ISerializationHooks in changelog code. * Get rid of some more ISerializationHooks for enums * And a little more * Apply suggestions from code review Co-authored-by: 0x6273 <0x40@keemail.me> --------- Co-authored-by: 0x6273 <0x40@keemail.me>
155 lines
4.6 KiB
C#
155 lines
4.6 KiB
C#
using System.Diagnostics.CodeAnalysis;
|
|
using Content.Server.NPC.Components;
|
|
using Content.Server.NPC.HTN;
|
|
using Content.Shared.CCVar;
|
|
using Content.Shared.Mobs;
|
|
using Content.Shared.Mobs.Systems;
|
|
using Content.Shared.NPC;
|
|
using Robust.Server.GameObjects;
|
|
using Robust.Shared.Configuration;
|
|
using Robust.Shared.Player;
|
|
|
|
namespace Content.Server.NPC.Systems
|
|
{
|
|
/// <summary>
|
|
/// Handles NPCs running every tick.
|
|
/// </summary>
|
|
public sealed partial class NPCSystem : EntitySystem
|
|
{
|
|
[Dependency] private readonly IConfigurationManager _configurationManager = default!;
|
|
[Dependency] private readonly HTNSystem _htn = default!;
|
|
[Dependency] private readonly MobStateSystem _mobState = default!;
|
|
|
|
/// <summary>
|
|
/// Whether any NPCs are allowed to run at all.
|
|
/// </summary>
|
|
public bool Enabled { get; set; } = true;
|
|
|
|
private int _maxUpdates;
|
|
|
|
private int _count;
|
|
|
|
/// <inheritdoc />
|
|
public override void Initialize()
|
|
{
|
|
base.Initialize();
|
|
|
|
Subs.CVar(_configurationManager, CCVars.NPCEnabled, value => Enabled = value, true);
|
|
Subs.CVar(_configurationManager, CCVars.NPCMaxUpdates, obj => _maxUpdates = obj, true);
|
|
}
|
|
|
|
public void OnPlayerNPCAttach(EntityUid uid, HTNComponent component, PlayerAttachedEvent args)
|
|
{
|
|
SleepNPC(uid, component);
|
|
}
|
|
|
|
public void OnPlayerNPCDetach(EntityUid uid, HTNComponent component, PlayerDetachedEvent args)
|
|
{
|
|
if (_mobState.IsIncapacitated(uid) || TerminatingOrDeleted(uid))
|
|
return;
|
|
|
|
WakeNPC(uid, component);
|
|
}
|
|
|
|
public void OnNPCMapInit(EntityUid uid, HTNComponent component, MapInitEvent args)
|
|
{
|
|
component.Blackboard.SetValue(NPCBlackboard.Owner, uid);
|
|
WakeNPC(uid, component);
|
|
}
|
|
|
|
public void OnNPCShutdown(EntityUid uid, HTNComponent component, ComponentShutdown args)
|
|
{
|
|
SleepNPC(uid, component);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Is the NPC awake and updating?
|
|
/// </summary>
|
|
public bool IsAwake(EntityUid uid, HTNComponent component, ActiveNPCComponent? active = null)
|
|
{
|
|
return Resolve(uid, ref active, false);
|
|
}
|
|
|
|
public bool TryGetNpc(EntityUid uid, [NotNullWhen(true)] out NPCComponent? component)
|
|
{
|
|
// If you add your own NPC components then add them here.
|
|
|
|
if (TryComp<HTNComponent>(uid, out var htn))
|
|
{
|
|
component = htn;
|
|
return true;
|
|
}
|
|
|
|
component = null;
|
|
return false;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Allows the NPC to actively be updated.
|
|
/// </summary>
|
|
public void WakeNPC(EntityUid uid, HTNComponent? component = null)
|
|
{
|
|
if (!Resolve(uid, ref component, false))
|
|
{
|
|
return;
|
|
}
|
|
|
|
Log.Debug($"Waking {ToPrettyString(uid)}");
|
|
EnsureComp<ActiveNPCComponent>(uid);
|
|
}
|
|
|
|
public void SleepNPC(EntityUid uid, HTNComponent? component = null)
|
|
{
|
|
if (!Resolve(uid, ref component, false))
|
|
{
|
|
return;
|
|
}
|
|
|
|
// Don't bother with an event
|
|
if (TryComp<HTNComponent>(uid, out var htn))
|
|
{
|
|
if (htn.Plan != null)
|
|
{
|
|
var currentOperator = htn.Plan.CurrentOperator;
|
|
_htn.ShutdownTask(currentOperator, htn.Blackboard, HTNOperatorStatus.Failed);
|
|
_htn.ShutdownPlan(htn);
|
|
htn.Plan = null;
|
|
}
|
|
}
|
|
|
|
Log.Debug($"Sleeping {ToPrettyString(uid)}");
|
|
RemComp<ActiveNPCComponent>(uid);
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public override void Update(float frameTime)
|
|
{
|
|
base.Update(frameTime);
|
|
|
|
if (!Enabled)
|
|
return;
|
|
|
|
_count = 0;
|
|
// Add your system here.
|
|
_htn.UpdateNPC(ref _count, _maxUpdates, frameTime);
|
|
}
|
|
|
|
public void OnMobStateChange(EntityUid uid, HTNComponent component, MobStateChangedEvent args)
|
|
{
|
|
if (HasComp<ActorComponent>(uid))
|
|
return;
|
|
|
|
switch (args.NewMobState)
|
|
{
|
|
case MobState.Alive:
|
|
WakeNPC(uid, component);
|
|
break;
|
|
case MobState.Critical:
|
|
case MobState.Dead:
|
|
SleepNPC(uid, component);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|