]> git.smokeofanarchy.ru Git - space-station-14.git/commitdiff
Revert "Revert "Emp more effects" (#16159)" (#16165)
authormetalgearsloth <31366439+metalgearsloth@users.noreply.github.com>
Sat, 6 May 2023 15:26:04 +0000 (01:26 +1000)
committerGitHub <noreply@github.com>
Sat, 6 May 2023 15:26:04 +0000 (11:26 -0400)
This reverts commit 0da5a7850958e6838fd97c297f6eac9f4dd1b9dd.

18 files changed:
Content.Client/Emp/EmpSystem.cs [new file with mode: 0644]
Content.Server/Anomaly/Effects/ElectricityAnomalySystem.cs
Content.Server/Emp/EmpOnTriggerComponent.cs
Content.Server/Emp/EmpSystem.cs
Content.Server/Light/EntitySystems/PoweredLightSystem.cs
Content.Server/Power/EntitySystems/ApcSystem.cs
Content.Server/Radio/EntitySystems/HeadsetSystem.cs
Content.Server/Radio/EntitySystems/RadioSystem.cs
Content.Server/SurveillanceCamera/Systems/SurveillanceCameraSystem.cs
Content.Server/VendingMachines/VendingMachineSystem.cs
Content.Server/Xenoarchaeology/XenoArtifacts/Effects/Components/EmpArtifactComponent.cs
Content.Server/Xenoarchaeology/XenoArtifacts/Effects/Systems/EmpArtifactSystem.cs
Content.Shared/Anomaly/Effects/Components/ElectricityAnomalyComponent.cs
Content.Shared/Emp/EmpDisabledComponent.cs [new file with mode: 0644]
Content.Shared/Emp/SharedEmpSystem.cs [new file with mode: 0644]
Content.Shared/VendingMachines/VendingMachineComponent.cs
Resources/Locale/en-US/apc/components/apc-component.ftl
Resources/Locale/en-US/emp/emp.ftl [new file with mode: 0644]

diff --git a/Content.Client/Emp/EmpSystem.cs b/Content.Client/Emp/EmpSystem.cs
new file mode 100644 (file)
index 0000000..5ed1022
--- /dev/null
@@ -0,0 +1,24 @@
+using Content.Shared.Emp;
+using Robust.Shared.Random;
+
+namespace Content.Client.Emp;
+
+public sealed class EmpSystem : SharedEmpSystem
+{
+    [Dependency] private readonly IRobustRandom _random = default!;
+
+    public override void Update(float frameTime)
+    {
+        base.Update(frameTime);
+
+        var query = EntityQueryEnumerator<EmpDisabledComponent, TransformComponent>();
+        while (query.MoveNext(out var uid, out var comp, out var transform))
+        {
+            if (Timing.CurTime > comp.TargetTime)
+            {
+                comp.TargetTime = Timing.CurTime + _random.NextFloat(0.8f, 1.2f) * TimeSpan.FromSeconds(comp.EffectCooldown);
+                Spawn(EmpDisabledEffectPrototype, transform.Coordinates);
+            }
+        }
+    }
+}
index 89a6361635a67038b0770f001a4f1d2cb9da9e62..0302c62f008c8ef794103f3db0615012323f6999 100644 (file)
@@ -59,7 +59,7 @@ public sealed class ElectricityAnomalySystem : EntitySystem
         }
 
         var empRange = component.MaxElectrocuteRange * 3;
-        _emp.EmpPulse(Transform(uid).MapPosition, empRange, component.EmpEnergyConsumption);
+        _emp.EmpPulse(Transform(uid).MapPosition, empRange, component.EmpEnergyConsumption, component.EmpDisabledDuration);
     }
 
     public override void Update(float frameTime)
index 89bb0fe1316922e01f3c652d57b7f143a45c4d66..b64c34cecf234d6ef7e114118338d3f3a3f2765f 100644 (file)
@@ -15,4 +15,10 @@ public sealed class EmpOnTriggerComponent : Component
     /// </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;
 }
index 2f348e4023764ac83626bb937b51faadc203b37f..b327f086db0f1f7f942b3e025fe8957df2071372 100644 (file)
 using Content.Server.Explosion.EntitySystems;
+using Content.Server.Power.EntitySystems;
+using Content.Server.Radio;
+using Content.Server.SurveillanceCamera;
+using Content.Shared.Emp;
+using Content.Shared.Examine;
 using Robust.Shared.Map;
 
 namespace Content.Server.Emp;
 
