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. /// [UsedImplicitly] public sealed partial class NPCSystem : EntitySystem { [Dependency] private readonly IConfigurationManager _configurationManager = default!; [Dependency] private readonly HTNSystem _htn = default!; [Dependency] private readonly MobStateSystem _mobState = default!; private ISawmill _sawmill = 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(); _sawmill = Logger.GetSawmill("npc"); _sawmill.Level = LogLevel.Info; SubscribeLocalEvent(OnMobStateChange); SubscribeLocalEvent(OnNPCMapInit); SubscribeLocalEvent(OnNPCShutdown); SubscribeLocalEvent(OnPlayerNPCAttach); SubscribeLocalEvent(OnPlayerNPCDetach); _configurationManager.OnValueChanged(CCVars.NPCEnabled, SetEnabled, true); _configurationManager.OnValueChanged(CCVars.NPCMaxUpdates, SetMaxUpdates, true); } private void OnPlayerNPCAttach(EntityUid uid, NPCComponent component, PlayerAttachedEvent args) { SleepNPC(uid, component); } private void OnPlayerNPCDetach(EntityUid uid, NPCComponent component, PlayerDetachedEvent args) { if (_mobState.IsIncapacitated(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); } private void OnNPCMapInit(EntityUid uid, NPCComponent component, MapInitEvent args) { component.Blackboard.SetValue(NPCBlackboard.Owner, uid); WakeNPC(uid, component); } private void OnNPCShutdown(EntityUid uid, NPCComponent component, ComponentShutdown args) { SleepNPC(uid, component); } /// /// Is the NPC awake and updating? /// public bool IsAwake(NPCComponent component, ActiveNPCComponent? active = null) { return Resolve(component.Owner, 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; } _sawmill.Debug($"Waking {ToPrettyString(component.Owner)}"); EnsureComp(component.Owner); } public void SleepNPC(EntityUid uid, NPCComponent? component = null) { if (!Resolve(uid, ref component, false)) { return; } _sawmill.Debug($"Sleeping {ToPrettyString(component.Owner)}"); RemComp(component.Owner); } /// public override void Update(float frameTime) { base.Update(frameTime); if (!Enabled) return; _count = 0; // Add your system here. _htn.UpdateNPC(ref _count, _maxUpdates, frameTime); } private 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; } } } }