using Content.Shared.Administration.Logs; using Content.Shared.Database; using Content.Shared.DeviceLinking; using Content.Shared.EntityTable; using Content.Shared.Item.ItemToggle; using Content.Shared.Mind; using Content.Shared.Popups; using Content.Shared.Roles; using Content.Shared.Timing; using Content.Shared.Trigger.Components; using Content.Shared.Whitelist; using Robust.Shared.Network; using Robust.Shared.Physics.Systems; using Robust.Shared.Timing; using Robust.Shared.Random; using Robust.Shared.Audio.Systems; namespace Content.Shared.Trigger.Systems; /// /// System containing the basic trigger API. /// /// /// If you add a custom trigger subscription or effect then don't put them here. /// Put them into a separate system so we don't end up with a giant list of imports. /// public sealed partial class TriggerSystem : EntitySystem { [Dependency] private readonly IGameTiming _timing = default!; [Dependency] private readonly SharedTransformSystem _transform = default!; [Dependency] private readonly SharedPopupSystem _popup = default!; [Dependency] private readonly ISharedAdminLogManager _adminLogger = default!; [Dependency] private readonly SharedPhysicsSystem _physics = default!; [Dependency] private readonly SharedAppearanceSystem _appearance = default!; [Dependency] private readonly FixtureSystem _fixture = default!; [Dependency] private readonly INetManager _net = default!; [Dependency] private readonly IRobustRandom _random = default!; [Dependency] private readonly SharedAudioSystem _audio = default!; [Dependency] private readonly UseDelaySystem _useDelay = default!; [Dependency] private readonly EntityWhitelistSystem _whitelist = default!; [Dependency] private readonly ItemToggleSystem _itemToggle = default!; [Dependency] private readonly SharedDeviceLinkSystem _deviceLink = default!; [Dependency] private readonly SharedRoleSystem _role = default!; [Dependency] private readonly SharedMindSystem _mind = default!; [Dependency] private readonly EntityTableSystem _entityTable = default!; public const string DefaultTriggerKey = "trigger"; public override void Initialize() { base.Initialize(); InitializeCollide(); InitializeCondition(); InitializeInteraction(); InitializeProximity(); InitializeSignal(); InitializeTimer(); InitializeSpawn(); InitializeVoice(); } /// /// Trigger the given entity. /// /// The entity that has the components that should be triggered. /// The user of the trigger. Some effects may target the user instead of the trigger entity. /// A key string to allow multiple, independent triggers on the same entity. If null then all triggers will activate. /// Whether or not the trigger has sucessfully activated an effect. public bool Trigger(EntityUid trigger, EntityUid? user = null, string? key = null) { var attemptTriggerEvent = new AttemptTriggerEvent(user, key); RaiseLocalEvent(trigger, ref attemptTriggerEvent); if (attemptTriggerEvent.Cancelled) return false; var triggerEvent = new TriggerEvent(user, key); RaiseLocalEvent(trigger, ref triggerEvent, true); return triggerEvent.Handled; } /// /// Activate a timer trigger on an entity with . /// /// Whether or not a timer was activated. public bool ActivateTimerTrigger(Entity ent, EntityUid? user = null) { if (!Resolve(ent, ref ent.Comp)) return false; if (HasComp(ent)) return false; // already activated if (user != null) { _adminLogger.Add(LogType.Trigger, $"{ToPrettyString(user.Value):user} started a {ent.Comp.Delay} second timer trigger on entity {ToPrettyString(ent.Owner):timer}"); } else { _adminLogger.Add(LogType.Trigger, $"{ent.Comp.Delay} second timer trigger started on entity {ToPrettyString(ent.Owner):timer}"); } if (ent.Comp.Popup != null) _popup.PopupPredicted(Loc.GetString(ent.Comp.Popup.Value, ("device", ent.Owner)), ent.Owner, user); AddComp(ent); var curTime = _timing.CurTime; ent.Comp.NextTrigger = curTime + ent.Comp.Delay; var delay = ent.Comp.InitialBeepDelay ?? ent.Comp.BeepInterval; ent.Comp.NextBeep = curTime + delay; ent.Comp.User = user; Dirty(ent); var ev = new ActiveTimerTriggerEvent(user); RaiseLocalEvent(ent.Owner, ref ev); if (TryComp(ent, out var appearance)) _appearance.SetData(ent.Owner, TriggerVisuals.VisualState, TriggerVisualState.Primed, appearance); return true; } /// /// Stop a timer trigger on an entity with . /// /// Whether or not a timer was stopped. public bool StopTimerTrigger(Entity ent) { if (!Resolve(ent, ref ent.Comp)) return false; if (!HasComp(ent)) return false; // the timer is not active RemComp(ent); if (TryComp(ent.Owner, out var appearance)) _appearance.SetData(ent.Owner, TriggerVisuals.VisualState, TriggerVisualState.Unprimed, appearance); _adminLogger.Add(LogType.Trigger, $"A timer trigger was stopped before triggering on entity {ToPrettyString(ent.Owner):timer}"); return true; } /// /// Delay an active timer trigger. /// Returns false if not active. /// /// The time to add. public bool TryDelay(Entity ent, TimeSpan amount) { if (!Resolve(ent, ref ent.Comp, false) || !HasComp(ent)) return false; ent.Comp.NextTrigger += amount; Dirty(ent); return true; } /// /// Setter for the Delay datafield. /// public void SetDelay(Entity ent, TimeSpan delay) { if (!Resolve(ent, ref ent.Comp)) return; ent.Comp.Delay = delay; Dirty(ent); } /// /// Gets the remaining time until the trigger will activate. /// Returns null if the trigger is not currently active. /// public TimeSpan? GetRemainingTime(Entity ent) { if (!Resolve(ent, ref ent.Comp, false) || !HasComp(ent)) return null; // not a timer or not currently active return ent.Comp.NextTrigger - _timing.CurTime; } public override void Update(float frameTime) { base.Update(frameTime); UpdateTimer(); UpdateRepeat(); UpdateProximity(); UpdateTimedCollide(); } }