-public sealed class EmpSystem : EntitySystem
+public sealed class EmpSystem : SharedEmpSystem
 {
     [Dependency] private readonly EntityLookupSystem _lookup = default!;
 
     public const string EmpPulseEffectPrototype = "EffectEmpPulse";
-    public const string EmpDisabledEffectPrototype = "EffectEmpDisabled";
 
     public override void Initialize()
     {
         base.Initialize();
+        SubscribeLocalEvent<EmpDisabledComponent, EntityUnpausedEvent>(OnUnpaused);
+        SubscribeLocalEvent<EmpDisabledComponent, ExaminedEvent>(OnExamine);
         SubscribeLocalEvent<EmpOnTriggerComponent, TriggerEvent>(HandleEmpTrigger);
+
+        SubscribeLocalEvent<EmpDisabledComponent, RadioSendAttemptEvent>(OnRadioSendAttempt);
+        SubscribeLocalEvent<EmpDisabledComponent, RadioReceiveAttemptEvent>(OnRadioReceiveAttempt);
+        SubscribeLocalEvent<EmpDisabledComponent, ApcToggleMainBreakerAttemptEvent>(OnApcToggleMainBreaker);
+        SubscribeLocalEvent<EmpDisabledComponent, SurveillanceCameraSetActiveAttemptEvent>(OnCameraSetActive);
     }
 
-    public void EmpPulse(MapCoordinates coordinates, float range, float energyConsumption)
+    public void EmpPulse(MapCoordinates coordinates, float range, float energyConsumption, float duration)
     {
         foreach (var uid in _lookup.GetEntitiesInRange(coordinates, range))
         {
-            var ev = new EmpPulseEvent(energyConsumption, false);
+            var ev = new EmpPulseEvent(energyConsumption, false, false);
             RaiseLocalEvent(uid, ref ev);
             if (ev.Affected)
+            {
                 Spawn(EmpDisabledEffectPrototype, Transform(uid).Coordinates);
+            }
+            if (ev.Disabled)
+            {
+                var disabled = EnsureComp<EmpDisabledComponent>(uid);
+                disabled.DisabledUntil = Timing.CurTime + TimeSpan.FromSeconds(duration);
+            }
         }
         Spawn(EmpPulseEffectPrototype, coordinates);
     }
 
+    public override void Update(float frameTime)
+    {
+        base.Update(frameTime);
+
+        var query = EntityQueryEnumerator<EmpDisabledComponent>();
+        while (query.MoveNext(out var uid, out var comp))
+        {
+            if (comp.DisabledUntil < Timing.CurTime)
+            {
+                RemComp<EmpDisabledComponent>(uid);
+                var ev = new EmpDisabledRemoved();
+                RaiseLocalEvent(uid, ref ev);
+            }
+        }
+    }
+
+    private void OnUnpaused(EntityUid uid, EmpDisabledComponent component, ref EntityUnpausedEvent args)
+    {
+        component.DisabledUntil += args.PausedTime;
+        component.TargetTime += args.PausedTime;
+    }
+
+    private void OnExamine(EntityUid uid, EmpDisabledComponent component, ExaminedEvent args)
+    {
+        args.PushMarkup(Loc.GetString("emp-disabled-comp-on-examine"));
+    }
+
     private void HandleEmpTrigger(EntityUid uid, EmpOnTriggerComponent comp, TriggerEvent args)
     {
-        EmpPulse(Transform(uid).MapPosition, comp.Range, comp.EnergyConsumption);
+        EmpPulse(Transform(uid).MapPosition, comp.Range, comp.EnergyConsumption, comp.DisableDuration);
         args.Handled = true;
     }
+
+    private void OnRadioSendAttempt(EntityUid uid, EmpDisabledComponent component, ref RadioSendAttemptEvent args)
+    {
+        args.Cancelled = true;
+    }
+
+    private void OnRadioReceiveAttempt(EntityUid uid, EmpDisabledComponent component, ref RadioReceiveAttemptEvent args)
+    {
+        args.Cancelled = true;
+    }
+
+    private void OnApcToggleMainBreaker(EntityUid uid, EmpDisabledComponent component, ref ApcToggleMainBreakerAttemptEvent args)
+    {
+        args.Cancelled = true;
+    }
+
+    private void OnCameraSetActive(EntityUid uid, EmpDisabledComponent component, ref SurveillanceCameraSetActiveAttemptEvent args)
+    {
+        args.Cancelled = true;
+    }
 }
 
 [ByRefEvent]
