diff --git a/Content.Client/_NF/M_Emp/M_EmpComponent.cs b/Content.Client/_NF/M_Emp/M_EmpComponent.cs
new file mode 100644
index 00000000000..18a740975cd
--- /dev/null
+++ b/Content.Client/_NF/M_Emp/M_EmpComponent.cs
@@ -0,0 +1,8 @@
+using Content.Shared._NF.M_Emp;
+using Robust.Shared.GameStates;
+using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
+
+[NetworkedComponent, RegisterComponent]
+public sealed class M_EmpGeneratorComponent : SharedM_EmpGeneratorComponent
+{
+}
diff --git a/Content.Server/Emp/EmpSystem.cs b/Content.Server/Emp/EmpSystem.cs
index b327f086db0..8248971c492 100644
--- a/Content.Server/Emp/EmpSystem.cs
+++ b/Content.Server/Emp/EmpSystem.cs
@@ -1,3 +1,4 @@
+using Content.Server.Entry;
using Content.Server.Explosion.EntitySystems;
using Content.Server.Power.EntitySystems;
using Content.Server.Radio;
diff --git a/Content.Server/Light/EntitySystems/PoweredLightSystem.cs b/Content.Server/Light/EntitySystems/PoweredLightSystem.cs
index bac13274ea7..3a0dc3ee8ce 100644
--- a/Content.Server/Light/EntitySystems/PoweredLightSystem.cs
+++ b/Content.Server/Light/EntitySystems/PoweredLightSystem.cs
@@ -429,8 +429,8 @@ private void OnDoAfter(EntityUid uid, PoweredLightComponent component, DoAfterEv
private void OnEmpPulse(EntityUid uid, PoweredLightComponent component, ref EmpPulseEvent args)
{
- if (TryDestroyBulb(uid, component))
- args.Affected = true;
+ // if (TryDestroyBulb(uid, component)) // Make it so EMP isnt exploding lights
+ // args.Affected = true; // Make it so EMP isnt exploding lights
}
}
}
diff --git a/Content.Server/Power/Components/ApcComponent.cs b/Content.Server/Power/Components/ApcComponent.cs
index e6e6dc58454..40ed3f87942 100644
--- a/Content.Server/Power/Components/ApcComponent.cs
+++ b/Content.Server/Power/Components/ApcComponent.cs
@@ -10,6 +10,9 @@ public sealed class ApcComponent : BaseApcNetComponent
[DataField("onReceiveMessageSound")]
public SoundSpecifier OnReceiveMessageSound = new SoundPathSpecifier("/Audio/Machines/machine_switch.ogg");
+ [DataField("apcIgnoreEmp")]
+ public bool ApcIgnoreEmp = false;
+
[ViewVariables]
public ApcChargeState LastChargeState;
public TimeSpan LastChargeStateTime;
diff --git a/Content.Server/Power/EntitySystems/ApcSystem.cs b/Content.Server/Power/EntitySystems/ApcSystem.cs
index db07eb7aa9a..ed07ee912a9 100644
--- a/Content.Server/Power/EntitySystems/ApcSystem.cs
+++ b/Content.Server/Power/EntitySystems/ApcSystem.cs
@@ -189,11 +189,19 @@ private ApcExternalPowerState CalcExtPowerState(EntityUid uid, PowerState.Batter
private void OnEmpPulse(EntityUid uid, ApcComponent component, ref EmpPulseEvent args)
{
- if (component.MainBreakerEnabled)
+ if (component.ApcIgnoreEmp)
{
- args.Affected = true;
- args.Disabled = true;
- ApcToggleBreaker(uid, component);
+ args.Affected = false;
+ args.Disabled = false;
+ }
+ else
+ {
+ if (component.MainBreakerEnabled)
+ {
+ args.Affected = true;
+ args.Disabled = true;
+ ApcToggleBreaker(uid, component);
+ }
}
}
}
diff --git a/Content.Server/_NF/M_Emp/M_EmpGeneratorComponent.cs b/Content.Server/_NF/M_Emp/M_EmpGeneratorComponent.cs
new file mode 100644
index 00000000000..65b6513e316
--- /dev/null
+++ b/Content.Server/_NF/M_Emp/M_EmpGeneratorComponent.cs
@@ -0,0 +1,116 @@
+using Content.Shared.Radio;
+using Content.Shared._NF.M_Emp;
+using Robust.Shared.GameStates;
+using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
+
+namespace Content.Server._NF.M_Emp
+{
+ ///
+ /// A M_Emp generator.
+ ///
+ [NetworkedComponent, RegisterComponent]
+ [Access(typeof(M_EmpSystem))]
+ public sealed class M_EmpGeneratorComponent : SharedM_EmpGeneratorComponent
+ {
+ ///
+ /// Current state of this generator
+ ///
+ [ViewVariables(VVAccess.ReadOnly)]
+ [DataField("generatorState")]
+ public GeneratorState GeneratorState = GeneratorState.Inactive;
+
+ ///
+ /// How long it takes for the generator to EMP
+ ///
+ [ViewVariables(VVAccess.ReadWrite)]
+ [DataField("baseActivatingTime")]
+ public TimeSpan BaseActivatingTime = TimeSpan.FromSeconds(3.5);
+
+ ///
+ /// How long it actually takes for the generator to EMP
+ ///
+ [ViewVariables(VVAccess.ReadWrite)]
+ [DataField("activatingTime")]
+ public TimeSpan ActivatingTime = TimeSpan.FromSeconds(3.5);
+
+ ///
+ /// How long the generator EMP is working
+ ///
+ [ViewVariables(VVAccess.ReadWrite)]
+ [DataField("engagedTime")]
+ public TimeSpan EngagedTime = TimeSpan.FromSeconds(60);
+
+ ///
+ /// How long the generator Cooling Down
+ ///
+ [ViewVariables(VVAccess.ReadWrite)]
+ [DataField("baseCoolingDownTime")]
+ public TimeSpan BaseCoolingDownTime = TimeSpan.FromSeconds(60);
+
+ ///
+ /// How long the generator actually has to cooldown after use
+ ///
+ [ViewVariables(VVAccess.ReadWrite)]
+ [DataField("coolingDownTime")]
+ public TimeSpan CoolingDownTime = TimeSpan.FromSeconds(60);
+
+ ///
+ /// How long the generator has to recharge after use
+ ///
+ [ViewVariables(VVAccess.ReadWrite)]
+ [DataField("baseRecharging")]
+ public TimeSpan BaseRecharging = TimeSpan.FromSeconds(60);
+
+ ///
+ /// How long the generator actually has to recharge after use
+ ///
+ [ViewVariables(VVAccess.ReadWrite)]
+ [DataField("Recharging")]
+ public TimeSpan Recharging = TimeSpan.FromSeconds(60);
+
+ [DataField("M_EmpChannel", customTypeSerializer: typeof(PrototypeIdSerializer))]
+ public string M_EmpChannel = "Security";
+
+ ///
+ /// Current how much charge the generator currently has
+ ///
+ [DataField("chargeRemaining")]
+ public int ChargeRemaining = 5;
+
+ ///
+ /// How much capacity the generator can hold
+ ///
+ [DataField("chargeCapacity")]
+ public int ChargeCapacity = 5;
+
+ ///
+ /// Used as a guard to prevent spamming the appearance system
+ ///
+ [DataField("previousCharge")]
+ public int PreviousCharge = 5;
+ }
+
+ [CopyByRef, DataRecord]
+ public record struct GeneratorState(GeneratorStateType StateType, TimeSpan Until)
+ {
+ public static readonly GeneratorState Inactive = new (GeneratorStateType.Inactive, TimeSpan.Zero);
+ };
+
+ public sealed class M_EmpGeneratorActivatedEvent : EntityEventArgs
+ {
+ public EntityUid Generator;
+
+ public M_EmpGeneratorActivatedEvent(EntityUid generator)
+ {
+ Generator = generator;
+ }
+ }
+ public enum GeneratorStateType
+ {
+ Inactive,
+ Activating,
+ Engaged,
+ CoolingDown,
+ Recharging,
+ }
+}
diff --git a/Content.Server/_NF/M_Emp/M_EmpSystem.cs b/Content.Server/_NF/M_Emp/M_EmpSystem.cs
new file mode 100644
index 00000000000..460e85683ab
--- /dev/null
+++ b/Content.Server/_NF/M_Emp/M_EmpSystem.cs
@@ -0,0 +1,351 @@
+using System.Linq;
+using Content.Server.Construction;
+using Content.Server.GameTicking;
+using Content.Server.Radio.EntitySystems;
+using Content.Shared.Examine;
+using Content.Shared.Interaction;
+using Content.Shared.Popups;
+using Content.Shared.Radio;
+using Content.Shared._NF.M_Emp;
+using Robust.Server.GameObjects;
+using Robust.Shared.Configuration;
+using Robust.Shared.Map;
+using Robust.Shared.Prototypes;
+using Robust.Shared.Utility;
+using Content.Server.Chat.Managers;
+using Content.Server.Station.Systems;
+using Robust.Shared.Timing;
+using Content.Server.Emp;
+using Content.Shared.DeviceLinking;
+using Content.Server.DeviceLinking.Systems;
+
+// TO ANYONE LOOKING AT THIS CODE, IM SORRY
+// This code was reused for the salvage magnet and is a mess right now as it is, it has no known issues with, I hope, but its not cleaned as it sould be.
+// If you know what you are doing, fix this please to look "usable"
+// - Dvir01
+
+namespace Content.Server._NF.M_Emp
+{
+ public sealed partial class M_EmpSystem : EntitySystem
+ {
+ [Dependency] private readonly IChatManager _chat = default!;
+ [Dependency] private readonly IConfigurationManager _configurationManager = default!;
+ [Dependency] private readonly IGameTiming _timing = default!;
+ [Dependency] private readonly IPrototypeManager _prototypeManager = default!;
+ [Dependency] private readonly SharedPopupSystem _popupSystem = default!;
+ [Dependency] private readonly RadioSystem _radioSystem = default!;
+ [Dependency] private readonly SharedAppearanceSystem _appearanceSystem = default!;
+ [Dependency] private readonly SharedAudioSystem _audio = default!;
+ [Dependency] private readonly SharedTransformSystem _transform = default!;
+ [Dependency] private readonly StationSystem _station = default!;
+ [Dependency] private readonly UserInterfaceSystem _ui = default!;
+ [Dependency] private readonly EmpSystem _emp = default!;
+ [Dependency] private readonly DeviceLinkSystem _linker = default!;
+
+ // TODO: This is probably not compatible with multi-station
+ private readonly Dictionary _M_EmpGridStates = new();
+
+ public override void Initialize()
+ {
+ base.Initialize();
+
+ SubscribeLocalEvent(OnInit); // TODO make this function later
+ SubscribeLocalEvent(OnInteractHand);
+ SubscribeLocalEvent(OnRefreshParts);
+ SubscribeLocalEvent(OnUpgradeExamine);
+ SubscribeLocalEvent(OnExamined);
+ SubscribeLocalEvent(OnGeneratorRemoval);
+ SubscribeLocalEvent(OnGridRemoval);
+
+ // Can't use RoundRestartCleanupEvent, I need to clean up before the grid, and components are gone to prevent the announcements
+ SubscribeLocalEvent(OnRoundEnd);
+ }
+
+ public override void Shutdown()
+ {
+ base.Shutdown();
+ }
+
+ private void OnRoundEnd(GameRunLevelChangedEvent ev)
+ {
+ if(ev.New != GameRunLevel.InRound)
+ {
+ _M_EmpGridStates.Clear();
+ }
+ }
+
+ private void UpdateAppearance(EntityUid uid, M_EmpGeneratorComponent? component = null)
+ {
+ if (!Resolve(uid, ref component, false))
+ return;
+
+ _appearanceSystem.SetData(uid, M_EmpGeneratorVisuals.ReadyBlinking, component.GeneratorState.StateType == GeneratorStateType.Activating);
+ _appearanceSystem.SetData(uid, M_EmpGeneratorVisuals.Ready, component.GeneratorState.StateType == GeneratorStateType.Engaged);
+ _appearanceSystem.SetData(uid, M_EmpGeneratorVisuals.Unready, component.GeneratorState.StateType == GeneratorStateType.Recharging);
+ _appearanceSystem.SetData(uid, M_EmpGeneratorVisuals.UnreadyBlinking, component.GeneratorState.StateType == GeneratorStateType.CoolingDown);
+ }
+
+ private void UpdateChargeStateAppearance(EntityUid uid, TimeSpan currentTime, M_EmpGeneratorComponent? component = null)
+ {
+ if (!Resolve(uid, ref component, false))
+ return;
+
+ var timeLeft = Convert.ToInt32(component.GeneratorState.Until.TotalSeconds - currentTime.TotalSeconds);
+
+ component.ChargeRemaining = component.GeneratorState.StateType switch
+ {
+ GeneratorStateType.Inactive => 5,
+ GeneratorStateType.Engaged => timeLeft / (Convert.ToInt32(component.EngagedTime.TotalSeconds) / component.ChargeCapacity) + 1,
+ GeneratorStateType.CoolingDown => 0,
+ GeneratorStateType.Recharging => component.ChargeCapacity - timeLeft / (Convert.ToInt32(component.Recharging.TotalSeconds) / component.ChargeCapacity) - 1,
+ _ => component.ChargeRemaining
+ };
+
+ if (component.PreviousCharge == component.ChargeRemaining)
+ return;
+ _appearanceSystem.SetData(uid, M_EmpGeneratorVisuals.ChargeState, component.ChargeRemaining);
+ component.PreviousCharge = component.ChargeRemaining;
+ }
+
+ private void OnGridRemoval(GridRemovalEvent ev)
+ {
+ // If we ever want to give generators names, and announce them individually, we would need to loop this, before removing it.
+ if (_M_EmpGridStates.Remove(ev.EntityUid))
+ {
+ // For the very unlikely possibility that the M_Emp generator was on a M_Emp, we will not return here
+ }
+ foreach(var gridState in _M_EmpGridStates)
+ {
+ foreach(var generator in gridState.Value.ActiveGenerators)
+ {
+ if (!TryComp(generator, out var generatorComponent))
+ continue;
+
+ generatorComponent.GeneratorState = GeneratorState.Inactive;
+ return;
+ }
+ }
+ }
+
+ private void OnGeneratorRemoval(EntityUid uid, M_EmpGeneratorComponent component, ComponentShutdown args)
+ {
+ if (component.GeneratorState.StateType == GeneratorStateType.Inactive)
+ return;
+
+ var generatorTranform = Transform(uid);
+ if (generatorTranform.GridUid is not { } gridId || !_M_EmpGridStates.TryGetValue(gridId, out var M_EmpGridState))
+ return;
+
+ component.GeneratorState = GeneratorState.Inactive;
+ }
+
+ private void OnRefreshParts(EntityUid uid, M_EmpGeneratorComponent component, RefreshPartsEvent args)
+ {
+ var rating = args.PartRatings[component.MachinePartDelay] - 1;
+ var factor = MathF.Pow(component.PartRatingDelay, rating);
+ component.CoolingDownTime = component.BaseCoolingDownTime * factor;
+ component.Recharging = component.BaseRecharging * factor;
+ }
+
+ private void OnUpgradeExamine(EntityUid uid, M_EmpGeneratorComponent component, UpgradeExamineEvent args)
+ {
+ args.AddPercentageUpgrade("m_emp-system-generator-delay-upgrade", (float) (component.Recharging / component.BaseRecharging));
+ }
+
+ private void OnExamined(EntityUid uid, M_EmpGeneratorComponent component, ExaminedEvent args)
+ {
+ if (!args.IsInDetailsRange)
+ return;
+
+ var gotGrid = false;
+ var remainingTime = TimeSpan.Zero;
+
+ if (Transform(uid).GridUid is { } gridId &&
+ _M_EmpGridStates.TryGetValue(gridId, out var M_EmpGridState))
+ {
+ remainingTime = component.GeneratorState.Until - M_EmpGridState.CurrentTime;
+ gotGrid = true;
+ }
+ else
+ {
+ Log.Warning("Failed to load M_Emp grid state, can't display remaining time");
+ }
+
+ switch (component.GeneratorState.StateType)
+ {
+ case GeneratorStateType.Inactive:
+ args.PushMarkup(Loc.GetString("m_emp-system-generator-examined-inactive"));
+ break;
+ case GeneratorStateType.Activating:
+ args.PushMarkup(Loc.GetString("m_emp-system-generator-examined-starting"));
+ break;
+ case GeneratorStateType.CoolingDown:
+ args.PushMarkup(Loc.GetString("m_emp-system-generator-examined-cooling-down"));
+ break;
+ case GeneratorStateType.Recharging:
+ if (gotGrid)
+ args.PushMarkup(Loc.GetString("m_emp-system-generator-examined-recharging", ("timeLeft", Math.Ceiling(remainingTime.TotalSeconds))));
+ break;
+ case GeneratorStateType.Engaged:
+ if (gotGrid)
+ args.PushMarkup(Loc.GetString("m_emp-system-generator-examined-active", ("timeLeft", Math.Ceiling(remainingTime.TotalSeconds))));
+ break;
+ default:
+ throw new ArgumentOutOfRangeException();
+ }
+ }
+
+ private void OnInteractHand(EntityUid uid, M_EmpGeneratorComponent component, InteractHandEvent args)
+ {
+ if (args.Handled)
+ return;
+ args.Handled = true;
+ StartGenerator(uid, component, args.User);
+ UpdateAppearance(uid, component);
+ }
+
+ private void OnInit(EntityUid uid, SharedM_EmpComponent component, ComponentInit args)
+ {
+ _linker.EnsureSinkPorts(uid, component.ReceiverPort);
+ }
+
+ private void StartGenerator(EntityUid uid, M_EmpGeneratorComponent component, EntityUid user)
+ {
+ switch (component.GeneratorState.StateType)
+ {
+ case GeneratorStateType.Inactive:
+ ShowPopup(uid, "m_emp-system-report-activate-success", user);
+ var generatorTransform = Transform(uid);
+ var gridId = generatorTransform.GridUid ?? throw new InvalidOperationException("Generator had no grid associated");
+ if (!_M_EmpGridStates.TryGetValue(gridId, out var gridState))
+ {
+ gridState = new M_EmpGridState();
+ _M_EmpGridStates[gridId] = gridState;
+ }
+ gridState.ActiveGenerators.Add(uid);
+
+ PlayActivatedSound(uid, component);
+
+ component.GeneratorState = new GeneratorState(GeneratorStateType.Activating, gridState.CurrentTime + component.ActivatingTime);
+ RaiseLocalEvent(new M_EmpGeneratorActivatedEvent(uid));
+
+ //Report(uid, component.M_EmpChannel, "m_emp-system-report-activate-success"); Removed to lower spam
+
+ break;
+ case GeneratorStateType.Activating:
+ case GeneratorStateType.Engaged:
+ ShowPopup(uid, "m_emp-system-report-already-active", user);
+ break;
+ case GeneratorStateType.CoolingDown:
+ case GeneratorStateType.Recharging:
+ ShowPopup(uid, "m_emp-system-report-recharging", user);
+ break;
+ default:
+ throw new ArgumentOutOfRangeException();
+ }
+ }
+ private void ShowPopup(EntityUid uid, string messageKey, EntityUid user)
+ {
+ _popupSystem.PopupEntity(Loc.GetString(messageKey), uid, user);
+ }
+
+ private bool SpawnM_Emp(EntityUid uid, M_EmpGeneratorComponent component)
+ {
+ var station = _station.GetOwningStation(uid);
+ var stationName = station is null ? null : Name(station.Value);
+
+ Report(uid, component.M_EmpChannel, "m_emp-system-announcement-active", ("timeLeft", component.EngagedTime.TotalSeconds), ("grid", stationName!));
+
+ var empRange = 100;
+ var empEnergyConsumption = 50000;
+ var empDisabledDuration = 60;
+
+ _emp.EmpPulse(Transform(uid).MapPosition, empRange, empEnergyConsumption, empDisabledDuration);
+
+ return true;
+ }
+
+ private void Report(EntityUid source, string channelName, string messageKey, params (string, object)[] args)
+ {
+ var message = args.Length == 0 ? Loc.GetString(messageKey) : Loc.GetString(messageKey, args);
+ var channel = _prototypeManager.Index(channelName);
+ _radioSystem.SendRadioMessage(source, message, channel, source);
+ }
+
+ private void Transition(EntityUid uid, M_EmpGeneratorComponent generator, TimeSpan currentTime)
+ {
+ switch (generator.GeneratorState.StateType)
+ {
+ case GeneratorStateType.Activating:
+ if (SpawnM_Emp(uid, generator))
+ {
+ generator.GeneratorState = new GeneratorState(GeneratorStateType.Engaged, currentTime + generator.EngagedTime);
+ }
+ else
+ {
+ generator.GeneratorState = new GeneratorState(GeneratorStateType.Recharging, currentTime + generator.Recharging);
+ }
+ break;
+ case GeneratorStateType.Engaged:
+ Report(uid, generator.M_EmpChannel, "m_emp-system-announcement-cooling-down", ("timeLeft", generator.CoolingDownTime.TotalSeconds));
+ generator.GeneratorState = new GeneratorState(GeneratorStateType.CoolingDown, currentTime + generator.CoolingDownTime);
+ break;
+ case GeneratorStateType.CoolingDown:
+ //Report(uid, generator.M_EmpChannel, "m_emp-system-announcement-recharging"); // Less chat spam
+ generator.GeneratorState = new GeneratorState(GeneratorStateType.Recharging, currentTime + generator.Recharging);
+ break;
+ case GeneratorStateType.Recharging:
+ generator.GeneratorState = GeneratorState.Inactive;
+ break;
+ }
+ UpdateAppearance(uid, generator);
+ UpdateChargeStateAppearance(uid, currentTime, generator);
+ }
+
+ public override void Update(float frameTime)
+ {
+ var secondsPassed = TimeSpan.FromSeconds(frameTime);
+ // Keep track of time, and state per grid
+ foreach (var (uid, state) in _M_EmpGridStates)
+ {
+ if (state.ActiveGenerators.Count == 0) continue;
+ // Not handling the case where the M_Emp we spawned got paused
+ // They both need to be paused, or it doesn't make sense
+ if (MetaData(uid).EntityPaused) continue;
+ state.CurrentTime += secondsPassed;
+
+ var deleteQueue = new RemQueue();
+
+ foreach(var generator in state.ActiveGenerators)
+ {
+ if (!TryComp(generator, out var generatorComp))
+ continue;
+
+ UpdateChargeStateAppearance(generator, state.CurrentTime, generatorComp);
+ if (generatorComp.GeneratorState.Until > state.CurrentTime) continue;
+ Transition(generator, generatorComp, state.CurrentTime);
+ if (generatorComp.GeneratorState.StateType == GeneratorStateType.Inactive)
+ {
+ deleteQueue.Add(generator);
+ }
+ }
+
+ foreach(var generator in deleteQueue)
+ {
+ state.ActiveGenerators.Remove(generator);
+ }
+ }
+ }
+ private void PlayActivatedSound(EntityUid uid, SharedM_EmpGeneratorComponent component)
+ {
+ _audio.PlayPvs(_audio.GetSound(component.ActivatedSound), uid);
+ }
+ }
+
+ public sealed class M_EmpGridState
+ {
+ public TimeSpan CurrentTime { get; set; }
+ public List ActiveGenerators { get; } = new();
+ }
+}
+
diff --git a/Content.Shared/Emp/EmpDisabledComponent.cs b/Content.Shared/Emp/EmpDisabledComponent.cs
index c84bab9d4dc..b1e5c805e36 100644
--- a/Content.Shared/Emp/EmpDisabledComponent.cs
+++ b/Content.Shared/Emp/EmpDisabledComponent.cs
@@ -20,6 +20,9 @@ public sealed class EmpDisabledComponent : Component
[DataField("effectCoolDown"), ViewVariables(VVAccess.ReadWrite)]
public float EffectCooldown = 3f;
+ [DataField("ignored"), ViewVariables(VVAccess.ReadWrite)]
+ public bool Ignored = false;
+
///
/// When next effect will be spawned
///
diff --git a/Content.Shared/_NF/M_Emp/SharedM_EmpComponent.cs b/Content.Shared/_NF/M_Emp/SharedM_EmpComponent.cs
new file mode 100644
index 00000000000..0f93204cb61
--- /dev/null
+++ b/Content.Shared/_NF/M_Emp/SharedM_EmpComponent.cs
@@ -0,0 +1,13 @@
+using Content.Shared._NF.M_Emp;
+using Robust.Shared.GameStates;
+using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
+using Content.Shared.MachineLinking;
+
+namespace Content.Shared._NF.M_Emp;
+
+[NetworkedComponent, RegisterComponent]
+public sealed class SharedM_EmpComponent : Component
+{
+ [DataField("receiverPort", customTypeSerializer: typeof(PrototypeIdSerializer)), ViewVariables(VVAccess.ReadWrite)]
+ public string ReceiverPort = "M_Emp";
+}
diff --git a/Content.Shared/_NF/M_Emp/SharedM_EmpGeneratorComponent.cs b/Content.Shared/_NF/M_Emp/SharedM_EmpGeneratorComponent.cs
new file mode 100644
index 00000000000..c5abcf44b27
--- /dev/null
+++ b/Content.Shared/_NF/M_Emp/SharedM_EmpGeneratorComponent.cs
@@ -0,0 +1,36 @@
+using Content.Shared.Construction.Prototypes;
+using Robust.Shared.Prototypes;
+using Robust.Shared.Serialization;
+using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
+using Robust.Shared.Audio;
+
+namespace Content.Shared._NF.M_Emp;
+
+public abstract class SharedM_EmpGeneratorComponent : Component
+{
+ ///
+ /// The machine part that affects the attaching and cooldown times
+ ///
+ [DataField("machinePartDelay", customTypeSerializer: typeof(PrototypeIdSerializer)), ViewVariables(VVAccess.ReadWrite)]
+ public string MachinePartDelay = "Capacitor";
+
+ ///
+ /// A multiplier applied to the attaching and cooldown times for each level of
+ ///
+ [DataField("partRatingDelay"), ViewVariables(VVAccess.ReadWrite)]
+ public float PartRatingDelay = 0.75f;
+
+ [DataField("activatedSound")]
+ public SoundSpecifier ActivatedSound =
+ new SoundPathSpecifier("/Audio/Effects/countdown.ogg");
+}
+
+[Serializable, NetSerializable]
+public enum M_EmpGeneratorVisuals : byte
+{
+ ChargeState,
+ Ready,
+ ReadyBlinking,
+ Unready,
+ UnreadyBlinking
+}
diff --git a/Content.Shared/_NF/M_Emp/SharedM_EmpSystem.cs b/Content.Shared/_NF/M_Emp/SharedM_EmpSystem.cs
new file mode 100644
index 00000000000..c40b6cf1ab9
--- /dev/null
+++ b/Content.Shared/_NF/M_Emp/SharedM_EmpSystem.cs
@@ -0,0 +1,16 @@
+using System.Linq;
+using Content.Shared.Dataset;
+using Content.Shared.Random;
+using Content.Shared.Random.Helpers;
+using Robust.Shared.Prototypes;
+using Robust.Shared.Random;
+using Robust.Shared.Serialization;
+using Robust.Shared.Utility;
+
+namespace Content.Shared._NF.M_Emp;
+
+public abstract class SharedM_EmpSystem : EntitySystem
+{
+ [Dependency] private readonly ILocalizationManager _loc = default!;
+ [Dependency] private readonly IPrototypeManager _proto = default!;
+}
diff --git a/Resources/Locale/en-US/_NF/machine-linking/receiver_ports.ftl b/Resources/Locale/en-US/_NF/machine-linking/receiver_ports.ftl
new file mode 100644
index 00000000000..90da17cdf23
--- /dev/null
+++ b/Resources/Locale/en-US/_NF/machine-linking/receiver_ports.ftl
@@ -0,0 +1,2 @@
+signal-port-name-m_emp = M_EMP
+signal-port-description-m_emp = Activate the device.
\ No newline at end of file
diff --git a/Resources/Locale/en-US/_NF/store/uplink-catalog.ftl b/Resources/Locale/en-US/_NF/store/uplink-catalog.ftl
new file mode 100644
index 00000000000..89f0c2cf6c2
--- /dev/null
+++ b/Resources/Locale/en-US/_NF/store/uplink-catalog.ftl
@@ -0,0 +1,2 @@
+uplink-emp-grenade-launcher-bundle-name = EMP China-Lake Bundle
+uplink-emp-grenade-launcher-bundle-desc = An old China-Lake grenade launcher bundled with 8 rounds of EMP.
\ No newline at end of file
diff --git a/Resources/Locale/en-US/m_emp/m_emp.ftl b/Resources/Locale/en-US/m_emp/m_emp.ftl
new file mode 100644
index 00000000000..4949388230a
--- /dev/null
+++ b/Resources/Locale/en-US/m_emp/m_emp.ftl
@@ -0,0 +1,19 @@
+m_emp-system-announcement-source = M_EMP Generator System
+m_emp-system-announcement-active = Engaging on {$grid}. EMP: {$timeLeft} seconds.
+m_emp-system-announcement-cooling-down = EMP effect is no longer active. Estimated Recharging: {$timeLeft} seconds.
+m_emp-system-announcement-recharging = Recharging.
+
+m_emp-system-report-already-active = The M_EMP Generator is already active.
+m_emp-system-report-cooling-down = The M_EMP Generator is cooling down.
+m_emp-system-report-activate-success = The M_EMP Generator engaging!
+
+m_emp-system-generator-examined-inactive = The M_EMP Generator is inactive.
+m_emp-system-generator-examined-starting = The M_EMP Generator is starting up.
+m_emp-system-generator-examined-active = The M_EMP active. EMP will last { $timeLeft ->
+ [1] one second.
+ *[other] { $timeLeft } seconds.
+}
+m_emp-system-generator-examined-cooling-down = Cooling down.
+m_emp-system-generator-examined-recharging = Recharging. Ready in: {$timeLeft} seconds.
+m_emp-system-generator-delay-upgrade = Cooling down / Recharging speed
+
diff --git a/Resources/Prototypes/Catalog/Fills/Boxes/ammunition.yml b/Resources/Prototypes/Catalog/Fills/Boxes/ammunition.yml
index af7776e6005..cd8b5f463b6 100644
--- a/Resources/Prototypes/Catalog/Fills/Boxes/ammunition.yml
+++ b/Resources/Prototypes/Catalog/Fills/Boxes/ammunition.yml
@@ -304,6 +304,17 @@
- id: MagazinePistolSubMachineGunRubber
amount: 3
+- type: entity
+ name: box of SMG .35 auto (emp) magazines
+ parent: BoxMagazine
+ id: BoxMagazinePistolSubMachineGunEmp
+ description: A box full of SMG .35 auto (emp) magazines.
+ components:
+ - type: StorageFill
+ contents:
+ - id: MagazinePistolSubMachineGunEmp
+ amount: 3
+
# Shotgun
- type: entity
name: box of (.50 pellet) ammo drums
@@ -466,7 +477,6 @@
- state: boxwide
- state: shellslug
-
# Rifle
- type: entity
name: box of .20 rifle magazines
diff --git a/Resources/Prototypes/Entities/Objects/Devices/Circuitboards/Machine/production.yml b/Resources/Prototypes/Entities/Objects/Devices/Circuitboards/Machine/production.yml
index 71427232eb4..5a0d520b400 100644
--- a/Resources/Prototypes/Entities/Objects/Devices/Circuitboards/Machine/production.yml
+++ b/Resources/Prototypes/Entities/Objects/Devices/Circuitboards/Machine/production.yml
@@ -928,3 +928,18 @@
Steel: 5
CableHV: 5
Cable: 2
+
+- type: entity
+ parent: BaseMachineCircuitboard
+ id: M_EmpMachineCircuitboard
+ name: M_EMP Generator machine board
+ description: A machine printed circuit board for a mobile EMP generator.
+ components:
+ - type: MachineBoard
+ prototype: M_Emp
+ requirements:
+ Capacitor: 4
+ materialRequirements:
+ Steel: 5
+ CableHV: 5
+ Cable: 2
\ No newline at end of file
diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Ammunition/Boxes/light_rifle.yml b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Ammunition/Boxes/light_rifle.yml
index 7018ff37647..942faa544d6 100644
--- a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Ammunition/Boxes/light_rifle.yml
+++ b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Ammunition/Boxes/light_rifle.yml
@@ -117,4 +117,4 @@
map: ["enum.GunVisualLayers.Base"]
- state: mag-1
map: ["enum.GunVisualLayers.Mag"]
- - state: incendiary
+ - state: incendiary
\ No newline at end of file
diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Ammunition/Boxes/pistol.yml b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Ammunition/Boxes/pistol.yml
index d8bab16cdbc..b386ee67ac5 100644
--- a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Ammunition/Boxes/pistol.yml
+++ b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Ammunition/Boxes/pistol.yml
@@ -85,8 +85,8 @@
- state: rubber
- type: entity
- id: MagazineBoxPistolIncendiary
parent: BaseMagazineBoxPistol
+ id: MagazineBoxPistolIncendiary
name: ammunition box (.35 auto incendiary)
components:
- type: BallisticAmmoProvider
@@ -98,3 +98,18 @@
- state: mag-1
map: ["enum.GunVisualLayers.Mag"]
- state: incendiary
+
+- type: entity
+ parent: BaseMagazineBoxPistol
+ id: MagazineBoxPistolEmp
+ name: ammunition box (.35 auto emp)
+ components:
+ - type: BallisticAmmoProvider
+ proto: CartridgePistolEmp
+ - type: Sprite
+ layers:
+ - state: base
+ map: ["enum.GunVisualLayers.Base"]
+ - state: mag-1
+ map: ["enum.GunVisualLayers.Mag"]
+ - state: emp
diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Ammunition/Boxes/rifle.yml b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Ammunition/Boxes/rifle.yml
index 260237c3c0e..b2093a3a5d6 100644
--- a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Ammunition/Boxes/rifle.yml
+++ b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Ammunition/Boxes/rifle.yml
@@ -137,4 +137,4 @@
map: ["enum.GunVisualLayers.Base"]
- state: mag-1
map: ["enum.GunVisualLayers.Mag"]
- - state: incendiary
+ - state: incendiary
\ No newline at end of file
diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Ammunition/Cartridges/caseless_rifle.yml b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Ammunition/Cartridges/caseless_rifle.yml
index ecf0c4017e5..9f2d06f7493 100644
--- a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Ammunition/Cartridges/caseless_rifle.yml
+++ b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Ammunition/Cartridges/caseless_rifle.yml
@@ -51,4 +51,4 @@
parent: BaseCartridgeCaselessRifle
components:
- type: CartridgeAmmo
- proto: BulletCaselessRifleRubber
+ proto: BulletCaselessRifleRubber
\ No newline at end of file
diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Ammunition/Cartridges/pistol.yml b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Ammunition/Cartridges/pistol.yml
index 39d9b945031..c7590865aec 100644
--- a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Ammunition/Cartridges/pistol.yml
+++ b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Ammunition/Cartridges/pistol.yml
@@ -59,3 +59,11 @@
components:
- type: CartridgeAmmo
proto: BulletPistolIncendiary
+
+- type: entity
+ id: CartridgePistolEmp
+ name: cartridge (.35 auto emp)
+ parent: BaseCartridgePistol
+ components:
+ - type: CartridgeAmmo
+ proto: BulletPistolEmp
diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Ammunition/Magazines/light_rifle.yml b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Ammunition/Magazines/light_rifle.yml
index f09d3e3bb62..98cb2d579fb 100644
--- a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Ammunition/Magazines/light_rifle.yml
+++ b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Ammunition/Magazines/light_rifle.yml
@@ -122,23 +122,4 @@
proto: CartridgeLightRifle
capacity: 96
- type: Sprite
- sprite: Objects/Weapons/Guns/Ammunition/Magazine/LightRifle/maxim.rsi
-
-- type: entity
- id: MagazineLightRiflePkBox
- name: "PK munitions box (.30 rifle)"
- parent: BaseMagazineLightRifle
- components:
- - type: Tag
- tags:
- - MagazineLightRifleBox
- - type: BallisticAmmoProvider
- proto: CartridgeLightRifle
- capacity: 80
- - type: Sprite
- sprite: Objects/Weapons/Guns/Ammunition/Magazine/LightRifle/pk_box.rsi
- - type: MagazineVisuals
- magState: mag
- steps: 7
- zeroVisible: false
- - type: Appearance
+ sprite: Objects/Weapons/Guns/Ammunition/Magazine/LightRifle/maxim.rsi
\ No newline at end of file
diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Ammunition/Magazines/pistol.yml b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Ammunition/Magazines/pistol.yml
index 42bb1487424..83b68c37eb2 100644
--- a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Ammunition/Magazines/pistol.yml
+++ b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Ammunition/Magazines/pistol.yml
@@ -301,3 +301,17 @@
map: ["enum.GunVisualLayers.Base"]
- state: mag-1
map: ["enum.GunVisualLayers.Mag"]
+
+- type: entity
+ id: MagazinePistolSubMachineGunEmp
+ name: SMG magazine (.35 auto emp)
+ parent: BaseMagazinePistolSubMachineGun
+ components:
+ - type: BallisticAmmoProvider
+ proto: CartridgePistolEmp
+ - type: Sprite
+ layers:
+ - state: rubber
+ map: ["enum.GunVisualLayers.Base"]
+ - state: mag-1
+ map: ["enum.GunVisualLayers.Mag"]
diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Ammunition/Projectiles/light_rifle.yml b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Ammunition/Projectiles/light_rifle.yml
index 8bf5ce5e625..be2484b3851 100644
--- a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Ammunition/Projectiles/light_rifle.yml
+++ b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Ammunition/Projectiles/light_rifle.yml
@@ -51,4 +51,4 @@
- type: Projectile
damage:
groups:
- Burn: 17
+ Burn: 17
\ No newline at end of file
diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Ammunition/Projectiles/pistol.yml b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Ammunition/Projectiles/pistol.yml
index 8474160ef77..994cb5acb63 100644
--- a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Ammunition/Projectiles/pistol.yml
+++ b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Ammunition/Projectiles/pistol.yml
@@ -52,3 +52,12 @@
damage:
groups:
Burn: 14
+
+- type: entity
+ id: BulletPistolEmp
+ name: bullet (.35 auto emp)
+ parent: BaseBulletEmp
+ noSpawn: true
+ components:
+ - type: Projectile
+
diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Projectiles/hitscan.yml b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Projectiles/hitscan.yml
index a9f4e3aeb81..bcceadcfd9d 100644
--- a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Projectiles/hitscan.yml
+++ b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Projectiles/hitscan.yml
@@ -105,4 +105,4 @@
state: beam_blue
impactFlash:
sprite: Objects/Weapons/Guns/Projectiles/projectiles.rsi
- state: impact_blue
+ state: impact_blue
\ No newline at end of file
diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Projectiles/projectiles.yml b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Projectiles/projectiles.yml
index 4bae5cf5245..7b43a2cadfc 100644
--- a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Projectiles/projectiles.yml
+++ b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Projectiles/projectiles.yml
@@ -721,3 +721,100 @@
- Impassable
- HighImpassable
- type: GrapplingProjectile
+
+- type: entity
+ id: BaseBulletEmp
+ name: base bullet emp
+ parent: BaseBulletTrigger
+ noSpawn: true
+ components:
+ - type: Projectile
+ damage:
+ types:
+ Shock: 0
+ - type: Sprite
+ sprite: Objects/Weapons/Guns/Projectiles/magic.rsi
+ layers:
+ - state: emp_projectile
+ shader: unshaded
+ - type: EmpOnTrigger
+ range: 0.3
+ energyConsumption: 50000
+ disableDuration: 10
+
+- type: entity
+ id: BulletGrenadeEmp
+ name: emp grenade
+ parent: BaseBulletTrigger
+ noSpawn: true
+ components:
+ - type: Projectile
+ damage:
+ types:
+ Shock: 0
+ - type: TimedDespawn
+ lifetime: 15
+ - type: Sprite
+ sprite: Objects/Weapons/Guns/Projectiles/projectiles2.rsi
+ layers:
+ - state: grenade
+ - type: EmpOnTrigger
+ range: 4
+ energyConsumption: 50000
+ disableDuration: 60
+
+- type: entity
+ id: BulletRocketEmp
+ name: rocket
+ parent: BaseBulletTrigger
+ noSpawn: true
+ components:
+ - type: Projectile
+ damage:
+ types:
+ Shock: 0
+ - type: TimedDespawn
+ lifetime: 15
+ - type: Sprite
+ sprite: Objects/Weapons/Guns/Projectiles/projectiles2.rsi
+ layers:
+ - state: frag-emp
+ - type: EmpOnTrigger
+ range: 4
+ energyConsumption: 50000
+ disableDuration: 60
+
+- type: entity
+ parent: BaseBulletTrigger
+ id: ProjectileEmp
+ name: emp projectile
+ noSpawn: true
+ components:
+ - type: Sprite
+ sprite: Objects/Weapons/Guns/Projectiles/magic.rsi
+ layers:
+ - state: emp_projectile
+ shader: unshaded
+ - type: Ammo
+ muzzleFlash: null
+ - type: Physics
+ - type: Fixtures
+ fixtures:
+ projectile:
+ shape:
+ !type:PhysShapeAabb
+ bounds: "-0.2,-0.2,0.2,0.2"
+ hard: false
+ mask:
+ - Opaque
+ fly-by: *flybyfixture
+ - type: Projectile
+ damage:
+ types:
+ Shock: 0
+ - type: TimedDespawn
+ lifetime: 15
+ - type: EmpOnTrigger
+ range: 2
+ energyConsumption: 50000
+ disableDuration: 30
diff --git a/Resources/Prototypes/_NF/Catalog/Fills/Backpacks/duffelbag.yml b/Resources/Prototypes/_NF/Catalog/Fills/Backpacks/duffelbag.yml
new file mode 100644
index 00000000000..acd53d4e257
--- /dev/null
+++ b/Resources/Prototypes/_NF/Catalog/Fills/Backpacks/duffelbag.yml
@@ -0,0 +1,11 @@
+- type: entity
+ parent: ClothingBackpackDuffelSyndicateBundle
+ id: ClothingBackpackDuffelSyndicateFilledEmpGrenadeLauncher
+ name: China-Lake EMP bundle
+ description: "An old China-Lake grenade launcher bundled with 8 rounds of EMP."
+ components:
+ - type: StorageFill
+ contents:
+ - id: WeaponLauncherChinaLakeEmp
+ - id: GrenadeEmp
+ amount: 8
diff --git a/Resources/Prototypes/_NF/Catalog/uplink_catalog.yml b/Resources/Prototypes/_NF/Catalog/uplink_catalog.yml
new file mode 100644
index 00000000000..f7c196cb41f
--- /dev/null
+++ b/Resources/Prototypes/_NF/Catalog/uplink_catalog.yml
@@ -0,0 +1,10 @@
+- type: listing
+ id: UplinkEmpGrenadeLauncherBundle
+ name: uplink-emp-grenade-launcher-bundle-name
+ description: uplink-emp-grenade-launcher-bundle-desc
+ icon: { sprite: /Textures/Objects/Weapons/Guns/Launchers/china_lake.rsi, state: icon }
+ productEntity: ClothingBackpackDuffelSyndicateFilledEmpGrenadeLauncher
+ cost:
+ Telecrystal: 25
+ categories:
+ - UplinkBundles
diff --git a/Resources/Prototypes/_NF/DeviceLinking/sink_ports.yml b/Resources/Prototypes/_NF/DeviceLinking/sink_ports.yml
new file mode 100644
index 00000000000..7523787a58c
--- /dev/null
+++ b/Resources/Prototypes/_NF/DeviceLinking/sink_ports.yml
@@ -0,0 +1,4 @@
+- type: sinkPort
+ id: M_Emp
+ name: signal-port-name-m_emp
+ description: signal-port-description-m_emp
\ No newline at end of file
diff --git a/Resources/Prototypes/_NF/DeviceLinking/source_ports.yml b/Resources/Prototypes/_NF/DeviceLinking/source_ports.yml
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/Resources/Prototypes/_NF/Entities/Objects/Weapons/Ammunition/Boxes/pistol.yml b/Resources/Prototypes/_NF/Entities/Objects/Weapons/Ammunition/Boxes/pistol.yml
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/Resources/Prototypes/_NF/Entities/Objects/Weapons/Ammunition/Cartridges/pistol.yml b/Resources/Prototypes/_NF/Entities/Objects/Weapons/Ammunition/Cartridges/pistol.yml
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/Resources/Prototypes/_NF/Entities/Objects/Weapons/Ammunition/Magazines/pistol.yml b/Resources/Prototypes/_NF/Entities/Objects/Weapons/Ammunition/Magazines/pistol.yml
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/Resources/Prototypes/_NF/Entities/Objects/Weapons/Ammunition/Projectiles/pistol.yml b/Resources/Prototypes/_NF/Entities/Objects/Weapons/Ammunition/Projectiles/pistol.yml
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/Resources/Prototypes/_NF/Entities/Objects/Weapons/Ammunition/explosives.yml b/Resources/Prototypes/_NF/Entities/Objects/Weapons/Ammunition/explosives.yml
new file mode 100644
index 00000000000..f41471aa9bc
--- /dev/null
+++ b/Resources/Prototypes/_NF/Entities/Objects/Weapons/Ammunition/explosives.yml
@@ -0,0 +1,38 @@
+# Rockets
+
+- type: entity
+ id: CartridgeRocketEmp
+ name: PG-7VL emp
+ parent: BaseItem
+ description: A 1.5 emp warhead designed for the RPG-7 launcher. Has tubular shape.
+ components:
+ - type: Tag
+ tags:
+ - CartridgeRocket
+ - type: Item
+ size: 5
+ - type: CartridgeAmmo
+ proto: BulletRocketEmp
+ deleteOnSpawn: true
+ - type: Sprite
+ sprite: Objects/Weapons/Guns/Ammunition/Explosives/explosives.rsi
+ state: rpg-emp
+
+# Grenades
+
+- type: entity
+ id: GrenadeEmp
+ name: emp grenade # Chinalake EMP
+ parent: BaseGrenade
+ components:
+ - type: CartridgeAmmo
+ proto: BulletGrenadeEmp
+ - type: Sprite
+ sprite: Objects/Weapons/Guns/Ammunition/Explosives/explosives.rsi
+ layers:
+ - state: emp
+ map: ["enum.AmmoVisualLayers.Base"]
+ - type: Appearance
+ - type: SpentAmmoVisuals
+ state: emp
+ suffix: false
diff --git a/Resources/Prototypes/_NF/Entities/Objects/Weapons/Guns/Battery/battery_guns.yml b/Resources/Prototypes/_NF/Entities/Objects/Weapons/Guns/Battery/battery_guns.yml
new file mode 100644
index 00000000000..7f64e8c49e6
--- /dev/null
+++ b/Resources/Prototypes/_NF/Entities/Objects/Weapons/Guns/Battery/battery_guns.yml
@@ -0,0 +1,34 @@
+- type: entity
+ name: emp emitter
+ parent: BaseWeaponBattery
+ id: WeaponEmpEmitter
+ description: Releases electromagnetic pulses that disrupt or damage many electronic devices or drain power cells.
+ components:
+ - type: Sprite
+ sprite: Objects/Weapons/Guns/Battery/xray.rsi
+ layers:
+ - state: base
+ map: ["enum.GunVisualLayers.Base"]
+ - state: mag-unshaded-0
+ map: ["enum.GunVisualLayers.MagUnshaded"]
+ shader: unshaded
+ - type: Clothing
+ sprite: Objects/Weapons/Guns/Battery/xray.rsi
+ - type: Gun
+ fireRate: 1
+ selectedMode: SemiAuto
+ availableModes:
+ - SemiAuto
+ soundGunshot:
+ path: /Audio/Weapons/Guns/Gunshots/laser3.ogg
+ - type: Battery
+ maxCharge: 1000
+ startingCharge: 1000
+ - type: ProjectileBatteryAmmoProvider
+ proto: ProjectileEmp
+ fireCost: 1000
+ - type: MagazineVisuals
+ magState: mag
+ steps: 5
+ zeroVisible: true
+ - type: Appearance
diff --git a/Resources/Prototypes/_NF/Entities/Objects/Weapons/Guns/Launchers/launchers.yml b/Resources/Prototypes/_NF/Entities/Objects/Weapons/Guns/Launchers/launchers.yml
new file mode 100644
index 00000000000..5ad52d050d9
--- /dev/null
+++ b/Resources/Prototypes/_NF/Entities/Objects/Weapons/Guns/Launchers/launchers.yml
@@ -0,0 +1,65 @@
+- type: entity
+ name: china lake
+ parent: BaseWeaponLauncher
+ suffix: EMP
+ id: WeaponLauncherChinaLakeEmp
+ description: PLOOP
+ components:
+ - type: Sprite
+ sprite: Objects/Weapons/Guns/Launchers/china_lake.rsi
+ layers:
+ - state: icon
+ map: ["enum.GunVisualLayers.Base"]
+ - type: Clothing
+ sprite: Objects/Weapons/Guns/Launchers/china_lake.rsi
+ - type: AmmoCounter
+ - type: Gun
+ fireRate: 1
+ selectedMode: SemiAuto
+ availableModes:
+ - SemiAuto
+ soundGunshot:
+ path: /Audio/Weapons/Guns/Gunshots/grenade_launcher.ogg
+ - type: BallisticAmmoProvider
+ whitelist:
+ tags:
+ - Grenade
+ autoCycle: false
+ capacity: 3
+ proto: GrenadeEmp
+ soundInsert:
+ path: /Audio/Weapons/Guns/MagIn/batrifle_magin.ogg
+
+- type: entity
+ name: RPG-7
+ suffix: EMP
+ parent: BaseWeaponLauncher
+ id: WeaponLauncherRocketEmp
+ description: A modified ancient rocket-propelled grenade launcher.
+ components:
+ - type: Sprite
+ sprite: Objects/Weapons/Guns/Launchers/rocket.rsi
+ layers:
+ - state: base
+ map: ["enum.GunVisualLayers.Base"]
+ - state: mag-0
+ map: ["enum.GunVisualLayers.Mag"]
+ - type: Clothing
+ sprite: Objects/Weapons/Guns/Launchers/rocket.rsi
+ - type: Gun
+ fireRate: 0.5
+ soundGunshot:
+ path: /Audio/Weapons/Guns/Gunshots/rpgfire.ogg
+ - type: BallisticAmmoProvider
+ whitelist:
+ tags:
+ - CartridgeRocket
+ proto: CartridgeRocketEmp
+ capacity: 1
+ soundInsert:
+ path: /Audio/Weapons/Guns/MagIn/batrifle_magin.ogg
+ - type: MagazineVisuals
+ magState: mag
+ steps: 2
+ zeroVisible: false
+ - type: Appearance
\ No newline at end of file
diff --git a/Resources/Prototypes/_NF/Entities/Objects/Weapons/Guns/Projectiles/projectiles.yml b/Resources/Prototypes/_NF/Entities/Objects/Weapons/Guns/Projectiles/projectiles.yml
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/Resources/Prototypes/_NF/Entities/Structures/Machines/m_emp.yml b/Resources/Prototypes/_NF/Entities/Structures/Machines/m_emp.yml
new file mode 100644
index 00000000000..b073361de92
--- /dev/null
+++ b/Resources/Prototypes/_NF/Entities/Structures/Machines/m_emp.yml
@@ -0,0 +1,72 @@
+- type: entity
+ parent: [ BaseMachinePowered, ConstructibleMachine ]
+ id: M_Emp
+ name: M_EMP Generator
+ description: Mobile EMP generator.
+ components:
+ - type: Sprite
+ sprite: _NF/Structures/Machines/m_emp.rsi
+ layers:
+ - state: m_emp
+ - state: m_emp-ready
+ visible: false
+ map: [ "ready" ]
+ - state: m_emp-ready-blinking
+ visible: false
+ map: [ "readyBlinking" ]
+ - state: m_emp-unready
+ visible: false
+ map: [ "unready" ]
+ - state: m_emp-unready-blinking
+ visible: false
+ map: [ "unreadyBlinking" ]
+ - state: m_emp-o4
+ map: ["chargeState"]
+ shader: unshaded
+ - type: Appearance
+ - type: GenericVisualizer
+ visuals:
+ enum.M_EmpGeneratorVisuals.ChargeState:
+ chargeState:
+ 0: { state: m_emp-o0, shader: "unshaded", visible: false }
+ 1: { state: m_emp-o0, shader: "unshaded", visible: true }
+ 2: { state: m_emp-o1, shader: "unshaded", visible: true }
+ 3: { state: m_emp-o2, shader: "unshaded", visible: true }
+ 4: { state: m_emp-o3, shader: "unshaded", visible: true }
+ 5: { state: m_emp-o4, shader: "unshaded", visible: true }
+ enum.M_EmpGeneratorVisuals.Ready:
+ ready:
+ False: { state: m_emp-ready, visible: false, shader: "unshaded" }
+ True: { state: m_emp-ready, visible: true, shader: "unshaded" }
+ enum.M_EmpGeneratorVisuals.ReadyBlinking:
+ readyBlinking:
+ False: { state: m_emp-ready-blinking, visible: false, shader: "unshaded" }
+ True: { state: m_emp-ready-blinking, visible: true, shader: "unshaded" }
+ enum.M_EmpGeneratorVisuals.Unready:
+ unready:
+ False: { state: m_emp-unready, visible: false, shader: "unshaded" }
+ True: { state: m_emp-unready, visible: true, shader: "unshaded" }
+ enum.M_EmpGeneratorVisuals.UnreadyBlinking:
+ unreadyBlinking:
+ False: { state: m_emp-unready-blinking, visible: false, shader: "unshaded" }
+ True: { state: m_emp-unready-blinking, visible: true, shader: "unshaded" }
+ - type: Rotatable
+ - type: Transform
+ noRot: false
+ - type: IntrinsicRadioReceiver
+ - type: ActiveRadio
+ channels:
+ - Security
+ - type: M_EmpGenerator
+ - type: ApcPowerReceiver
+ powerLoad: 2500 # TODO change this to a HV power draw that really hits the grid hard WHEN active
+ - type: Machine
+ board: M_EmpMachineCircuitboard
+ - type: DeviceNetwork
+ deviceNetId: Wireless
+ receiveFrequencyId: BasicDevice
+ - type: WirelessNetworkConnection
+ range: 200
+ - type: DeviceLinkSink
+ ports:
+ - M_Emp
\ No newline at end of file
diff --git a/Resources/Prototypes/_NF/Entities/Structures/Power/apc.yml b/Resources/Prototypes/_NF/Entities/Structures/Power/apc.yml
new file mode 100644
index 00000000000..3bdab73e958
--- /dev/null
+++ b/Resources/Prototypes/_NF/Entities/Structures/Power/apc.yml
@@ -0,0 +1,11 @@
+# APCs in use
+- type: entity
+ parent: BaseAPC
+ id: APCBasicEmpImmune
+ suffix: EMP Immune, 50kW
+ components:
+ - type: Battery
+ maxCharge: 50000
+ startingCharge: 50000
+ - type: Apc
+ apcIgnoreEmp: true
\ No newline at end of file
diff --git a/Resources/Prototypes/_NF/MachineLinking/transmitter_ports.yml b/Resources/Prototypes/_NF/MachineLinking/transmitter_ports.yml
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/Resources/Textures/Objects/Weapons/Guns/Ammunition/Boxes/pistol.rsi/emp.png b/Resources/Textures/Objects/Weapons/Guns/Ammunition/Boxes/pistol.rsi/emp.png
new file mode 100644
index 00000000000..f317d03c212
Binary files /dev/null and b/Resources/Textures/Objects/Weapons/Guns/Ammunition/Boxes/pistol.rsi/emp.png differ
diff --git a/Resources/Textures/Objects/Weapons/Guns/Ammunition/Boxes/pistol.rsi/meta.json b/Resources/Textures/Objects/Weapons/Guns/Ammunition/Boxes/pistol.rsi/meta.json
index 67d525fcfd9..6e4341d168c 100644
--- a/Resources/Textures/Objects/Weapons/Guns/Ammunition/Boxes/pistol.rsi/meta.json
+++ b/Resources/Textures/Objects/Weapons/Guns/Ammunition/Boxes/pistol.rsi/meta.json
@@ -30,6 +30,9 @@
},
{
"name": "rubber"
+ },
+ {
+ "name": "emp"
}
]
}
diff --git a/Resources/Textures/Objects/Weapons/Guns/Ammunition/Explosives/explosives.rsi/emp.png b/Resources/Textures/Objects/Weapons/Guns/Ammunition/Explosives/explosives.rsi/emp.png
new file mode 100644
index 00000000000..6f13f226772
Binary files /dev/null and b/Resources/Textures/Objects/Weapons/Guns/Ammunition/Explosives/explosives.rsi/emp.png differ
diff --git a/Resources/Textures/Objects/Weapons/Guns/Ammunition/Explosives/explosives.rsi/meta.json b/Resources/Textures/Objects/Weapons/Guns/Ammunition/Explosives/explosives.rsi/meta.json
index 5a58b6b6089..ba4c1d14240 100644
--- a/Resources/Textures/Objects/Weapons/Guns/Ammunition/Explosives/explosives.rsi/meta.json
+++ b/Resources/Textures/Objects/Weapons/Guns/Ammunition/Explosives/explosives.rsi/meta.json
@@ -18,10 +18,16 @@
},
{
"name": "frag"
+ },
+ {
+ "name": "emp"
},
{
"name": "rpg"
},
+ {
+ "name": "rpg-emp"
+ },
{
"name": "ball"
},
diff --git a/Resources/Textures/Objects/Weapons/Guns/Ammunition/Explosives/explosives.rsi/rpg-emp.png b/Resources/Textures/Objects/Weapons/Guns/Ammunition/Explosives/explosives.rsi/rpg-emp.png
new file mode 100644
index 00000000000..ddd1f4b6b38
Binary files /dev/null and b/Resources/Textures/Objects/Weapons/Guns/Ammunition/Explosives/explosives.rsi/rpg-emp.png differ
diff --git a/Resources/Textures/Objects/Weapons/Guns/Projectiles/magic.rsi/emp_projectile.png b/Resources/Textures/Objects/Weapons/Guns/Projectiles/magic.rsi/emp_projectile.png
new file mode 100644
index 00000000000..b824881c5f8
Binary files /dev/null and b/Resources/Textures/Objects/Weapons/Guns/Projectiles/magic.rsi/emp_projectile.png differ
diff --git a/Resources/Textures/Objects/Weapons/Guns/Projectiles/magic.rsi/meta.json b/Resources/Textures/Objects/Weapons/Guns/Projectiles/magic.rsi/meta.json
index 0dbedf2922b..b1022c3c939 100644
--- a/Resources/Textures/Objects/Weapons/Guns/Projectiles/magic.rsi/meta.json
+++ b/Resources/Textures/Objects/Weapons/Guns/Projectiles/magic.rsi/meta.json
@@ -18,6 +18,17 @@
]
]
},
+ {
+ "name": "emp_projectile",
+ "delays": [
+ [
+ 0.1,
+ 0.1,
+ 0.1,
+ 0.1
+ ]
+ ]
+ },
{
"name": "magicm_green",
"delays": [
diff --git a/Resources/Textures/Objects/Weapons/Guns/Projectiles/projectiles2.rsi/frag-emp.png b/Resources/Textures/Objects/Weapons/Guns/Projectiles/projectiles2.rsi/frag-emp.png
new file mode 100644
index 00000000000..05ff48cdc0b
Binary files /dev/null and b/Resources/Textures/Objects/Weapons/Guns/Projectiles/projectiles2.rsi/frag-emp.png differ
diff --git a/Resources/Textures/Objects/Weapons/Guns/Projectiles/projectiles2.rsi/meta.json b/Resources/Textures/Objects/Weapons/Guns/Projectiles/projectiles2.rsi/meta.json
index f9825de1a4a..9cfee3da832 100644
--- a/Resources/Textures/Objects/Weapons/Guns/Projectiles/projectiles2.rsi/meta.json
+++ b/Resources/Textures/Objects/Weapons/Guns/Projectiles/projectiles2.rsi/meta.json
@@ -28,6 +28,9 @@
{
"name": "frag"
},
+ {
+ "name": "frag-emp"
+ },
{
"name": "grenade",
"delays": [
diff --git a/Resources/Textures/_NF/Structures/Machines/m_emp.rsi/m_emp-o0.png b/Resources/Textures/_NF/Structures/Machines/m_emp.rsi/m_emp-o0.png
new file mode 100644
index 00000000000..6df11fbe99c
Binary files /dev/null and b/Resources/Textures/_NF/Structures/Machines/m_emp.rsi/m_emp-o0.png differ
diff --git a/Resources/Textures/_NF/Structures/Machines/m_emp.rsi/m_emp-o1.png b/Resources/Textures/_NF/Structures/Machines/m_emp.rsi/m_emp-o1.png
new file mode 100644
index 00000000000..f6cc385e4cf
Binary files /dev/null and b/Resources/Textures/_NF/Structures/Machines/m_emp.rsi/m_emp-o1.png differ
diff --git a/Resources/Textures/_NF/Structures/Machines/m_emp.rsi/m_emp-o2.png b/Resources/Textures/_NF/Structures/Machines/m_emp.rsi/m_emp-o2.png
new file mode 100644
index 00000000000..ac86dc152da
Binary files /dev/null and b/Resources/Textures/_NF/Structures/Machines/m_emp.rsi/m_emp-o2.png differ
diff --git a/Resources/Textures/_NF/Structures/Machines/m_emp.rsi/m_emp-o3.png b/Resources/Textures/_NF/Structures/Machines/m_emp.rsi/m_emp-o3.png
new file mode 100644
index 00000000000..6a313591d35
Binary files /dev/null and b/Resources/Textures/_NF/Structures/Machines/m_emp.rsi/m_emp-o3.png differ
diff --git a/Resources/Textures/_NF/Structures/Machines/m_emp.rsi/m_emp-o4.png b/Resources/Textures/_NF/Structures/Machines/m_emp.rsi/m_emp-o4.png
new file mode 100644
index 00000000000..5761f5bb75c
Binary files /dev/null and b/Resources/Textures/_NF/Structures/Machines/m_emp.rsi/m_emp-o4.png differ
diff --git a/Resources/Textures/_NF/Structures/Machines/m_emp.rsi/m_emp-ready-blinking.png b/Resources/Textures/_NF/Structures/Machines/m_emp.rsi/m_emp-ready-blinking.png
new file mode 100644
index 00000000000..ec74a68ba7c
Binary files /dev/null and b/Resources/Textures/_NF/Structures/Machines/m_emp.rsi/m_emp-ready-blinking.png differ
diff --git a/Resources/Textures/_NF/Structures/Machines/m_emp.rsi/m_emp-ready.png b/Resources/Textures/_NF/Structures/Machines/m_emp.rsi/m_emp-ready.png
new file mode 100644
index 00000000000..084dc7ecf00
Binary files /dev/null and b/Resources/Textures/_NF/Structures/Machines/m_emp.rsi/m_emp-ready.png differ
diff --git a/Resources/Textures/_NF/Structures/Machines/m_emp.rsi/m_emp-unready-blinking.png b/Resources/Textures/_NF/Structures/Machines/m_emp.rsi/m_emp-unready-blinking.png
new file mode 100644
index 00000000000..1bc46e2eb39
Binary files /dev/null and b/Resources/Textures/_NF/Structures/Machines/m_emp.rsi/m_emp-unready-blinking.png differ
diff --git a/Resources/Textures/_NF/Structures/Machines/m_emp.rsi/m_emp-unready.png b/Resources/Textures/_NF/Structures/Machines/m_emp.rsi/m_emp-unready.png
new file mode 100644
index 00000000000..a20b91edd16
Binary files /dev/null and b/Resources/Textures/_NF/Structures/Machines/m_emp.rsi/m_emp-unready.png differ
diff --git a/Resources/Textures/_NF/Structures/Machines/m_emp.rsi/m_emp.png b/Resources/Textures/_NF/Structures/Machines/m_emp.rsi/m_emp.png
new file mode 100644
index 00000000000..3d7d0541805
Binary files /dev/null and b/Resources/Textures/_NF/Structures/Machines/m_emp.rsi/m_emp.png differ
diff --git a/Resources/Textures/_NF/Structures/Machines/m_emp.rsi/meta.json b/Resources/Textures/_NF/Structures/Machines/m_emp.rsi/meta.json
new file mode 100644
index 00000000000..5eca3207be8
--- /dev/null
+++ b/Resources/Textures/_NF/Structures/Machines/m_emp.rsi/meta.json
@@ -0,0 +1,69 @@
+{
+ "version": 1,
+ "license": "CC-BY-SA-3.0",
+ "copyright": "Edit by 20kdc using small parts of autolathe from https://github.com/tgstation/tgstation/blob/acb091f9744e9ab7d5a27fb32dd0c03bd019f58c/icons/obj/stationobjs.dmi (may be an earlier version) and tcboss from https://github.com/tgstation/tgstation/blob/e32357e6b0ec0f0a1821d2773f0d1e1d6ce7d494/icons/obj/computer.dmi (may be an earlier version)",
+ "size": {
+ "x": 32,
+ "y": 64
+ },
+ "states": [
+ {
+ "name": "m_emp",
+ "directions": 4
+ },
+ {
+ "name": "m_emp-o0",
+ "directions": 4,
+ "commentary": "o0 - 04 represent the amount of time remaining during EMP and cooldown."
+ },
+ {
+ "name": "m_emp-o1",
+ "directions": 4
+ },
+ {
+ "name": "m_emp-o2",
+ "directions": 4
+ },
+ {
+ "name": "m_emp-o3",
+ "directions": 4
+ },
+ {
+ "name": "m_emp-o4",
+ "directions": 4
+ },
+ {
+ "name": "m_emp-unready",
+ "directions": 4,
+ "commentary": "Cooldown state"
+ },
+ {
+ "name": "m_emp-ready",
+ "directions": 4,
+ "commentary": "EMP state"
+ },
+ {
+ "name": "m_emp-ready-blinking",
+ "directions": 4,
+ "delays": [
+ [ 0.2, 0.2],
+ [ 0.2, 0.2],
+ [ 0.2, 0.2],
+ [ 0.2, 0.2]
+ ],
+ "commentary": "EMP active state"
+ },
+ {
+ "name": "m_emp-unready-blinking",
+ "directions": 4,
+ "delays": [
+ [ 0.2, 0.2],
+ [ 0.2, 0.2],
+ [ 0.2, 0.2],
+ [ 0.2, 0.2]
+ ],
+ "commentary": "EMP inactive state"
+ }
+ ]
+}
+