diff --git a/Content.Client/Entry/EntryPoint.cs b/Content.Client/Entry/EntryPoint.cs
index f42c2d27fb..d47d040259 100644
--- a/Content.Client/Entry/EntryPoint.cs
+++ b/Content.Client/Entry/EntryPoint.cs
@@ -1,3 +1,5 @@
+using Content.Client._RMC14.Explosion;
+using Content.Client._RMC14.Xenonids.Screech;
using Content.Client._Sunrise.Entry;
using Content.Client._Sunrise.ServersHub;
using Content.Client.Administration.Managers;
@@ -174,6 +176,10 @@ namespace Content.Client.Entry
_parallaxManager.LoadDefaultParallax();
_overlayManager.AddOverlay(new SingularityOverlay());
+ // Sunrise edit start
+ _overlayManager.AddOverlay(new RMCExplosionShockWaveOverlay());
+ _overlayManager.AddOverlay(new RMCXenoScreechShockWaveOverlay());
+ // Sunrise edit end
_overlayManager.AddOverlay(new RadiationPulseOverlay());
_chatManager.Initialize();
_clientPreferencesManager.Initialize();
diff --git a/Content.Client/_RMC14/Explosion/RMCExplosionShockWaveOverlay.cs b/Content.Client/_RMC14/Explosion/RMCExplosionShockWaveOverlay.cs
new file mode 100644
index 0000000000..c408fc6ec6
--- /dev/null
+++ b/Content.Client/_RMC14/Explosion/RMCExplosionShockWaveOverlay.cs
@@ -0,0 +1,101 @@
+using System.Numerics;
+using Content.Shared._RMC14.Explosion.Components;
+using Robust.Client.Graphics;
+using Robust.Shared.Enums;
+using Robust.Shared.Prototypes;
+using Robust.Shared.Timing;
+
+namespace Content.Client._RMC14.Explosion;
+
+public sealed class RMCExplosionShockWaveOverlay : Overlay, IEntityEventSubscriber
+{
+ [Dependency] private readonly IEntityManager _entMan = default!;
+ [Dependency] private readonly IPrototypeManager _prototypeManager = default!;
+ [Dependency] private readonly IGameTiming _timing = 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 = 10;
+
+ public RMCExplosionShockWaveOverlay()
+ {
+ IoCManager.InjectDependencies(this);
+ _shader = _prototypeManager.Index("RMCShockWave").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 readonly float[] _times = new float[MaxCount];
+ private int _count;
+
+ private readonly TimeSpan _timeCompensation = TimeSpan.FromSeconds(0.2f);
+
+ 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;
+
+ var currentTime = (float)(_timing.CurTime - distortion.CreationTime - _timeCompensation).TotalSeconds;
+ currentTime = Math.Clamp(currentTime, 0.1f, float.MaxValue);
+
+ _positions[_count] = tempCoords;
+ _falloffPower[_count] = distortion.FalloffPower ?? 20f; // Sunrise edit - фолбек
+ _sharpness[_count] = distortion.Sharpness;
+ _width[_count] = distortion.Width ?? 0.8f;
+ _times[_count] = currentTime;
+ _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("time", _times);
+ _shader?.SetParameter("SCREEN_TEXTURE", ScreenTexture);
+
+ var worldHandle = args.WorldHandle;
+ worldHandle.UseShader(_shader);
+ worldHandle.DrawRect(args.WorldBounds, Color.White);
+ worldHandle.UseShader(null);
+ }
+}
diff --git a/Content.Client/_RMC14/Xenonids/Screech/RMCXenoScreechShockWaveOverlay.cs b/Content.Client/_RMC14/Xenonids/Screech/RMCXenoScreechShockWaveOverlay.cs
new file mode 100644
index 0000000000..daaa8fe312
--- /dev/null
+++ b/Content.Client/_RMC14/Xenonids/Screech/RMCXenoScreechShockWaveOverlay.cs
@@ -0,0 +1,77 @@
+using System.Numerics;
+using Content.Shared._RMC14.Xenonids.Screech;
+using Robust.Client.Graphics;
+using Robust.Shared.Enums;
+using Robust.Shared.Prototypes;
+
+namespace Content.Client._RMC14.Xenonids.Screech;
+
+public sealed class RMCXenoScreechShockWaveOverlay : 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;
+
+ public RMCXenoScreechShockWaveOverlay()
+ {
+ IoCManager.InjectDependencies(this);
+ _shader = _prototypeManager.Index("RMCXenoScreechShockWave").Instance().Duplicate();
+ }
+
+ private Vector2 _position;
+ private float _waveStrength;
+ private float _waveSpeed;
+ private float _downScale;
+ 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();
+
+ if (query.MoveNext(out var uid, out var distortion, out var xform))
+ {
+ if (xform.MapID != args.MapId)
+ return false;
+
+ 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;
+
+ _position = tempCoords;
+ _waveStrength = distortion.WaveStrength;
+ _waveSpeed = distortion.WaveSpeed;
+ _downScale = distortion.DownScale;
+
+ return true;
+ }
+
+ return false;
+ }
+
+ protected override void Draw(in OverlayDrawArgs args)
+ {
+ if (ScreenTexture == null || args.Viewport.Eye == null)
+ return;
+
+ _shader?.SetParameter("position", _position);
+ _shader?.SetParameter("waveSpeed", _waveSpeed);
+ _shader?.SetParameter("downScale", _downScale);
+ _shader?.SetParameter("waveStrength", _waveStrength);
+ _shader?.SetParameter("SCREEN_TEXTURE", ScreenTexture);
+
+ var worldHandle = args.WorldHandle;
+ worldHandle.UseShader(_shader);
+ worldHandle.DrawRect(args.WorldBounds, Color.White);
+ worldHandle.UseShader(null);
+ }
+}
diff --git a/Content.Client/_RMC14/_Sunrise/Explosion/ClientExplosionShockWaveSystem.cs b/Content.Client/_RMC14/_Sunrise/Explosion/ClientExplosionShockWaveSystem.cs
new file mode 100644
index 0000000000..063ca10db2
--- /dev/null
+++ b/Content.Client/_RMC14/_Sunrise/Explosion/ClientExplosionShockWaveSystem.cs
@@ -0,0 +1,21 @@
+using Content.Shared._RMC14.Explosion.Components;
+using Robust.Shared.Timing;
+
+namespace Content.Client._RMC14._Sunrise.Explosion;
+
+public sealed class ClientExplosionShockWaveSystem : EntitySystem
+{
+ [Dependency] private readonly IGameTiming _timing = default!;
+
+ public override void Initialize()
+ {
+ base.Initialize();
+
+ SubscribeLocalEvent(OnInit);
+ }
+
+ private void OnInit(Entity ent, ref ComponentInit args)
+ {
+ ent.Comp.CreationTime = _timing.CurTime;
+ }
+}
diff --git a/Content.Client/_RMC14/_Sunrise/Explosion/RMCExplosionSystem.cs b/Content.Client/_RMC14/_Sunrise/Explosion/RMCExplosionSystem.cs
new file mode 100644
index 0000000000..f2b47ac5c8
--- /dev/null
+++ b/Content.Client/_RMC14/_Sunrise/Explosion/RMCExplosionSystem.cs
@@ -0,0 +1,82 @@
+using System.Numerics;
+using Content.Shared._RMC14.Explosion;
+using Robust.Client.Animations;
+using Robust.Client.GameObjects;
+using Robust.Shared.Animations;
+using Robust.Shared.Random;
+
+namespace Content.Client._RMC14._Sunrise.Explosion;
+
+// Омг это же партикл систем за 1$
+public sealed class RMCExplosionSystem : SharedRMCExplosionSystem
+{
+ [Dependency] private readonly SpriteSystem _sprite = default!;
+ [Dependency] private readonly AnimationPlayerSystem _player = default!;
+ [Dependency] private readonly IRobustRandom _random = default!;
+
+ private const string SmokeTrack = "smoke-animation";
+ private const string ExplosionTrack = "explosion-animation";
+
+ public override void Initialize()
+ {
+ base.Initialize();
+
+ SubscribeLocalEvent(OnSmokeStartup);
+ SubscribeLocalEvent(OnExplosionStartup);
+ }
+
+ private void OnSmokeStartup(Entity ent, ref ComponentStartup args)
+ {
+ if (!TryComp(ent, out var sprite))
+ return;
+
+ var targetX = 2f + _random.NextFloat(-ExplosionSmokeEffectComponent.Variation, ExplosionSmokeEffectComponent.Variation);
+ var targetY = 2f + _random.NextFloat(-ExplosionSmokeEffectComponent.Variation, ExplosionSmokeEffectComponent.Variation);
+
+ var animation = new Animation()
+ {
+ Length = TimeSpan.FromSeconds(ent.Comp.LifeTime),
+ AnimationTracks =
+ {
+ new AnimationTrackComponentProperty()
+ {
+ Property = nameof(SpriteComponent.Offset),
+ ComponentType = typeof(SpriteComponent),
+ InterpolationMode = AnimationInterpolationMode.Linear,
+ KeyFrames =
+ {
+ new AnimationTrackProperty.KeyFrame(sprite.Offset, 0f),
+ new AnimationTrackProperty.KeyFrame(new Vector2(targetX, targetY), ent.Comp.LifeTime),
+ },
+ },
+ new AnimationTrackComponentProperty()
+ {
+ Property = nameof(SpriteComponent.Color),
+ ComponentType = typeof(SpriteComponent),
+ InterpolationMode = AnimationInterpolationMode.Linear,
+ KeyFrames =
+ {
+ new AnimationTrackProperty.KeyFrame(sprite.Color, 0f),
+ new AnimationTrackProperty.KeyFrame(GetTransparentColor(sprite.Color), ent.Comp.LifeTime),
+ },
+ },
+ },
+ };
+
+ _player.Play(ent, animation, SmokeTrack);
+ }
+
+ private void OnExplosionStartup(Entity ent, ref ComponentStartup args)
+ {
+ if (!TryComp(ent, out var sprite))
+ return;
+
+ sprite.Scale = new Vector2(ent.Comp.SizeModifier);
+ _sprite.SetAutoAnimateSync(sprite, ent.Comp.LifeTime);
+ }
+
+ private static Color GetTransparentColor(Color color)
+ {
+ return new Color(color.R, color.G, color.B, 0f);
+ }
+}
diff --git a/Content.Server/Changeling/ChangelingSystem.Abilities.cs b/Content.Server/Changeling/ChangelingSystem.Abilities.cs
index ec25d1127b..a1fa1a904e 100644
--- a/Content.Server/Changeling/ChangelingSystem.Abilities.cs
+++ b/Content.Server/Changeling/ChangelingSystem.Abilities.cs
@@ -20,6 +20,8 @@ using Content.Shared.Stealth.Components;
using Content.Shared.Damage.Components;
using Content.Server.Radio.Components;
using Content.Shared._Sunrise.CollectiveMind;
+using Content.Shared._RMC14.Xenonids.Screech;
+using Content.Shared.Coordinates;
namespace Content.Server.Changeling;
@@ -263,6 +265,10 @@ public sealed partial class ChangelingSystem : EntitySystem
_popup.PopupEntity(Loc.GetString("changeling-stasis-exit"), uid, uid);
+ // Sunrise edit start
+ StartScreech(uid);
+ // Sunrise edit end
+
comp.IsInStasis = false;
args.Handled = true;
@@ -637,4 +643,17 @@ public sealed partial class ChangelingSystem : EntitySystem
}
#endregion
+
+ // Sunrise edit start
+ private void StartScreech(EntityUid uid, XenoScreechComponent? component = null, bool playSound = true)
+ {
+ if (!Resolve(uid, ref component))
+ return;
+
+ if (playSound)
+ _audio.PlayPvs(component.Sound, uid);
+
+ SpawnAttachedTo(component.Effect, uid.ToCoordinates());
+ }
+ // Sunrise edit end
}
diff --git a/Content.Server/Changeling/ChangelingSystem.cs b/Content.Server/Changeling/ChangelingSystem.cs
index 2fdd9a1d27..bec0bfc385 100644
--- a/Content.Server/Changeling/ChangelingSystem.cs
+++ b/Content.Server/Changeling/ChangelingSystem.cs
@@ -55,6 +55,7 @@ using Content.Shared.Mobs.Components;
using Content.Server.Stunnable;
using Content.Shared.Jittering;
using System.Linq;
+using Content.Shared._RMC14.Xenonids.Screech;
using Content.Shared.Forensics.Components;
using Content.Shared.Radio;
@@ -266,6 +267,10 @@ public sealed partial class ChangelingSystem : EntitySystem
{
_audio.PlayPvs(comp.ShriekSound, uid);
+ // Sunrise edit start
+ StartScreech(uid, playSound: false);
+ // Sunrise edit end
+
var center = Transform(uid).MapPosition;
var gamers = Filter.Empty();
gamers.AddInRange(center, comp.ShriekPower, _player, EntityManager);
@@ -607,6 +612,10 @@ public sealed partial class ChangelingSystem : EntitySystem
RemComp(uid);
EnsureComp(uid);
+ // Sunrise edit start
+ EnsureComp(uid);
+ // Sunrise edit end
+
// add actions
foreach (var actionId in comp.BaseChangelingActions)
_actions.AddAction(uid, actionId);
diff --git a/Content.Server/EntityEffects/Effects/ExplosionReactionEffect.cs b/Content.Server/EntityEffects/Effects/ExplosionReactionEffect.cs
index 24c58898c6..0a378ef230 100644
--- a/Content.Server/EntityEffects/Effects/ExplosionReactionEffect.cs
+++ b/Content.Server/EntityEffects/Effects/ExplosionReactionEffect.cs
@@ -5,6 +5,7 @@ using Content.Shared.Explosion;
using Robust.Shared.Prototypes;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
using System.Text.Json.Serialization;
+using Content.Shared._Sunrise.Explosion;
namespace Content.Server.EntityEffects.Effects;
@@ -45,7 +46,7 @@ public sealed partial class ExplosionReactionEffect : EntityEffect
///
[DataField]
public float IntensityPerUnit = 1;
-
+
///
/// Factor used to scale the explosion intensity when calculating tile break chances. Allows for stronger
/// explosives that don't space tiles, without having to create a new explosion-type prototype.
@@ -68,6 +69,11 @@ public sealed partial class ExplosionReactionEffect : EntityEffect
intensity = MathF.Min((float) reagentArgs.Quantity * IntensityPerUnit, MaxTotalIntensity);
}
+ // Sunrise edit start
+ args.EntityManager.System()
+ .TryAddExplosionEffect(args.TargetEntity, ExplosionType);
+ // Sunrise edit end
+
args.EntityManager.System()
.QueueExplosion(
args.TargetEntity,
diff --git a/Content.Server/Explosion/EntitySystems/ExplosionSystem.Visuals.cs b/Content.Server/Explosion/EntitySystems/ExplosionSystem.Visuals.cs
index 57323e4de7..f2bd914f61 100644
--- a/Content.Server/Explosion/EntitySystems/ExplosionSystem.Visuals.cs
+++ b/Content.Server/Explosion/EntitySystems/ExplosionSystem.Visuals.cs
@@ -37,9 +37,15 @@ public sealed partial class ExplosionSystem
///
/// Constructor for the shared using the server-exclusive explosion classes.
///
- private EntityUid CreateExplosionVisualEntity(MapCoordinates epicenter, string prototype, Matrix3x2 spaceMatrix, ExplosionSpaceTileFlood? spaceData, IEnumerable gridData, List iterationIntensity)
+ private EntityUid CreateExplosionVisualEntity(MapCoordinates epicenter, ExplosionPrototype prototype, Matrix3x2 spaceMatrix, ExplosionSpaceTileFlood? spaceData, IEnumerable gridData, List iterationIntensity)
{
var explosionEntity = Spawn(null, MapCoordinates.Nullspace);
+
+ // Sunrise added start
+ if (prototype.EffectType != ExplosionEffectType.Standard)
+ return explosionEntity;
+ // Sunrise added end
+
var comp = AddComp(explosionEntity);
foreach (var grid in gridData)
@@ -49,7 +55,7 @@ public sealed partial class ExplosionSystem
comp.SpaceTiles = spaceData?.TileLists;
comp.Epicenter = epicenter;
- comp.ExplosionType = prototype;
+ comp.ExplosionType = prototype.ID; // Sunrise edit
comp.Intensity = iterationIntensity;
comp.SpaceMatrix = spaceMatrix;
comp.SpaceTileSize = spaceData?.TileSize ?? DefaultTileSize;
diff --git a/Content.Server/Explosion/EntitySystems/ExplosionSystem.cs b/Content.Server/Explosion/EntitySystems/ExplosionSystem.cs
index beffbd6d68..43cca7a962 100644
--- a/Content.Server/Explosion/EntitySystems/ExplosionSystem.cs
+++ b/Content.Server/Explosion/EntitySystems/ExplosionSystem.cs
@@ -5,6 +5,7 @@ using Content.Server.Atmos.Components;
using Content.Server.Chat.Managers;
using Content.Server.NodeContainer.EntitySystems;
using Content.Server.NPC.Pathfinding;
+using Content.Shared._RMC14.Explosion;
using Content.Shared.Camera;
using Content.Shared.CCVar;
using Content.Shared.Damage;
@@ -319,6 +320,14 @@ public sealed partial class ExplosionSystem : SharedExplosionSystem
};
_explosionQueue.Enqueue(boom);
_queuedExplosions.Add(boom);
+
+ // Sunrise added start
+ if (!cause.HasValue)
+ return;
+
+ var ev = new CMExplosiveTriggeredEvent();
+ RaiseLocalEvent(cause.Value, ref ev);
+ // Sunrise added end
}
///
@@ -339,7 +348,8 @@ public sealed partial class ExplosionSystem : SharedExplosionSystem
var (area, iterationIntensity, spaceData, gridData, spaceMatrix) = results.Value;
- var visualEnt = CreateExplosionVisualEntity(pos, queued.Proto.ID, spaceMatrix, spaceData, gridData.Values, iterationIntensity);
+ // Sunrise edit - queued.Proto.ID -> queued.Proto
+ var visualEnt = CreateExplosionVisualEntity(pos, queued.Proto, spaceMatrix, spaceData, gridData.Values, iterationIntensity);
// camera shake
CameraShake(iterationIntensity.Count * 4f, pos, queued.TotalIntensity);
diff --git a/Content.Server/_Sunrise/Explosions/RMCExplosionSystem.cs b/Content.Server/_Sunrise/Explosions/RMCExplosionSystem.cs
new file mode 100644
index 0000000000..090c0bc683
--- /dev/null
+++ b/Content.Server/_Sunrise/Explosions/RMCExplosionSystem.cs
@@ -0,0 +1,8 @@
+using Content.Shared._RMC14.Explosion;
+
+namespace Content.Server._Sunrise.Explosions;
+
+public sealed class RMCExplosionSystem : SharedRMCExplosionSystem
+{
+
+}
diff --git a/Content.Shared/Explosion/ExplosionPrototype.cs b/Content.Shared/Explosion/ExplosionPrototype.cs
index df2fb18360..cbc6836249 100644
--- a/Content.Shared/Explosion/ExplosionPrototype.cs
+++ b/Content.Shared/Explosion/ExplosionPrototype.cs
@@ -1,6 +1,7 @@
using Content.Shared.Damage;
using Robust.Shared.Audio;
using Robust.Shared.Prototypes;
+using Robust.Shared.Serialization;
using Robust.Shared.Utility;
namespace Content.Shared.Explosion;
@@ -110,6 +111,11 @@ public sealed partial class ExplosionPrototype : IPrototype
[DataField("fireStates")]
public int FireStates = 3;
+ // Sunrise added start
+ [DataField]
+ public ExplosionEffectType EffectType = ExplosionEffectType.Fancy;
+ // Sunrise added end
+
///
/// Basic function for linear interpolation of the _tileBreakChance and _tileBreakIntensity arrays
///
@@ -133,3 +139,11 @@ public sealed partial class ExplosionPrototype : IPrototype
return _tileBreakChance[i - 1] + slope * (intensity - _tileBreakIntensity[i - 1]);
}
}
+
+[Serializable, NetSerializable]
+public enum ExplosionEffectType : byte
+{
+ Standard,
+ Fancy,
+}
+
diff --git a/Content.Shared/Projectiles/SharedProjectileSystem.cs b/Content.Shared/Projectiles/SharedProjectileSystem.cs
index be86fd1af2..5ae7a80c19 100644
--- a/Content.Shared/Projectiles/SharedProjectileSystem.cs
+++ b/Content.Shared/Projectiles/SharedProjectileSystem.cs
@@ -193,9 +193,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 426fe84c1c..4b7af890f7 100644
--- a/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.cs
+++ b/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.cs
@@ -484,7 +484,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;
// Sunrise-Start
diff --git a/Content.Shared/_RMC14/Explosion/CMExplosionEffectComponent.cs b/Content.Shared/_RMC14/Explosion/CMExplosionEffectComponent.cs
new file mode 100644
index 0000000000..dcd75d6264
--- /dev/null
+++ b/Content.Shared/_RMC14/Explosion/CMExplosionEffectComponent.cs
@@ -0,0 +1,44 @@
+using Robust.Shared.GameStates;
+using Robust.Shared.Prototypes;
+
+namespace Content.Shared._RMC14.Explosion;
+
+[RegisterComponent, NetworkedComponent]
+[Access(typeof(SharedRMCExplosionSystem))]
+public sealed partial class CMExplosionEffectComponent : Component
+{
+ [DataField]
+ public EntProtoId? Explosion = "CMExplosionEffectGrenade";
+
+ [DataField]
+ public EntProtoId? ShockWave = "RMCExplosionEffectGrenadeShockWave";
+
+ // Sunrise added
+ [DataField]
+ public EntProtoId? Smoke = "ExplosionEffectSmoke";
+ // Sunrise added
+}
+
+// Sunrise added start
+[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
+public sealed partial class ExplosionSmokeEffectComponent : Component
+{
+ public const float AnimationDuration = 2.5f;
+ public const float Variation = 1f;
+
+ [DataField, AutoNetworkedField]
+ public float LifeTime = AnimationDuration;
+}
+
+[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
+public sealed partial class ExplosionEffectComponent : Component
+{
+ public const float AnimationDuration = 2.5f;
+
+ [DataField, AutoNetworkedField]
+ public float LifeTime = AnimationDuration;
+
+ [DataField, AutoNetworkedField]
+ public float SizeModifier = 2f;
+}
+// Sunrise added end
diff --git a/Content.Shared/_RMC14/Explosion/RMCExplosionShockWaveComponent.cs b/Content.Shared/_RMC14/Explosion/RMCExplosionShockWaveComponent.cs
new file mode 100644
index 0000000000..c8776a1bfc
--- /dev/null
+++ b/Content.Shared/_RMC14/Explosion/RMCExplosionShockWaveComponent.cs
@@ -0,0 +1,30 @@
+using Robust.Shared.GameStates;
+
+namespace Content.Shared._RMC14.Explosion.Components
+{
+ [RegisterComponent, NetworkedComponent]
+ [AutoGenerateComponentState]
+ public sealed partial class RMCExplosionShockWaveComponent : Component
+ {
+ ///
+ /// The rate at which the wave fades, lower values means it's active for longer.
+ ///
+ [DataField, AutoNetworkedField, ViewVariables(VVAccess.ReadWrite)]
+ public float? FalloffPower = 20f;
+
+ ///
+ /// How sharp the wave distortion is. Higher values make the wave more pronounced.
+ ///
+ [DataField, AutoNetworkedField, ViewVariables(VVAccess.ReadWrite)]
+ public float Sharpness = 5.0f;
+
+ ///
+ /// Width of the wave.
+ ///
+ [DataField, AutoNetworkedField, ViewVariables(VVAccess.ReadWrite)]
+ public float? Width = 0.8f;
+
+ [DataField]
+ public TimeSpan CreationTime;
+ }
+}
diff --git a/Content.Shared/_RMC14/Explosion/SharedRMCExplosionSystem.cs b/Content.Shared/_RMC14/Explosion/SharedRMCExplosionSystem.cs
new file mode 100644
index 0000000000..31cc1d5181
--- /dev/null
+++ b/Content.Shared/_RMC14/Explosion/SharedRMCExplosionSystem.cs
@@ -0,0 +1,122 @@
+using Content.Shared._RMC14.Explosion.Components;
+using Content.Shared._Sunrise.Helpers;
+using Content.Shared.Explosion.Components;
+using Robust.Shared.Prototypes;
+using Robust.Shared.Random;
+using Robust.Shared.Spawners;
+
+namespace Content.Shared._RMC14.Explosion;
+
+public abstract class SharedRMCExplosionSystem : EntitySystem
+{
+ [Dependency] private readonly IRobustRandom _random = default!;
+
+ private const float MinSmokeCountPer100 = 12f;
+ private const float MaxSmokeCountPer100 = 17f;
+
+ private const float SmokeSpawnRadiusPer100 = 2f;
+
+ public override void Initialize()
+ {
+ SubscribeLocalEvent(OnExplosionEffectTriggered);
+ }
+
+ private void OnExplosionEffectTriggered(Entity ent, ref CMExplosiveTriggeredEvent args)
+ {
+ DoEffect(ent);
+ }
+
+ // Sunrise edit start
+ public void DoEffect(Entity ent)
+ {
+ if (!TryComp(ent, out var explosionComponent))
+ return;
+
+ if (ent.Comp.ShockWave is { } shockwave)
+ {
+ var wave = SpawnNextToOrDrop(shockwave, ent);
+ ModifyShockwave(wave, explosionComponent);
+ }
+
+ if (ent.Comp.Explosion is { } explosion)
+ {
+ var explosionEntity = SpawnNextToOrDrop(explosion, ent);
+ CreateFancyExplosionEffect(explosionEntity, explosionComponent);
+ }
+
+ if (ent.Comp.Smoke is { } smoke)
+ CreateFancySmoke(ent, explosionComponent, smoke);
+ }
+
+ private void ModifyShockwave(EntityUid wave, ExplosiveComponent explosionComponent)
+ {
+ // Дальше идут просто числа, которые я придумал особо не думая, мб нужно подумать
+ // Но идея в том, чтобы чем сильнее взрыв, тем сильнее эффект и наоборот
+ // TODO: Реализовать радиус действия волны и убрать стандартные значения в компоненте
+
+ if (TryComp(wave, out var waveComponent))
+ {
+ waveComponent.FalloffPower ??= explosionComponent.TotalIntensity / 4f;
+ waveComponent.Width ??= Math.Clamp(explosionComponent.TotalIntensity / 200f, 0.1f, 0.5f);
+
+ Dirty(wave, waveComponent);
+ }
+
+ if (TryComp(wave, out var timedDespawnComponent))
+ timedDespawnComponent.Lifetime = Math.Clamp(explosionComponent.TotalIntensity / 50f, 0.1f, 0.8f);
+ }
+
+ private void CreateFancyExplosionEffect(EntityUid explosionEntity, ExplosiveComponent explosionComponent)
+ {
+ if (!TryComp(explosionEntity, out var timedDespawnComponent))
+ return;
+
+ if (!TryComp(explosionEntity, out var explosionEffectComponent))
+ return;
+
+ var sizeModifier = Math.Clamp(explosionComponent.TotalIntensity / 50f, 1f, 12f);
+ explosionEffectComponent.SizeModifier = sizeModifier;
+ explosionEffectComponent.LifeTime = timedDespawnComponent.Lifetime;
+
+ Dirty(explosionEntity, explosionEffectComponent);
+ }
+
+ private void CreateFancySmoke(Entity ent, ExplosiveComponent explosionComponent, EntProtoId smokeId)
+ {
+ var modifier = explosionComponent.TotalIntensity / 100f;
+
+ var smokeCount = _random.NextFloat(MinSmokeCountPer100 * modifier, MaxSmokeCountPer100 * modifier);
+ var coords = Transform(ent).Coordinates;
+ var modifiedRadius = Math.Clamp(SmokeSpawnRadiusPer100 * modifier, 2f, 10f);
+
+ for (var i = 0; i < smokeCount; i++)
+ {
+ var smoke = Spawn(smokeId, coords.GetRandomInRadius(modifiedRadius));
+
+ if (!TryComp(smoke, out var timedDespawnComponent))
+ continue;
+
+ if (!TryComp(smoke, out var explosionSmokeEffectComponent))
+ continue;
+
+ timedDespawnComponent.Lifetime = ExplosionSmokeEffectComponent.AnimationDuration + _random.NextFloat(-ExplosionSmokeEffectComponent.Variation, ExplosionSmokeEffectComponent.Variation);
+
+ // Это нужно, чтобы анимация на клиенте знала, когда мы решили заканчиваться
+ explosionSmokeEffectComponent.LifeTime = timedDespawnComponent.Lifetime;
+ Dirty(smoke, explosionSmokeEffectComponent);
+ }
+ }
+ // Sunrise edit end
+
+ public void TryDoEffect(Entity ent)
+ {
+ if (!Resolve(ent, ref ent.Comp, false))
+ return;
+
+ DoEffect((ent, ent.Comp));
+ }
+}
+
+[ByRefEvent]
+public readonly record struct CMExplosiveTriggeredEvent;
+
diff --git a/Content.Shared/_RMC14/Xenonids/Screech/RMCXenoScreechShockWaveComponent.cs b/Content.Shared/_RMC14/Xenonids/Screech/RMCXenoScreechShockWaveComponent.cs
new file mode 100644
index 0000000000..f78caa9d12
--- /dev/null
+++ b/Content.Shared/_RMC14/Xenonids/Screech/RMCXenoScreechShockWaveComponent.cs
@@ -0,0 +1,27 @@
+using Robust.Shared.GameStates;
+
+namespace Content.Shared._RMC14.Xenonids.Screech;
+
+[RegisterComponent, NetworkedComponent]
+[AutoGenerateComponentState]
+public sealed partial class RMCXenoScreechShockWaveComponent : Component
+{
+ ///
+ /// The speed of each individual wave from the center axis.
+ ///
+ [DataField, AutoNetworkedField, ViewVariables(VVAccess.ReadWrite)]
+ public float WaveSpeed = 15.3f;
+
+ ///
+ /// The size of each wave in its width and distortion effect
+ ///
+ [DataField, AutoNetworkedField, ViewVariables(VVAccess.ReadWrite)]
+ public float WaveStrength = 1.0f;
+
+ ///
+ /// The scale of the effect, lower number means a larger total area while smaller numbers downscale it and reduce the effected area.
+ ///
+ [DataField, AutoNetworkedField, ViewVariables(VVAccess.ReadWrite)]
+ public float DownScale = 1f;
+}
+
diff --git a/Content.Shared/_RMC14/Xenonids/Screech/XenoScreechComponent.cs b/Content.Shared/_RMC14/Xenonids/Screech/XenoScreechComponent.cs
new file mode 100644
index 0000000000..08905ed73c
--- /dev/null
+++ b/Content.Shared/_RMC14/Xenonids/Screech/XenoScreechComponent.cs
@@ -0,0 +1,15 @@
+using Robust.Shared.Audio;
+using Robust.Shared.GameStates;
+using Robust.Shared.Prototypes;
+
+namespace Content.Shared._RMC14.Xenonids.Screech;
+
+[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
+public sealed partial class XenoScreechComponent : Component
+{
+ [DataField, AutoNetworkedField]
+ public EntProtoId Effect = "CMEffectScreechShort";
+
+ [DataField, AutoNetworkedField]
+ public SoundSpecifier Sound = new SoundPathSpecifier("/Audio/_RMC14/Xeno/alien_queen_screech.ogg", AudioParams.Default.WithVolume(-7).WithPlayOffset(1.4f));
+}
diff --git a/Content.Shared/_Sunrise/Explosion/SharedSunriseExplosionSystem.cs b/Content.Shared/_Sunrise/Explosion/SharedSunriseExplosionSystem.cs
new file mode 100644
index 0000000000..8a79e68a07
--- /dev/null
+++ b/Content.Shared/_Sunrise/Explosion/SharedSunriseExplosionSystem.cs
@@ -0,0 +1,35 @@
+using Content.Shared._RMC14.Explosion;
+using Content.Shared.Explosion;
+using Content.Shared.Explosion.Components;
+using Robust.Shared.Prototypes;
+
+namespace Content.Shared._Sunrise.Explosion;
+
+public sealed class SharedSunriseExplosionSystem : EntitySystem
+{
+ [Dependency] private readonly IPrototypeManager _prototype = default!;
+
+ public override void Initialize()
+ {
+ base.Initialize();
+
+ SubscribeLocalEvent(OnInit);
+ }
+
+ private void OnInit(Entity ent, ref ComponentInit args)
+ {
+ TryAddExplosionEffect(ent, ent.Comp.ExplosionType);
+ }
+
+ public bool TryAddExplosionEffect(EntityUid uid, string explosionType)
+ {
+ if (!_prototype.TryIndex(explosionType, out var explosionPrototype))
+ return false;
+
+ if (explosionPrototype.EffectType != ExplosionEffectType.Fancy)
+ return false;
+
+ EnsureComp(uid);
+ return true;
+ }
+}
diff --git a/Content.Shared/_Sunrise/Helpers/CoordinatesHelpers.cs b/Content.Shared/_Sunrise/Helpers/CoordinatesHelpers.cs
new file mode 100644
index 0000000000..fe97f3d28e
--- /dev/null
+++ b/Content.Shared/_Sunrise/Helpers/CoordinatesHelpers.cs
@@ -0,0 +1,39 @@
+using System.Numerics;
+using Robust.Shared.Map;
+using Robust.Shared.Random;
+
+namespace Content.Shared._Sunrise.Helpers;
+
+public static class EntityCoordinatesExtensions
+{
+ ///
+ /// Генерирует случайные координаты в заданном радиусе от исходных координат.
+ ///
+ /// Исходные координаты.
+ /// Радиус для генерации случайной точки.
+ /// Опциональный экземпляр Random для контроля генерации.
+ /// Новые координаты в пределах радиуса.
+ public static EntityCoordinates GetRandomInRadius(this EntityCoordinates origin, float radius, IRobustRandom? rand = null)
+ {
+ if (radius < 0)
+ throw new ArgumentOutOfRangeException(nameof(radius), "Radius cannot be negative.");
+
+ if (radius == 0)
+ return origin;
+
+ rand ??= IoCManager.Resolve();
+
+ // Генерируем угол и расстояние.
+ var angle = rand.NextDouble() * Math.Tau; // 0..2π
+ var distance = Math.Sqrt(rand.NextDouble()) * radius; // Равномерное распределение
+
+ // Преобразуем в декартовы координаты.
+ var x = (float)(distance * Math.Cos(angle));
+ var y = (float)(distance * Math.Sin(angle));
+
+ // Смещаем относительно исходной позиции.
+ var newPosition = origin.Position + new Vector2(x, y);
+
+ return new EntityCoordinates(origin.EntityId, newPosition);
+ }
+}
diff --git a/Resources/Audio/_RMC14/Xeno/alien_queen_screech.ogg b/Resources/Audio/_RMC14/Xeno/alien_queen_screech.ogg
new file mode 100644
index 0000000000..1fb9ccd531
Binary files /dev/null and b/Resources/Audio/_RMC14/Xeno/alien_queen_screech.ogg differ
diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Bombs/pipebomb.yml b/Resources/Prototypes/Entities/Objects/Weapons/Bombs/pipebomb.yml
index 5fb6829ac2..b60fad9773 100644
--- a/Resources/Prototypes/Entities/Objects/Weapons/Bombs/pipebomb.yml
+++ b/Resources/Prototypes/Entities/Objects/Weapons/Bombs/pipebomb.yml
@@ -59,4 +59,4 @@
- type: Construction
graph: PipeBomb
node: cable
- defaultTarget: pipebomb
\ No newline at end of file
+ defaultTarget: pipebomb
diff --git a/Resources/Prototypes/Entities/Structures/Machines/nuke.yml b/Resources/Prototypes/Entities/Structures/Machines/nuke.yml
index 08cf8dd300..ce7ebc5cb2 100644
--- a/Resources/Prototypes/Entities/Structures/Machines/nuke.yml
+++ b/Resources/Prototypes/Entities/Structures/Machines/nuke.yml
@@ -92,7 +92,7 @@
enabled: false
- type: NukeLabel
- type: Nuke
- explosionType: Default
+ explosionType: DefaultStandardEffect # Sunrise edit
maxIntensity: 100
intensitySlope: 5
totalIntensity: 5000000
diff --git a/Resources/Prototypes/_RMC14/Effects/explosive.yml b/Resources/Prototypes/_RMC14/Effects/explosive.yml
new file mode 100644
index 0000000000..45c6292916
--- /dev/null
+++ b/Resources/Prototypes/_RMC14/Effects/explosive.yml
@@ -0,0 +1,16 @@
+- type: entity
+ id: CMExplosionEffectGrenade
+ components:
+ - type: Sprite
+ sprite: _RMC14/Effects/grenade_explosion.rsi
+ state: grenade
+ - type: ExplosionEffect # Sunrise
+ - type: TimedDespawn
+ lifetime: 0.6
+
+- type: entity
+ id: RMCExplosionEffectGrenadeShockWave
+ components:
+ - type: TimedDespawn
+ lifetime: 0.5
+ - type: RMCExplosionShockWave
diff --git a/Resources/Prototypes/_RMC14/Effects/screech.yml b/Resources/Prototypes/_RMC14/Effects/screech.yml
new file mode 100644
index 0000000000..271ad70b89
--- /dev/null
+++ b/Resources/Prototypes/_RMC14/Effects/screech.yml
@@ -0,0 +1,35 @@
+- type: entity
+ # Just fades out with no movement animation
+ id: CMEffectScreech
+ categories: [ HideSpawnMenu ]
+ components:
+ - type: TimedDespawn
+ lifetime: 3.2
+ - type: RMCXenoScreechShockWave
+ - type: Sprite
+ sprite: _RMC14/Effects/xeno_screech.rsi
+ noRot: true
+ state: screech
+ drawdepth: Effects
+ - type: EffectVisuals
+ - type: Tag
+ tags:
+ - HideContextMenu
+
+- type: entity
+ # Just fades out with no movement animation
+ id: CMEffectScreechShort
+ categories: [ HideSpawnMenu ]
+ components:
+ - type: TimedDespawn
+ lifetime: 1.4
+ - type: RMCXenoScreechShockWave
+ - type: Sprite
+ sprite: _RMC14/Effects/xeno_screech.rsi
+ noRot: true
+ state: screech
+ drawdepth: Effects
+ - type: EffectVisuals
+ - type: Tag
+ tags:
+ - HideContextMenu
diff --git a/Resources/Prototypes/_RMC14/Shaders/shaders.yml b/Resources/Prototypes/_RMC14/Shaders/shaders.yml
new file mode 100644
index 0000000000..c2a368e1e7
--- /dev/null
+++ b/Resources/Prototypes/_RMC14/Shaders/shaders.yml
@@ -0,0 +1,9 @@
+- type: shader
+ id: RMCShockWave
+ kind: source
+ path: "/Textures/_RMC14/Shaders/shock_wave.swsl"
+
+- type: shader
+ id: RMCXenoScreechShockWave
+ kind: source
+ path: "/Textures/_RMC14/Shaders/screech_shock_wave.swsl"
\ No newline at end of file
diff --git a/Resources/Prototypes/_Sunrise/Entities/Effects/explosion.yml b/Resources/Prototypes/_Sunrise/Entities/Effects/explosion.yml
new file mode 100644
index 0000000000..e509a4eb59
--- /dev/null
+++ b/Resources/Prototypes/_Sunrise/Entities/Effects/explosion.yml
@@ -0,0 +1,11 @@
+- type: entity
+ id: ExplosionEffectSmoke
+ components:
+ - type: Sprite
+ drawdepth: Effects
+ sprite: _Sunrise/Effects/explosion_smoke.rsi
+ state: smoke
+ shader: unshaded
+ - type: ExplosionSmokeEffect
+ - type: TimedDespawn
+ lifetime: 5
diff --git a/Resources/Prototypes/_Sunrise/explosion.yml b/Resources/Prototypes/_Sunrise/explosion.yml
new file mode 100644
index 0000000000..a826a298a7
--- /dev/null
+++ b/Resources/Prototypes/_Sunrise/explosion.yml
@@ -0,0 +1,14 @@
+- type: explosion
+ id: DefaultStandardEffect
+ damagePerIntensity:
+ types:
+ Heat: 5
+ Blunt: 5
+ Piercing: 5
+ tileBreakChance: [0, 0.5, 1]
+ tileBreakIntensity: [0, 10, 30]
+ tileBreakRerollReduction: 20
+ lightColor: Orange
+ texturePath: /Textures/Effects/fire.rsi
+ fireStates: 3
+ effectType: Standard
diff --git a/Resources/Textures/_RMC14/Effects/grenade_explosion.rsi/grenade.png b/Resources/Textures/_RMC14/Effects/grenade_explosion.rsi/grenade.png
new file mode 100644
index 0000000000..d00b7283af
Binary files /dev/null and b/Resources/Textures/_RMC14/Effects/grenade_explosion.rsi/grenade.png differ
diff --git a/Resources/Textures/_RMC14/Effects/grenade_explosion.rsi/meta.json b/Resources/Textures/_RMC14/Effects/grenade_explosion.rsi/meta.json
new file mode 100644
index 0000000000..faef7a9557
--- /dev/null
+++ b/Resources/Textures/_RMC14/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/_RMC14/Effects/xeno_screech.rsi/meta.json b/Resources/Textures/_RMC14/Effects/xeno_screech.rsi/meta.json
new file mode 100644
index 0000000000..0d9e8d5bad
--- /dev/null
+++ b/Resources/Textures/_RMC14/Effects/xeno_screech.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/d5b119380250ea512db2a5319e36592c7f604250/icons/effects/xeno_screech.dmi",
+ "size": {
+ "x": 64,
+ "y": 64
+ },
+ "states": [
+ {
+ "name": "screech",
+ "delays": [
+ [
+ 0.2,
+ 0.2,
+ 0.2,
+ 0.2,
+ 0.2,
+ 0.2,
+ 0.2,
+ 0.2
+ ]
+ ]
+ }
+ ]
+}
\ No newline at end of file
diff --git a/Resources/Textures/_RMC14/Effects/xeno_screech.rsi/screech.png b/Resources/Textures/_RMC14/Effects/xeno_screech.rsi/screech.png
new file mode 100644
index 0000000000..e93bed0eab
Binary files /dev/null and b/Resources/Textures/_RMC14/Effects/xeno_screech.rsi/screech.png differ
diff --git a/Resources/Textures/_RMC14/Shaders/screech_shock_wave.swsl b/Resources/Textures/_RMC14/Shaders/screech_shock_wave.swsl
new file mode 100644
index 0000000000..28eeb23762
--- /dev/null
+++ b/Resources/Textures/_RMC14/Shaders/screech_shock_wave.swsl
@@ -0,0 +1,28 @@
+uniform sampler2D SCREEN_TEXTURE;
+uniform highp float waveStrength;
+uniform highp vec2 position;
+uniform highp float waveSpeed;
+uniform highp float downScale;
+
+void fragment()
+{
+ highp vec2 st = UV;
+ highp vec2 WaveCentre = position;
+ highp float ratio = SCREEN_PIXEL_SIZE.y / SCREEN_PIXEL_SIZE.x * 0.5;
+ WaveCentre.y *= ratio;
+ highp float dist = distance(vec2(st.x, st.y * ratio), WaveCentre) * downScale;
+ highp float val = dist;
+ highp float a = 3.0;
+ highp float cosFuns = cos(val * 20.0 - TIME * waveSpeed);
+ highp float powFuns = pow(val * 2.5, a);
+ highp float limtedPowFuns = 0.5 * pow(a / (a + powFuns), 2.0);
+ highp float finalRes = smoothstep(0.0, 1.0, limtedPowFuns * cosFuns) * waveStrength;
+ highp vec3 col = finalRes * vec3(1.0);
+ st = st * 2.0 - 1.0;
+ st *= 1.0 + finalRes * 0.1;
+ st = st * 0.5 + 0.5;
+ highp vec4 texCol = zTextureSpec(SCREEN_TEXTURE, st);
+ texCol += (texCol * finalRes) / (dist * 10.0);
+
+ COLOR = texCol;
+}
diff --git a/Resources/Textures/_RMC14/Shaders/shock_wave.swsl b/Resources/Textures/_RMC14/Shaders/shock_wave.swsl
new file mode 100644
index 0000000000..6e2ce1bc26
--- /dev/null
+++ b/Resources/Textures/_RMC14/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;
+uniform highp float[10] time;
+
+void fragment() {
+ highp vec2 coord = FRAGCOORD.xy * SCREEN_PIXEL_SIZE.xy;
+ highp float ratio = SCREEN_PIXEL_SIZE.y / SCREEN_PIXEL_SIZE.x * 0.6;
+
+ highp vec2 totalOffset = vec2(0.0);
+
+ 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)
+ );
+
+ highp float CurrentTime = time[i];
+
+ 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);
+
+ totalOffset += (DiffTexCoord * DiffTime) / (CurrentTime * Dist * falloffPower[i]);
+ }
+ }
+
+ coord += totalOffset;
+
+ highp vec4 Color = texture(SCREEN_TEXTURE, coord);
+ COLOR = Color;
+}
diff --git a/Resources/Textures/_Sunrise/Effects/explosion_smoke.rsi/dust.png b/Resources/Textures/_Sunrise/Effects/explosion_smoke.rsi/dust.png
new file mode 100644
index 0000000000..91101e019f
Binary files /dev/null and b/Resources/Textures/_Sunrise/Effects/explosion_smoke.rsi/dust.png differ
diff --git a/Resources/Textures/_Sunrise/Effects/explosion_smoke.rsi/fire.png b/Resources/Textures/_Sunrise/Effects/explosion_smoke.rsi/fire.png
new file mode 100644
index 0000000000..b8fd19b042
Binary files /dev/null and b/Resources/Textures/_Sunrise/Effects/explosion_smoke.rsi/fire.png differ
diff --git a/Resources/Textures/_Sunrise/Effects/explosion_smoke.rsi/gas.png b/Resources/Textures/_Sunrise/Effects/explosion_smoke.rsi/gas.png
new file mode 100644
index 0000000000..f8cae98ddf
Binary files /dev/null and b/Resources/Textures/_Sunrise/Effects/explosion_smoke.rsi/gas.png differ
diff --git a/Resources/Textures/_Sunrise/Effects/explosion_smoke.rsi/meta.json b/Resources/Textures/_Sunrise/Effects/explosion_smoke.rsi/meta.json
new file mode 100644
index 0000000000..f569eb2d7c
--- /dev/null
+++ b/Resources/Textures/_Sunrise/Effects/explosion_smoke.rsi/meta.json
@@ -0,0 +1,25 @@
+{
+ "version": 1,
+ "license": "CLA",
+ "copyright": "SUNRISE",
+ "size":
+ {
+ "x": 128,
+ "y": 128
+ },
+ "states":
+ [
+ {
+ "name": "smoke"
+ },
+ {
+ "name": "dust"
+ },
+ {
+ "name": "fire"
+ },
+ {
+ "name": "gas"
+ }
+ ]
+}
diff --git a/Resources/Textures/_Sunrise/Effects/explosion_smoke.rsi/smoke.png b/Resources/Textures/_Sunrise/Effects/explosion_smoke.rsi/smoke.png
new file mode 100644
index 0000000000..4d8da1a69e
Binary files /dev/null and b/Resources/Textures/_Sunrise/Effects/explosion_smoke.rsi/smoke.png differ