-public record struct EmpPulseEvent(float EnergyConsumption, bool Affected);
+public record struct EmpPulseEvent(float EnergyConsumption, bool Affected, bool Disabled);
+
+[ByRefEvent]
+public record struct EmpDisabledRemoved();
index 38632912a46e9355f252915c282dd1a2e26ac6f8..81e4998507d24fac4064d4188b5cdd4ed7db1cff 100644 (file)
@@ -224,19 +224,20 @@ namespace Content.Server.Light.EntitySystems
         /// <summary>
         ///     Try to break bulb inside light fixture
         /// </summary>
-        public void TryDestroyBulb(EntityUid uid, PoweredLightComponent? light = null)
+        public bool TryDestroyBulb(EntityUid uid, PoweredLightComponent? light = null)
         {
             // check bulb state
             var bulbUid = GetBulb(uid, light);
             if (bulbUid == null || !EntityManager.TryGetComponent(bulbUid.Value, out LightBulbComponent? lightBulb))
-                return;
+                return false;
             if (lightBulb.State == LightBulbState.Broken)
-                return;
+                return false;
 
             // break it
             _bulbSystem.SetState(bulbUid.Value, LightBulbState.Broken, lightBulb);
             _bulbSystem.PlayBreakSound(bulbUid.Value, lightBulb);
             UpdateLight(uid, light);
+            return true;
         }
         #endregion
 
@@ -428,8 +429,8 @@ namespace Content.Server.Light.EntitySystems
 
         private void OnEmpPulse(EntityUid uid, PoweredLightComponent component, ref EmpPulseEvent args)
         {
-            args.Affected = true;
-            TryDestroyBulb(uid, component);
+            if (TryDestroyBulb(uid, component))
+                args.Affected = true;
         }
     }
 }
index a5516d9e151b15035c7fbc403692020487f40ce1..db07eb7aa9acc7542adfcbfba9d8ee0a1b9137cf 100644 (file)
@@ -69,6 +69,15 @@ namespace Content.Server.Power.EntitySystems
         }
         private void OnToggleMainBreaker(EntityUid uid, ApcComponent component, ApcToggleMainBreakerMessage args)
         {
+            var attemptEv = new ApcToggleMainBreakerAttemptEvent();
+            RaiseLocalEvent(uid, ref attemptEv);
+            if (attemptEv.Cancelled)
+            {
+                _popup.PopupCursor(Loc.GetString("apc-component-on-toggle-cancel"),
+                    args.Session, PopupType.Medium);
+                return;
+            }
+
             TryComp<AccessReaderComponent>(uid, out var access);
             if (args.Session.AttachedEntity == null)
                 return;
@@ -183,8 +192,12 @@ namespace Content.Server.Power.EntitySystems
             if (component.MainBreakerEnabled)
             {
                 args.Affected = true;
+                args.Disabled = true;
                 ApcToggleBreaker(uid, component);
             }
         }
     }
+
+    [ByRefEvent]
+    public record struct ApcToggleMainBreakerAttemptEvent(bool Cancelled);
 }
index 4f7ab752c8d156e6bac788faf328393c046ce8e4..c84d28b4501cfe7c184996fff0aac8f07baafa5b 100644 (file)
@@ -1,4 +1,5 @@
 using Content.Server.Chat.Systems;
+using Content.Server.Emp;
 using Content.Server.Radio.Components;
 using Content.Shared.Inventory.Events;
 using Content.Shared.Radio;
@@ -21,6 +22,8 @@ public sealed class HeadsetSystem : SharedHeadsetSystem
         SubscribeLocalEvent<HeadsetComponent, EncryptionChannelsChangedEvent>(OnKeysChanged);
 
         SubscribeLocalEvent<WearingHeadsetComponent, EntitySpokeEvent>(OnSpeak);
+        
+        SubscribeLocalEvent<HeadsetComponent, EmpPulseEvent>(OnEmpPulse);
     }
 
     private void OnKeysChanged(EntityUid uid, HeadsetComponent component, EncryptionChannelsChangedEvent args)
@@ -98,4 +101,13 @@ public sealed class HeadsetSystem : SharedHeadsetSystem
         if (TryComp(Transform(uid).ParentUid, out ActorComponent? actor))
             _netMan.ServerSendMessage(args.ChatMsg, actor.PlayerSession.ConnectedClient);
     }
