diff --git a/Content.Client/Entry/EntryPoint.cs b/Content.Client/Entry/EntryPoint.cs
index 328c3e6e06..6478973a22 100644
--- a/Content.Client/Entry/EntryPoint.cs
+++ b/Content.Client/Entry/EntryPoint.cs
@@ -1,7 +1,6 @@
using Content.Client._White.Chat;
using Content.Client._White.JoinQueue;
using Content.Client._White.Jukebox;
-using Content.Client._White.Overlays;
using Content.Client._White.Reputation;
using Content.Client._White.Sponsors;
using Content.Client._White.Stalin;
@@ -10,7 +9,6 @@ using Content.Client.Administration.Managers;
using Content.Client.Changelog;
using Content.Client.Chat.Managers;
using Content.Client.Eui;
-using Content.Client.Flash;
using Content.Client.Fullscreen;
using Content.Client.GhostKick;
using Content.Client.Guidebook;
@@ -26,6 +24,7 @@ using Content.Client.Radiation.Overlays;
using Content.Client.Replay;
using Content.Client.Screenshot;
using Content.Client.Singularity;
+using Content.Client._White.Explosion;
using Content.Client.Stylesheets;
using Content.Client.Viewport;
using Content.Client.Voting;
@@ -179,6 +178,7 @@ namespace Content.Client.Entry
_parallaxManager.LoadDefaultParallax();
_overlayManager.AddOverlay(new SingularityOverlay());
+ _overlayManager.AddOverlay(new ExplosionShockWaveOverlay());
_overlayManager.AddOverlay(new RadiationPulseOverlay());
// _overlayManager.AddOverlay(new GrainOverlay());
// _overlayManager.AddOverlay(new AtmOverlay());
diff --git a/Content.Client/_White/Explosion/RMCExplosionShockWaveOverlay.cs b/Content.Client/_White/Explosion/RMCExplosionShockWaveOverlay.cs
new file mode 100644
index 0000000000..aa8efedc31
--- /dev/null
+++ b/Content.Client/_White/Explosion/RMCExplosionShockWaveOverlay.cs
@@ -0,0 +1,90 @@
+using Robust.Client.Graphics;
+using Robust.Shared.Enums;
+using Robust.Shared.Prototypes;
+using System.Numerics;
+using Content.Shared._White.Explosion;
+
+namespace Content.Client._White.Explosion;
+
+public sealed class ExplosionShockWaveOverlay : Overlay, IEntityEventSubscriber
+{
+ [Dependency] private readonly IEntityManager _entMan = default!;
+ [Dependency] private readonly IPrototypeManager _prototypeManager = default!;
+
+ private SharedTransformSystem? _xformSystem;
+
+ public override OverlaySpace Space => OverlaySpace.WorldSpace;
+ public override bool RequestScreenTexture => true;
+
+ private readonly ShaderInstance _shader;
+
+ ///
+ /// Maximum number of distortions that can be shown on screen at a time.
+ ///
+ public const int MaxCount = 30;
+
+ public ExplosionShockWaveOverlay()
+ {
+ IoCManager.InjectDependencies(this);
+ _shader = _prototypeManager.Index("ShockWave").Instance().Duplicate();
+ }
+
+ private readonly Vector2[] _positions = new Vector2[MaxCount];
+ private readonly float[] _falloffPower = new float[MaxCount];
+ private readonly float[] _sharpness = new float[MaxCount];
+ private readonly float[] _width = new float[MaxCount];
+ private int _count;
+ protected override bool BeforeDraw(in OverlayDrawArgs args)
+ {
+ if (args.Viewport.Eye == null || _xformSystem is null && !_entMan.TrySystem(out _xformSystem))
+ return false;
+
+ var query = _entMan.EntityQueryEnumerator();
+
+ _count = 0;
+
+ while (query.MoveNext(out var uid, out var distortion, out var xform))
+ {
+ if (xform.MapID != args.MapId)
+ continue;
+
+ var mapPos = _xformSystem.GetWorldPosition(uid);
+
+ var tempCoords = args.Viewport.WorldToLocal(mapPos);
+
+ // normalized coords, 0 - 1 plane. This is pure hell, we subtract 1 because fragment calculates from the bottom and local goes from the top of the viewport
+ tempCoords.Y = 1 - (tempCoords.Y / args.Viewport.Size.Y);
+ tempCoords.X /= args.Viewport.Size.X;
+
+ _positions[_count] = tempCoords;
+ _falloffPower[_count] = distortion.FalloffPower;
+ _sharpness[_count] = distortion.Sharpness;
+ _width[_count] = distortion.Width;
+ _count++;
+
+ if (_count == MaxCount)
+ break;
+ }
+
+ return _count > 0;
+ }
+
+ protected override void Draw(in OverlayDrawArgs args)
+ {
+ if (ScreenTexture == null || args.Viewport.Eye == null)
+ return;
+
+ _shader?.SetParameter("renderScale", args.Viewport.RenderScale * args.Viewport.Eye.Scale);
+ _shader?.SetParameter("count", _count);
+ _shader?.SetParameter("position", _positions);
+ _shader?.SetParameter("falloffPower", _falloffPower);
+ _shader?.SetParameter("sharpness", _sharpness);
+ _shader?.SetParameter("width", _width);
+ _shader?.SetParameter("SCREEN_TEXTURE", ScreenTexture);
+
+ var worldHandle = args.WorldHandle;
+ worldHandle.UseShader(_shader);
+ worldHandle.DrawRect(args.WorldAABB, Color.White);
+ worldHandle.UseShader(null);
+ }
+}
diff --git a/Content.Server/Explosion/EntitySystems/ClusterGrenadeSystem.cs b/Content.Server/Explosion/EntitySystems/ClusterGrenadeSystem.cs
index 78e41c59ae..68cdbce037 100644
--- a/Content.Server/Explosion/EntitySystems/ClusterGrenadeSystem.cs
+++ b/Content.Server/Explosion/EntitySystems/ClusterGrenadeSystem.cs
@@ -1,14 +1,14 @@
+using System.Numerics;
using Content.Server.Explosion.Components;
+using Content.Server.Weapons.Ranged.Systems;
+using Content.Shared.Explosion.Components;
using Content.Shared.Flash.Components;
using Content.Shared.Interaction;
using Content.Shared.Throwing;
-using Robust.Shared.Containers;
-using Robust.Shared.Random;
-using Content.Server.Weapons.Ranged.Systems;
-using System.Numerics;
-using Content.Shared.Explosion.Components;
using Robust.Server.Containers;
using Robust.Server.GameObjects;
+using Robust.Shared.Containers;
+using Robust.Shared.Random;
namespace Content.Server.Explosion.EntitySystems;
@@ -114,6 +114,7 @@ public sealed class ClusterGrenadeSystem : EntitySystem
RaiseLocalEvent(uid, ref ev);
}
}
+
// delete the empty shell of the clusterbomb
Del(uid);
}
diff --git a/Content.Server/Explosion/EntitySystems/ExplosionSystem.cs b/Content.Server/Explosion/EntitySystems/ExplosionSystem.cs
index 1b34a07a8e..dddc1a53f1 100644
--- a/Content.Server/Explosion/EntitySystems/ExplosionSystem.cs
+++ b/Content.Server/Explosion/EntitySystems/ExplosionSystem.cs
@@ -7,6 +7,7 @@ using Content.Server.Explosion.Components;
using Content.Server.NodeContainer.EntitySystems;
using Content.Server.NPC.Pathfinding;
using Content.Server.Station.Systems;
+using Content.Shared._White.Explosion;
using Content.Shared.Audio;
using Content.Shared.Armor;
using Content.Shared.Camera;
@@ -176,6 +177,11 @@ public sealed partial class ExplosionSystem
totalIntensity ??= RadiusToIntensity((float) radius, explosive.IntensitySlope, explosive.MaxIntensity);
totalIntensity ??= explosive.TotalIntensity;
+ // WD edit start
+ var ev = new ExplosiveTriggeredEvent();
+ RaiseLocalEvent(uid, ref ev);
+ // WD edit end
+
QueueExplosion(uid,
explosive.ExplosionType,
(float) totalIntensity,
diff --git a/Content.Server/Projectiles/ProjectileSystem.cs b/Content.Server/Projectiles/ProjectileSystem.cs
index cbe05d1a77..9f87e79505 100644
--- a/Content.Server/Projectiles/ProjectileSystem.cs
+++ b/Content.Server/Projectiles/ProjectileSystem.cs
@@ -69,16 +69,18 @@ public sealed class ProjectileSystem : SharedProjectileSystem
var modifiedDamage = _damageableSystem.TryChangeDamage(target, ev.Damage * DamageModifier, component.IgnoreResistances, origin: component.Shooter);
var deleted = Deleted(target);
- if (modifiedDamage is not null && EntityManager.EntityExists(component.Shooter))
+ if (modifiedDamage is not null && (EntityManager.EntityExists(component.Shooter) || EntityManager.EntityExists(component.Weapon)))
{
if (modifiedDamage.Any() && !deleted)
{
_color.RaiseEffect(Color.Red, new List { target }, Filter.Pvs(target, entityManager: EntityManager));
}
+ var shooterOrWeapon = EntityManager.EntityExists(component.Shooter) ? component.Shooter!.Value : component.Weapon!.Value;
+
_adminLogger.Add(LogType.BulletHit,
HasComp(target) ? LogImpact.Extreme : LogImpact.High,
- $"Projectile {ToPrettyString(uid):projectile} shot by {ToPrettyString(component.Shooter!.Value):user} hit {otherName:target} and dealt {modifiedDamage.GetTotal():damage} damage");
+ $"Projectile {ToPrettyString(uid):projectile} shot by {ToPrettyString(shooterOrWeapon):source} hit {otherName:target} and dealt {modifiedDamage.GetTotal():damage} damage");
}
if (!deleted)
diff --git a/Content.Shared/Projectiles/SharedProjectileSystem.cs b/Content.Shared/Projectiles/SharedProjectileSystem.cs
index 5fe5c7b208..ff051d24e5 100644
--- a/Content.Shared/Projectiles/SharedProjectileSystem.cs
+++ b/Content.Shared/Projectiles/SharedProjectileSystem.cs
@@ -203,9 +203,9 @@ public abstract partial class SharedProjectileSystem : EntitySystem
}
}
- public void SetShooter(EntityUid id, ProjectileComponent component, EntityUid shooterId)
+ public void SetShooter(EntityUid id, ProjectileComponent component, EntityUid? shooterId = null)
{
- if (component.Shooter == shooterId)
+ if (component.Shooter == shooterId || shooterId == null)
return;
component.Shooter = shooterId;
diff --git a/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.cs b/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.cs
index 878b6cbc3a..0502077356 100644
--- a/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.cs
+++ b/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.cs
@@ -423,7 +423,7 @@ public abstract partial class SharedGunSystem : EntitySystem
Physics.SetLinearVelocity(uid, finalLinear, body: physics);
var projectile = EnsureComp(uid);
- Projectiles.SetShooter(uid, projectile, user ?? gunUid);
+ Projectiles.SetShooter(uid, projectile, user);
projectile.Weapon = gunUid;
TransformSystem.SetWorldRotation(uid, direction.ToWorldAngle());
diff --git a/Content.Shared/_White/Explosion/CMExplosionEffectComponent.cs b/Content.Shared/_White/Explosion/CMExplosionEffectComponent.cs
new file mode 100644
index 0000000000..311384782c
--- /dev/null
+++ b/Content.Shared/_White/Explosion/CMExplosionEffectComponent.cs
@@ -0,0 +1,27 @@
+using Robust.Shared.GameStates;
+using Robust.Shared.Prototypes;
+
+namespace Content.Shared._White.Explosion;
+
+[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
+[Access(typeof(SharedExplosionSystem))]
+public sealed partial class ExplosionEffectComponent : Component
+{
+ [DataField, AutoNetworkedField]
+ public EntProtoId Explosion = "ExplosionEffectGrenade";
+
+ [DataField, AutoNetworkedField]
+ public EntProtoId ShockWave = "ExplosionEffectGrenadeShockWave";
+
+ [DataField, AutoNetworkedField]
+ public List ShrapnelEffects = new() { "ExplosionEffectShrapnel1", "ExplosionEffectShrapnel2" };
+
+ [DataField, AutoNetworkedField]
+ public int MinShrapnel = 5;
+
+ [DataField, AutoNetworkedField]
+ public int MaxShrapnel = 9;
+
+ [DataField, AutoNetworkedField]
+ public float ShrapnelSpeed = 5;
+}
diff --git a/Content.Shared/_White/Explosion/ExplosiveTriggeredEvent.cs b/Content.Shared/_White/Explosion/ExplosiveTriggeredEvent.cs
new file mode 100644
index 0000000000..a42cbb7bc9
--- /dev/null
+++ b/Content.Shared/_White/Explosion/ExplosiveTriggeredEvent.cs
@@ -0,0 +1,4 @@
+namespace Content.Shared._White.Explosion;
+
+[ByRefEvent]
+public readonly record struct ExplosiveTriggeredEvent;
diff --git a/Content.Shared/_White/Explosion/RMCExplosionShockWaveComponent.cs b/Content.Shared/_White/Explosion/RMCExplosionShockWaveComponent.cs
new file mode 100644
index 0000000000..44d14e65fa
--- /dev/null
+++ b/Content.Shared/_White/Explosion/RMCExplosionShockWaveComponent.cs
@@ -0,0 +1,28 @@
+using Robust.Shared.GameStates;
+
+namespace Content.Shared._White.Explosion
+{
+ [RegisterComponent, NetworkedComponent]
+ [AutoGenerateComponentState]
+ [Access(typeof(SharedExplosionSystem))]
+ public sealed partial class ExplosionShockWaveComponent : Component
+ {
+ ///
+ /// The rate at which the wave fades, lower values means it's active for longer.
+ ///
+ [DataField, AutoNetworkedField, ViewVariables(VVAccess.ReadWrite)]
+ public float FalloffPower = 40f;
+
+ ///
+ /// How sharp the wave distortion is. Higher values make the wave more pronounced.
+ ///
+ [DataField, AutoNetworkedField, ViewVariables(VVAccess.ReadWrite)]
+ public float Sharpness = 10.0f;
+
+ ///
+ /// Width of the wave.
+ ///
+ [DataField, AutoNetworkedField, ViewVariables(VVAccess.ReadWrite)]
+ public float Width = 0.8f;
+ }
+}
diff --git a/Content.Shared/_White/Explosion/SharedCMExplosionSystem.cs b/Content.Shared/_White/Explosion/SharedCMExplosionSystem.cs
new file mode 100644
index 0000000000..9cb343aba7
--- /dev/null
+++ b/Content.Shared/_White/Explosion/SharedCMExplosionSystem.cs
@@ -0,0 +1,36 @@
+using Content.Shared.Throwing;
+using Robust.Shared.Random;
+
+namespace Content.Shared._White.Explosion;
+
+public sealed class SharedExplosionSystem : EntitySystem
+{
+ [Dependency] private readonly IRobustRandom _random = default!;
+ [Dependency] private readonly ThrowingSystem _throwing = default!;
+
+ public override void Initialize()
+ {
+ SubscribeLocalEvent(OnExplosionEffectTriggered);
+ }
+
+ private void OnExplosionEffectTriggered(EntityUid uid, ExplosionEffectComponent component, ref ExplosiveTriggeredEvent args)
+ {
+ SpawnNextToOrDrop(component.ShockWave, uid);
+ SpawnNextToOrDrop(component.Explosion, uid);
+
+ if (component.MaxShrapnel <= 0)
+ return;
+
+ foreach (var effect in component.ShrapnelEffects)
+ {
+ var shrapnelCount = _random.Next(component.MinShrapnel, component.MaxShrapnel);
+ for (var i = 0; i < shrapnelCount; i++)
+ {
+ var angle = _random.NextAngle();
+ var direction = angle.ToVec().Normalized() * 10;
+ var shrapnel = SpawnNextToOrDrop(effect, uid);
+ _throwing.TryThrow(shrapnel, direction, component.ShrapnelSpeed / 10);
+ }
+ }
+ }
+}
diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Projectiles/magic.yml b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Projectiles/magic.yml
index e98dc0a087..535ebf6b0e 100644
--- a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Projectiles/magic.yml
+++ b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Projectiles/magic.yml
@@ -29,6 +29,8 @@
ignited: true
- type: IgniteOnCollide
fireStacks: 0.35
+ - type: ExplosionEffect # WD
+ maxShrapnel: 0
- type: entity
id: ProjectileAnomalyFireball
diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Projectiles/projectiles.yml b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Projectiles/projectiles.yml
index 685b72c962..607a287909 100644
--- a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Projectiles/projectiles.yml
+++ b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Projectiles/projectiles.yml
@@ -743,6 +743,8 @@
totalIntensity: 150 # a ~2 tile radius
intensitySlope: 5
maxIntensity: 10
+ - type: ExplosionEffect # WD
+ maxShrapnel: 0
- type: entity
id: BulletGrenadeFlash
@@ -781,6 +783,8 @@
totalIntensity: 175 # about a ~6 tile radius
intensitySlope: 1
maxIntensity: 10
+ - type: ExplosionEffect # WD
+ maxShrapnel: 0
- type: entity
id: BulletGrenadeEMP
diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Throwable/grenades.yml b/Resources/Prototypes/Entities/Objects/Weapons/Throwable/grenades.yml
index 0313a75868..1a1be4ac89 100644
--- a/Resources/Prototypes/Entities/Objects/Weapons/Throwable/grenades.yml
+++ b/Resources/Prototypes/Entities/Objects/Weapons/Throwable/grenades.yml
@@ -36,10 +36,18 @@
Primed: { state: primed }
Unprimed: { state: icon }
+- type: entity # WD
+ abstract: true
+ parent: GrenadeBase
+ id: GrenadeBaseExplosionEffect
+ components:
+ - type: ExplosionEffect
+ maxShrapnel: 0
+
- type: entity
name: explosive grenade
description: Grenade that creates a small but devastating explosion.
- parent: GrenadeBase
+ parent: GrenadeBaseExplosionEffect # WD
id: ExGrenade
components:
- type: ExplodeOnTrigger
@@ -49,13 +57,8 @@
intensitySlope: 3
totalIntensity: 120 # about a ~4 tile radius
canCreateVacuum: false
- - type: OnUseTimerTrigger
- beepSound:
- path: "/Audio/Effects/beep1.ogg"
- params:
- volume: 5
- initialBeepDelay: 0
- beepInterval: 2 # 2 beeps total (at 0 and 2)
+ - type: ExplosionEffect
+ maxShrapnel: 12
- type: entity
name: flashbang
@@ -102,7 +105,7 @@
- type: entity
name: syndicate minibomb
description: A syndicate-manufactured explosive used to stow destruction and cause chaos.
- parent: GrenadeBase
+ parent: GrenadeBaseExplosionEffect # WD
id: SyndieMiniBomb
components:
- type: Sprite
@@ -235,7 +238,7 @@
- type: entity
name: the nuclear option
description: Please don't throw it, think of the children.
- parent: GrenadeBase
+ parent: GrenadeBaseExplosionEffect # WD
id: NuclearGrenade
components:
- type: Sprite
@@ -326,7 +329,7 @@
- type: entity
name: holy hand grenade
description: O Lord, bless this thy hand grenade, that with it thou mayst blow thine enemies to tiny bits, in thy mercy.
- parent: GrenadeBase
+ parent: GrenadeBaseExplosionEffect # WD
id: HolyHandGrenade
components:
- type: Sprite
diff --git a/Resources/Prototypes/Entities/Structures/Storage/Tanks/tanks.yml b/Resources/Prototypes/Entities/Structures/Storage/Tanks/tanks.yml
index 455577a371..c820b5a236 100644
--- a/Resources/Prototypes/Entities/Structures/Storage/Tanks/tanks.yml
+++ b/Resources/Prototypes/Entities/Structures/Storage/Tanks/tanks.yml
@@ -35,6 +35,8 @@
explosionType: FireBomb # WD EDIT
totalIntensity: 120 # ~ 5 tile radius
canCreateVacuum: false
+ - type: ExplosionEffect # WD
+ maxShrapnel: 0
- type: entity
id: WeldingFuelTankFull
diff --git a/Resources/Prototypes/_White/Entities/Objects/Weapons/Throwable/grenades.yml b/Resources/Prototypes/_White/Entities/Objects/Weapons/Throwable/grenades.yml
new file mode 100644
index 0000000000..fea6ec5ec6
--- /dev/null
+++ b/Resources/Prototypes/_White/Entities/Objects/Weapons/Throwable/grenades.yml
@@ -0,0 +1,92 @@
+- type: entity
+ id: ExplosionEffectGrenade
+ noSpawn: true
+ components:
+ - type: Sprite
+ sprite: White/Effects/grenade_explosion.rsi
+ state: grenade
+ - type: TimedDespawn
+ lifetime: 0.5
+
+- type: entity
+ id: ExplosionEffectGrenadeShockWave
+ components:
+ - type: TimedDespawn
+ lifetime: 0.5
+ - type: ExplosionShockWave
+ falloffPower: 30
+ sharpness: 15
+ width: 1.3
+
+- type: entity
+ id: ExplosionEffectShrapnel1
+ noSpawn: true
+ components:
+ - type: CollisionWake
+ - type: Physics
+ bodyType: Dynamic
+ fixedRotation: true
+ - type: Fixtures
+ fixtures:
+ fix1:
+ shape:
+ !type:PhysShapeAabb
+ bounds: "-0.25,-0.25,0.25,0.25"
+ density: 20
+ mask:
+ - ItemMask
+ restitution: 0.3
+ friction: 0
+ - type: Sprite
+ sprite: White/Objects/Weapons/shrapnel.rsi
+ state: shrapnel_bright1
+ - type: TimedDespawn
+ lifetime: 0.5
+# - type: DeleteOnCollide
+
+- type: entity
+ parent: ExplosionEffectShrapnel1
+ id: ExplosionEffectShrapnel2
+ noSpawn: true
+ components:
+ - type: Sprite
+ state: shrapnel_bright2
+
+# Не работает каким-то хуем
+- type: entity
+ parent: BaseBullet
+ id: ProjectileShrapnel
+ name: shrapnel
+ noSpawn: true
+ components:
+ - type: Sprite
+ sprite: White/Objects/Weapons/Ammunition/Projectiles/shotgun_projectiles.rsi
+ layers:
+ - state: buckshot
+ - type: Fixtures
+ fixtures:
+ projectile:
+ shape:
+ !type:PhysShapeAabb
+ bounds: "-0.1,-0.1,0.1,0.1"
+ hard: false
+ mask:
+ - Impassable
+ - BulletImpassable
+ - LowImpassable
+ fly-by:
+ shape: !type:PhysShapeCircle
+ radius: 1.5
+ layer:
+ - Impassable
+ - MidImpassable
+ - HighImpassable
+ - LowImpassable
+ hard: False
+ - type: Projectile
+ damage:
+ types:
+ Piercing: 12
+ deleteOnCollide: true
+ - type: TimedDespawn
+ lifetime: 0.5
diff --git a/Resources/Prototypes/_White/Shaders/shaders.yml b/Resources/Prototypes/_White/Shaders/shaders.yml
new file mode 100644
index 0000000000..0f307d2742
--- /dev/null
+++ b/Resources/Prototypes/_White/Shaders/shaders.yml
@@ -0,0 +1,4 @@
+- type: shader
+ id: ShockWave
+ kind: source
+ path: "/Textures/White/Shaders/shock_wave.swsl"
diff --git a/Resources/Textures/White/Effects/grenade_explosion.rsi/grenade.png b/Resources/Textures/White/Effects/grenade_explosion.rsi/grenade.png
new file mode 100644
index 0000000000..d00b7283af
Binary files /dev/null and b/Resources/Textures/White/Effects/grenade_explosion.rsi/grenade.png differ
diff --git a/Resources/Textures/White/Effects/grenade_explosion.rsi/meta.json b/Resources/Textures/White/Effects/grenade_explosion.rsi/meta.json
new file mode 100644
index 0000000000..faef7a9557
--- /dev/null
+++ b/Resources/Textures/White/Effects/grenade_explosion.rsi/meta.json
@@ -0,0 +1,26 @@
+{
+ "version": 1,
+ "license": "CC-BY-SA-3.0",
+ "copyright": "Taken from cmss13 at https://github.com/cmss13-devs/cmss13/blob/add6123ac6b3263f257e4b233ef5a8fea5d3d317/icons/effects/explosion.dmi",
+ "size": {
+ "x": 48,
+ "y": 48
+ },
+ "states": [
+ {
+ "name": "grenade",
+ "delays": [
+ [
+ 0.1,
+ 0.1,
+ 0.1,
+ 0.1,
+ 0.1,
+ 0.1,
+ 0.1,
+ 0.1
+ ]
+ ]
+ }
+ ]
+}
\ No newline at end of file
diff --git a/Resources/Textures/White/Objects/Weapons/Ammunition/Projectiles/shotgun_projectiles.rsi/autocannon.png b/Resources/Textures/White/Objects/Weapons/Ammunition/Projectiles/shotgun_projectiles.rsi/autocannon.png
new file mode 100644
index 0000000000..9b69b25c94
Binary files /dev/null and b/Resources/Textures/White/Objects/Weapons/Ammunition/Projectiles/shotgun_projectiles.rsi/autocannon.png differ
diff --git a/Resources/Textures/White/Objects/Weapons/Ammunition/Projectiles/shotgun_projectiles.rsi/beanbag.png b/Resources/Textures/White/Objects/Weapons/Ammunition/Projectiles/shotgun_projectiles.rsi/beanbag.png
new file mode 100644
index 0000000000..89d54bb530
Binary files /dev/null and b/Resources/Textures/White/Objects/Weapons/Ammunition/Projectiles/shotgun_projectiles.rsi/beanbag.png differ
diff --git a/Resources/Textures/White/Objects/Weapons/Ammunition/Projectiles/shotgun_projectiles.rsi/buckshot.png b/Resources/Textures/White/Objects/Weapons/Ammunition/Projectiles/shotgun_projectiles.rsi/buckshot.png
new file mode 100644
index 0000000000..bf1cbac405
Binary files /dev/null and b/Resources/Textures/White/Objects/Weapons/Ammunition/Projectiles/shotgun_projectiles.rsi/buckshot.png differ
diff --git a/Resources/Textures/White/Objects/Weapons/Ammunition/Projectiles/shotgun_projectiles.rsi/flechette.png b/Resources/Textures/White/Objects/Weapons/Ammunition/Projectiles/shotgun_projectiles.rsi/flechette.png
new file mode 100644
index 0000000000..b8fec141ec
Binary files /dev/null and b/Resources/Textures/White/Objects/Weapons/Ammunition/Projectiles/shotgun_projectiles.rsi/flechette.png differ
diff --git a/Resources/Textures/White/Objects/Weapons/Ammunition/Projectiles/shotgun_projectiles.rsi/meta.json b/Resources/Textures/White/Objects/Weapons/Ammunition/Projectiles/shotgun_projectiles.rsi/meta.json
new file mode 100644
index 0000000000..0bbcb43c40
--- /dev/null
+++ b/Resources/Textures/White/Objects/Weapons/Ammunition/Projectiles/shotgun_projectiles.rsi/meta.json
@@ -0,0 +1,23 @@
+{
+ "version": 1,
+ "license": "CC-BY-SA-3.0",
+ "copyright": "Taken from cmss13 at https://github.com/cmss13-devs/cmss13/blob/a86a9e2e57e5c00195cab704713a4962e8bc973a/icons/obj/items/weapons/projectiles.dmi",
+ "size": {
+ "x": 32,
+ "y": 32
+ },
+ "states": [
+ {
+ "name": "autocannon"
+ },
+ {
+ "name": "buckshot"
+ },
+ {
+ "name": "beanbag"
+ },
+ {
+ "name": "flechette"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/Resources/Textures/White/Objects/Weapons/shrapnel.rsi/meta.json b/Resources/Textures/White/Objects/Weapons/shrapnel.rsi/meta.json
new file mode 100644
index 0000000000..a17dcce10c
--- /dev/null
+++ b/Resources/Textures/White/Objects/Weapons/shrapnel.rsi/meta.json
@@ -0,0 +1,55 @@
+{
+ "version": 1,
+ "license": "CC-BY-SA-3.0",
+ "copyright": "Taken from cmss13 at https://github.com/cmss13-devs/cmss13/blob/a86a9e2e57e5c00195cab704713a4962e8bc973a/icons/obj/items/weapons/projectiles.dmi",
+ "size": {
+ "x": 32,
+ "y": 32
+ },
+ "states": [
+ {
+ "name": "shrapnelshot"
+ },
+ {
+ "name": "shrapnelshot_bit"
+ },
+ {
+ "name": "shrapnel_glass"
+ },
+ {
+ "name": "shrapnel_light"
+ },
+ {
+ "name": "shrapnel_xeno"
+ },
+ {
+ "name": "shrapnel_human"
+ },
+ {
+ "name": "shrapnel_human1"
+ },
+ {
+ "name": "shrapnel_human2"
+ },
+ {
+ "name": "shrapnel_bright1",
+ "delays": [
+ [
+ 0.1,
+ 0.1,
+ 0.1
+ ]
+ ]
+ },
+ {
+ "name": "shrapnel_bright2",
+ "delays": [
+ [
+ 0.1,
+ 0.1,
+ 0.1
+ ]
+ ]
+ }
+ ]
+}
\ No newline at end of file
diff --git a/Resources/Textures/White/Objects/Weapons/shrapnel.rsi/shrapnel_bright1.png b/Resources/Textures/White/Objects/Weapons/shrapnel.rsi/shrapnel_bright1.png
new file mode 100644
index 0000000000..e3e8c5c8d2
Binary files /dev/null and b/Resources/Textures/White/Objects/Weapons/shrapnel.rsi/shrapnel_bright1.png differ
diff --git a/Resources/Textures/White/Objects/Weapons/shrapnel.rsi/shrapnel_bright2.png b/Resources/Textures/White/Objects/Weapons/shrapnel.rsi/shrapnel_bright2.png
new file mode 100644
index 0000000000..b13da7ccd3
Binary files /dev/null and b/Resources/Textures/White/Objects/Weapons/shrapnel.rsi/shrapnel_bright2.png differ
diff --git a/Resources/Textures/White/Objects/Weapons/shrapnel.rsi/shrapnel_glass.png b/Resources/Textures/White/Objects/Weapons/shrapnel.rsi/shrapnel_glass.png
new file mode 100644
index 0000000000..7c399cf53c
Binary files /dev/null and b/Resources/Textures/White/Objects/Weapons/shrapnel.rsi/shrapnel_glass.png differ
diff --git a/Resources/Textures/White/Objects/Weapons/shrapnel.rsi/shrapnel_human.png b/Resources/Textures/White/Objects/Weapons/shrapnel.rsi/shrapnel_human.png
new file mode 100644
index 0000000000..e0048bfb79
Binary files /dev/null and b/Resources/Textures/White/Objects/Weapons/shrapnel.rsi/shrapnel_human.png differ
diff --git a/Resources/Textures/White/Objects/Weapons/shrapnel.rsi/shrapnel_human1.png b/Resources/Textures/White/Objects/Weapons/shrapnel.rsi/shrapnel_human1.png
new file mode 100644
index 0000000000..30311c9210
Binary files /dev/null and b/Resources/Textures/White/Objects/Weapons/shrapnel.rsi/shrapnel_human1.png differ
diff --git a/Resources/Textures/White/Objects/Weapons/shrapnel.rsi/shrapnel_human2.png b/Resources/Textures/White/Objects/Weapons/shrapnel.rsi/shrapnel_human2.png
new file mode 100644
index 0000000000..aaa5fd277a
Binary files /dev/null and b/Resources/Textures/White/Objects/Weapons/shrapnel.rsi/shrapnel_human2.png differ
diff --git a/Resources/Textures/White/Objects/Weapons/shrapnel.rsi/shrapnel_light.png b/Resources/Textures/White/Objects/Weapons/shrapnel.rsi/shrapnel_light.png
new file mode 100644
index 0000000000..c61e866827
Binary files /dev/null and b/Resources/Textures/White/Objects/Weapons/shrapnel.rsi/shrapnel_light.png differ
diff --git a/Resources/Textures/White/Objects/Weapons/shrapnel.rsi/shrapnel_xeno.png b/Resources/Textures/White/Objects/Weapons/shrapnel.rsi/shrapnel_xeno.png
new file mode 100644
index 0000000000..a7bbe70de6
Binary files /dev/null and b/Resources/Textures/White/Objects/Weapons/shrapnel.rsi/shrapnel_xeno.png differ
diff --git a/Resources/Textures/White/Objects/Weapons/shrapnel.rsi/shrapnelshot.png b/Resources/Textures/White/Objects/Weapons/shrapnel.rsi/shrapnelshot.png
new file mode 100644
index 0000000000..b764cf7c00
Binary files /dev/null and b/Resources/Textures/White/Objects/Weapons/shrapnel.rsi/shrapnelshot.png differ
diff --git a/Resources/Textures/White/Objects/Weapons/shrapnel.rsi/shrapnelshot_bit.png b/Resources/Textures/White/Objects/Weapons/shrapnel.rsi/shrapnelshot_bit.png
new file mode 100644
index 0000000000..1b85ff3b8f
Binary files /dev/null and b/Resources/Textures/White/Objects/Weapons/shrapnel.rsi/shrapnelshot_bit.png differ
diff --git a/Resources/Textures/White/Shaders/shock_wave.swsl b/Resources/Textures/White/Shaders/shock_wave.swsl
new file mode 100644
index 0000000000..00531fb98a
--- /dev/null
+++ b/Resources/Textures/White/Shaders/shock_wave.swsl
@@ -0,0 +1,40 @@
+uniform sampler2D SCREEN_TEXTURE;
+uniform highp vec2 renderScale;
+uniform lowp int count;
+uniform highp vec2[10] position;
+uniform highp float[10] sharpness;
+uniform highp float[10] width;
+uniform highp float[10] falloffPower;
+
+void fragment() {
+ highp vec2 coord = FRAGCOORD.xy * SCREEN_PIXEL_SIZE.xy;
+ highp float ratio = SCREEN_PIXEL_SIZE.y / SCREEN_PIXEL_SIZE.x * 0.6;
+
+ for (int i = 0; i < count; ++i) {
+ highp vec2 WaveCentre = position[i];
+
+ highp float Dist = distance(
+ vec2(coord.x, coord.y * ratio),
+ vec2(WaveCentre.x, WaveCentre.y * ratio)
+ );
+
+ // removes the first few frames of the "animation"
+ Dist = max(Dist, 0.13);
+
+ highp float offset = (TIME - floor(TIME)) / TIME;
+ highp float CurrentTime = TIME * offset;
+
+ if (Dist <= (CurrentTime + 0.1) && Dist >= (CurrentTime - 0.1)) {
+ highp float Diff = Dist - CurrentTime;
+ highp float ScaleDiff = 1.0 - pow(abs(Diff * sharpness[i]), width[i]);
+ highp float DiffTime = Diff * ScaleDiff;
+ highp vec2 DiffTexCoord = normalize(coord - WaveCentre);
+ coord += (DiffTexCoord * DiffTime) / (CurrentTime * Dist * falloffPower[i]);
+ highp vec4 Color = zTextureSpec(SCREEN_TEXTURE, coord);
+ Color += (Color * ScaleDiff) / (CurrentTime * Dist * 40.0);
+ COLOR = Color;
+ } else {
+ COLOR = zTextureSpec(SCREEN_TEXTURE, UV);
+ }
+ }
+}