Trigger Refactor (#39034)
This commit is contained in:
@@ -1,7 +0,0 @@
|
||||
using Content.Shared.Explosion.EntitySystems;
|
||||
|
||||
namespace Content.Client.Explosion;
|
||||
|
||||
public sealed class SmokeOnTriggerSystem : SharedSmokeOnTriggerSystem
|
||||
{
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
using Content.Shared.Explosion;
|
||||
using Content.Shared.Explosion.Components;
|
||||
|
||||
namespace Content.Client.Explosion;
|
||||
|
||||
[RegisterComponent, Access(typeof(TriggerSystem))]
|
||||
public sealed partial class TriggerOnProximityComponent : SharedTriggerOnProximityComponent {}
|
||||
@@ -1,10 +0,0 @@
|
||||
namespace Content.Client.Explosion;
|
||||
|
||||
public sealed partial class TriggerSystem : EntitySystem
|
||||
{
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
InitializeProximity();
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
using Content.Shared.HotPotato;
|
||||
using Robust.Shared.Random;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Timing;
|
||||
|
||||
namespace Content.Client.HotPotato;
|
||||
@@ -10,6 +11,9 @@ public sealed class HotPotatoSystem : SharedHotPotatoSystem
|
||||
[Dependency] private readonly IRobustRandom _random = default!;
|
||||
[Dependency] private readonly SharedTransformSystem _transform = default!;
|
||||
|
||||
private readonly EntProtoId _hotPotatoEffectId = "HotPotatoEffect";
|
||||
|
||||
// TODO: particle system
|
||||
public override void Update(float frameTime)
|
||||
{
|
||||
base.Update(frameTime);
|
||||
@@ -23,7 +27,7 @@ public sealed class HotPotatoSystem : SharedHotPotatoSystem
|
||||
if (_timing.CurTime < comp.TargetTime)
|
||||
continue;
|
||||
comp.TargetTime = _timing.CurTime + TimeSpan.FromSeconds(comp.EffectCooldown);
|
||||
Spawn("HotPotatoEffect", _transform.GetMapCoordinates(uid).Offset(_random.NextVector2(0.25f)));
|
||||
Spawn(_hotPotatoEffectId, _transform.GetMapCoordinates(uid).Offset(_random.NextVector2(0.25f)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
using Content.Client.Trigger.Systems;
|
||||
using Robust.Client.Animations;
|
||||
using Robust.Shared.Audio;
|
||||
|
||||
namespace Content.Client.Trigger;
|
||||
namespace Content.Client.Trigger.Components;
|
||||
|
||||
[RegisterComponent]
|
||||
[Access(typeof(TimerTriggerVisualizerSystem))]
|
||||
@@ -16,28 +17,27 @@ public sealed partial class TimerTriggerVisualsComponent : Component
|
||||
/// <summary>
|
||||
/// The RSI state used while the device has not been primed.
|
||||
/// </summary>
|
||||
[DataField("unprimedSprite")]
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
[DataField]
|
||||
public string UnprimedSprite = "icon";
|
||||
|
||||
/// <summary>
|
||||
/// The RSI state used when the device is primed.
|
||||
/// Not VVWrite-able because it's only used at component init to construct the priming animation.
|
||||
/// </summary>
|
||||
[DataField("primingSprite")]
|
||||
[DataField]
|
||||
public string PrimingSprite = "primed";
|
||||
|
||||
/// <summary>
|
||||
/// The sound played when the device is primed.
|
||||
/// Not VVWrite-able because it's only used at component init to construct the priming animation.
|
||||
/// </summary>
|
||||
[DataField("primingSound")]
|
||||
[DataField, ViewVariables]
|
||||
public SoundSpecifier? PrimingSound;
|
||||
|
||||
/// <summary>
|
||||
/// The actual priming animation.
|
||||
/// Constructed at component init from the sprite and sound.
|
||||
/// </summary>
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
[ViewVariables]
|
||||
public Animation PrimingAnimation = default!;
|
||||
}
|
||||
@@ -1,11 +1,12 @@
|
||||
using Content.Shared.Trigger;
|
||||
using Content.Shared.Trigger.Components.Triggers;
|
||||
using Robust.Client.Animations;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Shared.Animations;
|
||||
|
||||
namespace Content.Client.Explosion;
|
||||
namespace Content.Client.Trigger.Systems;
|
||||
|
||||
public sealed partial class TriggerSystem
|
||||
public sealed class ProximityTriggerAnimationSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly AnimationPlayerSystem _player = default!;
|
||||
[Dependency] private readonly SharedAppearanceSystem _appearance = default!;
|
||||
@@ -18,7 +19,7 @@ public sealed partial class TriggerSystem
|
||||
|
||||
private const string AnimKey = "proximity";
|
||||
|
||||
private static readonly Animation _flasherAnimation = new Animation
|
||||
private static readonly Animation FlasherAnimation = new Animation
|
||||
{
|
||||
Length = TimeSpan.FromSeconds(0.6f),
|
||||
AnimationTracks = {
|
||||
@@ -42,8 +43,10 @@ public sealed partial class TriggerSystem
|
||||
}
|
||||
};
|
||||
|
||||
private void InitializeProximity()
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<TriggerOnProximityComponent, ComponentInit>(OnProximityInit);
|
||||
SubscribeLocalEvent<TriggerOnProximityComponent, AppearanceChangeEvent>(OnProxAppChange);
|
||||
SubscribeLocalEvent<TriggerOnProximityComponent, AnimationCompletedEvent>(OnProxAnimation);
|
||||
@@ -94,7 +97,7 @@ public sealed partial class TriggerSystem
|
||||
break;
|
||||
case ProximityTriggerVisuals.Active:
|
||||
if (_player.HasRunningAnimation(uid, player, AnimKey)) return;
|
||||
_player.Play((uid, player), _flasherAnimation, AnimKey);
|
||||
_player.Play((uid, player), FlasherAnimation, AnimKey);
|
||||
break;
|
||||
case ProximityTriggerVisuals.Off:
|
||||
default:
|
||||
@@ -0,0 +1,5 @@
|
||||
using Content.Shared.Trigger.Systems;
|
||||
|
||||
namespace Content.Client.Trigger.Systems;
|
||||
|
||||
public sealed class ReleaseGasOnTriggerSystem : SharedReleaseGasOnTriggerSystem;
|
||||
@@ -1,11 +1,10 @@
|
||||
using Content.Client.Trigger.Components;
|
||||
using Content.Shared.Trigger;
|
||||
using Robust.Client.Animations;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Shared.Audio;
|
||||
using Robust.Shared.Audio.Systems;
|
||||
using Robust.Shared.GameObjects;
|
||||
|
||||
namespace Content.Client.Trigger;
|
||||
namespace Content.Client.Trigger.Systems;
|
||||
|
||||
public sealed class TimerTriggerVisualizerSystem : VisualizerSystem<TimerTriggerVisualsComponent>
|
||||
{
|
||||
@@ -17,25 +16,26 @@ public sealed class TimerTriggerVisualizerSystem : VisualizerSystem<TimerTrigger
|
||||
SubscribeLocalEvent<TimerTriggerVisualsComponent, ComponentInit>(OnComponentInit);
|
||||
}
|
||||
|
||||
private void OnComponentInit(EntityUid uid, TimerTriggerVisualsComponent comp, ComponentInit args)
|
||||
private void OnComponentInit(Entity<TimerTriggerVisualsComponent> ent, ref ComponentInit args)
|
||||
{
|
||||
comp.PrimingAnimation = new Animation
|
||||
ent.Comp.PrimingAnimation = new Animation
|
||||
{
|
||||
Length = TimeSpan.MaxValue,
|
||||
AnimationTracks = {
|
||||
new AnimationTrackSpriteFlick() {
|
||||
new AnimationTrackSpriteFlick()
|
||||
{
|
||||
LayerKey = TriggerVisualLayers.Base,
|
||||
KeyFrames = { new AnimationTrackSpriteFlick.KeyFrame(comp.PrimingSprite, 0f) }
|
||||
KeyFrames = { new AnimationTrackSpriteFlick.KeyFrame(ent.Comp.PrimingSprite, 0f) }
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
if (comp.PrimingSound != null)
|
||||
if (ent.Comp.PrimingSound != null)
|
||||
{
|
||||
comp.PrimingAnimation.AnimationTracks.Add(
|
||||
ent.Comp.PrimingAnimation.AnimationTracks.Add(
|
||||
new AnimationTrackPlaySound()
|
||||
{
|
||||
KeyFrames = { new AnimationTrackPlaySound.KeyFrame(_audioSystem.ResolveSound(comp.PrimingSound), 0) }
|
||||
KeyFrames = { new AnimationTrackPlaySound.KeyFrame(_audioSystem.ResolveSound(ent.Comp.PrimingSound), 0) }
|
||||
}
|
||||
);
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
using Content.IntegrationTests.Tests.Interaction;
|
||||
using Content.Server.Explosion.Components;
|
||||
using Content.Shared.Explosion.Components;
|
||||
using Content.Shared.Trigger.Components;
|
||||
using Content.Shared.Trigger.Systems;
|
||||
using Robust.Shared.Containers;
|
||||
using Robust.Shared.GameObjects;
|
||||
|
||||
@@ -25,19 +25,19 @@ public sealed class ModularGrenadeTests : InteractionTest
|
||||
await InteractUsing(Cable);
|
||||
|
||||
// Insert & remove trigger
|
||||
AssertComp<OnUseTimerTriggerComponent>(false);
|
||||
AssertComp<TimerTriggerComponent>(false);
|
||||
await InteractUsing(Trigger);
|
||||
AssertComp<OnUseTimerTriggerComponent>();
|
||||
AssertComp<TimerTriggerComponent>();
|
||||
await FindEntity(Trigger, LookupFlags.Uncontained, shouldSucceed: false);
|
||||
await InteractUsing(Pry);
|
||||
AssertComp<OnUseTimerTriggerComponent>(false);
|
||||
AssertComp<TimerTriggerComponent>(false);
|
||||
|
||||
// Trigger was dropped to floor, not deleted.
|
||||
await FindEntity(Trigger, LookupFlags.Uncontained);
|
||||
|
||||
// Re-insert
|
||||
await InteractUsing(Trigger);
|
||||
AssertComp<OnUseTimerTriggerComponent>();
|
||||
AssertComp<TimerTriggerComponent>();
|
||||
|
||||
// Insert & remove payload.
|
||||
await InteractUsing(Payload);
|
||||
@@ -56,13 +56,14 @@ public sealed class ModularGrenadeTests : InteractionTest
|
||||
await Pickup();
|
||||
AssertComp<ActiveTimerTriggerComponent>(false);
|
||||
await UseInHand();
|
||||
AssertComp<ActiveTimerTriggerComponent>(true);
|
||||
|
||||
// So uhhh grenades in hands don't destroy themselves when exploding. Maybe that will be fixed eventually.
|
||||
await Drop();
|
||||
|
||||
// Wait until grenade explodes
|
||||
var timer = Comp<ActiveTimerTriggerComponent>();
|
||||
while (timer.TimeRemaining >= 0)
|
||||
var triggerSys = SEntMan.System<TriggerSystem>();
|
||||
while (Target != null && triggerSys.GetRemainingTime(SEntMan.GetEntity(Target.Value))?.TotalSeconds >= 0.0)
|
||||
{
|
||||
await RunTicks(10);
|
||||
}
|
||||
|
||||
@@ -1,33 +0,0 @@
|
||||
using Content.Server.AlertLevel.Systems;
|
||||
|
||||
namespace Content.Server.AlertLevel;
|
||||
/// <summary>
|
||||
/// This component is for changing the alert level of the station when triggered.
|
||||
/// </summary>
|
||||
[RegisterComponent, Access(typeof(AlertLevelChangeOnTriggerSystem))]
|
||||
public sealed partial class AlertLevelChangeOnTriggerComponent : Component
|
||||
{
|
||||
///<summary>
|
||||
///The alert level to change to when triggered.
|
||||
///</summary>
|
||||
[DataField]
|
||||
public string Level = "blue";
|
||||
|
||||
/// <summary>
|
||||
///Whether to play the sound when the alert level changes.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public bool PlaySound = true;
|
||||
|
||||
/// <summary>
|
||||
///Whether to say the announcement when the alert level changes.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public bool Announce = true;
|
||||
|
||||
/// <summary>
|
||||
///Force the alert change. This applies if the alert level is not selectable or not.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public bool Force = false;
|
||||
}
|
||||
@@ -5,13 +5,13 @@ using Content.Server.Animals.Components;
|
||||
using Content.Server.Mind;
|
||||
using Content.Server.Popups;
|
||||
using Content.Server.Radio;
|
||||
using Content.Server.Speech;
|
||||
using Content.Server.Speech.Components;
|
||||
using Content.Server.Vocalization.Systems;
|
||||
using Content.Shared.Animals.Components;
|
||||
using Content.Shared.Animals.Systems;
|
||||
using Content.Shared.Database;
|
||||
using Content.Shared.Mobs.Systems;
|
||||
using Content.Shared.Speech;
|
||||
using Content.Shared.Speech.Components;
|
||||
using Content.Shared.Whitelist;
|
||||
using Robust.Shared.Network;
|
||||
using Robust.Shared.Random;
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
using Content.Shared.Dataset;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Server.Chat;
|
||||
|
||||
/// <summary>
|
||||
/// Makes the entity speak when triggered. If the item has UseDelay component, the system will respect that cooldown.
|
||||
/// </summary>
|
||||
[RegisterComponent]
|
||||
public sealed partial class SpeakOnTriggerComponent : Component
|
||||
{
|
||||
/// <summary>
|
||||
/// The identifier for the dataset prototype containing messages to be spoken by this entity.
|
||||
/// </summary>
|
||||
[DataField(required: true)]
|
||||
public ProtoId<LocalizedDatasetPrototype> Pack = string.Empty;
|
||||
}
|
||||
@@ -67,6 +67,9 @@ public sealed class SuicideSystem : EntitySystem
|
||||
if (!suicideGhostEvent.Handled || _tagSystem.HasTag(victim, CannotSuicideTag))
|
||||
return false;
|
||||
|
||||
// TODO: fix this
|
||||
// This is a handled event, but the result is never used
|
||||
// It looks like TriggerOnMobstateChange is supposed to prevent you from suiciding
|
||||
var suicideEvent = new SuicideEvent(victim);
|
||||
RaiseLocalEvent(victim, suicideEvent);
|
||||
|
||||
|
||||
@@ -60,11 +60,6 @@ public sealed partial class ChatSystem : SharedChatSystem
|
||||
[Dependency] private readonly EntityWhitelistSystem _whitelistSystem = default!;
|
||||
[Dependency] private readonly ExamineSystemShared _examineSystem = default!;
|
||||
|
||||
public const int VoiceRange = 10; // how far voice goes in world units
|
||||
public const int WhisperClearRange = 2; // how far whisper goes while still being understandable, in world units
|
||||
public const int WhisperMuffledRange = 5; // how far whisper goes at all, in world units
|
||||
public const string DefaultAnnouncementSound = "/Audio/Announcements/announce.ogg";
|
||||
|
||||
private bool _loocEnabled = true;
|
||||
private bool _deadLoocEnabled;
|
||||
private bool _critLoocEnabled;
|
||||
|
||||
@@ -1,50 +0,0 @@
|
||||
using Content.Server.Explosion.EntitySystems;
|
||||
using Content.Shared.Damage;
|
||||
using Content.Shared.Damage.Components;
|
||||
|
||||
namespace Content.Server.Damage.Systems;
|
||||
|
||||
// System for damage that occurs on specific trigger, towards the user..
|
||||
public sealed class DamageUserOnTriggerSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly DamageableSystem _damageableSystem = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
SubscribeLocalEvent<DamageUserOnTriggerComponent, TriggerEvent>(OnTrigger);
|
||||
}
|
||||
|
||||
private void OnTrigger(EntityUid uid, DamageUserOnTriggerComponent component, TriggerEvent args)
|
||||
{
|
||||
if (args.User is null)
|
||||
return;
|
||||
|
||||
args.Handled |= OnDamageTrigger(uid, args.User.Value, component);
|
||||
}
|
||||
|
||||
private bool OnDamageTrigger(EntityUid source, EntityUid target, DamageUserOnTriggerComponent? component = null)
|
||||
{
|
||||
if (!Resolve(source, ref component))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var damage = new DamageSpecifier(component.Damage);
|
||||
var ev = new BeforeDamageUserOnTriggerEvent(damage, target);
|
||||
RaiseLocalEvent(source, ev);
|
||||
|
||||
return _damageableSystem.TryChangeDamage(target, ev.Damage, component.IgnoreResistances, origin: source) is not null;
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class BeforeDamageUserOnTriggerEvent : EntityEventArgs
|
||||
{
|
||||
public DamageSpecifier Damage { get; set; }
|
||||
public EntityUid Tripper { get; }
|
||||
|
||||
public BeforeDamageUserOnTriggerEvent(DamageSpecifier damage, EntityUid target)
|
||||
{
|
||||
Damage = damage;
|
||||
Tripper = target;
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,4 @@
|
||||
using Content.Server.Defusable.Components;
|
||||
using Content.Server.Explosion.Components;
|
||||
using Content.Server.Explosion.EntitySystems;
|
||||
using Content.Server.Popups;
|
||||
using Content.Server.Wires;
|
||||
@@ -8,13 +7,13 @@ using Content.Shared.Construction.Components;
|
||||
using Content.Shared.Database;
|
||||
using Content.Shared.Defusable;
|
||||
using Content.Shared.Examine;
|
||||
using Content.Shared.Explosion.Components;
|
||||
using Content.Shared.Explosion.Components.OnTrigger;
|
||||
using Content.Shared.Popups;
|
||||
using Content.Shared.Trigger.Components;
|
||||
using Content.Shared.Trigger.Components.Effects;
|
||||
using Content.Shared.Trigger.Systems;
|
||||
using Content.Shared.Verbs;
|
||||
using Content.Shared.Wires;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Shared.Audio;
|
||||
using Robust.Shared.Audio.Systems;
|
||||
|
||||
namespace Content.Server.Defusable.Systems;
|
||||
@@ -74,12 +73,13 @@ public sealed class DefusableSystem : SharedDefusableSystem
|
||||
{
|
||||
args.PushMarkup(Loc.GetString("defusable-examine-defused", ("name", uid)));
|
||||
}
|
||||
else if (comp.Activated && TryComp<ActiveTimerTriggerComponent>(uid, out var activeComp))
|
||||
else if (comp.Activated)
|
||||
{
|
||||
if (comp.DisplayTime)
|
||||
var remaining = _trigger.GetRemainingTime(uid);
|
||||
if (comp.DisplayTime && remaining != null)
|
||||
{
|
||||
args.PushMarkup(Loc.GetString("defusable-examine-live", ("name", uid),
|
||||
("time", MathF.Floor(activeComp.TimeRemaining))));
|
||||
("time", Math.Floor(remaining.Value.TotalSeconds))));
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -139,16 +139,9 @@ public sealed class DefusableSystem : SharedDefusableSystem
|
||||
SetActivated(comp, true);
|
||||
|
||||
_popup.PopupEntity(Loc.GetString("defusable-popup-begun", ("name", uid)), uid);
|
||||
if (TryComp<OnUseTimerTriggerComponent>(uid, out var timerTrigger))
|
||||
if (TryComp<TimerTriggerComponent>(uid, out var timerTrigger))
|
||||
{
|
||||
_trigger.HandleTimerTrigger(
|
||||
uid,
|
||||
user,
|
||||
timerTrigger.Delay,
|
||||
timerTrigger.BeepInterval,
|
||||
timerTrigger.InitialBeepDelay,
|
||||
timerTrigger.BeepSound
|
||||
);
|
||||
_trigger.ActivateTimerTrigger((uid, timerTrigger));
|
||||
}
|
||||
|
||||
RaiseLocalEvent(uid, new BombArmedEvent(uid));
|
||||
@@ -168,7 +161,7 @@ public sealed class DefusableSystem : SharedDefusableSystem
|
||||
|
||||
RaiseLocalEvent(uid, new BombDetonatedEvent(uid));
|
||||
|
||||
_explosion.TriggerExplosive(uid, user:detonator);
|
||||
_explosion.TriggerExplosive(uid, user: detonator);
|
||||
QueueDel(uid);
|
||||
|
||||
_appearance.SetData(uid, DefusableVisuals.Active, comp.Activated);
|
||||
@@ -188,7 +181,7 @@ public sealed class DefusableSystem : SharedDefusableSystem
|
||||
{
|
||||
SetUsable(comp, false);
|
||||
RemComp<ExplodeOnTriggerComponent>(uid);
|
||||
RemComp<OnUseTimerTriggerComponent>(uid);
|
||||
RemComp<TimerTriggerComponent>(uid);
|
||||
}
|
||||
RemComp<ActiveTimerTriggerComponent>(uid);
|
||||
|
||||
@@ -246,7 +239,7 @@ public sealed class DefusableSystem : SharedDefusableSystem
|
||||
if (comp is not { Activated: true, DelayWireUsed: false })
|
||||
return;
|
||||
|
||||
_trigger.TryDelay(wire.Owner, 30f);
|
||||
_trigger.TryDelay(wire.Owner, TimeSpan.FromSeconds(30));
|
||||
_popup.PopupEntity(Loc.GetString("defusable-popup-wire-chirp", ("name", wire.Owner)), wire.Owner);
|
||||
comp.DelayWireUsed = true;
|
||||
}
|
||||
@@ -268,7 +261,7 @@ public sealed class DefusableSystem : SharedDefusableSystem
|
||||
if (comp is { Activated: true, ProceedWireUsed: false })
|
||||
{
|
||||
comp.ProceedWireUsed = true;
|
||||
_trigger.TryDelay(wire.Owner, -15f);
|
||||
_trigger.TryDelay(wire.Owner, TimeSpan.FromSeconds(-15));
|
||||
}
|
||||
|
||||
_popup.PopupEntity(Loc.GetString("defusable-popup-wire-proceed-pulse", ("name", wire.Owner)), wire.Owner);
|
||||
@@ -298,7 +291,7 @@ public sealed class DefusableSystem : SharedDefusableSystem
|
||||
{
|
||||
if (!comp.ActivatedWireUsed)
|
||||
{
|
||||
_trigger.TryDelay(wire.Owner, 30f);
|
||||
_trigger.TryDelay(wire.Owner, TimeSpan.FromSeconds(30));
|
||||
_popup.PopupEntity(Loc.GetString("defusable-popup-wire-chirp", ("name", wire.Owner)), wire.Owner);
|
||||
comp.ActivatedWireUsed = true;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using Content.Server.Administration.Logs;
|
||||
using Content.Server.Atmos.EntitySystems;
|
||||
using Content.Server.Body.Systems;
|
||||
@@ -14,15 +15,13 @@ using Content.Shared.Damage;
|
||||
using Content.Shared.Database;
|
||||
using Content.Shared.Destructible;
|
||||
using Content.Shared.FixedPoint;
|
||||
using Content.Shared.Humanoid;
|
||||
using Content.Shared.Trigger.Systems;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Server.Audio;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Shared.Containers;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Random;
|
||||
using System.Linq;
|
||||
using Content.Shared.Humanoid;
|
||||
using Robust.Shared.Player;
|
||||
|
||||
namespace Content.Server.Destructible
|
||||
{
|
||||
|
||||
@@ -5,6 +5,6 @@ public sealed partial class TimerStartBehavior : IThresholdBehavior
|
||||
{
|
||||
public void Execute(EntityUid owner, DestructibleSystem system, EntityUid? cause = null)
|
||||
{
|
||||
system.TriggerSystem.StartTimer(owner, cause);
|
||||
system.TriggerSystem.ActivateTimerTrigger(owner, cause);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,18 @@
|
||||
namespace Content.Server.Destructible.Thresholds.Behaviors;
|
||||
using Content.Shared.Trigger.Systems;
|
||||
|
||||
namespace Content.Server.Destructible.Thresholds.Behaviors;
|
||||
|
||||
[DataDefinition]
|
||||
public sealed partial class TriggerBehavior : IThresholdBehavior
|
||||
{
|
||||
/// <summary>
|
||||
/// The trigger key to use when triggering.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public string? KeyOut { get; set; } = TriggerSystem.DefaultTriggerKey;
|
||||
|
||||
public void Execute(EntityUid owner, DestructibleSystem system, EntityUid? cause = null)
|
||||
{
|
||||
system.TriggerSystem.Trigger(owner, cause);
|
||||
system.TriggerSystem.Trigger(owner, cause, KeyOut);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
using Content.Server.DeviceLinking.Components;
|
||||
using Content.Server.DeviceNetwork;
|
||||
using Content.Server.DeviceNetwork.Components;
|
||||
using Content.Server.DeviceNetwork.Systems;
|
||||
using Content.Shared.DeviceLinking;
|
||||
using Content.Shared.DeviceLinking.Events;
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using Content.Server.DeviceLinking.Components;
|
||||
using Content.Server.DeviceNetwork;
|
||||
using Content.Shared.DeviceLinking;
|
||||
using Content.Shared.DeviceLinking.Events;
|
||||
using Content.Shared.DeviceNetwork;
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
using Content.Server.Administration.Logs;
|
||||
using Content.Server.DeviceLinking.Components;
|
||||
using Content.Server.Explosion.EntitySystems;
|
||||
using Content.Shared.Database;
|
||||
using Content.Shared.Interaction.Events;
|
||||
using Content.Shared.Timing;
|
||||
@@ -10,7 +9,6 @@ namespace Content.Server.DeviceLinking.Systems;
|
||||
public sealed class SignallerSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly DeviceLinkSystem _link = default!;
|
||||
[Dependency] private readonly UseDelaySystem _useDelay = default!;
|
||||
[Dependency] private readonly IAdminLogManager _adminLogger = default!;
|
||||
|
||||
public override void Initialize()
|
||||
@@ -19,7 +17,6 @@ public sealed class SignallerSystem : EntitySystem
|
||||
|
||||
SubscribeLocalEvent<SignallerComponent, ComponentInit>(OnInit);
|
||||
SubscribeLocalEvent<SignallerComponent, UseInHandEvent>(OnUseInHand);
|
||||
SubscribeLocalEvent<SignallerComponent, TriggerEvent>(OnTrigger);
|
||||
}
|
||||
|
||||
private void OnInit(EntityUid uid, SignallerComponent component, ComponentInit args)
|
||||
@@ -36,16 +33,4 @@ public sealed class SignallerSystem : EntitySystem
|
||||
_link.InvokePort(uid, component.Port);
|
||||
args.Handled = true;
|
||||
}
|
||||
|
||||
private void OnTrigger(EntityUid uid, SignallerComponent component, TriggerEvent args)
|
||||
{
|
||||
if (!TryComp(uid, out UseDelayComponent? useDelay)
|
||||
// if on cooldown, do nothing
|
||||
// and set cooldown to prevent clocks
|
||||
|| !_useDelay.TryResetDelay((uid, useDelay), true))
|
||||
return;
|
||||
|
||||
_link.InvokePort(uid, component.Port);
|
||||
args.Handled = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -58,7 +58,7 @@ public sealed class ElectrocutionSystem : SharedElectrocutionSystem
|
||||
[Dependency] private readonly MetaDataSystem _metaData = default!;
|
||||
[Dependency] private readonly TurfSystem _turf = default!;
|
||||
|
||||
private static readonly ProtoId<StatusEffectPrototype> StatusEffectKey = "Electrocution";
|
||||
private static readonly ProtoId<StatusEffectPrototype> StatusKeyIn = "Electrocution";
|
||||
private static readonly ProtoId<DamageTypePrototype> DamageType = "Shock";
|
||||
private static readonly ProtoId<TagPrototype> WindowTag = "Window";
|
||||
|
||||
@@ -386,12 +386,12 @@ public sealed class ElectrocutionSystem : SharedElectrocutionSystem
|
||||
}
|
||||
|
||||
if (!Resolve(uid, ref statusEffects, false) ||
|
||||
!_statusEffects.CanApplyEffect(uid, StatusEffectKey, statusEffects))
|
||||
!_statusEffects.CanApplyEffect(uid, StatusKeyIn, statusEffects))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!_statusEffects.TryAddStatusEffect<ElectrocutedComponent>(uid, StatusEffectKey, time, refresh, statusEffects))
|
||||
if (!_statusEffects.TryAddStatusEffect<ElectrocutedComponent>(uid, StatusKeyIn, time, refresh, statusEffects))
|
||||
return false;
|
||||
|
||||
var shouldStun = siemensCoefficient > 0.5f;
|
||||
|
||||
@@ -1,24 +0,0 @@
|
||||
namespace Content.Server.Emp;
|
||||
|
||||
/// <summary>
|
||||
/// Upon being triggered will EMP area around it.
|
||||
/// </summary>
|
||||
[RegisterComponent]
|
||||
[Access(typeof(EmpSystem))]
|
||||
public sealed partial class EmpOnTriggerComponent : Component
|
||||
{
|
||||
[DataField("range"), ViewVariables(VVAccess.ReadWrite)]
|
||||
public float Range = 1.0f;
|
||||
|
||||
/// <summary>
|
||||
/// How much energy will be consumed per battery in range
|
||||
/// </summary>
|
||||
[DataField("energyConsumption"), ViewVariables(VVAccess.ReadWrite)]
|
||||
public float EnergyConsumption;
|
||||
|
||||
/// <summary>
|
||||
/// How long it disables targets in seconds
|
||||
/// </summary>
|
||||
[DataField("disableDuration"), ViewVariables(VVAccess.ReadWrite)]
|
||||
public float DisableDuration = 60f;
|
||||
}
|
||||
@@ -1,4 +1,3 @@
|
||||
using Content.Server.Explosion.EntitySystems;
|
||||
using Content.Server.Power.EntitySystems;
|
||||
using Content.Server.Radio;
|
||||
using Content.Server.SurveillanceCamera;
|
||||
@@ -20,7 +19,6 @@ public sealed class EmpSystem : SharedEmpSystem
|
||||
{
|
||||
base.Initialize();
|
||||
SubscribeLocalEvent<EmpDisabledComponent, ExaminedEvent>(OnExamine);
|
||||
SubscribeLocalEvent<EmpOnTriggerComponent, TriggerEvent>(HandleEmpTrigger);
|
||||
|
||||
SubscribeLocalEvent<EmpDisabledComponent, RadioSendAttemptEvent>(OnRadioSendAttempt);
|
||||
SubscribeLocalEvent<EmpDisabledComponent, RadioReceiveAttemptEvent>(OnRadioReceiveAttempt);
|
||||
@@ -28,14 +26,7 @@ public sealed class EmpSystem : SharedEmpSystem
|
||||
SubscribeLocalEvent<EmpDisabledComponent, SurveillanceCameraSetActiveAttemptEvent>(OnCameraSetActive);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Triggers an EMP pulse at the given location, by first raising an <see cref="EmpAttemptEvent"/>, then a raising <see cref="EmpPulseEvent"/> on all entities in range.
|
||||
/// </summary>
|
||||
/// <param name="coordinates">The location to trigger the EMP pulse at.</param>
|
||||
/// <param name="range">The range of the EMP pulse.</param>
|
||||
/// <param name="energyConsumption">The amount of energy consumed by the EMP pulse.</param>
|
||||
/// <param name="duration">The duration of the EMP effects.</param>
|
||||
public void EmpPulse(MapCoordinates coordinates, float range, float energyConsumption, float duration)
|
||||
public override void EmpPulse(MapCoordinates coordinates, float range, float energyConsumption, float duration)
|
||||
{
|
||||
foreach (var uid in _lookup.GetEntitiesInRange(coordinates, range))
|
||||
{
|
||||
@@ -118,12 +109,6 @@ public sealed class EmpSystem : SharedEmpSystem
|
||||
args.PushMarkup(Loc.GetString("emp-disabled-comp-on-examine"));
|
||||
}
|
||||
|
||||
private void HandleEmpTrigger(EntityUid uid, EmpOnTriggerComponent comp, TriggerEvent args)
|
||||
{
|
||||
EmpPulse(_transform.GetMapCoordinates(uid), comp.Range, comp.EnergyConsumption, comp.DisableDuration);
|
||||
args.Handled = true;
|
||||
}
|
||||
|
||||
private void OnRadioSendAttempt(EntityUid uid, EmpDisabledComponent component, ref RadioSendAttemptEvent args)
|
||||
{
|
||||
args.Cancelled = true;
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
namespace Content.Server.Explosion.Components;
|
||||
|
||||
[RegisterComponent]
|
||||
public sealed partial class ActiveTriggerOnTimedCollideComponent : Component { }
|
||||
@@ -1,9 +0,0 @@
|
||||
namespace Content.Server.Explosion.Components;
|
||||
|
||||
/// <summary>
|
||||
/// Disallows starting the timer by hand, must be stuck or triggered by a system using <c>StartTimer</c>.
|
||||
/// </summary>
|
||||
[RegisterComponent]
|
||||
public sealed partial class AutomatedTimerComponent : Component
|
||||
{
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
using Content.Server.Explosion.EntitySystems;
|
||||
|
||||
namespace Content.Server.Explosion.Components;
|
||||
|
||||
/// <summary>
|
||||
/// Will anchor the attached entity upon a <see cref="TriggerEvent"/>.
|
||||
/// </summary>
|
||||
[RegisterComponent]
|
||||
public sealed partial class AnchorOnTriggerComponent : Component
|
||||
{
|
||||
[DataField("removeOnTrigger")]
|
||||
public bool RemoveOnTrigger = true;
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
using Content.Server.Explosion.EntitySystems;
|
||||
|
||||
namespace Content.Server.Explosion.Components;
|
||||
|
||||
/// <summary>
|
||||
/// Will delete the attached entity upon a <see cref="TriggerEvent"/>.
|
||||
/// </summary>
|
||||
[RegisterComponent]
|
||||
public sealed partial class DeleteOnTriggerComponent : Component
|
||||
{
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
namespace Content.Server.Explosion.Components;
|
||||
|
||||
/// <summary>
|
||||
/// Gibs on trigger, self explanatory.
|
||||
/// Also in case of an implant using this, gibs the implant user instead.
|
||||
/// </summary>
|
||||
[RegisterComponent]
|
||||
public sealed partial class GibOnTriggerComponent : Component
|
||||
{
|
||||
/// <summary>
|
||||
/// Should gibbing also delete the owners items?
|
||||
/// </summary>
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
[DataField("deleteItems")]
|
||||
public bool DeleteItems = false;
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
using Content.Server.Explosion.EntitySystems;
|
||||
using Robust.Shared.Audio;
|
||||
|
||||
namespace Content.Server.Explosion.Components;
|
||||
|
||||
/// <summary>
|
||||
/// Will play sound from the attached entity upon a <see cref="TriggerEvent"/>.
|
||||
/// </summary>
|
||||
[RegisterComponent]
|
||||
public sealed partial class SoundOnTriggerComponent : Component
|
||||
{
|
||||
[DataField("removeOnTrigger")]
|
||||
public bool RemoveOnTrigger = true;
|
||||
|
||||
[DataField("sound")]
|
||||
public SoundSpecifier? Sound = new SoundPathSpecifier("/Audio/Effects/Grenades/supermatter_start.ogg");
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom;
|
||||
|
||||
namespace Content.Server.Explosion.Components.OnTrigger;
|
||||
|
||||
/// <summary>
|
||||
/// After being triggered applies the specified components and runs triggers again.
|
||||
/// </summary>
|
||||
[RegisterComponent, AutoGenerateComponentPause]
|
||||
public sealed partial class TwoStageTriggerComponent : Component
|
||||
{
|
||||
/// <summary>
|
||||
/// How long it takes for the second stage to be triggered.
|
||||
/// </summary>
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
[DataField("triggerDelay")]
|
||||
public TimeSpan TriggerDelay = TimeSpan.FromSeconds(10);
|
||||
|
||||
/// <summary>
|
||||
/// This list of components that will be added for the second trigger.
|
||||
/// </summary>
|
||||
[DataField("components", required: true)]
|
||||
public ComponentRegistry SecondStageComponents = new();
|
||||
|
||||
[DataField("nextTriggerTime", customTypeSerializer: typeof(TimeOffsetSerializer))]
|
||||
[AutoPausedField]
|
||||
public TimeSpan? NextTriggerTime;
|
||||
|
||||
[DataField("triggered")]
|
||||
public bool Triggered = false;
|
||||
|
||||
[DataField("ComponentsIsLoaded")]
|
||||
public bool ComponentsIsLoaded = false;
|
||||
}
|
||||
@@ -45,4 +45,10 @@ public sealed partial class ProjectileGrenadeComponent : Component
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public float MaxVelocity = 6f;
|
||||
|
||||
/// <summary>
|
||||
/// The trigger key that will activate the grenade.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public string TriggerKey = "timer";
|
||||
}
|
||||
|
||||
@@ -1,37 +0,0 @@
|
||||
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom;
|
||||
using Content.Server.Explosion.EntitySystems;
|
||||
|
||||
namespace Content.Server.Explosion.Components;
|
||||
|
||||
/// <summary>
|
||||
/// A component that electrocutes an entity having this component when a trigger is triggered.
|
||||
/// </summary>
|
||||
[RegisterComponent, AutoGenerateComponentPause]
|
||||
[Access(typeof(TriggerSystem))]
|
||||
public sealed partial class ShockOnTriggerComponent : Component
|
||||
{
|
||||
/// <summary>
|
||||
/// The force of an electric shock when the trigger is triggered.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public int Damage = 5;
|
||||
|
||||
/// <summary>
|
||||
/// Duration of electric shock when the trigger is triggered.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public TimeSpan Duration = TimeSpan.FromSeconds(2);
|
||||
|
||||
/// <summary>
|
||||
/// The minimum delay between repeating triggers.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public TimeSpan Cooldown = TimeSpan.FromSeconds(4);
|
||||
|
||||
/// <summary>
|
||||
/// When can the trigger run again?
|
||||
/// </summary>
|
||||
[DataField(customTypeSerializer: typeof(TimeOffsetSerializer))]
|
||||
[AutoPausedField]
|
||||
public TimeSpan NextTrigger = TimeSpan.Zero;
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
using Content.Server.Explosion.EntitySystems;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Server.Explosion.Components;
|
||||
|
||||
/// <summary>
|
||||
/// Spawns a protoype when triggered.
|
||||
/// </summary>
|
||||
[RegisterComponent, Access(typeof(TriggerSystem))]
|
||||
public sealed partial class SpawnOnTriggerComponent : Component
|
||||
{
|
||||
/// <summary>
|
||||
/// The prototype to spawn.
|
||||
/// </summary>
|
||||
[DataField(required: true)]
|
||||
public EntProtoId Proto = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Use MapCoordinates for spawning?
|
||||
/// Set to true if you don't want the new entity parented to the spawner.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public bool mapCoords;
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
using Content.Shared.DeviceLinking;
|
||||
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
|
||||
|
||||
namespace Content.Server.Explosion.Components
|
||||
{
|
||||
/// <summary>
|
||||
/// Sends a trigger when signal is received.
|
||||
/// </summary>
|
||||
[RegisterComponent]
|
||||
public sealed partial class TimerStartOnSignalComponent : Component
|
||||
{
|
||||
[DataField("port", customTypeSerializer: typeof(PrototypeIdSerializer<SinkPortPrototype>))]
|
||||
public string Port = "Timer";
|
||||
}
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
namespace Content.Server.Explosion.Components;
|
||||
|
||||
/// <summary>
|
||||
/// Triggers on click.
|
||||
/// </summary>
|
||||
[RegisterComponent]
|
||||
public sealed partial class TriggerOnActivateComponent : Component { }
|
||||
@@ -1,20 +0,0 @@
|
||||
namespace Content.Server.Explosion.Components;
|
||||
|
||||
/// <summary>
|
||||
/// Triggers when colliding with another entity.
|
||||
/// </summary>
|
||||
[RegisterComponent]
|
||||
public sealed partial class TriggerOnCollideComponent : Component
|
||||
{
|
||||
/// <summary>
|
||||
/// The fixture with which to collide.
|
||||
/// </summary>
|
||||
[DataField(required: true)]
|
||||
public string FixtureID = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Doesn't trigger if the other colliding fixture is nonhard.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public bool IgnoreOtherNonHard = true;
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
using Content.Shared.Mobs;
|
||||
|
||||
namespace Content.Server.Explosion.Components;
|
||||
|
||||
/// <summary>
|
||||
/// Use where you want something to trigger on mobstate change
|
||||
/// </summary>
|
||||
[RegisterComponent]
|
||||
public sealed partial class TriggerOnMobstateChangeComponent : Component
|
||||
{
|
||||
/// <summary>
|
||||
/// What state should trigger this?
|
||||
/// </summary>
|
||||
[ViewVariables]
|
||||
[DataField("mobState", required: true)]
|
||||
public List<MobState> MobState = new();
|
||||
|
||||
/// <summary>
|
||||
/// If true, prevents suicide attempts for the trigger to prevent cheese.
|
||||
/// </summary>
|
||||
[ViewVariables]
|
||||
[DataField("preventSuicide")]
|
||||
public bool PreventSuicide = false;
|
||||
}
|
||||
@@ -1,93 +0,0 @@
|
||||
using Content.Server.Explosion.EntitySystems;
|
||||
using Content.Shared.Explosion;
|
||||
using Content.Shared.Explosion.Components;
|
||||
using Content.Shared.Physics;
|
||||
using Robust.Shared.Physics.Collision.Shapes;
|
||||
using Robust.Shared.Physics.Components;
|
||||
using Robust.Shared.Physics.Dynamics;
|
||||
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom;
|
||||
|
||||
namespace Content.Server.Explosion.Components
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// Raises a <see cref="TriggerEvent"/> whenever an entity collides with a fixture attached to the owner of this component.
|
||||
/// </summary>
|
||||
[RegisterComponent, AutoGenerateComponentPause]
|
||||
public sealed partial class TriggerOnProximityComponent : SharedTriggerOnProximityComponent
|
||||
{
|
||||
public const string FixtureID = "trigger-on-proximity-fixture";
|
||||
|
||||
[ViewVariables]
|
||||
public readonly Dictionary<EntityUid, PhysicsComponent> Colliding = new();
|
||||
|
||||
/// <summary>
|
||||
/// What is the shape of the proximity fixture?
|
||||
/// </summary>
|
||||
[ViewVariables]
|
||||
[DataField("shape")]
|
||||
public IPhysShape Shape = new PhysShapeCircle(2f);
|
||||
|
||||
/// <summary>
|
||||
/// How long the the proximity trigger animation plays for.
|
||||
/// </summary>
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
[DataField("animationDuration")]
|
||||
public TimeSpan AnimationDuration = TimeSpan.FromSeconds(0.6f);
|
||||
|
||||
/// <summary>
|
||||
/// Whether the entity needs to be anchored for the proximity to work.
|
||||
/// </summary>
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
[DataField("requiresAnchored")]
|
||||
public bool RequiresAnchored = true;
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
[DataField("enabled")]
|
||||
public bool Enabled = true;
|
||||
|
||||
/// <summary>
|
||||
/// The minimum delay between repeating triggers.
|
||||
/// </summary>
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
[DataField("cooldown")]
|
||||
public TimeSpan Cooldown = TimeSpan.FromSeconds(5);
|
||||
|
||||
/// <summary>
|
||||
/// When can the trigger run again?
|
||||
/// </summary>
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
[DataField("nextTrigger", customTypeSerializer: typeof(TimeOffsetSerializer))]
|
||||
[AutoPausedField]
|
||||
public TimeSpan NextTrigger = TimeSpan.Zero;
|
||||
|
||||
/// <summary>
|
||||
/// When will the visual state be updated again after activation?
|
||||
/// </summary>
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
[DataField("nextVisualUpdate", customTypeSerializer: typeof(TimeOffsetSerializer))]
|
||||
[AutoPausedField]
|
||||
public TimeSpan NextVisualUpdate = TimeSpan.Zero;
|
||||
|
||||
/// <summary>
|
||||
/// What speed should the other object be moving at to trigger the proximity fixture?
|
||||
/// </summary>
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
[DataField("triggerSpeed")]
|
||||
public float TriggerSpeed = 3.5f;
|
||||
|
||||
/// <summary>
|
||||
/// If this proximity is triggered should we continually repeat it?
|
||||
/// </summary>
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
[DataField("repeating")]
|
||||
public bool Repeating = true;
|
||||
|
||||
/// <summary>
|
||||
/// What layer is the trigger fixture on?
|
||||
/// </summary>
|
||||
[ViewVariables]
|
||||
[DataField("layer", customTypeSerializer: typeof(FlagSerializer<CollisionLayer>))]
|
||||
public int Layer = (int) (CollisionGroup.MidImpassable | CollisionGroup.LowImpassable | CollisionGroup.HighImpassable);
|
||||
}
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
using Content.Shared.DeviceLinking;
|
||||
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
|
||||
|
||||
namespace Content.Server.Explosion.Components
|
||||
{
|
||||
/// <summary>
|
||||
/// Sends a trigger when signal is received.
|
||||
/// </summary>
|
||||
[RegisterComponent]
|
||||
public sealed partial class TriggerOnSignalComponent : Component
|
||||
{
|
||||
[DataField("port", customTypeSerializer: typeof(PrototypeIdSerializer<SinkPortPrototype>))]
|
||||
public string Port = "Trigger";
|
||||
}
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
namespace Content.Server.Explosion.Components;
|
||||
|
||||
[RegisterComponent]
|
||||
public sealed partial class TriggerOnSlipComponent : Component
|
||||
{
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
namespace Content.Server.Explosion.Components;
|
||||
|
||||
/// <summary>
|
||||
/// calls the trigger when the object is initialized
|
||||
/// </summary>
|
||||
[RegisterComponent]
|
||||
public sealed partial class TriggerOnSpawnComponent : Component
|
||||
{
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
namespace Content.Server.Explosion.Components;
|
||||
|
||||
/// <summary>
|
||||
/// This is used for entities that want the more generic 'trigger' behavior after a step trigger occurs.
|
||||
/// Not done by default, since it's not useful for everything and might cause weird behavior. But it is useful for a lot of stuff like mousetraps.
|
||||
/// </summary>
|
||||
[RegisterComponent]
|
||||
public sealed partial class TriggerOnStepTriggerComponent : Component
|
||||
{
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
namespace Content.Server.Explosion.Components;
|
||||
|
||||
/// <summary>
|
||||
/// Triggers when the entity is overlapped for the specified duration.
|
||||
/// </summary>
|
||||
[RegisterComponent]
|
||||
public sealed partial class TriggerOnTimedCollideComponent : Component
|
||||
{
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
[DataField("threshold")]
|
||||
public float Threshold;
|
||||
|
||||
/// <summary>
|
||||
/// A collection of entities that are colliding with this, and their own unique accumulator.
|
||||
/// </summary>
|
||||
[ViewVariables]
|
||||
public readonly Dictionary<EntityUid, float> Colliding = new();
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
namespace Content.Server.Explosion.Components;
|
||||
|
||||
/// <summary>
|
||||
/// Triggers on use in hand.
|
||||
/// </summary>
|
||||
[RegisterComponent]
|
||||
public sealed partial class TriggerOnUseComponent : Component { }
|
||||
@@ -1,28 +0,0 @@
|
||||
namespace Content.Server.Explosion.Components
|
||||
{
|
||||
/// <summary>
|
||||
/// Sends a trigger when the keyphrase is heard
|
||||
/// </summary>
|
||||
[RegisterComponent]
|
||||
public sealed partial class TriggerOnVoiceComponent : Component
|
||||
{
|
||||
public bool IsListening => IsRecording || !string.IsNullOrWhiteSpace(KeyPhrase);
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
[DataField("keyPhrase")]
|
||||
public string? KeyPhrase;
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
[DataField("listenRange")]
|
||||
public int ListenRange { get; private set; } = 4;
|
||||
|
||||
[DataField("isRecording")]
|
||||
public bool IsRecording = false;
|
||||
|
||||
[DataField("minLength")]
|
||||
public int MinLength = 3;
|
||||
|
||||
[DataField("maxLength")]
|
||||
public int MaxLength = 50;
|
||||
}
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
namespace Content.Server.Explosion.Components;
|
||||
|
||||
/// <summary>
|
||||
/// Triggers a gun when attempting to shoot while it's empty
|
||||
/// </summary>
|
||||
[RegisterComponent]
|
||||
public sealed partial class TriggerWhenEmptyComponent : Component
|
||||
{
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
using Content.Shared.Whitelist;
|
||||
|
||||
namespace Content.Server.Explosion.Components;
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the user of a Trigger satisfies a whitelist and blacklist condition.
|
||||
/// Cancels the trigger otherwise.
|
||||
/// </summary>
|
||||
[RegisterComponent]
|
||||
public sealed partial class TriggerWhitelistComponent : Component
|
||||
{
|
||||
/// <summary>
|
||||
/// Whitelist for what entites can cause this trigger.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public EntityWhitelist? Whitelist;
|
||||
|
||||
/// <summary>
|
||||
/// Blacklist for what entites can cause this trigger.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public EntityWhitelist? Blacklist;
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
using Content.Server.Explosion.Components;
|
||||
using Content.Server.Weapons.Ranged.Systems;
|
||||
using Content.Shared.Trigger;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Shared.Containers;
|
||||
using Robust.Shared.Map;
|
||||
@@ -45,6 +46,9 @@ public sealed class ProjectileGrenadeSystem : EntitySystem
|
||||
/// </summary>
|
||||
private void OnFragTrigger(Entity<ProjectileGrenadeComponent> entity, ref TriggerEvent args)
|
||||
{
|
||||
if (args.Key != entity.Comp.TriggerKey)
|
||||
return;
|
||||
|
||||
FragmentIntoProjectiles(entity.Owner, entity.Comp);
|
||||
args.Handled = true;
|
||||
}
|
||||
|
||||
@@ -1,79 +0,0 @@
|
||||
using Content.Server.Atmos.EntitySystems;
|
||||
using Content.Shared.Explosion.Components.OnTrigger;
|
||||
using Content.Shared.Explosion.EntitySystems;
|
||||
using Robust.Shared.Timing;
|
||||
|
||||
namespace Content.Server.Explosion.EntitySystems;
|
||||
|
||||
/// <summary>
|
||||
/// Releases a gas mixture to the atmosphere when triggered.
|
||||
/// Can also release gas over a set timespan to prevent trolling people
|
||||
/// with the instant-wall-of-pressure-inator.
|
||||
/// </summary>
|
||||
public sealed partial class ReleaseGasOnTriggerSystem : SharedReleaseGasOnTriggerSystem
|
||||
{
|
||||
[Dependency] private readonly SharedAppearanceSystem _appearance = default!;
|
||||
[Dependency] private readonly AtmosphereSystem _atmosphereSystem = default!;
|
||||
[Dependency] private readonly IGameTiming _timing = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<ReleaseGasOnTriggerComponent, TriggerEvent>(OnTrigger);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Shrimply sets the component to active when triggered, allowing it to release over time.
|
||||
/// </summary>
|
||||
private void OnTrigger(Entity<ReleaseGasOnTriggerComponent> ent, ref TriggerEvent args)
|
||||
{
|
||||
ent.Comp.Active = true;
|
||||
ent.Comp.NextReleaseTime = _timing.CurTime;
|
||||
ent.Comp.StartingTotalMoles = ent.Comp.Air.TotalMoles;
|
||||
UpdateAppearance(ent.Owner, true);
|
||||
}
|
||||
|
||||
public override void Update(float frameTime)
|
||||
{
|
||||
base.Update(frameTime);
|
||||
|
||||
var curTime = _timing.CurTime;
|
||||
var query = EntityQueryEnumerator<ReleaseGasOnTriggerComponent>();
|
||||
|
||||
while (query.MoveNext(out var uid, out var comp))
|
||||
{
|
||||
if (!comp.Active || comp.NextReleaseTime > curTime)
|
||||
continue;
|
||||
|
||||
var giverGasMix = comp.Air.Remove(comp.StartingTotalMoles * comp.RemoveFraction);
|
||||
var environment = _atmosphereSystem.GetContainingMixture(uid, false, true);
|
||||
|
||||
if (environment == null)
|
||||
{
|
||||
UpdateAppearance(uid, false);
|
||||
RemCompDeferred<ReleaseGasOnTriggerComponent>(uid);
|
||||
continue;
|
||||
}
|
||||
|
||||
_atmosphereSystem.Merge(environment, giverGasMix);
|
||||
comp.NextReleaseTime += comp.ReleaseInterval;
|
||||
|
||||
if (comp.PressureLimit != 0 && environment.Pressure >= comp.PressureLimit ||
|
||||
comp.Air.TotalMoles <= 0)
|
||||
{
|
||||
UpdateAppearance(uid, false);
|
||||
RemCompDeferred<ReleaseGasOnTriggerComponent>(uid);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateAppearance(Entity<AppearanceComponent?> entity, bool state)
|
||||
{
|
||||
if (!Resolve(entity, ref entity.Comp, false))
|
||||
return;
|
||||
|
||||
_appearance.SetData(entity, ReleaseGasOnTriggerVisuals.Key, state);
|
||||
}
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
using Content.Shared.Explosion.Components.OnTrigger;
|
||||
using Content.Shared.Explosion.EntitySystems;
|
||||
using Content.Shared.RepulseAttract;
|
||||
using Content.Shared.Timing;
|
||||
|
||||
namespace Content.Server.Explosion.EntitySystems;
|
||||
|
||||
public sealed class RepulseAttractOnTriggerSystem : SharedRepulseAttractOnTriggerSystem
|
||||
{
|
||||
[Dependency] private readonly RepulseAttractSystem _repulse = default!;
|
||||
[Dependency] private readonly SharedTransformSystem _transform = default!;
|
||||
[Dependency] private readonly UseDelaySystem _delay = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<SharedRepulseAttractOnTriggerComponent, TriggerEvent>(OnTrigger);
|
||||
}
|
||||
|
||||
private void OnTrigger(Entity<SharedRepulseAttractOnTriggerComponent> ent, ref TriggerEvent args)
|
||||
{
|
||||
if (_delay.IsDelayed(ent.Owner))
|
||||
return;
|
||||
|
||||
var position = _transform.GetMapCoordinates(ent);
|
||||
_repulse.TryRepulseAttract(position, args.User, ent.Comp.Speed, ent.Comp.Range, ent.Comp.Whitelist, ent.Comp.CollisionMask);
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,8 @@
|
||||
using Content.Shared.Explosion.Components;
|
||||
using Content.Shared.Throwing;
|
||||
using Content.Shared.Trigger;
|
||||
using Content.Shared.Trigger.Systems;
|
||||
using Content.Shared.Trigger.Components;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Shared.Containers;
|
||||
using Robust.Shared.Map;
|
||||
@@ -15,6 +18,7 @@ public sealed class ScatteringGrenadeSystem : SharedScatteringGrenadeSystem
|
||||
[Dependency] private readonly IRobustRandom _random = default!;
|
||||
[Dependency] private readonly ThrowingSystem _throwingSystem = default!;
|
||||
[Dependency] private readonly TransformSystem _transformSystem = default!;
|
||||
[Dependency] private readonly TriggerSystem _trigger = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
@@ -30,6 +34,9 @@ public sealed class ScatteringGrenadeSystem : SharedScatteringGrenadeSystem
|
||||
/// </summary>
|
||||
private void OnScatteringTrigger(Entity<ScatteringGrenadeComponent> entity, ref TriggerEvent args)
|
||||
{
|
||||
if (args.Key != entity.Comp.TriggerKey)
|
||||
return;
|
||||
|
||||
entity.Comp.IsTriggered = true;
|
||||
args.Handled = true;
|
||||
}
|
||||
@@ -76,13 +83,12 @@ public sealed class ScatteringGrenadeSystem : SharedScatteringGrenadeSystem
|
||||
|
||||
_throwingSystem.TryThrow(contentUid, direction, component.Velocity);
|
||||
|
||||
if (component.TriggerContents)
|
||||
if (component.TriggerContents && TryComp<TimerTriggerComponent>(contentUid, out var contentTimer))
|
||||
{
|
||||
additionalIntervalDelay += _random.NextFloat(component.IntervalBetweenTriggersMin, component.IntervalBetweenTriggersMax);
|
||||
var contentTimer = EnsureComp<ActiveTimerTriggerComponent>(contentUid);
|
||||
contentTimer.TimeRemaining = component.DelayBeforeTriggerContents + additionalIntervalDelay;
|
||||
var ev = new ActiveTimerTriggerEvent(contentUid, uid);
|
||||
RaiseLocalEvent(contentUid, ref ev);
|
||||
|
||||
_trigger.SetDelay((contentUid, contentTimer), TimeSpan.FromSeconds(component.DelayBeforeTriggerContents + additionalIntervalDelay));
|
||||
_trigger.ActivateTimerTrigger((contentUid, contentTimer));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,57 +0,0 @@
|
||||
using Content.Shared.Explosion.Components;
|
||||
using Content.Shared.Explosion.EntitySystems;
|
||||
using Content.Server.Fluids.EntitySystems;
|
||||
using Content.Server.Spreader;
|
||||
using Content.Shared.Chemistry.Components;
|
||||
using Content.Shared.Coordinates.Helpers;
|
||||
using Content.Shared.Maps;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Shared.Map;
|
||||
|
||||
namespace Content.Server.Explosion.EntitySystems;
|
||||
|
||||
/// <summary>
|
||||
/// Handles creating smoke when <see cref="SmokeOnTriggerComponent"/> is triggered.
|
||||
/// </summary>
|
||||
public sealed class SmokeOnTriggerSystem : SharedSmokeOnTriggerSystem
|
||||
{
|
||||
[Dependency] private readonly IMapManager _mapMan = default!;
|
||||
[Dependency] private readonly SharedMapSystem _map = default!;
|
||||
[Dependency] private readonly SmokeSystem _smoke = default!;
|
||||
[Dependency] private readonly TransformSystem _transform = default!;
|
||||
[Dependency] private readonly SpreaderSystem _spreader = default!;
|
||||
[Dependency] private readonly TurfSystem _turf = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<SmokeOnTriggerComponent, TriggerEvent>(OnTrigger);
|
||||
}
|
||||
|
||||
private void OnTrigger(EntityUid uid, SmokeOnTriggerComponent comp, TriggerEvent args)
|
||||
{
|
||||
var xform = Transform(uid);
|
||||
var mapCoords = _transform.GetMapCoordinates(uid, xform);
|
||||
if (!_mapMan.TryFindGridAt(mapCoords, out var gridUid, out var grid) ||
|
||||
!_map.TryGetTileRef(gridUid, grid, xform.Coordinates, out var tileRef) ||
|
||||
tileRef.Tile.IsEmpty)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (_spreader.RequiresFloorToSpread(comp.SmokePrototype.ToString()) && _turf.IsSpace(tileRef))
|
||||
return;
|
||||
|
||||
var coords = _map.MapToGrid(gridUid, mapCoords);
|
||||
var ent = Spawn(comp.SmokePrototype, coords.SnapToGrid());
|
||||
if (!TryComp<SmokeComponent>(ent, out var smoke))
|
||||
{
|
||||
Log.Error($"Smoke prototype {comp.SmokePrototype} was missing SmokeComponent");
|
||||
Del(ent);
|
||||
return;
|
||||
}
|
||||
|
||||
_smoke.StartSmoke(ent, comp.Solution, comp.Duration, comp.SpreadAmount, smoke);
|
||||
}
|
||||
}
|
||||
@@ -1,170 +0,0 @@
|
||||
using Content.Server.Explosion.Components;
|
||||
using Content.Shared.Examine;
|
||||
using Content.Shared.Explosion.Components;
|
||||
using Content.Shared.Interaction.Events;
|
||||
using Content.Shared.Popups;
|
||||
using Content.Shared.Sticky;
|
||||
using Content.Shared.Verbs;
|
||||
|
||||
namespace Content.Server.Explosion.EntitySystems;
|
||||
|
||||
public sealed partial class TriggerSystem
|
||||
{
|
||||
[Dependency] private readonly SharedPopupSystem _popupSystem = default!;
|
||||
|
||||
private void InitializeOnUse()
|
||||
{
|
||||
SubscribeLocalEvent<OnUseTimerTriggerComponent, UseInHandEvent>(OnTimerUse);
|
||||
SubscribeLocalEvent<OnUseTimerTriggerComponent, ExaminedEvent>(OnExamined);
|
||||
SubscribeLocalEvent<OnUseTimerTriggerComponent, GetVerbsEvent<AlternativeVerb>>(OnGetAltVerbs);
|
||||
SubscribeLocalEvent<OnUseTimerTriggerComponent, EntityStuckEvent>(OnStuck);
|
||||
SubscribeLocalEvent<RandomTimerTriggerComponent, MapInitEvent>(OnRandomTimerTriggerMapInit);
|
||||
}
|
||||
|
||||
private void OnStuck(EntityUid uid, OnUseTimerTriggerComponent component, ref EntityStuckEvent args)
|
||||
{
|
||||
if (!component.StartOnStick)
|
||||
return;
|
||||
|
||||
StartTimer((uid, component), args.User);
|
||||
}
|
||||
|
||||
private void OnExamined(EntityUid uid, OnUseTimerTriggerComponent component, ExaminedEvent args)
|
||||
{
|
||||
if (args.IsInDetailsRange && component.Examinable)
|
||||
args.PushText(Loc.GetString("examine-trigger-timer", ("time", component.Delay)));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Add an alt-click interaction that cycles through delays.
|
||||
/// </summary>
|
||||
private void OnGetAltVerbs(EntityUid uid, OnUseTimerTriggerComponent component, GetVerbsEvent<AlternativeVerb> args)
|
||||
{
|
||||
if (!args.CanInteract || !args.CanAccess || args.Hands == null)
|
||||
return;
|
||||
|
||||
if (component.UseVerbInstead)
|
||||
{
|
||||
args.Verbs.Add(new AlternativeVerb()
|
||||
{
|
||||
Text = Loc.GetString("verb-start-detonation"),
|
||||
Act = () => StartTimer((uid, component), args.User),
|
||||
Priority = 2
|
||||
});
|
||||
}
|
||||
|
||||
if (component.AllowToggleStartOnStick)
|
||||
{
|
||||
args.Verbs.Add(new AlternativeVerb()
|
||||
{
|
||||
Text = Loc.GetString("verb-toggle-start-on-stick"),
|
||||
Act = () => ToggleStartOnStick(uid, args.User, component)
|
||||
});
|
||||
}
|
||||
|
||||
if (component.DelayOptions == null || component.DelayOptions.Count == 1)
|
||||
return;
|
||||
|
||||
args.Verbs.Add(new AlternativeVerb()
|
||||
{
|
||||
Category = TimerOptions,
|
||||
Text = Loc.GetString("verb-trigger-timer-cycle"),
|
||||
Act = () => CycleDelay(component, args.User),
|
||||
Priority = 1
|
||||
});
|
||||
|
||||
foreach (var option in component.DelayOptions)
|
||||
{
|
||||
if (MathHelper.CloseTo(option, component.Delay))
|
||||
{
|
||||
args.Verbs.Add(new AlternativeVerb()
|
||||
{
|
||||
Category = TimerOptions,
|
||||
Text = Loc.GetString("verb-trigger-timer-set-current", ("time", option)),
|
||||
Disabled = true,
|
||||
Priority = (int) (-100 * option)
|
||||
});
|
||||
continue;
|
||||
}
|
||||
|
||||
args.Verbs.Add(new AlternativeVerb()
|
||||
{
|
||||
Category = TimerOptions,
|
||||
Text = Loc.GetString("verb-trigger-timer-set", ("time", option)),
|
||||
Priority = (int) (-100 * option),
|
||||
|
||||
Act = () =>
|
||||
{
|
||||
component.Delay = option;
|
||||
_popupSystem.PopupEntity(Loc.GetString("popup-trigger-timer-set", ("time", option)), args.User, args.User);
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private void OnRandomTimerTriggerMapInit(Entity<RandomTimerTriggerComponent> ent, ref MapInitEvent args)
|
||||
{
|
||||
var (_, comp) = ent;
|
||||
|
||||
if (!TryComp<OnUseTimerTriggerComponent>(ent, out var timerTriggerComp))
|
||||
return;
|
||||
|
||||
timerTriggerComp.Delay = _random.NextFloat(comp.Min, comp.Max);
|
||||
}
|
||||
|
||||
private void CycleDelay(OnUseTimerTriggerComponent component, EntityUid user)
|
||||
{
|
||||
if (component.DelayOptions == null || component.DelayOptions.Count == 1)
|
||||
return;
|
||||
|
||||
// This is somewhat inefficient, but its good enough. This is run rarely, and the lists should be short.
|
||||
|
||||
component.DelayOptions.Sort();
|
||||
|
||||
if (component.DelayOptions[^1] <= component.Delay)
|
||||
{
|
||||
component.Delay = component.DelayOptions[0];
|
||||
_popupSystem.PopupEntity(Loc.GetString("popup-trigger-timer-set", ("time", component.Delay)), user, user);
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var option in component.DelayOptions)
|
||||
{
|
||||
if (option > component.Delay)
|
||||
{
|
||||
component.Delay = option;
|
||||
_popupSystem.PopupEntity(Loc.GetString("popup-trigger-timer-set", ("time", option)), user, user);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void ToggleStartOnStick(EntityUid grenade, EntityUid user, OnUseTimerTriggerComponent comp)
|
||||
{
|
||||
if (comp.StartOnStick)
|
||||
{
|
||||
comp.StartOnStick = false;
|
||||
_popupSystem.PopupEntity(Loc.GetString("popup-start-on-stick-off"), grenade, user);
|
||||
}
|
||||
else
|
||||
{
|
||||
comp.StartOnStick = true;
|
||||
_popupSystem.PopupEntity(Loc.GetString("popup-start-on-stick-on"), grenade, user);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnTimerUse(EntityUid uid, OnUseTimerTriggerComponent component, UseInHandEvent args)
|
||||
{
|
||||
if (args.Handled || HasComp<AutomatedTimerComponent>(uid) || component.UseVerbInstead)
|
||||
return;
|
||||
|
||||
if (component.DoPopup)
|
||||
_popupSystem.PopupEntity(Loc.GetString("trigger-activated", ("device", uid)), args.User, args.User);
|
||||
|
||||
StartTimer((uid, component), args.User);
|
||||
|
||||
args.Handled = true;
|
||||
}
|
||||
|
||||
public static VerbCategory TimerOptions = new("verb-categories-timer", "/Textures/Interface/VerbIcons/clock.svg.192dpi.png");
|
||||
}
|
||||
@@ -1,154 +0,0 @@
|
||||
using Content.Server.Explosion.Components;
|
||||
using Content.Shared.Trigger;
|
||||
using Robust.Shared.Physics.Components;
|
||||
using Robust.Shared.Physics.Events;
|
||||
using Robust.Shared.Utility;
|
||||
using Robust.Shared.Timing;
|
||||
|
||||
namespace Content.Server.Explosion.EntitySystems;
|
||||
|
||||
public sealed partial class TriggerSystem
|
||||
{
|
||||
[Dependency] private readonly IGameTiming _timing = default!;
|
||||
[Dependency] private readonly SharedAppearanceSystem _appearance = default!;
|
||||
|
||||
private void InitializeProximity()
|
||||
{
|
||||
SubscribeLocalEvent<TriggerOnProximityComponent, StartCollideEvent>(OnProximityStartCollide);
|
||||
SubscribeLocalEvent<TriggerOnProximityComponent, EndCollideEvent>(OnProximityEndCollide);
|
||||
SubscribeLocalEvent<TriggerOnProximityComponent, MapInitEvent>(OnMapInit);
|
||||
SubscribeLocalEvent<TriggerOnProximityComponent, ComponentShutdown>(OnProximityShutdown);
|
||||
// Shouldn't need re-anchoring.
|
||||
SubscribeLocalEvent<TriggerOnProximityComponent, AnchorStateChangedEvent>(OnProximityAnchor);
|
||||
}
|
||||
|
||||
private void OnProximityAnchor(EntityUid uid, TriggerOnProximityComponent component, ref AnchorStateChangedEvent args)
|
||||
{
|
||||
component.Enabled = !component.RequiresAnchored ||
|
||||
args.Anchored;
|
||||
|
||||
SetProximityAppearance(uid, component);
|
||||
|
||||
if (!component.Enabled)
|
||||
{
|
||||
component.Colliding.Clear();
|
||||
}
|
||||
// Re-check for contacts as we cleared them.
|
||||
else if (TryComp<PhysicsComponent>(uid, out var body))
|
||||
{
|
||||
_broadphase.RegenerateContacts((uid, body));
|
||||
}
|
||||
}
|
||||
|
||||
private void OnProximityShutdown(EntityUid uid, TriggerOnProximityComponent component, ComponentShutdown args)
|
||||
{
|
||||
component.Colliding.Clear();
|
||||
}
|
||||
|
||||
private void OnMapInit(EntityUid uid, TriggerOnProximityComponent component, MapInitEvent args)
|
||||
{
|
||||
component.Enabled = !component.RequiresAnchored ||
|
||||
Transform(uid).Anchored;
|
||||
|
||||
SetProximityAppearance(uid, component);
|
||||
|
||||
if (!TryComp<PhysicsComponent>(uid, out var body))
|
||||
return;
|
||||
|
||||
_fixtures.TryCreateFixture(
|
||||
uid,
|
||||
component.Shape,
|
||||
TriggerOnProximityComponent.FixtureID,
|
||||
hard: false,
|
||||
body: body,
|
||||
collisionLayer: component.Layer);
|
||||
}
|
||||
|
||||
private void OnProximityStartCollide(EntityUid uid, TriggerOnProximityComponent component, ref StartCollideEvent args)
|
||||
{
|
||||
if (args.OurFixtureId != TriggerOnProximityComponent.FixtureID)
|
||||
return;
|
||||
|
||||
component.Colliding[args.OtherEntity] = args.OtherBody;
|
||||
}
|
||||
|
||||
private static void OnProximityEndCollide(EntityUid uid, TriggerOnProximityComponent component, ref EndCollideEvent args)
|
||||
{
|
||||
if (args.OurFixtureId != TriggerOnProximityComponent.FixtureID)
|
||||
return;
|
||||
|
||||
component.Colliding.Remove(args.OtherEntity);
|
||||
}
|
||||
|
||||
private void SetProximityAppearance(EntityUid uid, TriggerOnProximityComponent component)
|
||||
{
|
||||
if (TryComp(uid, out AppearanceComponent? appearance))
|
||||
{
|
||||
_appearance.SetData(uid, ProximityTriggerVisualState.State, component.Enabled ? ProximityTriggerVisuals.Inactive : ProximityTriggerVisuals.Off, appearance);
|
||||
}
|
||||
}
|
||||
|
||||
private void Activate(EntityUid uid, EntityUid user, TriggerOnProximityComponent component)
|
||||
{
|
||||
DebugTools.Assert(component.Enabled);
|
||||
|
||||
var curTime = _timing.CurTime;
|
||||
|
||||
if (!component.Repeating)
|
||||
{
|
||||
component.Enabled = false;
|
||||
component.Colliding.Clear();
|
||||
}
|
||||
else
|
||||
{
|
||||
component.NextTrigger = curTime + component.Cooldown;
|
||||
}
|
||||
|
||||
// Queue a visual update for when the animation is complete.
|
||||
component.NextVisualUpdate = curTime + component.AnimationDuration;
|
||||
|
||||
if (TryComp(uid, out AppearanceComponent? appearance))
|
||||
{
|
||||
_appearance.SetData(uid, ProximityTriggerVisualState.State, ProximityTriggerVisuals.Active, appearance);
|
||||
}
|
||||
|
||||
Trigger(uid, user);
|
||||
}
|
||||
|
||||
private void UpdateProximity()
|
||||
{
|
||||
var curTime = _timing.CurTime;
|
||||
|
||||
var query = EntityQueryEnumerator<TriggerOnProximityComponent>();
|
||||
while (query.MoveNext(out var uid, out var trigger))
|
||||
{
|
||||
if (curTime >= trigger.NextVisualUpdate)
|
||||
{
|
||||
// Update the visual state once the animation is done.
|
||||
trigger.NextVisualUpdate = TimeSpan.MaxValue;
|
||||
SetProximityAppearance(uid, trigger);
|
||||
}
|
||||
|
||||
if (!trigger.Enabled)
|
||||
continue;
|
||||
|
||||
if (curTime < trigger.NextTrigger)
|
||||
// The trigger's on cooldown.
|
||||
continue;
|
||||
|
||||
// Check for anything colliding and moving fast enough.
|
||||
foreach (var (collidingUid, colliding) in trigger.Colliding)
|
||||
{
|
||||
if (Deleted(collidingUid))
|
||||
continue;
|
||||
|
||||
if (colliding.LinearVelocity.Length() < trigger.TriggerSpeed)
|
||||
continue;
|
||||
|
||||
// Trigger!
|
||||
Activate(uid, collidingUid, trigger);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
using Content.Server.DeviceLinking.Systems;
|
||||
using Content.Server.Explosion.Components;
|
||||
using Content.Shared.DeviceLinking.Events;
|
||||
|
||||
namespace Content.Server.Explosion.EntitySystems
|
||||
{
|
||||
public sealed partial class TriggerSystem
|
||||
{
|
||||
[Dependency] private readonly DeviceLinkSystem _signalSystem = default!;
|
||||
private void InitializeSignal()
|
||||
{
|
||||
SubscribeLocalEvent<TriggerOnSignalComponent,SignalReceivedEvent>(OnSignalReceived);
|
||||
SubscribeLocalEvent<TriggerOnSignalComponent,ComponentInit>(OnInit);
|
||||
|
||||
SubscribeLocalEvent<TimerStartOnSignalComponent,SignalReceivedEvent>(OnTimerSignalReceived);
|
||||
SubscribeLocalEvent<TimerStartOnSignalComponent,ComponentInit>(OnTimerSignalInit);
|
||||
}
|
||||
|
||||
private void OnSignalReceived(EntityUid uid, TriggerOnSignalComponent component, ref SignalReceivedEvent args)
|
||||
{
|
||||
if (args.Port != component.Port)
|
||||
return;
|
||||
|
||||
Trigger(uid, args.Trigger);
|
||||
}
|
||||
private void OnInit(EntityUid uid, TriggerOnSignalComponent component, ComponentInit args)
|
||||
{
|
||||
_signalSystem.EnsureSinkPorts(uid, component.Port);
|
||||
}
|
||||
|
||||
private void OnTimerSignalReceived(EntityUid uid, TimerStartOnSignalComponent component, ref SignalReceivedEvent args)
|
||||
{
|
||||
if (args.Port != component.Port)
|
||||
return;
|
||||
|
||||
StartTimer(uid, args.Trigger);
|
||||
}
|
||||
private void OnTimerSignalInit(EntityUid uid, TimerStartOnSignalComponent component, ComponentInit args)
|
||||
{
|
||||
_signalSystem.EnsureSinkPorts(uid, component.Port);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,59 +0,0 @@
|
||||
using System.Linq;
|
||||
using Content.Server.Explosion.Components;
|
||||
using Content.Server.Explosion.EntitySystems;
|
||||
using Robust.Shared.Physics.Dynamics;
|
||||
using Robust.Shared.Physics.Events;
|
||||
|
||||
namespace Content.Server.Explosion.EntitySystems;
|
||||
|
||||
public sealed partial class TriggerSystem
|
||||
{
|
||||
private void InitializeTimedCollide()
|
||||
{
|
||||
SubscribeLocalEvent<TriggerOnTimedCollideComponent, StartCollideEvent>(OnTimerCollide);
|
||||
SubscribeLocalEvent<TriggerOnTimedCollideComponent, EndCollideEvent>(OnTimerEndCollide);
|
||||
SubscribeLocalEvent<TriggerOnTimedCollideComponent, ComponentRemove>(OnComponentRemove);
|
||||
}
|
||||
|
||||
private void OnTimerCollide(EntityUid uid, TriggerOnTimedCollideComponent component, ref StartCollideEvent args)
|
||||
{
|
||||
//Ensures the entity trigger will have an active component
|
||||
EnsureComp<ActiveTriggerOnTimedCollideComponent>(uid);
|
||||
var otherUID = args.OtherEntity;
|
||||
if (component.Colliding.ContainsKey(otherUID))
|
||||
return;
|
||||
component.Colliding.Add(otherUID, 0);
|
||||
}
|
||||
|
||||
private void OnTimerEndCollide(EntityUid uid, TriggerOnTimedCollideComponent component, ref EndCollideEvent args)
|
||||
{
|
||||
var otherUID = args.OtherEntity;
|
||||
component.Colliding.Remove(otherUID);
|
||||
|
||||
if (component.Colliding.Count == 0 && HasComp<ActiveTriggerOnTimedCollideComponent>(uid))
|
||||
RemComp<ActiveTriggerOnTimedCollideComponent>(uid);
|
||||
}
|
||||
|
||||
private void OnComponentRemove(EntityUid uid, TriggerOnTimedCollideComponent component, ComponentRemove args)
|
||||
{
|
||||
if (HasComp<ActiveTriggerOnTimedCollideComponent>(uid))
|
||||
RemComp<ActiveTriggerOnTimedCollideComponent>(uid);
|
||||
}
|
||||
|
||||
private void UpdateTimedCollide(float frameTime)
|
||||
{
|
||||
var query = EntityQueryEnumerator<ActiveTriggerOnTimedCollideComponent, TriggerOnTimedCollideComponent>();
|
||||
while (query.MoveNext(out var uid, out _, out var triggerOnTimedCollide))
|
||||
{
|
||||
foreach (var (collidingEntity, collidingTimer) in triggerOnTimedCollide.Colliding)
|
||||
{
|
||||
triggerOnTimedCollide.Colliding[collidingEntity] += frameTime;
|
||||
if (collidingTimer > triggerOnTimedCollide.Threshold)
|
||||
{
|
||||
RaiseLocalEvent(uid, new TriggerEvent(uid, collidingEntity), true);
|
||||
triggerOnTimedCollide.Colliding[collidingEntity] -= triggerOnTimedCollide.Threshold;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,154 +0,0 @@
|
||||
using Content.Server.Explosion.Components;
|
||||
using Content.Server.Speech;
|
||||
using Content.Server.Speech.Components;
|
||||
using Content.Shared.Database;
|
||||
using Content.Shared.Examine;
|
||||
using Content.Shared.Verbs;
|
||||
|
||||
namespace Content.Server.Explosion.EntitySystems
|
||||
{
|
||||
public sealed partial class TriggerSystem
|
||||
{
|
||||
private void InitializeVoice()
|
||||
{
|
||||
SubscribeLocalEvent<TriggerOnVoiceComponent, ComponentInit>(OnVoiceInit);
|
||||
SubscribeLocalEvent<TriggerOnVoiceComponent, ExaminedEvent>(OnVoiceExamine);
|
||||
SubscribeLocalEvent<TriggerOnVoiceComponent, GetVerbsEvent<AlternativeVerb>>(OnVoiceGetAltVerbs);
|
||||
SubscribeLocalEvent<TriggerOnVoiceComponent, ListenEvent>(OnListen);
|
||||
}
|
||||
|
||||
private void OnVoiceInit(EntityUid uid, TriggerOnVoiceComponent component, ComponentInit args)
|
||||
{
|
||||
if (component.IsListening)
|
||||
EnsureComp<ActiveListenerComponent>(uid).Range = component.ListenRange;
|
||||
else
|
||||
RemCompDeferred<ActiveListenerComponent>(uid);
|
||||
}
|
||||
|
||||
private void OnListen(Entity<TriggerOnVoiceComponent> ent, ref ListenEvent args)
|
||||
{
|
||||
var component = ent.Comp;
|
||||
var message = args.Message.Trim();
|
||||
|
||||
if (component.IsRecording)
|
||||
{
|
||||
var ev = new ListenAttemptEvent(args.Source);
|
||||
RaiseLocalEvent(ent, ev);
|
||||
|
||||
if (ev.Cancelled)
|
||||
return;
|
||||
|
||||
if (message.Length >= component.MinLength && message.Length <= component.MaxLength)
|
||||
FinishRecording(ent, args.Source, args.Message);
|
||||
else if (message.Length > component.MaxLength)
|
||||
_popupSystem.PopupEntity(Loc.GetString("popup-trigger-voice-record-failed-too-long"), ent);
|
||||
else if (message.Length < component.MinLength)
|
||||
_popupSystem.PopupEntity(Loc.GetString("popup-trigger-voice-record-failed-too-short"), ent);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(component.KeyPhrase) && message.IndexOf(component.KeyPhrase, StringComparison.InvariantCultureIgnoreCase) is var index and >= 0 )
|
||||
{
|
||||
_adminLogger.Add(LogType.Trigger, LogImpact.Medium,
|
||||
$"A voice-trigger on {ToPrettyString(ent):entity} was triggered by {ToPrettyString(args.Source):speaker} speaking the key-phrase {component.KeyPhrase}.");
|
||||
Trigger(ent, args.Source);
|
||||
|
||||
var messageWithoutPhrase = message.Remove(index, component.KeyPhrase.Length).Trim();
|
||||
|
||||
var voice = new VoiceTriggeredEvent(args.Source, message, messageWithoutPhrase);
|
||||
RaiseLocalEvent(ent, ref voice);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnVoiceGetAltVerbs(Entity<TriggerOnVoiceComponent> ent, ref GetVerbsEvent<AlternativeVerb> args)
|
||||
{
|
||||
if (!args.CanInteract || !args.CanAccess)
|
||||
return;
|
||||
|
||||
var component = ent.Comp;
|
||||
|
||||
var @event = args;
|
||||
args.Verbs.Add(new AlternativeVerb()
|
||||
{
|
||||
Text = Loc.GetString(component.IsRecording ? "verb-trigger-voice-stop" : "verb-trigger-voice-record"),
|
||||
Act = () =>
|
||||
{
|
||||
if (component.IsRecording)
|
||||
StopRecording(ent);
|
||||
else
|
||||
StartRecording(ent, @event.User);
|
||||
},
|
||||
Priority = 1
|
||||
});
|
||||
|
||||
if (string.IsNullOrWhiteSpace(component.KeyPhrase))
|
||||
return;
|
||||
|
||||
args.Verbs.Add(new AlternativeVerb()
|
||||
{
|
||||
Text = Loc.GetString("verb-trigger-voice-clear"),
|
||||
Act = () =>
|
||||
{
|
||||
component.KeyPhrase = null;
|
||||
component.IsRecording = false;
|
||||
RemComp<ActiveListenerComponent>(ent);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void StartRecording(Entity<TriggerOnVoiceComponent> ent, EntityUid user)
|
||||
{
|
||||
var component = ent.Comp;
|
||||
component.IsRecording = true;
|
||||
EnsureComp<ActiveListenerComponent>(ent).Range = component.ListenRange;
|
||||
|
||||
_adminLogger.Add(LogType.Trigger, LogImpact.Low,
|
||||
$"A voice-trigger on {ToPrettyString(ent):entity} has started recording. User: {ToPrettyString(user):user}");
|
||||
|
||||
_popupSystem.PopupEntity(Loc.GetString("popup-trigger-voice-start-recording"), ent);
|
||||
}
|
||||
|
||||
public void StopRecording(Entity<TriggerOnVoiceComponent> ent)
|
||||
{
|
||||
var component = ent.Comp;
|
||||
component.IsRecording = false;
|
||||
if (string.IsNullOrWhiteSpace(component.KeyPhrase))
|
||||
RemComp<ActiveListenerComponent>(ent);
|
||||
|
||||
_popupSystem.PopupEntity(Loc.GetString("popup-trigger-voice-stop-recording"), ent);
|
||||
}
|
||||
|
||||
public void FinishRecording(Entity<TriggerOnVoiceComponent> ent, EntityUid source, string message)
|
||||
{
|
||||
var component = ent.Comp;
|
||||
component.KeyPhrase = message;
|
||||
component.IsRecording = false;
|
||||
|
||||
_adminLogger.Add(LogType.Trigger, LogImpact.Low,
|
||||
$"A voice-trigger on {ToPrettyString(ent):entity} has recorded a new keyphrase: '{component.KeyPhrase}'. Recorded from {ToPrettyString(source):speaker}");
|
||||
|
||||
_popupSystem.PopupEntity(Loc.GetString("popup-trigger-voice-recorded", ("keyphrase", component.KeyPhrase!)), ent);
|
||||
}
|
||||
|
||||
private void OnVoiceExamine(EntityUid uid, TriggerOnVoiceComponent component, ExaminedEvent args)
|
||||
{
|
||||
if (args.IsInDetailsRange)
|
||||
{
|
||||
args.PushText(string.IsNullOrWhiteSpace(component.KeyPhrase)
|
||||
? Loc.GetString("trigger-voice-uninitialized")
|
||||
: Loc.GetString("examine-trigger-voice", ("keyphrase", component.KeyPhrase)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Raised when a voice trigger is activated, containing the message that triggered it.
|
||||
/// </summary>
|
||||
/// <param name="Source"> The EntityUid of the entity sending the message</param>
|
||||
/// <param name="Message"> The contents of the message</param>
|
||||
/// <param name="MessageWithoutPhrase"> The message without the phrase that triggered it.</param>
|
||||
[ByRefEvent]
|
||||
public readonly record struct VoiceTriggeredEvent(EntityUid Source, string Message, string MessageWithoutPhrase);
|
||||
@@ -1,454 +0,0 @@
|
||||
using Content.Server.Administration.Logs;
|
||||
using Content.Server.Body.Systems;
|
||||
using Content.Server.Explosion.Components;
|
||||
using Content.Shared.Flash;
|
||||
using Content.Server.Electrocution;
|
||||
using Content.Server.Pinpointer;
|
||||
using Content.Shared.Chemistry.EntitySystems;
|
||||
using Content.Shared.Flash.Components;
|
||||
using Content.Server.Radio.EntitySystems;
|
||||
using Content.Shared.Chemistry.Components;
|
||||
using Content.Shared.Chemistry.Components.SolutionManager;
|
||||
using Content.Shared.Database;
|
||||
using Content.Shared.Explosion.Components;
|
||||
using Content.Shared.Explosion.Components.OnTrigger;
|
||||
using Content.Shared.Implants.Components;
|
||||
using Content.Shared.Interaction;
|
||||
using Content.Shared.Interaction.Events;
|
||||
using Content.Shared.Inventory;
|
||||
using Content.Shared.Mobs;
|
||||
using Content.Shared.Mobs.Components;
|
||||
using Content.Shared.Payload.Components;
|
||||
using Content.Shared.Radio;
|
||||
using Content.Shared.Slippery;
|
||||
using Content.Shared.StepTrigger.Systems;
|
||||
using Content.Shared.Trigger;
|
||||
using Content.Shared.Weapons.Ranged.Events;
|
||||
using Content.Shared.Whitelist;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Shared.Audio;
|
||||
using Robust.Shared.Audio.Systems;
|
||||
using Robust.Shared.Containers;
|
||||
using Robust.Shared.Physics.Events;
|
||||
using Robust.Shared.Physics.Systems;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Random;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Server.Explosion.EntitySystems
|
||||
{
|
||||
/// <summary>
|
||||
/// Raised whenever something is Triggered on the entity.
|
||||
/// </summary>
|
||||
public sealed class TriggerEvent : HandledEntityEventArgs
|
||||
{
|
||||
public EntityUid Triggered { get; }
|
||||
public EntityUid? User { get; }
|
||||
|
||||
public TriggerEvent(EntityUid triggered, EntityUid? user = null)
|
||||
{
|
||||
Triggered = triggered;
|
||||
User = user;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Raised before a trigger is activated.
|
||||
/// </summary>
|
||||
[ByRefEvent]
|
||||
public record struct BeforeTriggerEvent(EntityUid Triggered, EntityUid? User, bool Cancelled = false);
|
||||
|
||||
/// <summary>
|
||||
/// Raised when timer trigger becomes active.
|
||||
/// </summary>
|
||||
[ByRefEvent]
|
||||
public readonly record struct ActiveTimerTriggerEvent(EntityUid Triggered, EntityUid? User);
|
||||
|
||||
[UsedImplicitly]
|
||||
public sealed partial class TriggerSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly ExplosionSystem _explosions = default!;
|
||||
[Dependency] private readonly FixtureSystem _fixtures = default!;
|
||||
[Dependency] private readonly SharedFlashSystem _flashSystem = default!;
|
||||
[Dependency] private readonly SharedBroadphaseSystem _broadphase = default!;
|
||||
[Dependency] private readonly IAdminLogManager _adminLogger = default!;
|
||||
[Dependency] private readonly SharedContainerSystem _container = default!;
|
||||
[Dependency] private readonly BodySystem _body = default!;
|
||||
[Dependency] private readonly SharedAudioSystem _audio = default!;
|
||||
[Dependency] private readonly SharedTransformSystem _transformSystem = default!;
|
||||
[Dependency] private readonly NavMapSystem _navMap = default!;
|
||||
[Dependency] private readonly RadioSystem _radioSystem = default!;
|
||||
[Dependency] private readonly IRobustRandom _random = default!;
|
||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||
[Dependency] private readonly SharedSolutionContainerSystem _solutionContainerSystem = default!;
|
||||
[Dependency] private readonly InventorySystem _inventory = default!;
|
||||
[Dependency] private readonly ElectrocutionSystem _electrocution = default!;
|
||||
[Dependency] private readonly EntityWhitelistSystem _whitelist = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
InitializeProximity();
|
||||
InitializeOnUse();
|
||||
InitializeSignal();
|
||||
InitializeTimedCollide();
|
||||
InitializeVoice();
|
||||
InitializeMobstate();
|
||||
|
||||
SubscribeLocalEvent<TriggerOnSpawnComponent, MapInitEvent>(OnSpawnTriggered);
|
||||
SubscribeLocalEvent<TriggerOnCollideComponent, StartCollideEvent>(OnTriggerCollide);
|
||||
SubscribeLocalEvent<TriggerOnActivateComponent, ActivateInWorldEvent>(OnActivate);
|
||||
SubscribeLocalEvent<TriggerOnUseComponent, UseInHandEvent>(OnUse);
|
||||
SubscribeLocalEvent<TriggerImplantActionComponent, ActivateImplantEvent>(OnImplantTrigger);
|
||||
SubscribeLocalEvent<TriggerOnStepTriggerComponent, StepTriggeredOffEvent>(OnStepTriggered);
|
||||
SubscribeLocalEvent<TriggerOnSlipComponent, SlipEvent>(OnSlipTriggered);
|
||||
SubscribeLocalEvent<TriggerWhenEmptyComponent, OnEmptyGunShotEvent>(OnEmptyTriggered);
|
||||
SubscribeLocalEvent<RepeatingTriggerComponent, MapInitEvent>(OnRepeatInit);
|
||||
|
||||
SubscribeLocalEvent<SpawnOnTriggerComponent, TriggerEvent>(OnSpawnTrigger);
|
||||
SubscribeLocalEvent<DeleteOnTriggerComponent, TriggerEvent>(HandleDeleteTrigger);
|
||||
SubscribeLocalEvent<ExplodeOnTriggerComponent, TriggerEvent>(HandleExplodeTrigger);
|
||||
SubscribeLocalEvent<FlashOnTriggerComponent, TriggerEvent>(HandleFlashTrigger);
|
||||
SubscribeLocalEvent<GibOnTriggerComponent, TriggerEvent>(HandleGibTrigger);
|
||||
|
||||
SubscribeLocalEvent<AnchorOnTriggerComponent, TriggerEvent>(OnAnchorTrigger);
|
||||
SubscribeLocalEvent<SoundOnTriggerComponent, TriggerEvent>(OnSoundTrigger);
|
||||
SubscribeLocalEvent<ShockOnTriggerComponent, TriggerEvent>(HandleShockTrigger);
|
||||
SubscribeLocalEvent<RattleComponent, TriggerEvent>(HandleRattleTrigger);
|
||||
|
||||
SubscribeLocalEvent<TriggerWhitelistComponent, BeforeTriggerEvent>(HandleWhitelist);
|
||||
}
|
||||
|
||||
private void HandleWhitelist(Entity<TriggerWhitelistComponent> ent, ref BeforeTriggerEvent args)
|
||||
{
|
||||
args.Cancelled = !_whitelist.CheckBoth(args.User, ent.Comp.Blacklist, ent.Comp.Whitelist);
|
||||
}
|
||||
|
||||
private void OnSoundTrigger(EntityUid uid, SoundOnTriggerComponent component, TriggerEvent args)
|
||||
{
|
||||
if (component.RemoveOnTrigger) // if the component gets removed when it's triggered
|
||||
{
|
||||
var xform = Transform(uid);
|
||||
_audio.PlayPvs(component.Sound, xform.Coordinates); // play the sound at its last known coordinates
|
||||
}
|
||||
else // if the component doesn't get removed when triggered
|
||||
{
|
||||
_audio.PlayPvs(component.Sound, uid); // have the sound follow the entity itself
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleShockTrigger(Entity<ShockOnTriggerComponent> shockOnTrigger, ref TriggerEvent args)
|
||||
{
|
||||
if (!_container.TryGetContainingContainer(shockOnTrigger.Owner, out var container))
|
||||
return;
|
||||
|
||||
var containerEnt = container.Owner;
|
||||
var curTime = _timing.CurTime;
|
||||
|
||||
if (curTime < shockOnTrigger.Comp.NextTrigger)
|
||||
{
|
||||
// The trigger's on cooldown.
|
||||
return;
|
||||
}
|
||||
|
||||
_electrocution.TryDoElectrocution(containerEnt, null, shockOnTrigger.Comp.Damage, shockOnTrigger.Comp.Duration, true, ignoreInsulation: true);
|
||||
shockOnTrigger.Comp.NextTrigger = curTime + shockOnTrigger.Comp.Cooldown;
|
||||
}
|
||||
|
||||
private void OnAnchorTrigger(EntityUid uid, AnchorOnTriggerComponent component, TriggerEvent args)
|
||||
{
|
||||
var xform = Transform(uid);
|
||||
|
||||
if (xform.Anchored)
|
||||
return;
|
||||
|
||||
_transformSystem.AnchorEntity(uid, xform);
|
||||
|
||||
if (component.RemoveOnTrigger)
|
||||
RemCompDeferred<AnchorOnTriggerComponent>(uid);
|
||||
}
|
||||
|
||||
private void OnSpawnTrigger(Entity<SpawnOnTriggerComponent> ent, ref TriggerEvent args)
|
||||
{
|
||||
var xform = Transform(ent);
|
||||
|
||||
if (ent.Comp.mapCoords)
|
||||
{
|
||||
var mapCoords = _transformSystem.GetMapCoordinates(ent, xform);
|
||||
Spawn(ent.Comp.Proto, mapCoords);
|
||||
}
|
||||
else
|
||||
{
|
||||
var coords = xform.Coordinates;
|
||||
if (!coords.IsValid(EntityManager))
|
||||
return;
|
||||
Spawn(ent.Comp.Proto, coords);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleExplodeTrigger(EntityUid uid, ExplodeOnTriggerComponent component, TriggerEvent args)
|
||||
{
|
||||
_explosions.TriggerExplosive(uid, user: args.User);
|
||||
args.Handled = true;
|
||||
}
|
||||
|
||||
private void HandleFlashTrigger(EntityUid uid, FlashOnTriggerComponent component, TriggerEvent args)
|
||||
{
|
||||
_flashSystem.FlashArea(uid, args.User, component.Range, component.Duration, probability: component.Probability);
|
||||
args.Handled = true;
|
||||
}
|
||||
|
||||
private void HandleDeleteTrigger(EntityUid uid, DeleteOnTriggerComponent component, TriggerEvent args)
|
||||
{
|
||||
QueueDel(uid);
|
||||
args.Handled = true;
|
||||
}
|
||||
|
||||
private void HandleGibTrigger(EntityUid uid, GibOnTriggerComponent component, TriggerEvent args)
|
||||
{
|
||||
if (!TryComp(uid, out TransformComponent? xform))
|
||||
return;
|
||||
if (component.DeleteItems)
|
||||
{
|
||||
var items = _inventory.GetHandOrInventoryEntities(xform.ParentUid);
|
||||
foreach (var item in items)
|
||||
{
|
||||
Del(item);
|
||||
}
|
||||
}
|
||||
_body.GibBody(xform.ParentUid, true);
|
||||
args.Handled = true;
|
||||
}
|
||||
|
||||
|
||||
private void HandleRattleTrigger(EntityUid uid, RattleComponent component, TriggerEvent args)
|
||||
{
|
||||
if (!TryComp<SubdermalImplantComponent>(uid, out var implanted))
|
||||
return;
|
||||
|
||||
if (implanted.ImplantedEntity == null)
|
||||
return;
|
||||
|
||||
// Gets location of the implant
|
||||
var posText = FormattedMessage.RemoveMarkupOrThrow(_navMap.GetNearestBeaconString(uid));
|
||||
var critMessage = Loc.GetString(component.CritMessage, ("user", implanted.ImplantedEntity.Value), ("position", posText));
|
||||
var deathMessage = Loc.GetString(component.DeathMessage, ("user", implanted.ImplantedEntity.Value), ("position", posText));
|
||||
|
||||
if (!TryComp<MobStateComponent>(implanted.ImplantedEntity, out var mobstate))
|
||||
return;
|
||||
|
||||
// Sends a message to the radio channel specified by the implant
|
||||
if (mobstate.CurrentState == MobState.Critical)
|
||||
_radioSystem.SendRadioMessage(uid, critMessage, _prototypeManager.Index<RadioChannelPrototype>(component.RadioChannel), uid);
|
||||
if (mobstate.CurrentState == MobState.Dead)
|
||||
_radioSystem.SendRadioMessage(uid, deathMessage, _prototypeManager.Index<RadioChannelPrototype>(component.RadioChannel), uid);
|
||||
|
||||
args.Handled = true;
|
||||
}
|
||||
|
||||
private void OnTriggerCollide(EntityUid uid, TriggerOnCollideComponent component, ref StartCollideEvent args)
|
||||
{
|
||||
if (args.OurFixtureId == component.FixtureID && (!component.IgnoreOtherNonHard || args.OtherFixture.Hard))
|
||||
Trigger(uid, args.OtherEntity);
|
||||
}
|
||||
|
||||
private void OnSpawnTriggered(EntityUid uid, TriggerOnSpawnComponent component, MapInitEvent args)
|
||||
{
|
||||
Trigger(uid);
|
||||
}
|
||||
|
||||
private void OnActivate(EntityUid uid, TriggerOnActivateComponent component, ActivateInWorldEvent args)
|
||||
{
|
||||
if (args.Handled || !args.Complex)
|
||||
return;
|
||||
|
||||
Trigger(uid, args.User);
|
||||
args.Handled = true;
|
||||
}
|
||||
|
||||
private void OnUse(Entity<TriggerOnUseComponent> ent, ref UseInHandEvent args)
|
||||
{
|
||||
if (args.Handled)
|
||||
return;
|
||||
|
||||
Trigger(ent.Owner, args.User);
|
||||
args.Handled = true;
|
||||
}
|
||||
|
||||
private void OnImplantTrigger(EntityUid uid, TriggerImplantActionComponent component, ActivateImplantEvent args)
|
||||
{
|
||||
args.Handled = Trigger(uid);
|
||||
}
|
||||
|
||||
private void OnStepTriggered(EntityUid uid, TriggerOnStepTriggerComponent component, ref StepTriggeredOffEvent args)
|
||||
{
|
||||
Trigger(uid, args.Tripper);
|
||||
}
|
||||
|
||||
private void OnSlipTriggered(EntityUid uid, TriggerOnSlipComponent component, ref SlipEvent args)
|
||||
{
|
||||
Trigger(uid, args.Slipped);
|
||||
}
|
||||
|
||||
private void OnEmptyTriggered(EntityUid uid, TriggerWhenEmptyComponent component, ref OnEmptyGunShotEvent args)
|
||||
{
|
||||
Trigger(uid, args.EmptyGun);
|
||||
}
|
||||
|
||||
private void OnRepeatInit(Entity<RepeatingTriggerComponent> ent, ref MapInitEvent args)
|
||||
{
|
||||
ent.Comp.NextTrigger = _timing.CurTime + ent.Comp.Delay;
|
||||
}
|
||||
|
||||
public bool Trigger(EntityUid trigger, EntityUid? user = null)
|
||||
{
|
||||
var beforeTriggerEvent = new BeforeTriggerEvent(trigger, user);
|
||||
RaiseLocalEvent(trigger, ref beforeTriggerEvent);
|
||||
if (beforeTriggerEvent.Cancelled)
|
||||
return false;
|
||||
|
||||
var triggerEvent = new TriggerEvent(trigger, user);
|
||||
EntityManager.EventBus.RaiseLocalEvent(trigger, triggerEvent, true);
|
||||
return triggerEvent.Handled;
|
||||
}
|
||||
|
||||
public void TryDelay(EntityUid uid, float amount, ActiveTimerTriggerComponent? comp = null)
|
||||
{
|
||||
if (!Resolve(uid, ref comp, false))
|
||||
return;
|
||||
|
||||
comp.TimeRemaining += amount;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Start the timer for triggering the device.
|
||||
/// </summary>
|
||||
public void StartTimer(Entity<OnUseTimerTriggerComponent?> ent, EntityUid? user)
|
||||
{
|
||||
if (!Resolve(ent, ref ent.Comp, false))
|
||||
return;
|
||||
|
||||
var comp = ent.Comp;
|
||||
HandleTimerTrigger(ent, user, comp.Delay, comp.BeepInterval, comp.InitialBeepDelay, comp.BeepSound);
|
||||
}
|
||||
|
||||
public void HandleTimerTrigger(EntityUid uid, EntityUid? user, float delay, float beepInterval, float? initialBeepDelay, SoundSpecifier? beepSound)
|
||||
{
|
||||
if (delay <= 0)
|
||||
{
|
||||
RemComp<ActiveTimerTriggerComponent>(uid);
|
||||
Trigger(uid, user);
|
||||
return;
|
||||
}
|
||||
|
||||
if (HasComp<ActiveTimerTriggerComponent>(uid))
|
||||
return;
|
||||
|
||||
if (user != null)
|
||||
{
|
||||
// Check if entity is bomb/mod. grenade/etc
|
||||
if (_container.TryGetContainer(uid, "payload", out BaseContainer? container) &&
|
||||
container.ContainedEntities.Count > 0 &&
|
||||
TryComp(container.ContainedEntities[0], out ChemicalPayloadComponent? chemicalPayloadComponent))
|
||||
{
|
||||
// If a beaker is missing, the entity won't explode, so no reason to log it
|
||||
if (chemicalPayloadComponent?.BeakerSlotA.Item is not { } beakerA ||
|
||||
chemicalPayloadComponent?.BeakerSlotB.Item is not { } beakerB ||
|
||||
!TryComp(beakerA, out SolutionContainerManagerComponent? containerA) ||
|
||||
!TryComp(beakerB, out SolutionContainerManagerComponent? containerB) ||
|
||||
!TryComp(beakerA, out FitsInDispenserComponent? fitsA) ||
|
||||
!TryComp(beakerB, out FitsInDispenserComponent? fitsB) ||
|
||||
!_solutionContainerSystem.TryGetSolution((beakerA, containerA), fitsA.Solution, out _, out var solutionA) ||
|
||||
!_solutionContainerSystem.TryGetSolution((beakerB, containerB), fitsB.Solution, out _, out var solutionB))
|
||||
return;
|
||||
|
||||
_adminLogger.Add(LogType.Trigger,
|
||||
$"{ToPrettyString(user.Value):user} started a {delay} second timer trigger on entity {ToPrettyString(uid):timer}, which contains {SharedSolutionContainerSystem.ToPrettyString(solutionA)} in one beaker and {SharedSolutionContainerSystem.ToPrettyString(solutionB)} in the other.");
|
||||
}
|
||||
else
|
||||
{
|
||||
_adminLogger.Add(LogType.Trigger,
|
||||
$"{ToPrettyString(user.Value):user} started a {delay} second timer trigger on entity {ToPrettyString(uid):timer}");
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
_adminLogger.Add(LogType.Trigger,
|
||||
$"{delay} second timer trigger started on entity {ToPrettyString(uid):timer}");
|
||||
}
|
||||
|
||||
var active = AddComp<ActiveTimerTriggerComponent>(uid);
|
||||
active.TimeRemaining = delay;
|
||||
active.User = user;
|
||||
active.BeepSound = beepSound;
|
||||
active.BeepInterval = beepInterval;
|
||||
active.TimeUntilBeep = initialBeepDelay == null ? active.BeepInterval : initialBeepDelay.Value;
|
||||
|
||||
var ev = new ActiveTimerTriggerEvent(uid, user);
|
||||
RaiseLocalEvent(uid, ref ev);
|
||||
|
||||
if (TryComp<AppearanceComponent>(uid, out var appearance))
|
||||
_appearance.SetData(uid, TriggerVisuals.VisualState, TriggerVisualState.Primed, appearance);
|
||||
}
|
||||
|
||||
public override void Update(float frameTime)
|
||||
{
|
||||
base.Update(frameTime);
|
||||
|
||||
UpdateProximity();
|
||||
UpdateTimer(frameTime);
|
||||
UpdateTimedCollide(frameTime);
|
||||
UpdateRepeat();
|
||||
}
|
||||
|
||||
private void UpdateTimer(float frameTime)
|
||||
{
|
||||
HashSet<EntityUid> toRemove = new();
|
||||
var query = EntityQueryEnumerator<ActiveTimerTriggerComponent>();
|
||||
while (query.MoveNext(out var uid, out var timer))
|
||||
{
|
||||
timer.TimeRemaining -= frameTime;
|
||||
timer.TimeUntilBeep -= frameTime;
|
||||
|
||||
if (timer.TimeRemaining <= 0)
|
||||
{
|
||||
Trigger(uid, timer.User);
|
||||
toRemove.Add(uid);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (timer.BeepSound == null || timer.TimeUntilBeep > 0)
|
||||
continue;
|
||||
|
||||
timer.TimeUntilBeep += timer.BeepInterval;
|
||||
_audio.PlayPvs(timer.BeepSound, uid, timer.BeepSound.Params);
|
||||
}
|
||||
|
||||
foreach (var uid in toRemove)
|
||||
{
|
||||
RemComp<ActiveTimerTriggerComponent>(uid);
|
||||
|
||||
// In case this is a re-usable grenade, un-prime it.
|
||||
if (TryComp<AppearanceComponent>(uid, out var appearance))
|
||||
_appearance.SetData(uid, TriggerVisuals.VisualState, TriggerVisualState.Unprimed, appearance);
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateRepeat()
|
||||
{
|
||||
var now = _timing.CurTime;
|
||||
var query = EntityQueryEnumerator<RepeatingTriggerComponent>();
|
||||
while (query.MoveNext(out var uid, out var comp))
|
||||
{
|
||||
if (comp.NextTrigger > now)
|
||||
continue;
|
||||
|
||||
comp.NextTrigger = now + comp.Delay;
|
||||
Trigger(uid);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,64 +0,0 @@
|
||||
using Robust.Shared.Timing;
|
||||
using Robust.Shared.Serialization.Manager;
|
||||
using Content.Server.Explosion.Components.OnTrigger;
|
||||
|
||||
namespace Content.Server.Explosion.EntitySystems;
|
||||
|
||||
public sealed class TwoStageTriggerSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly IGameTiming _timing = default!;
|
||||
[Dependency] private readonly ISerializationManager _serializationManager = default!;
|
||||
[Dependency] private readonly TriggerSystem _triggerSystem = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
SubscribeLocalEvent<TwoStageTriggerComponent, TriggerEvent>(OnTrigger);
|
||||
}
|
||||
|
||||
private void OnTrigger(EntityUid uid, TwoStageTriggerComponent component, TriggerEvent args)
|
||||
{
|
||||
if (component.Triggered)
|
||||
return;
|
||||
|
||||
component.Triggered = true;
|
||||
component.NextTriggerTime = _timing.CurTime + component.TriggerDelay;
|
||||
}
|
||||
|
||||
private void LoadComponents(EntityUid uid, TwoStageTriggerComponent component)
|
||||
{
|
||||
foreach (var (name, entry) in component.SecondStageComponents)
|
||||
{
|
||||
var comp = (Component) Factory.GetComponent(name);
|
||||
var temp = (object)comp;
|
||||
|
||||
if (EntityManager.TryGetComponent(uid, entry.Component.GetType(), out var c))
|
||||
RemComp(uid, c);
|
||||
|
||||
_serializationManager.CopyTo(entry.Component, ref temp);
|
||||
AddComp(uid, comp);
|
||||
}
|
||||
component.ComponentsIsLoaded = true;
|
||||
}
|
||||
|
||||
public override void Update(float frameTime)
|
||||
{
|
||||
base.Update(frameTime);
|
||||
|
||||
var enumerator = EntityQueryEnumerator<TwoStageTriggerComponent>();
|
||||
while (enumerator.MoveNext(out var uid, out var component))
|
||||
{
|
||||
if (!component.Triggered)
|
||||
continue;
|
||||
|
||||
if (!component.ComponentsIsLoaded)
|
||||
LoadComponents(uid, component);
|
||||
|
||||
if (_timing.CurTime < component.NextTriggerTime)
|
||||
continue;
|
||||
|
||||
component.NextTriggerTime = null;
|
||||
_triggerSystem.Trigger(uid);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
namespace Content.Server.GhostKick;
|
||||
|
||||
[RegisterComponent]
|
||||
public sealed partial class GhostKickUserOnTriggerComponent : Component
|
||||
{
|
||||
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
using Content.Server.Explosion.EntitySystems;
|
||||
using Robust.Shared.Player;
|
||||
|
||||
namespace Content.Server.GhostKick;
|
||||
|
||||
public sealed class GhostKickUserOnTriggerSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly GhostKickManager _ghostKickManager = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
SubscribeLocalEvent<GhostKickUserOnTriggerComponent, TriggerEvent>(HandleMineTriggered);
|
||||
}
|
||||
|
||||
private void HandleMineTriggered(EntityUid uid, GhostKickUserOnTriggerComponent userOnTriggerComponent, TriggerEvent args)
|
||||
{
|
||||
if (!TryComp(args.User, out ActorComponent? actor))
|
||||
return;
|
||||
|
||||
_ghostKickManager.DoDisconnect(
|
||||
actor.PlayerSession.Channel,
|
||||
"Tripped over a kick mine, crashed through the fourth wall");
|
||||
|
||||
args.Handled = true;
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
using Content.Server.Chat.Systems;
|
||||
using Content.Server.Popups;
|
||||
using Content.Server.Power.EntitySystems;
|
||||
using Content.Server.Speech.Components;
|
||||
using Content.Server.Telephone;
|
||||
using Content.Shared.Access.Systems;
|
||||
using Content.Shared.Audio;
|
||||
@@ -12,6 +11,7 @@ using Content.Shared.Labels.Components;
|
||||
using Content.Shared.Power;
|
||||
using Content.Shared.Silicons.StationAi;
|
||||
using Content.Shared.Speech;
|
||||
using Content.Shared.Speech.Components;
|
||||
using Content.Shared.Telephone;
|
||||
using Content.Shared.UserInterface;
|
||||
using Content.Shared.Verbs;
|
||||
@@ -560,7 +560,7 @@ public sealed class HolopadSystem : SharedHolopadSystem
|
||||
entity.Comp.User = (user.Value, holopadUser);
|
||||
}
|
||||
|
||||
// Add the new user to PVS and sync their appearance with any
|
||||
// Add the new user to PVS and sync their appearance with any
|
||||
// holopads connected to the one they are using
|
||||
_pvs.AddGlobalOverride(user.Value);
|
||||
SyncHolopadHologramAppearanceWithTarget(entity, entity.Comp.User);
|
||||
|
||||
@@ -1,61 +1,5 @@
|
||||
using Content.Server.Audio;
|
||||
using Content.Server.Explosion.EntitySystems;
|
||||
using Content.Shared.Damage.Systems;
|
||||
using Content.Shared.Hands.Components;
|
||||
using Content.Shared.Hands.EntitySystems;
|
||||
using Content.Shared.HotPotato;
|
||||
using Content.Shared.Popups;
|
||||
using Content.Shared.Weapons.Melee.Events;
|
||||
|
||||
namespace Content.Server.HotPotato;
|
||||
|
||||
public sealed class HotPotatoSystem : SharedHotPotatoSystem
|
||||
{
|
||||
[Dependency] private readonly SharedHandsSystem _hands = default!;
|
||||
[Dependency] private readonly SharedPopupSystem _popup = default!;
|
||||
[Dependency] private readonly AmbientSoundSystem _ambientSound = default!;
|
||||
[Dependency] private readonly DamageOnHoldingSystem _damageOnHolding = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
SubscribeLocalEvent<HotPotatoComponent, ActiveTimerTriggerEvent>(OnActiveTimer);
|
||||
SubscribeLocalEvent<HotPotatoComponent, MeleeHitEvent>(OnMeleeHit);
|
||||
}
|
||||
|
||||
private void OnActiveTimer(EntityUid uid, HotPotatoComponent comp, ref ActiveTimerTriggerEvent args)
|
||||
{
|
||||
EnsureComp<ActiveHotPotatoComponent>(uid);
|
||||
comp.CanTransfer = false;
|
||||
_ambientSound.SetAmbience(uid, true);
|
||||
_damageOnHolding.SetEnabled(uid, true);
|
||||
Dirty(uid, comp);
|
||||
}
|
||||
|
||||
private void OnMeleeHit(EntityUid uid, HotPotatoComponent comp, MeleeHitEvent args)
|
||||
{
|
||||
if (!HasComp<ActiveHotPotatoComponent>(uid))
|
||||
return;
|
||||
|
||||
comp.CanTransfer = true;
|
||||
foreach (var hitEntity in args.HitEntities)
|
||||
{
|
||||
if (!TryComp<HandsComponent>(hitEntity, out var hands))
|
||||
continue;
|
||||
|
||||
if (!_hands.IsHolding((hitEntity, hands), uid, out _) && _hands.TryForcePickupAnyHand(hitEntity, uid, handsComp: hands))
|
||||
{
|
||||
_popup.PopupEntity(Loc.GetString("hot-potato-passed",
|
||||
("from", args.User), ("to", hitEntity)), uid, PopupType.Medium);
|
||||
break;
|
||||
}
|
||||
|
||||
_popup.PopupEntity(Loc.GetString("hot-potato-failed",
|
||||
("to", hitEntity)), uid, PopupType.Medium);
|
||||
|
||||
break;
|
||||
}
|
||||
comp.CanTransfer = false;
|
||||
Dirty(uid, comp);
|
||||
}
|
||||
}
|
||||
public sealed class HotPotatoSystem : SharedHotPotatoSystem;
|
||||
|
||||
@@ -1,30 +0,0 @@
|
||||
using Robust.Shared.Audio;
|
||||
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom;
|
||||
|
||||
namespace Content.Server.IgnitionSource;
|
||||
|
||||
/// <summary>
|
||||
/// Ignites for a certain length of time when triggered.
|
||||
/// Requires <see cref="Shared.IgnitionSourceComponent"/> along with triggering components.
|
||||
/// </summary>
|
||||
[RegisterComponent, Access(typeof(IgniteOnTriggerSystem))]
|
||||
public sealed partial class IgniteOnTriggerComponent : Component
|
||||
{
|
||||
/// <summary>
|
||||
/// Once ignited, the time it will unignite at.
|
||||
/// </summary>
|
||||
[DataField(customTypeSerializer: typeof(TimeOffsetSerializer)), ViewVariables(VVAccess.ReadWrite)]
|
||||
public TimeSpan IgnitedUntil = TimeSpan.Zero;
|
||||
|
||||
/// <summary>
|
||||
/// How long the ignition source is active for after triggering.
|
||||
/// </summary>
|
||||
[DataField, ViewVariables(VVAccess.ReadWrite)]
|
||||
public TimeSpan IgnitedTime = TimeSpan.FromSeconds(0.5);
|
||||
|
||||
/// <summary>
|
||||
/// Sound to play when igniting.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public SoundSpecifier IgniteSound = new SoundCollectionSpecifier("WelderOn");
|
||||
}
|
||||
@@ -1,9 +1,9 @@
|
||||
using Content.Server.Explosion.EntitySystems;
|
||||
using Content.Shared.Armable;
|
||||
using Content.Shared.Item.ItemToggle.Components;
|
||||
using Content.Shared.LandMines;
|
||||
using Content.Shared.Popups;
|
||||
using Content.Shared.StepTrigger.Systems;
|
||||
using Content.Shared.Trigger.Systems;
|
||||
using Robust.Shared.Audio.Systems;
|
||||
|
||||
namespace Content.Server.LandMines;
|
||||
@@ -28,15 +28,15 @@ public sealed class LandMineSystem : EntitySystem
|
||||
/// </summary>
|
||||
private void HandleStepOnTriggered(EntityUid uid, LandMineComponent component, ref StepTriggeredOnEvent args)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(component.TriggerText))
|
||||
{
|
||||
_popupSystem.PopupCoordinates(
|
||||
Loc.GetString(component.TriggerText, ("mine", uid)),
|
||||
Transform(uid).Coordinates,
|
||||
args.Tripper,
|
||||
PopupType.LargeCaution);
|
||||
}
|
||||
_audioSystem.PlayPvs(component.Sound, uid);
|
||||
if (!string.IsNullOrEmpty(component.TriggerText))
|
||||
{
|
||||
_popupSystem.PopupCoordinates(
|
||||
Loc.GetString(component.TriggerText, ("mine", uid)),
|
||||
Transform(uid).Coordinates,
|
||||
args.Tripper,
|
||||
PopupType.LargeCaution);
|
||||
}
|
||||
_audioSystem.PlayPvs(component.Sound, uid);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -44,7 +44,8 @@ public sealed class LandMineSystem : EntitySystem
|
||||
/// </summary>
|
||||
private void HandleStepOffTriggered(EntityUid uid, LandMineComponent component, ref StepTriggeredOffEvent args)
|
||||
{
|
||||
_trigger.Trigger(uid, args.Tripper);
|
||||
// TODO: Adjust to the new trigger system
|
||||
_trigger.Trigger(uid, args.Tripper, TriggerSystem.DefaultTriggerKey);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -1,79 +0,0 @@
|
||||
using Content.Server.Damage.Systems;
|
||||
using Content.Server.Explosion.EntitySystems;
|
||||
using Content.Server.Popups;
|
||||
using Content.Shared.Interaction.Events;
|
||||
using Content.Shared.Inventory;
|
||||
using Content.Shared.Mousetrap;
|
||||
using Content.Shared.StepTrigger;
|
||||
using Content.Shared.StepTrigger.Systems;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Shared.Physics.Components;
|
||||
using Robust.Shared.Player;
|
||||
|
||||
namespace Content.Server.Mousetrap;
|
||||
|
||||
public sealed class MousetrapSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly PopupSystem _popupSystem = default!;
|
||||
[Dependency] private readonly SharedAppearanceSystem _appearance = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
SubscribeLocalEvent<MousetrapComponent, UseInHandEvent>(OnUseInHand);
|
||||
SubscribeLocalEvent<MousetrapComponent, BeforeDamageUserOnTriggerEvent>(BeforeDamageOnTrigger);
|
||||
SubscribeLocalEvent<MousetrapComponent, StepTriggerAttemptEvent>(OnStepTriggerAttempt);
|
||||
SubscribeLocalEvent<MousetrapComponent, TriggerEvent>(OnTrigger);
|
||||
}
|
||||
|
||||
private void OnUseInHand(EntityUid uid, MousetrapComponent component, UseInHandEvent args)
|
||||
{
|
||||
if (args.Handled)
|
||||
return;
|
||||
|
||||
component.IsActive = !component.IsActive;
|
||||
_popupSystem.PopupEntity(component.IsActive
|
||||
? Loc.GetString("mousetrap-on-activate")
|
||||
: Loc.GetString("mousetrap-on-deactivate"),
|
||||
uid,
|
||||
args.User);
|
||||
|
||||
UpdateVisuals(uid);
|
||||
|
||||
args.Handled = true;
|
||||
}
|
||||
|
||||
private void OnStepTriggerAttempt(EntityUid uid, MousetrapComponent component, ref StepTriggerAttemptEvent args)
|
||||
{
|
||||
args.Continue |= component.IsActive;
|
||||
}
|
||||
|
||||
private void BeforeDamageOnTrigger(EntityUid uid, MousetrapComponent component, BeforeDamageUserOnTriggerEvent args)
|
||||
{
|
||||
if (TryComp(args.Tripper, out PhysicsComponent? physics) && physics.Mass != 0)
|
||||
{
|
||||
// The idea here is inverse,
|
||||
// Small - big damage,
|
||||
// Large - small damage
|
||||
// yes i punched numbers into a calculator until the graph looked right
|
||||
var scaledDamage = -50 * Math.Atan(physics.Mass - component.MassBalance) + (25 * Math.PI);
|
||||
args.Damage *= scaledDamage;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnTrigger(EntityUid uid, MousetrapComponent component, TriggerEvent args)
|
||||
{
|
||||
component.IsActive = false;
|
||||
UpdateVisuals(uid);
|
||||
}
|
||||
|
||||
private void UpdateVisuals(EntityUid uid, MousetrapComponent? mousetrap = null, AppearanceComponent? appearance = null)
|
||||
{
|
||||
if (!Resolve(uid, ref mousetrap, ref appearance, false))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_appearance.SetData(uid, MousetrapVisuals.Visual,
|
||||
mousetrap.IsActive ? MousetrapVisuals.Armed : MousetrapVisuals.Unarmed, appearance);
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,3 @@
|
||||
using Content.Server.Explosion.EntitySystems;
|
||||
using Content.Server.Mind;
|
||||
using Content.Server.Objectives.Components;
|
||||
using Content.Server.Popups;
|
||||
@@ -7,6 +6,7 @@ using Content.Shared.Ninja.Components;
|
||||
using Content.Shared.Ninja.Systems;
|
||||
using Content.Shared.Roles;
|
||||
using Content.Shared.Sticky;
|
||||
using Content.Shared.Trigger;
|
||||
|
||||
namespace Content.Server.Ninja.Systems;
|
||||
|
||||
@@ -80,6 +80,9 @@ public sealed class SpiderChargeSystem : SharedSpiderChargeSystem
|
||||
/// </summary>
|
||||
private void OnExplode(EntityUid uid, SpiderChargeComponent comp, TriggerEvent args)
|
||||
{
|
||||
if (args.Key != comp.TriggerKey)
|
||||
return;
|
||||
|
||||
if (!TryComp<SpaceNinjaComponent>(comp.Planter, out var ninja))
|
||||
return;
|
||||
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
using Content.Server.Explosion.EntitySystems;
|
||||
using Content.Server.Fluids.EntitySystems;
|
||||
using Content.Server.Nutrition.Components;
|
||||
using Content.Server.Popups;
|
||||
using Content.Shared.Containers.ItemSlots;
|
||||
using Content.Shared.Explosion.Components;
|
||||
using Content.Shared.IdentityManagement;
|
||||
using Content.Shared.Nutrition;
|
||||
using Content.Shared.Nutrition.Components;
|
||||
using Content.Shared.Nutrition.EntitySystems;
|
||||
using Content.Shared.Rejuvenate;
|
||||
using Content.Shared.Throwing;
|
||||
using Content.Shared.Trigger.Components;
|
||||
using Content.Shared.Trigger.Systems;
|
||||
using Content.Shared.Chemistry.EntitySystems;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Shared.Audio;
|
||||
@@ -77,15 +77,9 @@ namespace Content.Server.Nutrition.EntitySystems
|
||||
{
|
||||
if (_itemSlots.TryEject(uid, itemSlot, user: null, out var item))
|
||||
{
|
||||
if (TryComp<OnUseTimerTriggerComponent>(item.Value, out var timerTrigger))
|
||||
if (TryComp<TimerTriggerComponent>(item.Value, out var timerTrigger))
|
||||
{
|
||||
_trigger.HandleTimerTrigger(
|
||||
item.Value,
|
||||
null,
|
||||
timerTrigger.Delay,
|
||||
timerTrigger.BeepInterval,
|
||||
timerTrigger.InitialBeepDelay,
|
||||
timerTrigger.BeepSound);
|
||||
_trigger.ActivateTimerTrigger((item.Value, timerTrigger));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
using Content.Server.Administration.Logs;
|
||||
using Content.Server.Explosion.EntitySystems;
|
||||
using Content.Shared.Chemistry.Components;
|
||||
using Content.Shared.Database;
|
||||
using Content.Shared.Examine;
|
||||
using Content.Shared.Payload.Components;
|
||||
using Content.Shared.Tag;
|
||||
using Content.Shared.Trigger;
|
||||
using Content.Shared.Chemistry.EntitySystems;
|
||||
using Robust.Shared.Containers;
|
||||
using Robust.Shared.Serialization.Manager;
|
||||
@@ -54,18 +54,22 @@ public sealed class PayloadSystem : EntitySystem
|
||||
|
||||
private void OnCaseTriggered(EntityUid uid, PayloadCaseComponent component, TriggerEvent args)
|
||||
{
|
||||
// TODO: Adjust to the new trigger system
|
||||
|
||||
if (!TryComp(uid, out ContainerManagerComponent? contMan))
|
||||
return;
|
||||
|
||||
// Pass trigger event onto all contained payloads. Payload capacity configurable by construction graphs.
|
||||
foreach (var ent in GetAllPayloads(uid, contMan))
|
||||
{
|
||||
RaiseLocalEvent(ent, args, false);
|
||||
RaiseLocalEvent(ent, ref args, false);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnTriggerTriggered(EntityUid uid, PayloadTriggerComponent component, TriggerEvent args)
|
||||
{
|
||||
// TODO: Adjust to the new trigger system
|
||||
|
||||
if (!component.Active)
|
||||
return;
|
||||
|
||||
@@ -75,7 +79,7 @@ public sealed class PayloadSystem : EntitySystem
|
||||
// Ensure we don't enter a trigger-loop
|
||||
DebugTools.Assert(!_tagSystem.HasTag(uid, PayloadTag));
|
||||
|
||||
RaiseLocalEvent(parent, args, false);
|
||||
RaiseLocalEvent(parent, ref args);
|
||||
}
|
||||
|
||||
private void OnEntityInserted(EntityUid uid, PayloadCaseComponent _, EntInsertedIntoContainerMessage args)
|
||||
@@ -146,6 +150,7 @@ public sealed class PayloadSystem : EntitySystem
|
||||
|
||||
private void HandleChemicalPayloadTrigger(Entity<ChemicalPayloadComponent> entity, ref TriggerEvent args)
|
||||
{
|
||||
// TODO: Adjust to the new trigger system
|
||||
if (entity.Comp.BeakerSlotA.Item is not EntityUid beakerA
|
||||
|| entity.Comp.BeakerSlotB.Item is not EntityUid beakerB
|
||||
|| !TryComp(beakerA, out FitsInDispenserComponent? compA)
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
using Content.Shared.Polymorph;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Server.Polymorph.Components;
|
||||
|
||||
/// <summary>
|
||||
/// Intended for use with the trigger system.
|
||||
/// Polymorphs the user of the trigger.
|
||||
/// </summary>
|
||||
[RegisterComponent]
|
||||
public sealed partial class PolymorphOnTriggerComponent : Component
|
||||
{
|
||||
/// <summary>
|
||||
/// Polymorph settings.
|
||||
/// </summary>
|
||||
[DataField(required: true)]
|
||||
public ProtoId<PolymorphPrototype> Polymorph;
|
||||
}
|
||||
@@ -1,41 +0,0 @@
|
||||
using Content.Shared.Polymorph;
|
||||
using Content.Server.Polymorph.Components;
|
||||
using Content.Server.Explosion.EntitySystems;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Server.Polymorph.Systems;
|
||||
|
||||
public sealed partial class PolymorphSystem
|
||||
{
|
||||
/// <summary>
|
||||
/// Need to do this so we don't get a collection enumeration error in physics by polymorphing
|
||||
/// an entity we're colliding with in case of TriggerOnCollide.
|
||||
/// Also makes sure other trigger effects don't activate in nullspace after we have polymorphed.
|
||||
/// </summary>
|
||||
private Queue<(EntityUid Ent, ProtoId<PolymorphPrototype> Polymorph)> _queuedPolymorphUpdates = new();
|
||||
|
||||
private void InitializeTrigger()
|
||||
{
|
||||
SubscribeLocalEvent<PolymorphOnTriggerComponent, TriggerEvent>(OnTrigger);
|
||||
}
|
||||
|
||||
private void OnTrigger(Entity<PolymorphOnTriggerComponent> ent, ref TriggerEvent args)
|
||||
{
|
||||
if (args.User == null)
|
||||
return;
|
||||
|
||||
_queuedPolymorphUpdates.Enqueue((args.User.Value, ent.Comp.Polymorph));
|
||||
args.Handled = true;
|
||||
}
|
||||
|
||||
public void UpdateTrigger()
|
||||
{
|
||||
while (_queuedPolymorphUpdates.TryDequeue(out var data))
|
||||
{
|
||||
if (TerminatingOrDeleted(data.Item1))
|
||||
continue;
|
||||
|
||||
PolymorphEntity(data.Item1, data.Item2);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -58,7 +58,6 @@ public sealed partial class PolymorphSystem : EntitySystem
|
||||
SubscribeLocalEvent<PolymorphedEntityComponent, DestructionEventArgs>(OnDestruction);
|
||||
|
||||
InitializeMap();
|
||||
InitializeTrigger();
|
||||
}
|
||||
|
||||
public override void Update(float frameTime)
|
||||
@@ -85,8 +84,6 @@ public sealed partial class PolymorphSystem : EntitySystem
|
||||
Revert((uid, comp));
|
||||
}
|
||||
}
|
||||
|
||||
UpdateTrigger();
|
||||
}
|
||||
|
||||
private void OnComponentStartup(Entity<PolymorphableComponent> ent, ref ComponentStartup args)
|
||||
|
||||
@@ -2,15 +2,14 @@ using System.Linq;
|
||||
using Content.Server.Chat.Systems;
|
||||
using Content.Server.Interaction;
|
||||
using Content.Server.Popups;
|
||||
using Content.Server.Power.Components;
|
||||
using Content.Server.Power.EntitySystems;
|
||||
using Content.Server.Radio.Components;
|
||||
using Content.Server.Speech;
|
||||
using Content.Server.Speech.Components;
|
||||
using Content.Shared.Examine;
|
||||
using Content.Shared.Interaction;
|
||||
using Content.Shared.Power;
|
||||
using Content.Shared.Radio;
|
||||
using Content.Shared.Speech;
|
||||
using Content.Shared.Speech.Components;
|
||||
using Content.Shared.Chat;
|
||||
using Content.Shared.Radio.Components;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
@@ -119,7 +119,7 @@ public sealed partial class BorgSystem
|
||||
|
||||
var message = Loc.GetString(ent.Comp.DestroyingPopup, ("name", Name(ent)));
|
||||
Popup.PopupEntity(message, ent);
|
||||
_trigger.StartTimer(ent.Owner, user: null);
|
||||
_trigger.ActivateTimerTrigger(ent.Owner);
|
||||
|
||||
// prevent a shitter borg running into people
|
||||
RemComp<InputMoverComponent>(ent);
|
||||
|
||||
@@ -4,7 +4,6 @@ using Content.Server.Actions;
|
||||
using Content.Server.Administration.Logs;
|
||||
using Content.Server.Administration.Managers;
|
||||
using Content.Server.DeviceNetwork.Systems;
|
||||
using Content.Server.Explosion.EntitySystems;
|
||||
using Content.Server.Hands.Systems;
|
||||
using Content.Server.PowerCell;
|
||||
using Content.Shared.Alert;
|
||||
@@ -25,6 +24,7 @@ using Content.Shared.Roles;
|
||||
using Content.Shared.Silicons.Borgs;
|
||||
using Content.Shared.Silicons.Borgs.Components;
|
||||
using Content.Shared.Throwing;
|
||||
using Content.Shared.Trigger.Systems;
|
||||
using Content.Shared.Whitelist;
|
||||
using Content.Shared.Wires;
|
||||
using Robust.Server.GameObjects;
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
using Content.Server.Explosion.EntitySystems;
|
||||
using Content.Shared.Sound.Components;
|
||||
|
||||
namespace Content.Server.Sound.Components
|
||||
{
|
||||
/// <summary>
|
||||
/// Whenever a <see cref="TriggerEvent"/> is run play a sound in PVS range.
|
||||
/// </summary>
|
||||
[RegisterComponent]
|
||||
public sealed partial class EmitSoundOnTriggerComponent : BaseEmitSoundComponent
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,3 @@
|
||||
using Content.Server.Explosion.EntitySystems;
|
||||
using Content.Server.Sound.Components;
|
||||
using Content.Shared.UserInterface;
|
||||
using Content.Shared.Sound;
|
||||
using Content.Shared.Sound.Components;
|
||||
using Robust.Shared.Timing;
|
||||
@@ -38,16 +35,9 @@ public sealed class EmitSoundSystem : SharedEmitSoundSystem
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<EmitSoundOnTriggerComponent, TriggerEvent>(HandleEmitSoundOnTrigger);
|
||||
SubscribeLocalEvent<SpamEmitSoundComponent, MapInitEvent>(HandleSpamEmitSoundMapInit);
|
||||
}
|
||||
|
||||
private void HandleEmitSoundOnTrigger(EntityUid uid, EmitSoundOnTriggerComponent component, TriggerEvent args)
|
||||
{
|
||||
TryEmitSound(uid, component, args.User, false);
|
||||
args.Handled = true;
|
||||
}
|
||||
|
||||
private void HandleSpamEmitSoundMapInit(Entity<SpamEmitSoundComponent> entity, ref MapInitEvent args)
|
||||
{
|
||||
SpamEmitSoundReset(entity);
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
using Content.Server.Chat.Systems;
|
||||
|
||||
namespace Content.Server.Speech.Components;
|
||||
|
||||
/// <summary>
|
||||
/// This component is used to relay speech events to other systems.
|
||||
/// </summary>
|
||||
[RegisterComponent]
|
||||
public sealed partial class ActiveListenerComponent : Component
|
||||
{
|
||||
[DataField("range")]
|
||||
public float Range = ChatSystem.VoiceRange;
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
using Content.Server.Speech.Components;
|
||||
using Content.Shared.Speech;
|
||||
|
||||
namespace Content.Server.Speech.EntitySystems;
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using Content.Server.Chat.Systems;
|
||||
using Content.Server.Speech.Components;
|
||||
using Content.Shared.Speech;
|
||||
using Content.Shared.Speech.Components;
|
||||
|
||||
namespace Content.Server.Speech.EntitySystems;
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
using Content.Server.Chat.Systems;
|
||||
using Content.Server.Speech;
|
||||
using Content.Server.Speech.Components;
|
||||
using Content.Shared.Speech;
|
||||
using Content.Shared.Speech.Components;
|
||||
using Content.Shared.Whitelist;
|
||||
using Robust.Shared.Player;
|
||||
using static Content.Server.Chat.Systems.ChatSystem;
|
||||
|
||||
@@ -3,8 +3,6 @@ using Content.Server.Administration.Logs;
|
||||
using Content.Server.Chat.Systems;
|
||||
using Content.Server.Interaction;
|
||||
using Content.Server.Power.EntitySystems;
|
||||
using Content.Server.Speech;
|
||||
using Content.Server.Speech.Components;
|
||||
using Content.Shared.Chat;
|
||||
using Content.Shared.Database;
|
||||
using Content.Shared.Labels.Components;
|
||||
@@ -13,6 +11,7 @@ using Content.Shared.Power;
|
||||
using Content.Shared.Silicons.StationAi;
|
||||
using Content.Shared.Silicons.Borgs.Components;
|
||||
using Content.Shared.Speech;
|
||||
using Content.Shared.Speech.Components;
|
||||
using Content.Shared.Telephone;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Shared.Audio.Systems;
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
using Content.Server.AlertLevel;
|
||||
using Content.Server.Explosion.EntitySystems;
|
||||
using Content.Shared.Trigger;
|
||||
using Content.Shared.Trigger.Components.Effects;
|
||||
using Content.Server.Station.Systems;
|
||||
|
||||
namespace Content.Server.AlertLevel.Systems;
|
||||
namespace Content.Server.Trigger.Systems;
|
||||
|
||||
public sealed class AlertLevelChangeOnTriggerSystem : EntitySystem
|
||||
{
|
||||
@@ -18,10 +19,14 @@ public sealed class AlertLevelChangeOnTriggerSystem : EntitySystem
|
||||
|
||||
private void OnTrigger(Entity<AlertLevelChangeOnTriggerComponent> ent, ref TriggerEvent args)
|
||||
{
|
||||
if (args.Key != null && !ent.Comp.KeysIn.Contains(args.Key))
|
||||
return;
|
||||
|
||||
var stationUid = _station.GetOwningStation(ent.Owner);
|
||||
if (!stationUid.HasValue)
|
||||
if (stationUid == null)
|
||||
return;
|
||||
|
||||
_alertLevelSystem.SetLevel(stationUid.Value, ent.Comp.Level, ent.Comp.PlaySound, ent.Comp.Announce, ent.Comp.Force);
|
||||
args.Handled = true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
using Content.Shared.Trigger;
|
||||
using Content.Shared.Trigger.Components.Effects;
|
||||
using Content.Server.GhostKick;
|
||||
using Robust.Shared.Player;
|
||||
|
||||
namespace Content.Server.Trigger.Systems;
|
||||
|
||||
public sealed class GhostKickUserOnTriggerSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly GhostKickManager _ghostKickManager = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<GhostKickOnTriggerComponent, TriggerEvent>(OnTrigger);
|
||||
}
|
||||
|
||||
private void OnTrigger(Entity<GhostKickOnTriggerComponent> ent, ref TriggerEvent args)
|
||||
{
|
||||
if (args.Key != null && !ent.Comp.KeysIn.Contains(args.Key))
|
||||
return;
|
||||
|
||||
var target = ent.Comp.TargetUser ? args.User : ent.Owner;
|
||||
|
||||
if (target == null)
|
||||
return;
|
||||
|
||||
if (!TryComp(target, out ActorComponent? actor))
|
||||
return;
|
||||
|
||||
_ghostKickManager.DoDisconnect(
|
||||
actor.PlayerSession.Channel,
|
||||
Loc.GetString(ent.Comp.Reason));
|
||||
|
||||
args.Handled = true;
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,9 @@
|
||||
using Content.Server.Explosion.EntitySystems;
|
||||
using Content.Shared.IgnitionSource;
|
||||
using Content.Shared.Timing;
|
||||
using Robust.Shared.Audio.Systems;
|
||||
using Content.Shared.Trigger;
|
||||
using Content.Shared.Trigger.Components.Effects;
|
||||
using Robust.Shared.Timing;
|
||||
|
||||
namespace Content.Server.IgnitionSource;
|
||||
namespace Content.Server.Trigger.Systems;
|
||||
|
||||
/// <summary>
|
||||
/// Handles igniting when triggered and stopping ignition after the delay.
|
||||
@@ -13,8 +12,6 @@ public sealed class IgniteOnTriggerSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly IGameTiming _timing = default!;
|
||||
[Dependency] private readonly SharedIgnitionSourceSystem _source = default!;
|
||||
[Dependency] private readonly SharedAudioSystem _audio = default!;
|
||||
[Dependency] private readonly UseDelaySystem _useDelay = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
@@ -23,6 +20,8 @@ public sealed class IgniteOnTriggerSystem : EntitySystem
|
||||
SubscribeLocalEvent<IgniteOnTriggerComponent, TriggerEvent>(OnTrigger);
|
||||
}
|
||||
|
||||
// TODO: move this into ignition source component
|
||||
// it already has an update loop
|
||||
public override void Update(float deltaTime)
|
||||
{
|
||||
base.Update(deltaTime);
|
||||
@@ -42,14 +41,18 @@ public sealed class IgniteOnTriggerSystem : EntitySystem
|
||||
|
||||
private void OnTrigger(Entity<IgniteOnTriggerComponent> ent, ref TriggerEvent args)
|
||||
{
|
||||
// prevent spamming sound and ignition
|
||||
if (!TryComp(ent.Owner, out UseDelayComponent? useDelay) || _useDelay.IsDelayed((ent.Owner, useDelay)))
|
||||
if (args.Key != null && !ent.Comp.KeysIn.Contains(args.Key))
|
||||
return;
|
||||
|
||||
_source.SetIgnited(ent.Owner);
|
||||
_audio.PlayPvs(ent.Comp.IgniteSound, ent);
|
||||
var target = ent.Comp.TargetUser ? args.User : ent.Owner;
|
||||
|
||||
_useDelay.TryResetDelay((ent.Owner, useDelay));
|
||||
if (target == null)
|
||||
return;
|
||||
|
||||
_source.SetIgnited(target.Value);
|
||||
ent.Comp.IgnitedUntil = _timing.CurTime + ent.Comp.IgnitedTime;
|
||||
Dirty(ent);
|
||||
|
||||
args.Handled = true;
|
||||
}
|
||||
}
|
||||
51
Content.Server/Trigger/Systems/PolymorphOnTriggerSystem.cs
Normal file
51
Content.Server/Trigger/Systems/PolymorphOnTriggerSystem.cs
Normal file
@@ -0,0 +1,51 @@
|
||||
using Content.Server.Polymorph.Systems;
|
||||
using Content.Shared.Polymorph;
|
||||
using Content.Shared.Trigger;
|
||||
using Content.Shared.Trigger.Components.Effects;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Server.Trigger.Systems;
|
||||
|
||||
public sealed partial class PolymorphOnTriggerSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly PolymorphSystem _polymorph = default!;
|
||||
|
||||
/// <summary>
|
||||
/// Need to do this so we don't get a collection enumeration error in physics by polymorphing
|
||||
/// an entity we're colliding with in case of TriggerOnCollide.
|
||||
/// Also makes sure other trigger effects don't activate in nullspace after we have polymorphed.
|
||||
/// </summary>
|
||||
private Queue<(EntityUid Uid, ProtoId<PolymorphPrototype> Polymorph)> _queuedPolymorphUpdates = new();
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<PolymorphOnTriggerComponent, TriggerEvent>(OnTrigger);
|
||||
}
|
||||
|
||||
private void OnTrigger(Entity<PolymorphOnTriggerComponent> ent, ref TriggerEvent args)
|
||||
{
|
||||
if (args.Key != null && !ent.Comp.KeysIn.Contains(args.Key))
|
||||
return;
|
||||
|
||||
var target = ent.Comp.TargetUser ? args.User : ent.Owner;
|
||||
|
||||
if (target == null)
|
||||
return;
|
||||
|
||||
_queuedPolymorphUpdates.Enqueue((target.Value, ent.Comp.Polymorph));
|
||||
args.Handled = true;
|
||||
}
|
||||
|
||||
public override void Update(float frametime)
|
||||
{
|
||||
while (_queuedPolymorphUpdates.TryDequeue(out var data))
|
||||
{
|
||||
if (TerminatingOrDeleted(data.Uid))
|
||||
continue;
|
||||
|
||||
_polymorph.PolymorphEntity(data.Uid, data.Polymorph);
|
||||
}
|
||||
}
|
||||
}
|
||||
49
Content.Server/Trigger/Systems/RattleOnTriggerSystem.cs
Normal file
49
Content.Server/Trigger/Systems/RattleOnTriggerSystem.cs
Normal file
@@ -0,0 +1,49 @@
|
||||
using Content.Server.Radio.EntitySystems;
|
||||
using Content.Server.Pinpointer;
|
||||
using Content.Shared.Mobs.Components;
|
||||
using Content.Shared.Trigger;
|
||||
using Content.Shared.Trigger.Components.Effects;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Server.Trigger.Systems;
|
||||
|
||||
public sealed class RattleOnTriggerSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||
[Dependency] private readonly RadioSystem _radio = default!;
|
||||
[Dependency] private readonly NavMapSystem _navMap = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<RattleOnTriggerComponent, TriggerEvent>(OnTrigger);
|
||||
}
|
||||
|
||||
private void OnTrigger(Entity<RattleOnTriggerComponent> ent, ref TriggerEvent args)
|
||||
{
|
||||
if (args.Key != null && !ent.Comp.KeysIn.Contains(args.Key))
|
||||
return;
|
||||
|
||||
var target = ent.Comp.TargetUser ? args.User : ent.Owner;
|
||||
|
||||
if (target == null)
|
||||
return;
|
||||
|
||||
if (!TryComp<MobStateComponent>(target.Value, out var mobstate))
|
||||
return;
|
||||
|
||||
args.Handled = true;
|
||||
|
||||
if (!ent.Comp.Messages.TryGetValue(mobstate.CurrentState, out var messageId))
|
||||
return;
|
||||
|
||||
// Gets the location of the user
|
||||
var posText = FormattedMessage.RemoveMarkupOrThrow(_navMap.GetNearestBeaconString(target.Value));
|
||||
|
||||
var message = Loc.GetString(messageId, ("user", target.Value), ("position", posText));
|
||||
// Sends a message to the radio channel specified by the implant
|
||||
_radio.SendRadioMessage(ent.Owner, message, _prototypeManager.Index(ent.Comp.RadioChannel), ent.Owner);
|
||||
}
|
||||
}
|
||||
48
Content.Server/Trigger/Systems/ReleaseGasOnTriggerSystem.cs
Normal file
48
Content.Server/Trigger/Systems/ReleaseGasOnTriggerSystem.cs
Normal file
@@ -0,0 +1,48 @@
|
||||
using Content.Server.Atmos.EntitySystems;
|
||||
using Content.Shared.Trigger.Components.Effects;
|
||||
using Content.Shared.Trigger.Systems;
|
||||
using Robust.Shared.Timing;
|
||||
|
||||
namespace Content.Server.Trigger.Systems;
|
||||
|
||||
public sealed class ReleaseGasOnTriggerSystem : SharedReleaseGasOnTriggerSystem
|
||||
{
|
||||
[Dependency] private readonly AtmosphereSystem _atmosphereSystem = default!;
|
||||
[Dependency] private readonly SharedAppearanceSystem _appearance = default!;
|
||||
[Dependency] private readonly IGameTiming _timing = default!;
|
||||
|
||||
|
||||
public override void Update(float frameTime)
|
||||
{
|
||||
base.Update(frameTime);
|
||||
|
||||
var curTime = _timing.CurTime;
|
||||
var query = EntityQueryEnumerator<ReleaseGasOnTriggerComponent>();
|
||||
|
||||
while (query.MoveNext(out var uid, out var comp))
|
||||
{
|
||||
if (!comp.Active || comp.NextReleaseTime > curTime)
|
||||
continue;
|
||||
|
||||
var giverGasMix = comp.Air.Remove(comp.StartingTotalMoles * comp.RemoveFraction);
|
||||
var environment = _atmosphereSystem.GetContainingMixture(uid, false, true);
|
||||
|
||||
if (environment == null)
|
||||
{
|
||||
_appearance.SetData(uid, ReleaseGasOnTriggerVisuals.Key, false);
|
||||
RemCompDeferred<ReleaseGasOnTriggerComponent>(uid);
|
||||
continue;
|
||||
}
|
||||
|
||||
_atmosphereSystem.Merge(environment, giverGasMix);
|
||||
comp.NextReleaseTime += comp.ReleaseInterval;
|
||||
|
||||
if (comp.PressureLimit != 0 && environment.Pressure >= comp.PressureLimit ||
|
||||
comp.Air.TotalMoles <= 0)
|
||||
{
|
||||
_appearance.SetData(uid, ReleaseGasOnTriggerVisuals.Key, false);
|
||||
RemCompDeferred<ReleaseGasOnTriggerComponent>(uid);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
68
Content.Server/Trigger/Systems/SmokeOnTriggerSystem.cs
Normal file
68
Content.Server/Trigger/Systems/SmokeOnTriggerSystem.cs
Normal file
@@ -0,0 +1,68 @@
|
||||
using Content.Server.Fluids.EntitySystems;
|
||||
using Content.Server.Spreader;
|
||||
using Content.Shared.Chemistry.Components;
|
||||
using Content.Shared.Coordinates.Helpers;
|
||||
using Content.Shared.Maps;
|
||||
using Content.Shared.Trigger;
|
||||
using Content.Shared.Trigger.Components.Effects;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Shared.Map;
|
||||
|
||||
namespace Content.Server.Trigger.Systems;
|
||||
|
||||
/// <summary>
|
||||
/// Handles creating smoke when <see cref="SmokeOnTriggerComponent"/> is triggered.
|
||||
/// </summary>
|
||||
public sealed class SmokeOnTriggerSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly IMapManager _mapMan = default!;
|
||||
[Dependency] private readonly MapSystem _map = default!;
|
||||
[Dependency] private readonly SmokeSystem _smoke = default!;
|
||||
[Dependency] private readonly TransformSystem _transform = default!;
|
||||
[Dependency] private readonly SpreaderSystem _spreader = default!;
|
||||
[Dependency] private readonly TurfSystem _turf = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<SmokeOnTriggerComponent, TriggerEvent>(OnTrigger);
|
||||
}
|
||||
|
||||
private void OnTrigger(Entity<SmokeOnTriggerComponent> ent, ref TriggerEvent args)
|
||||
{
|
||||
if (args.Key != null && !ent.Comp.KeysIn.Contains(args.Key))
|
||||
return;
|
||||
|
||||
var target = ent.Comp.TargetUser ? args.User : ent.Owner;
|
||||
|
||||
if (target == null)
|
||||
return;
|
||||
|
||||
// TODO: move all of this into an API function in SmokeSystem
|
||||
var xform = Transform(target.Value);
|
||||
var mapCoords = _transform.GetMapCoordinates(target.Value, xform);
|
||||
if (!_mapMan.TryFindGridAt(mapCoords, out var gridUid, out var gridComp) ||
|
||||
!_map.TryGetTileRef(gridUid, gridComp, xform.Coordinates, out var tileRef) ||
|
||||
tileRef.Tile.IsEmpty)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (_spreader.RequiresFloorToSpread(ent.Comp.SmokePrototype.ToString()) && _turf.IsSpace(tileRef))
|
||||
return;
|
||||
|
||||
var coords = _map.MapToGrid(gridUid, mapCoords);
|
||||
var smoke = Spawn(ent.Comp.SmokePrototype, coords.SnapToGrid());
|
||||
if (!TryComp<SmokeComponent>(smoke, out var smokeComp))
|
||||
{
|
||||
Log.Error($"Smoke prototype {ent.Comp.SmokePrototype} was missing SmokeComponent");
|
||||
Del(smoke);
|
||||
return;
|
||||
}
|
||||
|
||||
_smoke.StartSmoke(smoke, ent.Comp.Solution, (float)ent.Comp.Duration.TotalSeconds, ent.Comp.SpreadAmount, smokeComp);
|
||||
|
||||
args.Handled = true;
|
||||
}
|
||||
}
|
||||
@@ -1,13 +1,13 @@
|
||||
using Content.Server.Explosion.EntitySystems;
|
||||
using Content.Shared.Timing;
|
||||
using Content.Server.Chat.Systems;
|
||||
using Content.Shared.Trigger;
|
||||
using Content.Shared.Trigger.Components.Effects;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Random;
|
||||
|
||||
namespace Content.Server.Chat.Systems;
|
||||
namespace Content.Server.Trigger.Systems;
|
||||
|
||||
public sealed class SpeakOnTriggerSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly UseDelaySystem _useDelay = default!;
|
||||
[Dependency] private readonly IRobustRandom _random = default!;
|
||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||
[Dependency] private readonly ChatSystem _chat = default!;
|
||||
@@ -15,32 +15,34 @@ public sealed class SpeakOnTriggerSystem : EntitySystem
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<SpeakOnTriggerComponent, TriggerEvent>(OnTrigger);
|
||||
}
|
||||
|
||||
private void OnTrigger(Entity<SpeakOnTriggerComponent> ent, ref TriggerEvent args)
|
||||
{
|
||||
TrySpeak(ent);
|
||||
args.Handled = true;
|
||||
}
|
||||
|
||||
private void TrySpeak(Entity<SpeakOnTriggerComponent> ent)
|
||||
{
|
||||
// If it doesn't have the use delay component, still send the message.
|
||||
if (TryComp<UseDelayComponent>(ent.Owner, out var useDelay) && _useDelay.IsDelayed((ent.Owner, useDelay)))
|
||||
if (args.Key != null && !ent.Comp.KeysIn.Contains(args.Key))
|
||||
return;
|
||||
|
||||
if (!_prototypeManager.TryIndex(ent.Comp.Pack, out var messagePack))
|
||||
var target = ent.Comp.TargetUser ? args.User : ent.Owner;
|
||||
|
||||
if (target == null)
|
||||
return;
|
||||
|
||||
var message = Loc.GetString(_random.Pick(messagePack.Values));
|
||||
string message;
|
||||
if (ent.Comp.Text != null)
|
||||
message = Loc.GetString(ent.Comp.Text);
|
||||
else
|
||||
{
|
||||
if (!_prototypeManager.TryIndex(ent.Comp.Pack, out var messagePack))
|
||||
return;
|
||||
message = Loc.GetString(_random.Pick(messagePack.Values));
|
||||
}
|
||||
// Chatcode moment: messages starting with "." are considered radio messages.
|
||||
// Prepending ">" forces the message to be spoken instead.
|
||||
// TODO chat refactor: remove this
|
||||
message = '>' + message;
|
||||
_chat.TrySendInGameICMessage(ent.Owner, message, InGameICChatType.Speak, true);
|
||||
|
||||
if (useDelay != null)
|
||||
_useDelay.TryResetDelay((ent.Owner, useDelay));
|
||||
_chat.TrySendInGameICMessage(target.Value, message, InGameICChatType.Speak, true);
|
||||
args.Handled = true;
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,7 @@ using Content.Shared.Database;
|
||||
using Content.Shared.Inventory;
|
||||
using Content.Shared.Popups;
|
||||
using Content.Shared.Storage;
|
||||
using Content.Shared.Trigger;
|
||||
using Robust.Server.Containers;
|
||||
|
||||
namespace Content.Server.VoiceTrigger;
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
using Content.Shared.Explosion.Components.OnTrigger;
|
||||
using Content.Shared.Explosion.Components;
|
||||
|
||||
namespace Content.Server.Xenoarchaeology.Artifact.XAE.Components;
|
||||
|
||||
/// <summary>
|
||||
/// Activates 'trigger' for <see cref="ExplodeOnTriggerComponent"/>.
|
||||
/// Activates <see cref="ExplosiveComponent"/> to explode.
|
||||
/// </summary>
|
||||
[RegisterComponent, Access(typeof(XAETriggerExplosivesSystem))]
|
||||
public sealed partial class XAETriggerExplosivesComponent : Component;
|
||||
|
||||
@@ -24,6 +24,11 @@ public abstract class SharedChatSystem : EntitySystem
|
||||
public const char WhisperPrefix = ',';
|
||||
public const char DefaultChannelKey = 'h';
|
||||
|
||||
public const int VoiceRange = 10; // how far voice goes in world units
|
||||
public const int WhisperClearRange = 2; // how far whisper goes while still being understandable, in world units
|
||||
public const int WhisperMuffledRange = 5; // how far whisper goes at all, in world units
|
||||
public const string DefaultAnnouncementSound = "/Audio/Announcements/announce.ogg";
|
||||
|
||||
public static readonly ProtoId<RadioChannelPrototype> CommonChannel = "Common";
|
||||
|
||||
public static readonly string DefaultChannelPrefix = $"{RadioChannelPrefix}{DefaultChannelKey}";
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
namespace Content.Shared.Damage.Components;
|
||||
|
||||
[RegisterComponent]
|
||||
public sealed partial class DamageUserOnTriggerComponent : Component
|
||||
{
|
||||
[DataField("ignoreResistances")] public bool IgnoreResistances;
|
||||
|
||||
[DataField("damage", required: true)]
|
||||
public DamageSpecifier Damage = default!;
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Timing;
|
||||
|
||||
namespace Content.Shared.Emp;
|
||||
@@ -7,4 +8,15 @@ public abstract class SharedEmpSystem : EntitySystem
|
||||
[Dependency] protected readonly IGameTiming Timing = default!;
|
||||
|
||||
protected const string EmpDisabledEffectPrototype = "EffectEmpDisabled";
|
||||
|
||||
/// <summary>
|
||||
/// Triggers an EMP pulse at the given location, by first raising an <see cref="EmpAttemptEvent"/>, then a raising <see cref="EmpPulseEvent"/> on all entities in range.
|
||||
/// </summary>
|
||||
/// <param name="coordinates">The location to trigger the EMP pulse at.</param>
|
||||
/// <param name="range">The range of the EMP pulse.</param>
|
||||
/// <param name="energyConsumption">The amount of energy consumed by the EMP pulse.</param>
|
||||
/// <param name="duration">The duration of the EMP effects.</param>
|
||||
public virtual void EmpPulse(MapCoordinates coordinates, float range, float energyConsumption, float duration)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
using Robust.Shared.Audio;
|
||||
using Robust.Shared.GameStates;
|
||||
|
||||
namespace Content.Shared.Explosion.Components;
|
||||
|
||||
/// <summary>
|
||||
/// Component for tracking active trigger timers. A timers can activated by some other component, e.g. <see cref="OnUseTimerTriggerComponent"/>.
|
||||
/// </summary>
|
||||
[RegisterComponent]
|
||||
public sealed partial class ActiveTimerTriggerComponent : Component
|
||||
{
|
||||
[DataField] public float TimeRemaining;
|
||||
|
||||
[DataField] public EntityUid? User;
|
||||
|
||||
[DataField] public float BeepInterval;
|
||||
|
||||
[DataField] public float TimeUntilBeep;
|
||||
|
||||
[DataField] public SoundSpecifier? BeepSound;
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
using Robust.Shared.GameStates;
|
||||
|
||||
namespace Content.Shared.Explosion.Components.OnTrigger;
|
||||
|
||||
/// <summary>
|
||||
/// Explode using the entity's <see cref="ExplosiveComponent"/> if Triggered.
|
||||
/// </summary>
|
||||
[RegisterComponent, NetworkedComponent]
|
||||
public sealed partial class ExplodeOnTriggerComponent : Component
|
||||
{
|
||||
}
|
||||
@@ -1,66 +0,0 @@
|
||||
using System.Linq;
|
||||
using Content.Shared.Guidebook;
|
||||
using Robust.Shared.Audio;
|
||||
using Robust.Shared.GameStates;
|
||||
|
||||
namespace Content.Shared.Explosion.Components
|
||||
{
|
||||
[RegisterComponent, NetworkedComponent]
|
||||
public sealed partial class OnUseTimerTriggerComponent : Component
|
||||
{
|
||||
[DataField] public float Delay = 1f;
|
||||
|
||||
/// <summary>
|
||||
/// If not null, a user can use verbs to configure the delay to one of these options.
|
||||
/// </summary>
|
||||
[DataField] public List<float>? DelayOptions = null;
|
||||
|
||||
/// <summary>
|
||||
/// If not null, this timer will periodically play this sound while active.
|
||||
/// </summary>
|
||||
[DataField] public SoundSpecifier? BeepSound;
|
||||
|
||||
/// <summary>
|
||||
/// Time before beeping starts. Defaults to a single beep interval. If set to zero, will emit a beep immediately after use.
|
||||
/// </summary>
|
||||
[DataField] public float? InitialBeepDelay;
|
||||
|
||||
[DataField] public float BeepInterval = 1;
|
||||
|
||||
/// <summary>
|
||||
/// Whether the timer should instead be activated through a verb in the right-click menu
|
||||
/// </summary>
|
||||
[DataField] public bool UseVerbInstead = false;
|
||||
|
||||
/// <summary>
|
||||
/// Should timer be started when it was stuck to another entity.
|
||||
/// Used for C4 charges and similar behaviour.
|
||||
/// </summary>
|
||||
[DataField] public bool StartOnStick;
|
||||
|
||||
/// <summary>
|
||||
/// Allows changing the start-on-stick quality.
|
||||
/// </summary>
|
||||
[DataField("canToggleStartOnStick")] public bool AllowToggleStartOnStick;
|
||||
|
||||
/// <summary>
|
||||
/// Whether you can examine the item to see its timer or not.
|
||||
/// </summary>
|
||||
[DataField] public bool Examinable = true;
|
||||
|
||||
/// <summary>
|
||||
/// Whether or not to show the user a popup when starting the timer.
|
||||
/// </summary>
|
||||
[DataField] public bool DoPopup = true;
|
||||
|
||||
#region GuidebookData
|
||||
|
||||
[GuidebookData]
|
||||
public float? ShortestDelayOption => DelayOptions?.Min();
|
||||
|
||||
[GuidebookData]
|
||||
public float? LongestDelayOption => DelayOptions?.Max();
|
||||
|
||||
#endregion GuidebookData
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user