+
+    private void OnEmpPulse(EntityUid uid, HeadsetComponent component, ref EmpPulseEvent args)
+    {
+        if (component.Enabled)
+        {
+            args.Affected = true;
+            args.Disabled = true;
+        }
+    }
 }
index e7e159b2deb1436c2a1a7e0b63c1b4c83dc02894..b5ced6637e45021bf15a515c3bc647177ceb6f4b 100644 (file)
@@ -80,6 +80,7 @@ public sealed class RadioSystem : EntitySystem
 
         var sendAttemptEv = new RadioSendAttemptEvent(channel, radioSource);
         RaiseLocalEvent(ref sendAttemptEv);
+        RaiseLocalEvent(radioSource, ref sendAttemptEv);
         var canSend = !sendAttemptEv.Cancelled;
 
         var sourceMapId = Transform(radioSource).MapID;
@@ -105,6 +106,7 @@ public sealed class RadioSystem : EntitySystem
             // check if message can be sent to specific receiver
             var attemptEv = new RadioReceiveAttemptEvent(channel, radioSource, receiver);
             RaiseLocalEvent(ref attemptEv);
+            RaiseLocalEvent(receiver, ref attemptEv);
             if (attemptEv.Cancelled)
                 continue;
 
index acb55c36b64d4c0b9ad37b78674f7c85fb80ac90..32bb5676780064beb83cee6edfffee13da3ead7f 100644 (file)
@@ -1,6 +1,7 @@
 using Content.Server.DeviceNetwork;
 using Content.Server.DeviceNetwork.Components;
 using Content.Server.DeviceNetwork.Systems;
+using Content.Server.Emp;
 using Content.Server.Power.Components;
 using Content.Shared.ActionBlocker;
 using Content.Shared.DeviceNetwork;
@@ -57,6 +58,9 @@ public sealed class SurveillanceCameraSystem : EntitySystem
         SubscribeLocalEvent<SurveillanceCameraComponent, SurveillanceCameraSetupSetName>(OnSetName);
         SubscribeLocalEvent<SurveillanceCameraComponent, SurveillanceCameraSetupSetNetwork>(OnSetNetwork);
         SubscribeLocalEvent<SurveillanceCameraComponent, GetVerbsEvent<AlternativeVerb>>(AddVerbs);
+        
+        SubscribeLocalEvent<SurveillanceCameraComponent, EmpPulseEvent>(OnEmpPulse);
+        SubscribeLocalEvent<SurveillanceCameraComponent, EmpDisabledRemoved>(OnEmpDisabledRemoved);
     }
 
     private void OnPacketReceived(EntityUid uid, SurveillanceCameraComponent component, DeviceNetworkPacketEvent args)
@@ -271,6 +275,10 @@ public sealed class SurveillanceCameraSystem : EntitySystem
 
         if (setting)
         {
+            var attemptEv = new SurveillanceCameraSetActiveAttemptEvent();
+            RaiseLocalEvent(camera, ref attemptEv);
+            if (attemptEv.Cancelled)
+                return;
             component.Active = setting;
         }
         else
@@ -392,6 +400,21 @@ public sealed class SurveillanceCameraSystem : EntitySystem
 
         _appearance.SetData(uid, SurveillanceCameraVisualsKey.Key, key, appearance);
     }
+
+    private void OnEmpPulse(EntityUid uid, SurveillanceCameraComponent component, ref EmpPulseEvent args)
+    {
+        if (component.Active)
+        {
+            args.Affected = true;
+            args.Disabled = true;
+            SetActive(uid, false);
+        }
+    }
+
+    private void OnEmpDisabledRemoved(EntityUid uid, SurveillanceCameraComponent component, ref EmpDisabledRemoved args)
+    {
+        SetActive(uid, true);
+    }
 }
 
 public sealed class OnSurveillanceCameraViewerAddEvent : EntityEventArgs
@@ -414,3 +437,6 @@ public sealed class SurveillanceCameraDeactivateEvent : EntityEventArgs
         Camera = camera;
     }
 }
+
+[ByRefEvent]
+public record struct SurveillanceCameraSetActiveAttemptEvent(bool Cancelled);
index 2f31da2cd450df67ac3808751d74fd7c241aa0c2..9d8c6d98f5a7c357e1ae89662b014e20b401bf6b 100644 (file)
@@ -1,5 +1,6 @@
 using System.Linq;
 using Content.Server.Cargo.Systems;
+using Content.Server.Emp;
 using Content.Server.Power.Components;
 using Content.Server.Power.EntitySystems;
 using Content.Server.UserInterface;
