using Content.Server.NPC.Components; using Content.Server.NPC.HTN; using Content.Shared.CCVar; using Content.Shared.Mobs; using Content.Shared.Mobs.Systems; using JetBrains.Annotations; using Robust.Server.GameObjects; using Robust.Shared.Configuration; namespace Content.Server.NPC.Systems { /// /// Handles NPCs running every tick. /// public sealed partial class NPCSystem : EntitySystem { [Dependency] private readonly IConfigurationManager _configurationManager = default!; [Dependency] private readonly HTNSystem _htn = default!; [Dependency] private readonly MobStateSystem _mobState = default!; /// /// Whether any NPCs are allowed to run at all. /// public bool Enabled { get; set; } = true; private int _maxUpdates; private int _count; /// public override void Initialize() { base.Initialize(); _configurationManager.OnValueChanged(CCVars.NPCEnabled, SetEnabled, true); _configurationManager.OnValueChanged(CCVars.NPCMaxUpdates, SetMaxUpdates, true); } public void OnPlayerNPCAttach(EntityUid uid, NPCComponent component, PlayerAttachedEvent args) { SleepNPC(uid, component); } public void OnPlayerNPCDetach(EntityUid uid, NPCComponent component, PlayerDetachedEvent args) { if (_mobState.IsIncapacitated(uid) || TerminatingOrDeleted(uid)) return; WakeNPC(uid, component); } private void SetMaxUpdates(int obj) => _maxUpdates = obj; private void SetEnabled(bool value) => Enabled = value; public override void Shutdown() { base.Shutdown(); _configurationManager.UnsubValueChanged(CCVars.NPCEnabled, SetEnabled); _configurationManager.UnsubValueChanged(CCVars.NPCMaxUpdates, SetMaxUpdates); } public void OnNPCMapInit(EntityUid uid, NPCComponent component, MapInitEvent args) { component.Blackboard.SetValue(NPCBlackboard.Owner, uid); WakeNPC(uid, component); } public void OnNPCShutdown(EntityUid uid, NPCComponent component, ComponentShutdown args) { SleepNPC(uid, component); } /// /// Is the NPC awake and updating? /// public bool IsAwake(EntityUid uid, NPCComponent component, ActiveNPCComponent? active = null) { return Resolve(uid, ref active, false); } /// /// Allows the NPC to actively be updated. /// public void WakeNPC(EntityUid uid, NPCComponent? component = null) { if (!Resolve(uid, ref component, false)) { return; } Log.Debug($"Waking {ToPrettyString(uid)}"); EnsureComp(uid); } public void SleepNPC(EntityUid uid, NPCComponent? component = null) { if (!Resolve(uid, ref component, false)) { return; } // Don't bother with an event if (TryComp(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(uid); } /// 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, NPCComponent component, MobStateChangedEvent args) { if (HasComp(uid)) return; switch (args.NewMobState) { case MobState.Alive: WakeNPC(uid, component); break; case MobState.Critical: case MobState.Dead: SleepNPC(uid, component); break; } } } }