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.Popups;
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 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();
}
}