@@ -12,6 +13,7 @@ using Content.Shared.Destructible;
 using Content.Shared.DoAfter;
 using Content.Shared.Emag.Components;
 using Content.Shared.Emag.Systems;
+using Content.Shared.Emp;
 using Content.Shared.Popups;
 using Content.Shared.Throwing;
 using Content.Shared.VendingMachines;
@@ -19,6 +21,7 @@ using Robust.Server.GameObjects;
 using Robust.Shared.Audio;
 using Robust.Shared.Prototypes;
 using Robust.Shared.Random;
+using Robust.Shared.Timing;
 
 namespace Content.Server.VendingMachines
 {
@@ -31,6 +34,7 @@ namespace Content.Server.VendingMachines
         [Dependency] private readonly PricingSystem _pricing = default!;
         [Dependency] private readonly ThrowingSystem _throwingSystem = default!;
         [Dependency] private readonly UserInterfaceSystem _userInterfaceSystem = default!;
+        [Dependency] private readonly IGameTiming _timing = default!;
 
         private ISawmill _sawmill = default!;
 
@@ -44,6 +48,7 @@ namespace Content.Server.VendingMachines
             SubscribeLocalEvent<VendingMachineComponent, GotEmaggedEvent>(OnEmagged);
             SubscribeLocalEvent<VendingMachineComponent, DamageChangedEvent>(OnDamage);
             SubscribeLocalEvent<VendingMachineComponent, PriceCalculationEvent>(OnVendingPrice);
+            SubscribeLocalEvent<VendingMachineComponent, EmpPulseEvent>(OnEmpPulse);
 
             SubscribeLocalEvent<VendingMachineComponent, ActivatableUIOpenAttemptEvent>(OnActivatableUIOpenAttempt);
             SubscribeLocalEvent<VendingMachineComponent, BoundUIOpenedEvent>(OnBoundUIOpened);
@@ -437,6 +442,15 @@ namespace Content.Server.VendingMachines
                     }
                 }
             }
+            var disabled = EntityQueryEnumerator<EmpDisabledComponent, VendingMachineComponent>();
+            while (disabled.MoveNext(out var uid, out _, out var comp))
+            {
+                if (comp.NextEmpEject < _timing.CurTime)
+                {
+                    EjectRandom(uid, true, false, comp);
+                    comp.NextEmpEject += TimeSpan.FromSeconds(5 * comp.EjectDelay);
+                }
+            }
         }
 
         public void TryRestockInventory(EntityUid uid, VendingMachineComponent? vendComponent = null)
@@ -473,5 +487,15 @@ namespace Content.Server.VendingMachines
 
             args.Price += priceSets.Max();
         }
+
+        private void OnEmpPulse(EntityUid uid, VendingMachineComponent component, ref EmpPulseEvent args)
+        {
+            if (!component.Broken && this.IsPowered(uid, EntityManager))
+            {
+                args.Affected = true;
+                args.Disabled = true;
+                component.NextEmpEject = _timing.CurTime;
+            }
+        }
     }
 }
index dc37782a52ee4f80954d3b7cca966eaeaf275a8e..cea776f05de2e446ecd8b39546126f75bf8a332f 100644 (file)
@@ -14,4 +14,7 @@ public sealed class EmpArtifactComponent : Component
 
     [DataField("energyConsumption"), ViewVariables(VVAccess.ReadWrite)]
     public float EnergyConsumption = 1000000;
+
+    [DataField("disableDuration"), ViewVariables(VVAccess.ReadWrite)]
+    public float DisableDuration = 60f;
 }
index c4346b51bcf65954bcf914580b52ba25f3b3efa2..d4ed8272aa3c27d3486fb6aa733192e002b15ec2 100644 (file)
@@ -16,6 +16,6 @@ public sealed class EmpArtifactSystem : EntitySystem
 
     private void OnActivate(EntityUid uid, EmpArtifactComponent component, ArtifactActivatedEvent args)
     {
-        _emp.EmpPulse(Transform(uid).MapPosition, component.Range, component.EnergyConsumption);
+        _emp.EmpPulse(Transform(uid).MapPosition, component.Range, component.EnergyConsumption, component.DisableDuration);
     }
 }
