diff --git a/Content.Server/Emp/EmpOnTriggerComponent.cs b/Content.Server/Emp/EmpOnTriggerComponent.cs new file mode 100644 index 0000000000..b3f486b895 --- /dev/null +++ b/Content.Server/Emp/EmpOnTriggerComponent.cs @@ -0,0 +1,17 @@ +namespace Content.Server.Emp; + +/// +/// Upon being triggered will EMP area around it. +/// +[RegisterComponent] +sealed class EmpOnTriggerComponent : Component +{ + [DataField("range"), ViewVariables(VVAccess.ReadWrite)] + public float Range = 1.0f; + + /// + /// How much energy will be consumed per battery in range + /// + [DataField("energyConsumption"), ViewVariables(VVAccess.ReadWrite)] + public float EnergyConsumption; +} diff --git a/Content.Server/Emp/EmpSystem.cs b/Content.Server/Emp/EmpSystem.cs new file mode 100644 index 0000000000..865f6a6947 --- /dev/null +++ b/Content.Server/Emp/EmpSystem.cs @@ -0,0 +1,39 @@ +using Content.Server.Explosion.EntitySystems; +using Robust.Shared.Map; + +namespace Content.Server.Emp; + +public sealed class EmpSystem : EntitySystem +{ + [Dependency] private readonly EntityLookupSystem _lookup = default!; + + public const string EmpPulseEffectPrototype = "EffectEmpPulse"; + public const string EmpDisabledEffectPrototype = "EffectEmpDisabled"; + + public override void Initialize() + { + base.Initialize(); + SubscribeLocalEvent(HandleEmpTrigger); + } + + public void EmpPulse(MapCoordinates coordinates, float range, float energyConsumption) + { + foreach (var uid in _lookup.GetEntitiesInRange(coordinates, range)) + { + var ev = new EmpPulseEvent(energyConsumption, false); + RaiseLocalEvent(uid, ref ev); + if (ev.Affected) + Spawn(EmpDisabledEffectPrototype, Transform(uid).Coordinates); + } + Spawn(EmpPulseEffectPrototype, coordinates); + } + + private void HandleEmpTrigger(EntityUid uid, EmpOnTriggerComponent comp, TriggerEvent args) + { + EmpPulse(Transform(uid).Coordinates.ToMap(EntityManager), comp.Range, comp.EnergyConsumption); + args.Handled = true; + } +} + +[ByRefEvent] +public record struct EmpPulseEvent(float EnergyConsumption, bool Affected); diff --git a/Content.Server/Light/EntitySystems/PoweredLightSystem.cs b/Content.Server/Light/EntitySystems/PoweredLightSystem.cs index bef74772a7..545ad4d4f3 100644 --- a/Content.Server/Light/EntitySystems/PoweredLightSystem.cs +++ b/Content.Server/Light/EntitySystems/PoweredLightSystem.cs @@ -22,6 +22,7 @@ using Robust.Shared.Containers; using Robust.Shared.Player; using Robust.Shared.Timing; using Content.Shared.DoAfter; +using Content.Server.Emp; namespace Content.Server.Light.EntitySystems { @@ -63,6 +64,8 @@ namespace Content.Server.Light.EntitySystems SubscribeLocalEvent(OnPowerChanged); SubscribeLocalEvent(OnDoAfter); + + SubscribeLocalEvent(OnEmpPulse); } private void OnInit(EntityUid uid, PoweredLightComponent light, ComponentInit args) @@ -421,5 +424,11 @@ namespace Content.Server.Light.EntitySystems args.Handled = true; } + + private void OnEmpPulse(EntityUid uid, PoweredLightComponent component, ref EmpPulseEvent args) + { + args.Affected = true; + TryDestroyBulb(uid, component); + } } } diff --git a/Content.Server/Power/EntitySystems/ApcSystem.cs b/Content.Server/Power/EntitySystems/ApcSystem.cs index 8ce9d26ec0..d1333759bf 100644 --- a/Content.Server/Power/EntitySystems/ApcSystem.cs +++ b/Content.Server/Power/EntitySystems/ApcSystem.cs @@ -1,3 +1,4 @@ +using Content.Server.Emp; using Content.Server.Popups; using Content.Server.Power.Components; using Content.Shared.Access.Components; @@ -44,6 +45,8 @@ namespace Content.Server.Power.EntitySystems SubscribeLocalEvent(OnToolFinished); SubscribeLocalEvent(OnInteractUsing); SubscribeLocalEvent(OnExamine); + + SubscribeLocalEvent(OnEmpPulse); } // Change the APC's state only when the battery state changes, or when it's first created. @@ -247,5 +250,14 @@ namespace Content.Server.Power.EntitySystems ? "apc-component-on-examine-panel-open" : "apc-component-on-examine-panel-closed")); } + + private void OnEmpPulse(EntityUid uid, ApcComponent component, ref EmpPulseEvent args) + { + if (component.MainBreakerEnabled) + { + args.Affected = true; + ApcToggleBreaker(uid, component); + } + } } } diff --git a/Content.Server/Power/EntitySystems/BatterySystem.cs b/Content.Server/Power/EntitySystems/BatterySystem.cs index a46db7dcb9..b8dc676485 100644 --- a/Content.Server/Power/EntitySystems/BatterySystem.cs +++ b/Content.Server/Power/EntitySystems/BatterySystem.cs @@ -1,4 +1,5 @@ using Content.Server.Cargo.Systems; +using Content.Server.Emp; using Content.Server.Power.Components; using Content.Shared.Examine; using Content.Shared.Rejuvenate; @@ -17,6 +18,7 @@ namespace Content.Server.Power.EntitySystems SubscribeLocalEvent(OnNetBatteryRejuvenate); SubscribeLocalEvent(OnBatteryRejuvenate); SubscribeLocalEvent(CalculateBatteryPrice); + SubscribeLocalEvent(OnEmpPulse); SubscribeLocalEvent(PreSync); SubscribeLocalEvent(PostSync); @@ -87,5 +89,11 @@ namespace Content.Server.Power.EntitySystems { args.Price += component.CurrentCharge * component.PricePerJoule; } + + private void OnEmpPulse(EntityUid uid, BatteryComponent component, ref EmpPulseEvent args) + { + args.Affected = true; + component.UseCharge(args.EnergyConsumption); + } } } diff --git a/Resources/Locale/en-US/store/uplink-catalog.ftl b/Resources/Locale/en-US/store/uplink-catalog.ftl index 71de41859a..12d15201ab 100644 --- a/Resources/Locale/en-US/store/uplink-catalog.ftl +++ b/Resources/Locale/en-US/store/uplink-catalog.ftl @@ -39,6 +39,9 @@ uplink-c4-desc = Use it to breach walls, airlocks or sabotage equipment. It can uplink-c4-bundle-name = C-4 bundle uplink-c4-bundle-desc = Because sometimes quantity is quality. Contains 8 C-4 plastic explosives. +uplink-emp-grenade-name = Emp Grenade +uplink-emp-grenade-desc = Releases electromagnetic pulses that disrupt or damage many electronic devices or drain power cells. + # Ammo uplink-pistol-magazine-name = Pistol Magazine (.35 auto) uplink-pistol-magazine-desc = Pistol magazine with 10 catridges. Compatible with Viper. diff --git a/Resources/Prototypes/Catalog/uplink_catalog.yml b/Resources/Prototypes/Catalog/uplink_catalog.yml index 2265e019fc..ba23f6988d 100644 --- a/Resources/Prototypes/Catalog/uplink_catalog.yml +++ b/Resources/Prototypes/Catalog/uplink_catalog.yml @@ -142,6 +142,16 @@ categories: - UplinkExplosives +- type: listing + id: UplinkEmpGrenade + name: uplink-emp-grenade-name + description: uplink-emp-grenade-desc + productEntity: EmpGrenade + cost: + Telecrystal: 4 + categories: + - UplinkExplosives + # Ammo - type: listing diff --git a/Resources/Prototypes/Entities/Effects/emp_effects.yml b/Resources/Prototypes/Entities/Effects/emp_effects.yml new file mode 100644 index 0000000000..c04b765dec --- /dev/null +++ b/Resources/Prototypes/Entities/Effects/emp_effects.yml @@ -0,0 +1,44 @@ +- type: entity + id: EffectEmpPulse + noSpawn: true + components: + - type: TimedDespawn + lifetime: 0.8 + - type: Sprite + netsync: false + drawdepth: Effects + noRot: true + layers: + - shader: unshaded + map: ["enum.EffectLayers.Unshaded"] + sprite: Effects/emp.rsi + state: emp_pulse + - type: EffectVisuals + - type: Tag + tags: + - HideContextMenu + - type: EmitSoundOnSpawn + sound: + path: /Audio/Effects/Lightning/lightningbolt.ogg + - type: AnimationPlayer + +- type: entity + id: EffectEmpDisabled + noSpawn: true + components: + - type: TimedDespawn + lifetime: 0.4 + - type: Sprite + netsync: false + drawdepth: Effects + noRot: true + layers: + - shader: unshaded + map: ["enum.EffectLayers.Unshaded"] + sprite: Effects/emp.rsi + state: emp_disable + - type: EffectVisuals + - type: Tag + tags: + - HideContextMenu + - type: AnimationPlayer diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Throwable/grenades.yml b/Resources/Prototypes/Entities/Objects/Weapons/Throwable/grenades.yml index 016860ea27..a980fa956a 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Throwable/grenades.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Throwable/grenades.yml @@ -154,3 +154,21 @@ enum.Trigger.TriggerVisualState.Unprimed: complete - type: StaticPrice price: 25 + +- type: entity + name: emp grenade + description: Releases electromagnetic pulses that disrupt or damage many electronic devices or drain power cells. + parent: GrenadeBase + id: EmpGrenade + components: + - type: Sprite + sprite: Objects/Weapons/Grenades/empgrenade.rsi + - type: EmpOnTrigger + range: 4 + energyConsumption: 50000 + - type: DeleteOnTrigger + - type: Appearance + visuals: + - type: TimerTriggerVisualizer + countdown_sound: + path: /Audio/Effects/countdown.ogg diff --git a/Resources/Textures/Effects/emp.rsi/emp_disable.png b/Resources/Textures/Effects/emp.rsi/emp_disable.png new file mode 100644 index 0000000000..fe45eabc71 Binary files /dev/null and b/Resources/Textures/Effects/emp.rsi/emp_disable.png differ diff --git a/Resources/Textures/Effects/emp.rsi/emp_pulse.png b/Resources/Textures/Effects/emp.rsi/emp_pulse.png new file mode 100644 index 0000000000..49f4be26ac Binary files /dev/null and b/Resources/Textures/Effects/emp.rsi/emp_pulse.png differ diff --git a/Resources/Textures/Effects/emp.rsi/meta.json b/Resources/Textures/Effects/emp.rsi/meta.json new file mode 100644 index 0000000000..378759c5b4 --- /dev/null +++ b/Resources/Textures/Effects/emp.rsi/meta.json @@ -0,0 +1,56 @@ +{ + "version": 1, + "license": "CC-BY-SA-3.0", + "copyright": "Taken from tgstation at https://github.com/tgstation/tgstation/commit/e52683d3872347af447bb0ff74c420d4a4e91ea8", + "size": { + "x": 32, + "y": 32 + }, + "states": [ + { + "name": "emp_disable", + "directions": 4, + "delays": [ + [ + 0.1, + 0.1, + 0.1, + 0.1 + ], + [ + 0.1, + 0.1, + 0.1, + 0.1 + ], + [ + 0.1, + 0.1, + 0.1, + 0.1 + ], + [ + 0.1, + 0.1, + 0.1, + 0.1 + ] + ] + }, + { + "name": "emp_pulse", + "delays": [ + [ + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1 + ] + ] + } + ] +} diff --git a/Resources/Textures/Objects/Weapons/Grenades/empgrenade.rsi/equipped-BELT.png b/Resources/Textures/Objects/Weapons/Grenades/empgrenade.rsi/equipped-BELT.png new file mode 100644 index 0000000000..c8d813b753 Binary files /dev/null and b/Resources/Textures/Objects/Weapons/Grenades/empgrenade.rsi/equipped-BELT.png differ diff --git a/Resources/Textures/Objects/Weapons/Grenades/empgrenade.rsi/icon.png b/Resources/Textures/Objects/Weapons/Grenades/empgrenade.rsi/icon.png new file mode 100644 index 0000000000..5f43e51ee0 Binary files /dev/null and b/Resources/Textures/Objects/Weapons/Grenades/empgrenade.rsi/icon.png differ diff --git a/Resources/Textures/Objects/Weapons/Grenades/empgrenade.rsi/meta.json b/Resources/Textures/Objects/Weapons/Grenades/empgrenade.rsi/meta.json new file mode 100644 index 0000000000..889ac5cb4c --- /dev/null +++ b/Resources/Textures/Objects/Weapons/Grenades/empgrenade.rsi/meta.json @@ -0,0 +1,27 @@ +{ + "version": 1, + "license": "CC-BY-SA-3.0", + "copyright": "Taken from tgstation at https://github.com/tgstation/tgstation/commit/e52683d3872347af447bb0ff74c420d4a4e91ea8", + "size": { + "x": 32, + "y": 32 + }, + "states": [ + { + "name": "icon" + }, + { + "name": "primed", + "delays": [ + [ + 0.1, + 0.1 + ] + ] + }, + { + "name": "equipped-BELT", + "directions": 4 + } + ] +} diff --git a/Resources/Textures/Objects/Weapons/Grenades/empgrenade.rsi/primed.png b/Resources/Textures/Objects/Weapons/Grenades/empgrenade.rsi/primed.png new file mode 100644 index 0000000000..a11f70171f Binary files /dev/null and b/Resources/Textures/Objects/Weapons/Grenades/empgrenade.rsi/primed.png differ