using Content.Server.AI.Components; using Content.Shared.CCVar; using Content.Shared.MobState; using JetBrains.Annotations; using Robust.Shared.Configuration; using Robust.Shared.Prototypes; using Robust.Shared.Reflection; namespace Content.Server.AI.EntitySystems { /// /// Handles NPCs running every tick. /// [UsedImplicitly] public sealed partial class NPCSystem : EntitySystem { [Dependency] private readonly IConfigurationManager _configurationManager = default!; [Dependency] private readonly IDynamicTypeFactory _typeFactory = default!; [Dependency] private readonly IPrototypeManager _prototypeManager = default!; [Dependency] private readonly IReflectionManager _reflectionManager = 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(); // Makes physics etc debugging easier. #if DEBUG _configurationManager.OverrideDefault(CCVars.NPCEnabled, false); #endif _sawmill = Logger.GetSawmill("npc"); _sawmill.Level = LogLevel.Info; InitializeUtility(); SubscribeLocalEvent(OnMobStateChange); SubscribeLocalEvent(OnNPCInit); SubscribeLocalEvent(OnNPCShutdown); _configurationManager.OnValueChanged(CCVars.NPCEnabled, SetEnabled, true); _configurationManager.OnValueChanged(CCVars.NPCMaxUpdates, SetMaxUpdates, true); } 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 OnNPCInit(EntityUid uid, NPCComponent component, ComponentInit args) { WakeNPC(component); } private void OnNPCShutdown(EntityUid uid, NPCComponent component, ComponentShutdown args) { SleepNPC(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(NPCComponent component) { _sawmill.Debug($"Waking {ToPrettyString(component.Owner)}"); EnsureComp(component.Owner); } /// /// Stops the NPC from actively being updated. /// public void SleepNPC(NPCComponent component) { _sawmill.Debug($"Sleeping {ToPrettyString(component.Owner)}"); RemComp(component.Owner); } /// public override void Update(float frameTime) { base.Update(frameTime); if (!Enabled) return; _count = 0; UpdateUtility(frameTime); } private void OnMobStateChange(EntityUid uid, NPCComponent component, MobStateChangedEvent args) { switch (args.CurrentMobState) { case DamageState.Alive: WakeNPC(component); break; case DamageState.Critical: case DamageState.Dead: SleepNPC(component); break; } } } }