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" + } + ] +} +