using Robust.Shared.Timing; using Content.Server.Administration.Systems; using Content.Shared.Alert; using Content.Shared.CombatMode.Pacification; using Content.Shared.Humanoid; using Content.Shared.Mind; using Content.Shared.Roles.Jobs; using Robust.Shared.Prototypes; namespace Content.Server._NF.PacifiedZone; public sealed class PacifiedZoneGeneratorSystem : EntitySystem { [Dependency] private readonly EntityLookupSystem _lookup = default!; [Dependency] private readonly IGameTiming _gameTiming = default!; [Dependency] private readonly SharedMindSystem _mindSystem = default!; [Dependency] private readonly SharedJobSystem _jobSystem = default!; [Dependency] private readonly AlertsSystem _alerts = default!; [Dependency] private readonly AdminSystem _admin = default!; private static readonly ProtoId AlertProto = "PacifiedZone"; public override void Initialize() { base.Initialize(); SubscribeLocalEvent(OnComponentInit); SubscribeLocalEvent(OnComponentShutdown); } private void OnComponentInit(EntityUid uid, PacifiedZoneGeneratorComponent component, ComponentInit args) { UpdatePacifiedState(uid, component); } private void OnComponentShutdown(EntityUid uid, PacifiedZoneGeneratorComponent component, ComponentShutdown args) { foreach (var entity in component.TrackedEntities) { RemComp(entity); RemComp(entity); DisableAlert(entity); } } public override void Update(float frameTime) { base.Update(frameTime); var genQuery = AllEntityQuery(); while (genQuery.MoveNext(out var genUid, out var component)) { // Not yet update time, skip this if (_gameTiming.CurTime < component.NextUpdate) continue; UpdatePacifiedState(genUid, component); } } private void UpdatePacifiedState(EntityUid genUid, PacifiedZoneGeneratorComponent component) { List newEntities = new List(); var query = _lookup.GetEntitiesInRange(Transform(genUid).Coordinates, component.Radius); foreach (var humanoidUid in query) { // Check preconditions for an entity to be pacified at all. // If player matches an immune role, or has playtime above a zone's threshold, it should not be pacified. if (!_mindSystem.TryGetMind(humanoidUid, out var mindId, out var mind)) continue; _jobSystem.MindTryGetJobId(mindId, out var jobId); if (jobId != null && component.ImmuneRoles.Contains(jobId.Value)) continue; if (component.ImmunePlaytime != null) { var playerInfo = _admin.GetCachedPlayerInfo(mind?.UserId); if (playerInfo != null && playerInfo.OverallPlaytime >= component.ImmunePlaytime) { continue; } } // Existing entity, note it still exists. if (component.TrackedEntities.Contains(humanoidUid)) { // Entity still in zone. newEntities.Add(humanoidUid); component.TrackedEntities.Remove(humanoidUid); } else { // Player is pacified (either naturally or by another zone), skip them. if (HasComp(humanoidUid)) continue; // New entity in zone, needs the Pacified comp. var pacifiedComponent = AddComp(humanoidUid); EnableAlert(humanoidUid, pacifiedComponent); AddComp(humanoidUid); newEntities.Add(humanoidUid); } } // Anything left in our old set has left the zone, remove their pacified status. foreach (var humanoid_net_uid in component.TrackedEntities) { RemComp(humanoid_net_uid); RemComp(humanoid_net_uid); DisableAlert(humanoid_net_uid); } // Update state for next run. component.TrackedEntities = newEntities; component.NextUpdate = _gameTiming.CurTime + component.UpdateInterval; } // Overrides the default Pacified alert with one for the pacified zone. private void EnableAlert(EntityUid entity, PacifiedComponent pacified) { _alerts.ClearAlert(entity, pacified.PacifiedAlert); _alerts.ShowAlert(entity, AlertProto); } // Hides our pacified zone alert. private void DisableAlert(EntityUid entity) { _alerts.ClearAlert(entity, AlertProto); } }