\ No newline at end of file
index 8da6dd4c42edd855b7cc6a1e2f28884d1b36bab4..06d03f1019846b881232c4e1fc0cb4751e8bd5fe 100644 (file)
@@ -44,4 +44,10 @@ public sealed class ElectricityAnomalyComponent : Component
     /// <summary>
     [DataField("empEnergyConsumption"), ViewVariables(VVAccess.ReadWrite)]
     public float EmpEnergyConsumption = 100000f;
+
+    /// <summary>
+    /// Duration of devices being disabled by the emp pulse upon going supercritical.
+    /// <summary>
+    [DataField("empDisabledDuration"), ViewVariables(VVAccess.ReadWrite)]
+    public float EmpDisabledDuration = 60f;
 }
diff --git a/Content.Shared/Emp/EmpDisabledComponent.cs b/Content.Shared/Emp/EmpDisabledComponent.cs
new file mode 100644 (file)
index 0000000..c84bab9
--- /dev/null
@@ -0,0 +1,27 @@
+using Robust.Shared.GameStates;
+using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom;
+
+namespace Content.Shared.Emp;
+
+/// <summary>
+/// While entity has this component it is "disabled" by EMP.
+/// Add desired behaviour in other systems 
+/// </summary>
+[RegisterComponent, NetworkedComponent]
+[Access(typeof(SharedEmpSystem))]
+public sealed class EmpDisabledComponent : Component
+{
+    /// <summary>
+    /// Moment of time when component is removed and entity stops being "disabled"
+    /// </summary>
+    [DataField("timeLeft", customTypeSerializer: typeof(TimeOffsetSerializer)), ViewVariables(VVAccess.ReadWrite)]
+    public TimeSpan DisabledUntil;
+
+    [DataField("effectCoolDown"), ViewVariables(VVAccess.ReadWrite)]
+    public float EffectCooldown = 3f;
+
+    /// <summary>
+    /// When next effect will be spawned
+    /// </summary>
+    public TimeSpan TargetTime = TimeSpan.Zero;
+}
diff --git a/Content.Shared/Emp/SharedEmpSystem.cs b/Content.Shared/Emp/SharedEmpSystem.cs
new file mode 100644 (file)
index 0000000..6e4478b
--- /dev/null
@@ -0,0 +1,10 @@
+using Robust.Shared.Timing;
+
+namespace Content.Shared.Emp;
+
+public abstract class SharedEmpSystem : EntitySystem
+{
+    [Dependency] protected readonly IGameTiming Timing = default!;
+
+    protected const string EmpDisabledEffectPrototype = "EffectEmpDisabled";
+}
index 85e1178d46872c41101ed6ada4499bc64fcd791c..b648d36f4e0e43c652a51384591863f4bc48f3d7 100644 (file)
@@ -3,6 +3,7 @@ using Content.Shared.Actions.ActionTypes;
 using Robust.Shared.Audio;
 using Robust.Shared.GameStates;
 using Robust.Shared.Serialization;
+using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom;
 using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
 
 namespace Content.Shared.VendingMachines
@@ -115,6 +116,12 @@ namespace Content.Shared.VendingMachines
         public float DenyAccumulator = 0f;
         public float DispenseOnHitAccumulator = 0f;
 
+        /// <summary>
+        ///     While disabled by EMP it randomly ejects items
+        /// </summary>
+        [DataField("nextEmpEject", customTypeSerializer: typeof(TimeOffsetSerializer))]
+        public TimeSpan NextEmpEject = TimeSpan.Zero;
+
         #region Client Visuals
         /// <summary>
         /// RSI state for when the vending machine is unpowered.
index d180bcbe0632593973c919ed1b92959c2bc5ef09..6e152adb1d6d5e21eb608610e0a2e8477f08c028 100644 (file)
@@ -1,3 +1,4 @@
 apc-component-insufficient-access = Insufficient access!
 apc-component-on-examine-panel-open = The [color=lightgray]APC electronics panel[/color] is [color=red]open[/color].
-apc-component-on-examine-panel-closed = The [color=lightgray]APC electronics panel[/color] is [color=darkgreen]closed[/color].
\ No newline at end of file
+apc-component-on-examine-panel-closed = The [color=lightgray]APC electronics panel[/color] is [color=darkgreen]closed[/color].
+apc-component-on-toggle-cancel = It does nothing!
diff --git a/Resources/Locale/en-US/emp/emp.ftl b/Resources/Locale/en-US/emp/emp.ftl
new file mode 100644 (file)
index 0000000..6d5e790
--- /dev/null
@@ -0,0 +1 @@
+emp-disabled-comp-on-examine = [color=lightblue]It's disrupted by an electric field... [/color]